Merge "Refactor passwords/pins/patterns to byte[]"
diff --git a/Android.bp b/Android.bp
index 6288940..4b22f9e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -845,6 +845,7 @@
 // specified on the build command line.
 java_library {
     name: "framework-atb-backward-compatibility",
+    installable: true,
     srcs: [
         "core/java/android/content/pm/AndroidTestBaseUpdater.java",
     ],
@@ -1265,10 +1266,7 @@
         "test-base/src/**/*.java",
         ":opt-telephony-srcs",
         ":opt-net-voip-srcs",
-        ":openjdk_javadoc_files",
-        ":non_openjdk_javadoc_files",
-        ":android_icu4j_src_files_for_docs",
-        ":conscrypt_public_api_files",
+        ":core_public_api_files",
         ":updatable-media-srcs-without-aidls",
         "test-mock/src/**/*.java",
         "test-runner/src/**/*.java",
@@ -1327,10 +1325,7 @@
     srcs: [
         ":opt-telephony-srcs",
         ":opt-net-voip-srcs",
-        ":openjdk_javadoc_files",
-        ":non_openjdk_javadoc_files",
-        ":android_icu4j_src_files_for_docs",
-        ":conscrypt_public_api_files",
+        ":core_public_api_files",
         ":updatable-media-srcs-without-aidls",
     ],
     srcs_lib: "framework",
@@ -1514,7 +1509,7 @@
     ],
     proofread_file: "ds-docs-proofrerad.txt",
     args: framework_docs_only_args +
-        " -toroot / -samplegroup Admin " +
+        " -toroot / -yamlV2 -metalavaApiSince -samplegroup Admin " +
         " -samplegroup Background " +
         " -samplegroup Connectivity " +
         " -samplegroup Content " +
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
index e7fe235..e805ab9 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
@@ -71,7 +71,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         RenderNode node = RenderNode.create("LinearLayout", null);
         while (state.keepRunning()) {
-            node.startRecording(100, 100);
+            node.beginRecording(100, 100);
             node.endRecording();
         }
     }
@@ -86,7 +86,7 @@
 
         while (state.keepRunning()) {
             for (int i = 0; i < nodes.length; i++) {
-                nodes[i].startRecording(100, 100);
+                nodes[i].beginRecording(100, 100);
             }
             for (int i = nodes.length - 1; i >= 0; i--) {
                 nodes[i].endRecording();
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index bd7522d..8a6c60f 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -265,7 +265,7 @@
             state.pauseTiming();
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.startRecording(1200, 200);
+            final RecordingCanvas c = node.beginRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
@@ -282,7 +282,7 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.startRecording(1200, 200);
+            final RecordingCanvas c = node.beginRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
@@ -299,7 +299,7 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.startRecording(1200, 200);
+            final RecordingCanvas c = node.beginRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
@@ -316,7 +316,7 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.startRecording(1200, 200);
+            final RecordingCanvas c = node.beginRecording(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
@@ -334,7 +334,7 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.startRecording(1200, 200);
+            final RecordingCanvas c = node.beginRecording(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
@@ -353,7 +353,7 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.startRecording(1200, 200);
+            final RecordingCanvas c = node.beginRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
@@ -371,7 +371,7 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.startRecording(1200, 200);
+            final RecordingCanvas c = node.beginRecording(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
@@ -389,7 +389,7 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.startRecording(1200, 200);
+            final RecordingCanvas c = node.beginRecording(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
@@ -408,7 +408,7 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final RecordingCanvas c = node.startRecording(1200, 200);
+            final RecordingCanvas c = node.beginRecording(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
index 0bc9ee4e..55d54e4 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
@@ -344,7 +344,7 @@
             textView.setText(text);
             textView.measure(width, height);
             textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
-            final RecordingCanvas c = node.startRecording(
+            final RecordingCanvas c = node.beginRecording(
                 textView.getMeasuredWidth(), textView.getMeasuredHeight());
             textView.nullLayouts();
             Canvas.freeTextLayoutCaches();
@@ -371,7 +371,7 @@
             textView.setText(text);
             textView.measure(width, height);
             textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
-            final RecordingCanvas c = node.startRecording(
+            final RecordingCanvas c = node.beginRecording(
                 textView.getMeasuredWidth(), textView.getMeasuredHeight());
             textView.nullLayouts();
             Canvas.freeTextLayoutCaches();
@@ -400,7 +400,7 @@
             textView.setText(text);
             textView.measure(width, height);
             textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
-            final RecordingCanvas c = node.startRecording(
+            final RecordingCanvas c = node.beginRecording(
                 textView.getMeasuredWidth(), textView.getMeasuredHeight());
             textView.nullLayouts();
             Canvas.freeTextLayoutCaches();
@@ -430,7 +430,7 @@
             textView.setText(text);
             textView.measure(width, height);
             textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
-            final RecordingCanvas c = node.startRecording(
+            final RecordingCanvas c = node.beginRecording(
                 textView.getMeasuredWidth(), textView.getMeasuredHeight());
             textView.nullLayouts();
             Canvas.freeTextLayoutCaches();
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java
index e417ca7..c1362dc 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java
@@ -60,11 +60,13 @@
         if (size == 0) {
             return 0f;
         }
-        Collections.sort(mResults);
+
+        final ArrayList<Long> resultsCopy = new ArrayList<>(mResults);
+        Collections.sort(resultsCopy);
         final int idx = size / 2;
         return size % 2 == 0
-                ? (double) (mResults.get(idx) + mResults.get(idx - 1)) / 2
-                : mResults.get(idx);
+                ? (double) (resultsCopy.get(idx) + resultsCopy.get(idx - 1)) / 2
+                : resultsCopy.get(idx);
     }
 
     private double standardDeviation() {
diff --git a/api/current.txt b/api/current.txt
index 0d67dcb..8ec2f5a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5478,7 +5478,7 @@
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.PendingIntent getIntent();
     method public boolean getSuppressInitialNotification();
-    method public CharSequence getTitle();
+    method @Deprecated public CharSequence getTitle();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.Notification.BubbleMetadata> CREATOR;
   }
@@ -5492,7 +5492,7 @@
     method public android.app.Notification.BubbleMetadata.Builder setIcon(android.graphics.drawable.Icon);
     method public android.app.Notification.BubbleMetadata.Builder setIntent(android.app.PendingIntent);
     method public android.app.Notification.BubbleMetadata.Builder setSuppressInitialNotification(boolean);
-    method public android.app.Notification.BubbleMetadata.Builder setTitle(CharSequence);
+    method @Deprecated public android.app.Notification.BubbleMetadata.Builder setTitle(CharSequence);
   }
 
   public static class Notification.Builder {
@@ -6664,7 +6664,7 @@
     method public int getPasswordMinimumUpperCase(@Nullable android.content.ComponentName);
     method public int getPasswordQuality(@Nullable android.content.ComponentName);
     method @Nullable public android.app.admin.SystemUpdateInfo getPendingSystemUpdate(@NonNull android.content.ComponentName);
-    method public int getPermissionGrantState(@Nullable android.content.ComponentName, String, String);
+    method public int getPermissionGrantState(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
     method public int getPermissionPolicy(android.content.ComponentName);
     method @Nullable public java.util.List<java.lang.String> getPermittedAccessibilityServices(@NonNull android.content.ComponentName);
     method @Nullable public java.util.List<java.lang.String> getPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName);
@@ -6775,7 +6775,7 @@
     method public void setPasswordMinimumSymbols(@NonNull android.content.ComponentName, int);
     method public void setPasswordMinimumUpperCase(@NonNull android.content.ComponentName, int);
     method public void setPasswordQuality(@NonNull android.content.ComponentName, int);
-    method public boolean setPermissionGrantState(@NonNull android.content.ComponentName, String, String, int);
+    method public boolean setPermissionGrantState(@NonNull android.content.ComponentName, @NonNull String, @NonNull String, int);
     method public void setPermissionPolicy(@NonNull android.content.ComponentName, int);
     method public boolean setPermittedAccessibilityServices(@NonNull android.content.ComponentName, java.util.List<java.lang.String>);
     method public boolean setPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName, @Nullable java.util.List<java.lang.String>);
@@ -7435,7 +7435,10 @@
     method @NonNull public android.content.Intent createRequestRoleIntent(@NonNull String);
     method public boolean isRoleAvailable(@NonNull String);
     method public boolean isRoleHeld(@NonNull String);
+    field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
     field public static final String ROLE_BROWSER = "android.app.role.BROWSER";
+    field public static final String ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION";
+    field public static final String ROLE_CALL_SCREENING = "android.app.role.CALL_SCREENING";
     field public static final String ROLE_DIALER = "android.app.role.DIALER";
     field public static final String ROLE_EMERGENCY = "android.app.role.EMERGENCY";
     field public static final String ROLE_GALLERY = "android.app.role.GALLERY";
@@ -11238,7 +11241,7 @@
   public class LauncherApps {
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(String, android.os.UserHandle);
     method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllPackageInstallerSessions();
-    method @Nullable public android.content.pm.LauncherApps.AppUsageLimit getAppUsageLimit(String, android.os.UserHandle);
+    method @Nullable public android.content.pm.LauncherApps.AppUsageLimit getAppUsageLimit(@NonNull String, @NonNull android.os.UserHandle);
     method public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
     method public java.util.List<android.os.UserHandle> getProfiles();
@@ -11375,6 +11378,7 @@
     method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getMySessions();
     method @Nullable public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
     method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getStagedSessions();
+    method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.INSTALL_EXISTING_PACKAGES"}) public void installExistingPackage(@NonNull String, int, @Nullable android.content.IntentSender);
     method @NonNull public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException;
     method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback);
     method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler);
@@ -11456,10 +11460,10 @@
     method public boolean isActive();
     method public boolean isMultiPackage();
     method public boolean isSealed();
-    method public boolean isSessionApplied();
-    method public boolean isSessionFailed();
-    method public boolean isSessionReady();
     method public boolean isStaged();
+    method public boolean isStagedSessionApplied();
+    method public boolean isStagedSessionFailed();
+    method public boolean isStagedSessionReady();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
     field public static final int INVALID_ID = -1; // 0xffffffff
@@ -11475,7 +11479,6 @@
     method public void setAppIcon(@Nullable android.graphics.Bitmap);
     method public void setAppLabel(@Nullable CharSequence);
     method public void setAppPackageName(@Nullable String);
-    method public void setInstallAsApex();
     method public void setInstallLocation(int);
     method public void setInstallReason(int);
     method public void setMultiPackage();
@@ -11664,6 +11667,7 @@
     field public static final String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
     field public static final String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
     field public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+    field public static final String FEATURE_FOLDABLE = "android.hardware.type.foldable";
     field public static final String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management";
     field public static final String FEATURE_GAMEPAD = "android.hardware.gamepad";
     field public static final String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
@@ -14945,13 +14949,15 @@
 
   public final class RenderNode {
     ctor public RenderNode(@Nullable String);
-    method public int computeApproximateMemoryUsage();
+    method public android.graphics.RecordingCanvas beginRecording(int, int);
+    method public android.graphics.RecordingCanvas beginRecording();
+    method public long computeApproximateMemoryUsage();
     method public void discardDisplayList();
     method public void endRecording();
     method public float getAlpha();
-    method public int getAmbientShadowColor();
+    method @ColorInt public int getAmbientShadowColor();
     method public int getBottom();
-    method public float getCameraDistance();
+    method @FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE) public float getCameraDistance();
     method public boolean getClipToBounds();
     method public boolean getClipToOutline();
     method public float getElevation();
@@ -14962,12 +14968,12 @@
     method public float getPivotX();
     method public float getPivotY();
     method public int getRight();
-    method public float getRotation();
     method public float getRotationX();
     method public float getRotationY();
+    method public float getRotationZ();
     method public float getScaleX();
     method public float getScaleY();
-    method public int getSpotShadowColor();
+    method @ColorInt public int getSpotShadowColor();
     method public int getTop();
     method public float getTranslationX();
     method public float getTranslationY();
@@ -14985,36 +14991,31 @@
     method public boolean offsetTopAndBottom(int);
     method public boolean resetPivot();
     method public boolean setAlpha(float);
-    method public boolean setAmbientShadowColor(int);
-    method public boolean setBottom(int);
-    method public boolean setCameraDistance(float);
-    method public boolean setClipBounds(@Nullable android.graphics.Rect);
+    method public boolean setAmbientShadowColor(@ColorInt int);
+    method public boolean setCameraDistance(@FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE) float);
+    method public boolean setClipRect(@Nullable android.graphics.Rect);
     method public boolean setClipToBounds(boolean);
     method public boolean setClipToOutline(boolean);
     method public boolean setElevation(float);
     method public boolean setForceDarkAllowed(boolean);
     method public boolean setHasOverlappingRendering(boolean);
-    method public boolean setLeft(int);
-    method public boolean setLeftTopRightBottom(int, int, int, int);
     method public boolean setOutline(@Nullable android.graphics.Outline);
     method public boolean setPivotX(float);
     method public boolean setPivotY(float);
+    method public boolean setPosition(int, int, int, int);
+    method public boolean setPosition(android.graphics.Rect);
     method public boolean setProjectBackwards(boolean);
     method public boolean setProjectionReceiver(boolean);
-    method public boolean setRight(int);
-    method public boolean setRotation(float);
     method public boolean setRotationX(float);
     method public boolean setRotationY(float);
+    method public boolean setRotationZ(float);
     method public boolean setScaleX(float);
     method public boolean setScaleY(float);
-    method public boolean setSpotShadowColor(int);
-    method public boolean setTop(int);
+    method public boolean setSpotShadowColor(@ColorInt int);
     method public boolean setTranslationX(float);
     method public boolean setTranslationY(float);
     method public boolean setTranslationZ(float);
     method public boolean setUseCompositingLayer(boolean, @Nullable android.graphics.Paint);
-    method public android.graphics.RecordingCanvas startRecording(int, int);
-    method public android.graphics.RecordingCanvas startRecording();
   }
 
   public class Shader {
@@ -23385,7 +23386,7 @@
   }
 
   public static final class AudioPlaybackCaptureConfiguration.Builder {
-    ctor public AudioPlaybackCaptureConfiguration.Builder();
+    ctor public AudioPlaybackCaptureConfiguration.Builder(@NonNull android.media.projection.MediaProjection);
     method public android.media.AudioPlaybackCaptureConfiguration.Builder addMatchingUid(int);
     method public android.media.AudioPlaybackCaptureConfiguration.Builder addMatchingUsage(@NonNull android.media.AudioAttributes);
     method public android.media.AudioPlaybackCaptureConfiguration build();
@@ -24014,8 +24015,8 @@
     method public int getMaxImages();
     method public android.view.Surface getSurface();
     method public int getWidth();
-    method public static android.media.ImageReader newInstance(int, int, int, int);
-    method public static android.media.ImageReader newInstance(int, int, int, int, long);
+    method @NonNull public static android.media.ImageReader newInstance(@IntRange(from=1) int, @IntRange(from=1) int, int, @IntRange(from=1) int);
+    method @NonNull public static android.media.ImageReader newInstance(@IntRange(from=1) int, @IntRange(from=1) int, int, @IntRange(from=1) int, long);
     method public void setOnImageAvailableListener(android.media.ImageReader.OnImageAvailableListener, android.os.Handler);
   }
 
@@ -24028,8 +24029,8 @@
     method public android.media.Image dequeueInputImage();
     method public int getFormat();
     method public int getMaxImages();
-    method public static android.media.ImageWriter newInstance(android.view.Surface, int);
-    method public static android.media.ImageWriter newInstance(android.view.Surface, int, int);
+    method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int);
+    method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int, int);
     method public void queueInputImage(android.media.Image);
     method public void setOnImageReleasedListener(android.media.ImageWriter.OnImageReleasedListener, android.os.Handler);
   }
@@ -27458,6 +27459,7 @@
     method @Nullable public CharSequence getQueueTitle();
     method public int getRatingType();
     method @Nullable public android.app.PendingIntent getSessionActivity();
+    method @Nullable public android.os.Bundle getSessionInfo();
     method @NonNull public android.media.session.MediaSession.Token getSessionToken();
     method @NonNull public android.media.session.MediaController.TransportControls getTransportControls();
     method public void registerCallback(@NonNull android.media.session.MediaController.Callback);
@@ -27517,6 +27519,7 @@
 
   public final class MediaSession {
     ctor public MediaSession(@NonNull android.content.Context, @NonNull String);
+    ctor public MediaSession(@NonNull android.content.Context, @NonNull String, @Nullable android.os.Bundle);
     method @NonNull public android.media.session.MediaController getController();
     method @NonNull public android.media.session.MediaSessionManager.RemoteUserInfo getCurrentControllerInfo();
     method @NonNull public android.media.session.MediaSession.Token getSessionToken();
@@ -29987,7 +29990,7 @@
     method @Deprecated public boolean reconnect();
     method @Deprecated public boolean removeNetwork(int);
     method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public int removeNetworkSuggestions(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>);
-    method @Deprecated @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", "android.permission.NETWORK_SETUP_WIZARD"}) public void removePasspointConfiguration(String);
+    method @Deprecated @RequiresPermission("android.permission.NETWORK_SETTINGS") public void removePasspointConfiguration(String);
     method @Deprecated public boolean saveConfiguration();
     method public void setTdlsEnabled(java.net.InetAddress, boolean);
     method public void setTdlsEnabledWithMacAddress(String, boolean);
@@ -38832,6 +38835,7 @@
     field public static final String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS";
     field public static final String ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS";
     field public static final String ACTION_NIGHT_DISPLAY_SETTINGS = "android.settings.NIGHT_DISPLAY_SETTINGS";
+    field public static final String ACTION_NOTIFICATION_ASSISTANT_SETTINGS = "android.settings.NOTIFICATION_ASSISTANT_SETTINGS";
     field public static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
     field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
     field public static final String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
@@ -38895,6 +38899,7 @@
     field public static final String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
     field public static final String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities";
     field public static final String ANIMATOR_DURATION_SCALE = "animator_duration_scale";
+    field public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer";
     field public static final String AUTO_TIME = "auto_time";
     field public static final String AUTO_TIME_ZONE = "auto_time_zone";
     field public static final String BLUETOOTH_ON = "bluetooth_on";
@@ -43339,11 +43344,11 @@
 
   public final class CallIdentification implements android.os.Parcelable {
     method public int describeContents();
-    method @NonNull public String getCallScreeningAppName();
+    method @NonNull public CharSequence getCallScreeningAppName();
     method @NonNull public String getCallScreeningPackageName();
-    method @Nullable public String getDescription();
-    method @Nullable public String getDetails();
-    method @Nullable public String getName();
+    method @Nullable public CharSequence getDescription();
+    method @Nullable public CharSequence getDetails();
+    method @Nullable public CharSequence getName();
     method public int getNuisanceConfidence();
     method @Nullable public android.graphics.drawable.Icon getPhoto();
     method public void writeToParcel(android.os.Parcel, int);
@@ -43358,9 +43363,9 @@
   public static class CallIdentification.Builder {
     ctor public CallIdentification.Builder();
     method public android.telecom.CallIdentification build();
-    method public android.telecom.CallIdentification.Builder setDescription(@Nullable String);
-    method public android.telecom.CallIdentification.Builder setDetails(@Nullable String);
-    method public android.telecom.CallIdentification.Builder setName(@Nullable String);
+    method public android.telecom.CallIdentification.Builder setDescription(@Nullable CharSequence);
+    method public android.telecom.CallIdentification.Builder setDetails(@Nullable CharSequence);
+    method public android.telecom.CallIdentification.Builder setName(@Nullable CharSequence);
     method public android.telecom.CallIdentification.Builder setNuisanceConfidence(int);
     method public android.telecom.CallIdentification.Builder setPhoto(@Nullable android.graphics.drawable.Icon);
   }
@@ -44858,6 +44863,7 @@
     method @Deprecated public int getCdmaDbm();
     method @Deprecated public int getCdmaEcio();
     method @NonNull public java.util.List<android.telephony.CellSignalStrength> getCellSignalStrengths();
+    method public <T extends android.telephony.CellSignalStrength> java.util.List<T> getCellSignalStrengths(@NonNull Class<T>);
     method @Deprecated public int getEvdoDbm();
     method @Deprecated public int getEvdoEcio();
     method @Deprecated public int getEvdoSnr();
@@ -45344,13 +45350,13 @@
   }
 
   public final class UiccCardInfo implements android.os.Parcelable {
-    ctor public UiccCardInfo(boolean, int, String, String, int);
     method public int describeContents();
     method public int getCardId();
     method public String getEid();
     method public String getIccId();
     method public int getSlotIndex();
     method public boolean isEuicc();
+    method public boolean isRemovable();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.UiccCardInfo> CREATOR;
   }
@@ -45665,465 +45671,6 @@
 
 }
 
-package android.telephony.ims {
-
-  public class Rcs1To1Thread extends android.telephony.ims.RcsThread {
-    method @WorkerThread public long getFallbackThreadId() throws android.telephony.ims.RcsMessageStoreException;
-    method @NonNull @WorkerThread public android.telephony.ims.RcsParticipant getRecipient() throws android.telephony.ims.RcsMessageStoreException;
-    method public boolean isGroup();
-    method @WorkerThread public void setFallbackThreadId(long) throws android.telephony.ims.RcsMessageStoreException;
-  }
-
-  public abstract class RcsEvent {
-    ctor protected RcsEvent(long);
-    method public long getTimestamp();
-  }
-
-  public final class RcsEventQueryParams implements android.os.Parcelable {
-    method public int describeContents();
-    method @android.telephony.ims.RcsEventQueryParams.EventType public int getEventType();
-    method public int getLimit();
-    method public boolean getSortDirection();
-    method public int getSortingProperty();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final int ALL_EVENTS = -1; // 0xffffffff
-    field public static final int ALL_GROUP_THREAD_EVENTS = 0; // 0x0
-    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsEventQueryParams> CREATOR;
-    field public static final int GROUP_THREAD_ICON_CHANGED_EVENT = 8; // 0x8
-    field public static final int GROUP_THREAD_NAME_CHANGED_EVENT = 16; // 0x10
-    field public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT = 2; // 0x2
-    field public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT = 4; // 0x4
-    field public static final int PARTICIPANT_ALIAS_CHANGED_EVENT = 1; // 0x1
-    field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
-    field public static final int SORT_BY_TIMESTAMP = 1; // 0x1
-  }
-
-  public static class RcsEventQueryParams.Builder {
-    ctor public RcsEventQueryParams.Builder();
-    method public android.telephony.ims.RcsEventQueryParams build();
-    method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setEventType(@android.telephony.ims.RcsEventQueryParams.EventType int);
-    method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setGroupThread(@NonNull android.telephony.ims.RcsGroupThread);
-    method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
-    method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortDirection(boolean);
-    method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortProperty(@android.telephony.ims.RcsEventQueryParams.SortingProperty int);
-  }
-
-  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.ALL_EVENTS, android.telephony.ims.RcsEventQueryParams.ALL_GROUP_THREAD_EVENTS, android.telephony.ims.RcsEventQueryParams.PARTICIPANT_ALIAS_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_JOINED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_LEFT_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_NAME_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_ICON_CHANGED_EVENT}) public static @interface RcsEventQueryParams.EventType {
-  }
-
-  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsEventQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsEventQueryParams.SortingProperty {
-  }
-
-  public class RcsEventQueryResult {
-    method public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
-    method public java.util.List<android.telephony.ims.RcsEvent> getEvents();
-  }
-
-  public final class RcsFileTransferCreationParams implements android.os.Parcelable {
-    method public int describeContents();
-    method public String getContentMimeType();
-    method public android.net.Uri getContentUri();
-    method public long getFileSize();
-    method @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus();
-    method public int getHeight();
-    method public long getMediaDuration();
-    method public String getPreviewMimeType();
-    method public android.net.Uri getPreviewUri();
-    method public String getRcsFileTransferSessionId();
-    method public long getTransferOffset();
-    method public int getWidth();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsFileTransferCreationParams> CREATOR;
-  }
-
-  public class RcsFileTransferCreationParams.Builder {
-    ctor public RcsFileTransferCreationParams.Builder();
-    method public android.telephony.ims.RcsFileTransferCreationParams build();
-    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentMimeType(String);
-    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentUri(android.net.Uri);
-    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileSize(long);
-    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferSessionId(String);
-    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int);
-    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setHeight(int);
-    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setMediaDuration(long);
-    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewMimeType(String);
-    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewUri(android.net.Uri);
-    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setTransferOffset(long);
-    method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setWidth(int);
-  }
-
-  public class RcsFileTransferPart {
-    method @WorkerThread @Nullable public String getContentMimeType() throws android.telephony.ims.RcsMessageStoreException;
-    method @Nullable @WorkerThread public android.net.Uri getContentUri() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public long getFileSize() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public String getFileTransferSessionId() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public int getHeight() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public long getLength() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public String getPreviewMimeType() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public android.net.Uri getPreviewUri() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public long getTransferOffset() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public int getWidth() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setContentMimeType(String) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setContentUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setFileSize(long) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setFileTransferSessionId(String) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setHeight(int) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setLength(long) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setPreviewMimeType(String) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setPreviewUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setTransferOffset(long) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setWidth(int) throws android.telephony.ims.RcsMessageStoreException;
-    field public static final int DOWNLOADING = 6; // 0x6
-    field public static final int DOWNLOADING_CANCELLED = 9; // 0x9
-    field public static final int DOWNLOADING_FAILED = 8; // 0x8
-    field public static final int DOWNLOADING_PAUSED = 7; // 0x7
-    field public static final int DRAFT = 1; // 0x1
-    field public static final int NOT_SET = 0; // 0x0
-    field public static final int SENDING = 2; // 0x2
-    field public static final int SENDING_CANCELLED = 5; // 0x5
-    field public static final int SENDING_FAILED = 4; // 0x4
-    field public static final int SENDING_PAUSED = 3; // 0x3
-    field public static final int SUCCEEDED = 10; // 0xa
-  }
-
-  @IntDef({android.telephony.ims.RcsFileTransferPart.DRAFT, android.telephony.ims.RcsFileTransferPart.SENDING, android.telephony.ims.RcsFileTransferPart.SENDING_PAUSED, android.telephony.ims.RcsFileTransferPart.SENDING_FAILED, android.telephony.ims.RcsFileTransferPart.SENDING_CANCELLED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_PAUSED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_FAILED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_CANCELLED, android.telephony.ims.RcsFileTransferPart.SUCCEEDED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsFileTransferPart.RcsFileTransferStatus {
-  }
-
-  public class RcsGroupThread extends android.telephony.ims.RcsThread {
-    method @WorkerThread public void addParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
-    method @Nullable @WorkerThread public android.net.Uri getConferenceUri() throws android.telephony.ims.RcsMessageStoreException;
-    method @Nullable public android.net.Uri getGroupIcon() throws android.telephony.ims.RcsMessageStoreException;
-    method @Nullable @WorkerThread public String getGroupName() throws android.telephony.ims.RcsMessageStoreException;
-    method @Nullable @WorkerThread public android.telephony.ims.RcsParticipant getOwner() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public java.util.Set<android.telephony.ims.RcsParticipant> getParticipants() throws android.telephony.ims.RcsMessageStoreException;
-    method public boolean isGroup();
-    method @WorkerThread public void removeParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
-    method @Nullable @WorkerThread public void setConferenceUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setGroupIcon(@Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setGroupName(String) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setOwner(@Nullable android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
-  }
-
-  public abstract class RcsGroupThreadEvent extends android.telephony.ims.RcsEvent {
-    method @NonNull public android.telephony.ims.RcsParticipant getOriginatingParticipant();
-    method @NonNull public android.telephony.ims.RcsGroupThread getRcsGroupThread();
-  }
-
-  public final class RcsGroupThreadIconChangedEvent extends android.telephony.ims.RcsGroupThreadEvent {
-    ctor public RcsGroupThreadIconChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable android.net.Uri);
-    method @Nullable public android.net.Uri getNewIcon();
-  }
-
-  public final class RcsGroupThreadNameChangedEvent extends android.telephony.ims.RcsGroupThreadEvent {
-    ctor public RcsGroupThreadNameChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable String);
-    method @Nullable public String getNewName();
-  }
-
-  public final class RcsGroupThreadParticipantJoinedEvent extends android.telephony.ims.RcsGroupThreadEvent {
-    ctor public RcsGroupThreadParticipantJoinedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant);
-    method public android.telephony.ims.RcsParticipant getJoinedParticipant();
-  }
-
-  public final class RcsGroupThreadParticipantLeftEvent extends android.telephony.ims.RcsGroupThreadEvent {
-    ctor public RcsGroupThreadParticipantLeftEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant);
-    method @NonNull public android.telephony.ims.RcsParticipant getLeavingParticipant();
-    method public void persist() throws android.telephony.ims.RcsMessageStoreException;
-  }
-
-  public class RcsIncomingMessage extends android.telephony.ims.RcsMessage {
-    method @WorkerThread public long getArrivalTimestamp() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public android.telephony.ims.RcsParticipant getSenderParticipant() throws android.telephony.ims.RcsMessageStoreException;
-    method public boolean isIncoming();
-    method @WorkerThread public void setArrivalTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
-  }
-
-  public final class RcsIncomingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable {
-    method public int describeContents();
-    method public long getArrivalTimestamp();
-    method public long getSeenTimestamp();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsIncomingMessageCreationParams> CREATOR;
-  }
-
-  public static class RcsIncomingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder {
-    ctor public RcsIncomingMessageCreationParams.Builder(long, long, int);
-    method public android.telephony.ims.RcsIncomingMessageCreationParams build();
-    method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setArrivalTimestamp(long);
-    method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSeenTimestamp(long);
-    method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSenderParticipant(android.telephony.ims.RcsParticipant);
-  }
-
-  public class RcsManager {
-    method public android.telephony.ims.RcsMessageStore getRcsMessageStore();
-  }
-
-  public abstract class RcsMessage {
-    method @NonNull @WorkerThread public java.util.Set<android.telephony.ims.RcsFileTransferPart> getFileTransferParts() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public double getLatitude() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public double getLongitude() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public long getOriginationTimestamp() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public String getRcsMessageId() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException;
-    method public int getSubscriptionId() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public String getText() throws android.telephony.ims.RcsMessageStoreException;
-    method @NonNull @WorkerThread public android.telephony.ims.RcsFileTransferPart insertFileTransfer(android.telephony.ims.RcsFileTransferCreationParams) throws android.telephony.ims.RcsMessageStoreException;
-    method public abstract boolean isIncoming();
-    method @WorkerThread public void removeFileTransferPart(@NonNull android.telephony.ims.RcsFileTransferPart) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setLatitude(double) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setLongitude(double) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setOriginationTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setRcsMessageId(String) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setSubscriptionId(int) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setText(String) throws android.telephony.ims.RcsMessageStoreException;
-    field public static final int DRAFT = 1; // 0x1
-    field public static final int FAILED = 6; // 0x6
-    field public static final double LOCATION_NOT_SET = 4.9E-324;
-    field public static final int NOT_SET = 0; // 0x0
-    field public static final int QUEUED = 2; // 0x2
-    field public static final int RECEIVED = 7; // 0x7
-    field public static final int RETRYING = 5; // 0x5
-    field public static final int SEEN = 9; // 0x9
-    field public static final int SENDING = 3; // 0x3
-    field public static final int SENT = 4; // 0x4
-  }
-
-  @IntDef({android.telephony.ims.RcsMessage.DRAFT, android.telephony.ims.RcsMessage.QUEUED, android.telephony.ims.RcsMessage.SENDING, android.telephony.ims.RcsMessage.SENT, android.telephony.ims.RcsMessage.RETRYING, android.telephony.ims.RcsMessage.FAILED, android.telephony.ims.RcsMessage.RECEIVED, android.telephony.ims.RcsMessage.SEEN}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsMessage.RcsMessageStatus {
-  }
-
-  public class RcsMessageCreationParams {
-    ctor protected RcsMessageCreationParams(android.telephony.ims.RcsMessageCreationParams.Builder);
-    method public double getLatitude();
-    method public double getLongitude();
-    method public int getMessageStatus();
-    method public long getOriginationTimestamp();
-    method @Nullable public String getRcsMessageGlobalId();
-    method public int getSubId();
-    method @Nullable public String getText();
-  }
-
-  public static class RcsMessageCreationParams.Builder {
-    method public android.telephony.ims.RcsMessageCreationParams build();
-    method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLatitude(double);
-    method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLongitude(double);
-    method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setRcsMessageId(String);
-    method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int);
-    method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setText(String);
-  }
-
-  public final class RcsMessageQueryParams implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getFileTransferPresence();
-    method public int getLimit();
-    method public String getMessageLike();
-    method public int getMessageType();
-    method public boolean getSortDirection();
-    method @android.telephony.ims.RcsMessageQueryParams.SortingProperty public int getSortingProperty();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryParams> CREATOR;
-    field public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 8; // 0x8
-    field public static final int MESSAGES_WITH_FILE_TRANSFERS = 4; // 0x4
-    field public static final int MESSAGE_TYPE_INCOMING = 1; // 0x1
-    field public static final int MESSAGE_TYPE_OUTGOING = 2; // 0x2
-    field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
-    field public static final int SORT_BY_TIMESTAMP = 1; // 0x1
-  }
-
-  public static class RcsMessageQueryParams.Builder {
-    ctor public RcsMessageQueryParams.Builder();
-    method public android.telephony.ims.RcsMessageQueryParams build();
-    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setFileTransferPresence(int);
-    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageLike(String);
-    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageType(int);
-    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
-    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortDirection(boolean);
-    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortProperty(@android.telephony.ims.RcsMessageQueryParams.SortingProperty int);
-    method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setThread(@Nullable android.telephony.ims.RcsThread);
-  }
-
-  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsMessageQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsMessageQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsMessageQueryParams.SortingProperty {
-  }
-
-  public final class RcsMessageQueryResult implements android.os.Parcelable {
-    method public int describeContents();
-    method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
-    method @NonNull public java.util.List<android.telephony.ims.RcsMessage> getMessages();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryResult> CREATOR;
-  }
-
-  public final class RcsMessageSnippet implements android.os.Parcelable {
-    method public int describeContents();
-    method @android.telephony.ims.RcsMessage.RcsMessageStatus public int getSnippetStatus();
-    method @Nullable public String getSnippetText();
-    method public long getSnippetTimestamp();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageSnippet> CREATOR;
-  }
-
-  public class RcsMessageStore {
-    ctor public RcsMessageStore();
-    method @WorkerThread @NonNull public android.telephony.ims.RcsGroupThread createGroupThread(@Nullable java.util.List<android.telephony.ims.RcsParticipant>, @Nullable String, @Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public android.telephony.ims.Rcs1To1Thread createRcs1To1Thread(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public android.telephony.ims.RcsParticipant createRcsParticipant(String, @Nullable String) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void deleteThread(@NonNull android.telephony.ims.RcsThread) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@Nullable android.telephony.ims.RcsEventQueryParams) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@Nullable android.telephony.ims.RcsMessageQueryParams) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@Nullable android.telephony.ims.RcsParticipantQueryParams) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@Nullable android.telephony.ims.RcsThreadQueryParams) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public void persistRcsEvent(android.telephony.ims.RcsEvent) throws android.telephony.ims.RcsMessageStoreException;
-  }
-
-  public class RcsMessageStoreException extends java.lang.Exception {
-    ctor public RcsMessageStoreException(String);
-  }
-
-  public class RcsOutgoingMessage extends android.telephony.ims.RcsMessage {
-    method @NonNull @WorkerThread public java.util.List<android.telephony.ims.RcsOutgoingMessageDelivery> getOutgoingDeliveries() throws android.telephony.ims.RcsMessageStoreException;
-    method public boolean isIncoming();
-  }
-
-  public final class RcsOutgoingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable {
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsOutgoingMessageCreationParams> CREATOR;
-  }
-
-  public static class RcsOutgoingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder {
-    ctor public RcsOutgoingMessageCreationParams.Builder(long, int);
-    method public android.telephony.ims.RcsOutgoingMessageCreationParams build();
-  }
-
-  public class RcsOutgoingMessageDelivery {
-    method @WorkerThread public long getDeliveredTimestamp() throws android.telephony.ims.RcsMessageStoreException;
-    method @NonNull public android.telephony.ims.RcsOutgoingMessage getMessage();
-    method @NonNull public android.telephony.ims.RcsParticipant getRecipient();
-    method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setDeliveredTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException;
-  }
-
-  public class RcsParticipant {
-    method @Nullable @WorkerThread public String getAlias() throws android.telephony.ims.RcsMessageStoreException;
-    method @Nullable @WorkerThread public String getCanonicalAddress() throws android.telephony.ims.RcsMessageStoreException;
-    method @Nullable @WorkerThread public String getContactId() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setAlias(String) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void setContactId(String) throws android.telephony.ims.RcsMessageStoreException;
-  }
-
-  public final class RcsParticipantAliasChangedEvent extends android.telephony.ims.RcsEvent {
-    ctor public RcsParticipantAliasChangedEvent(long, @NonNull android.telephony.ims.RcsParticipant, @Nullable String);
-    method @Nullable public String getNewAlias();
-    method @NonNull public android.telephony.ims.RcsParticipant getParticipant();
-  }
-
-  public final class RcsParticipantQueryParams implements android.os.Parcelable {
-    method public int describeContents();
-    method public String getAliasLike();
-    method public String getCanonicalAddressLike();
-    method public int getLimit();
-    method public boolean getSortDirection();
-    method public int getSortingProperty();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryParams> CREATOR;
-    field public static final int SORT_BY_ALIAS = 1; // 0x1
-    field public static final int SORT_BY_CANONICAL_ADDRESS = 2; // 0x2
-    field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
-  }
-
-  public static class RcsParticipantQueryParams.Builder {
-    ctor public RcsParticipantQueryParams.Builder();
-    method public android.telephony.ims.RcsParticipantQueryParams build();
-    method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setAliasLike(String);
-    method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setCanonicalAddressLike(String);
-    method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
-    method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortDirection(boolean);
-    method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortProperty(@android.telephony.ims.RcsParticipantQueryParams.SortingProperty int);
-    method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setThread(android.telephony.ims.RcsThread);
-  }
-
-  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_ALIAS, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS}) public static @interface RcsParticipantQueryParams.SortingProperty {
-  }
-
-  public final class RcsParticipantQueryResult implements android.os.Parcelable {
-    method public int describeContents();
-    method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
-    method @NonNull public java.util.List<android.telephony.ims.RcsParticipant> getParticipants();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryResult> CREATOR;
-  }
-
-  public final class RcsQueryContinuationToken implements android.os.Parcelable {
-    method public int describeContents();
-    method @android.telephony.ims.RcsQueryContinuationToken.ContinuationTokenType public int getQueryType();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsQueryContinuationToken> CREATOR;
-    field public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0; // 0x0
-    field public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1; // 0x1
-    field public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2; // 0x2
-    field public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3; // 0x3
-  }
-
-  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsQueryContinuationToken.EVENT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.THREAD_QUERY_CONTINUATION_TOKEN_TYPE}) public static @interface RcsQueryContinuationToken.ContinuationTokenType {
-  }
-
-  public abstract class RcsThread {
-    method @WorkerThread @NonNull public android.telephony.ims.RcsIncomingMessage addIncomingMessage(@NonNull android.telephony.ims.RcsIncomingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public android.telephony.ims.RcsOutgoingMessage addOutgoingMessage(@NonNull android.telephony.ims.RcsOutgoingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread public void deleteMessage(@NonNull android.telephony.ims.RcsMessage) throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getMessages() throws android.telephony.ims.RcsMessageStoreException;
-    method @WorkerThread @NonNull public android.telephony.ims.RcsMessageSnippet getSnippet() throws android.telephony.ims.RcsMessageStoreException;
-    method public abstract boolean isGroup();
-  }
-
-  public final class RcsThreadQueryParams implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getLimit();
-    method public boolean getSortDirection();
-    method @android.telephony.ims.RcsThreadQueryParams.SortingProperty public int getSortingProperty();
-    method public int getThreadType();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryParams> CREATOR;
-    field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
-    field public static final int SORT_BY_TIMESTAMP = 1; // 0x1
-    field public static final int THREAD_TYPE_1_TO_1 = 2; // 0x2
-    field public static final int THREAD_TYPE_GROUP = 1; // 0x1
-  }
-
-  public static class RcsThreadQueryParams.Builder {
-    ctor public RcsThreadQueryParams.Builder();
-    method public android.telephony.ims.RcsThreadQueryParams build();
-    method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipant(@NonNull android.telephony.ims.RcsParticipant);
-    method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipants(@NonNull java.util.List<android.telephony.ims.RcsParticipant>);
-    method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
-    method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortDirection(boolean);
-    method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortProperty(@android.telephony.ims.RcsThreadQueryParams.SortingProperty int);
-    method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setThreadType(int);
-  }
-
-  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsThreadQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsThreadQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsThreadQueryParams.SortingProperty {
-  }
-
-  public final class RcsThreadQueryResult implements android.os.Parcelable {
-    method public int describeContents();
-    method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
-    method @NonNull public java.util.List<android.telephony.ims.RcsThread> getThreads();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryResult> CREATOR;
-  }
-
-}
-
 package android.telephony.mbms {
 
   public class DownloadProgressListener {
diff --git a/api/system-current.txt b/api/system-current.txt
index 20cbbfd..86a13bb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -15,6 +15,7 @@
     field public static final String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS";
     field public static final String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
     field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
+    field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
     field public static final String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE";
     field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
     field public static final String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
@@ -1110,7 +1111,6 @@
     method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String);
     method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>);
     field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1
-    field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
   }
 
   public interface RoleManagerCallback {
@@ -1172,7 +1172,7 @@
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
     method public int getUsageSource();
-    method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, android.app.PendingIntent);
+    method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @Nullable android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerAppUsageObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerUsageSessionObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent, @Nullable android.app.PendingIntent);
     method public void reportUsageStart(@NonNull android.app.Activity, @NonNull String);
@@ -1304,8 +1304,8 @@
   }
 
   public abstract class ContentResolver {
-    method @Nullable public android.os.Bundle getCache(@NonNull android.net.Uri);
-    method public void putCache(@NonNull android.net.Uri, @Nullable android.os.Bundle);
+    method @Nullable @RequiresPermission("android.permission.CACHE_CONTENT") public android.os.Bundle getCache(@NonNull android.net.Uri);
+    method @RequiresPermission("android.permission.CACHE_CONTENT") public void putCache(@NonNull android.net.Uri, @Nullable android.os.Bundle);
   }
 
   public abstract class Context {
@@ -1557,6 +1557,7 @@
     method public void setDontKillApp(boolean);
     method public void setEnableRollback();
     method @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) public void setGrantedRuntimePermissions(String[]);
+    method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex();
     method public void setInstallAsInstantApp(boolean);
     method public void setInstallAsVirtualPreload();
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged();
@@ -1575,6 +1576,7 @@
     method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
     method public abstract boolean arePermissionsIndividuallyControlled();
     method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(String);
+    method public boolean getAppDetailsActivityEnabled(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public android.content.pm.dex.ArtManager getArtManager();
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract String getDefaultBrowserPackageNameAsUser(int);
@@ -1590,8 +1592,8 @@
     method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(String, String, @NonNull android.os.UserHandle);
     method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]);
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
-    method public abstract int installExistingPackage(String) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public abstract int installExistingPackage(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated public abstract int installExistingPackage(String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated public abstract int installExistingPackage(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle);
     method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
     method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
@@ -1601,6 +1603,7 @@
     method @Deprecated public void replacePreferredActivity(@NonNull android.content.IntentFilter, int, @NonNull java.util.List<android.content.ComponentName>, @NonNull android.content.ComponentName);
     method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method public void sendDeviceCustomizationReadyBroadcast();
+    method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setAppDetailsActivityEnabled(@NonNull String, boolean);
     method @RequiresPermission(allOf={android.Manifest.permission.SET_PREFERRED_APPLICATIONS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public abstract boolean setDefaultBrowserPackageNameAsUser(String, int);
     method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setDistractingPackageRestrictions(@NonNull String[], int);
     method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence);
@@ -1711,8 +1714,13 @@
     field public boolean handleAllWebDataURI;
   }
 
+  public final class ShortcutInfo implements android.os.Parcelable {
+    method @Nullable public android.app.Person[] getPersons();
+  }
+
   public class ShortcutManager {
     method @NonNull public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(@NonNull android.content.IntentFilter);
+    method public boolean hasShareTargets(@NonNull String);
   }
 
   public static final class ShortcutManager.ShareShortcutInfo implements android.os.Parcelable {
@@ -1890,7 +1898,7 @@
   public final class BrightnessConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
-    method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(String);
+    method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String);
     method public android.util.Pair<float[],float[]> getCurve();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
@@ -1898,8 +1906,8 @@
 
   public static class BrightnessConfiguration.Builder {
     ctor public BrightnessConfiguration.Builder(float[], float[]);
-    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection);
-    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(String, android.hardware.display.BrightnessCorrection);
+    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, @NonNull android.hardware.display.BrightnessCorrection);
+    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(@NonNull String, @NonNull android.hardware.display.BrightnessCorrection);
     method public android.hardware.display.BrightnessConfiguration build();
     method public int getMaxCorrectionsByCategory();
     method public int getMaxCorrectionsByPackageName();
@@ -1907,7 +1915,7 @@
   }
 
   public final class BrightnessCorrection implements android.os.Parcelable {
-    method public float apply(float);
+    method @FloatRange(from=0.0) public float apply(@FloatRange(from=0.0) float);
     method @NonNull public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -3036,21 +3044,21 @@
     method public double getHorizontalPositionUncertaintyMeters();
     method public double getLatitudeDegrees();
     method public double getLongitudeDegrees();
-    method @Nullable public java.util.List<android.location.GnssSingleSatCorrection> getSingleSatCorrectionList();
+    method @Nullable public java.util.List<android.location.GnssSingleSatCorrection> getSingleSatelliteCorrectionList();
     method public long getToaGpsNanosecondsOfWeek();
     method public double getVerticalPositionUncertaintyMeters();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.GnssMeasurementCorrections> CREATOR;
   }
 
-  public static class GnssMeasurementCorrections.Builder {
+  public static final class GnssMeasurementCorrections.Builder {
     ctor public GnssMeasurementCorrections.Builder();
     method public android.location.GnssMeasurementCorrections build();
     method public android.location.GnssMeasurementCorrections.Builder setAltitudeMeters(double);
     method public android.location.GnssMeasurementCorrections.Builder setHorizontalPositionUncertaintyMeters(double);
     method public android.location.GnssMeasurementCorrections.Builder setLatitudeDegrees(double);
     method public android.location.GnssMeasurementCorrections.Builder setLongitudeDegrees(double);
-    method public android.location.GnssMeasurementCorrections.Builder setSingleSatCorrectionList(@Nullable java.util.List<android.location.GnssSingleSatCorrection>);
+    method public android.location.GnssMeasurementCorrections.Builder setSingleSatelliteCorrectionList(@Nullable java.util.List<android.location.GnssSingleSatCorrection>);
     method public android.location.GnssMeasurementCorrections.Builder setToaGpsNanosecondsOfWeek(long);
     method public android.location.GnssMeasurementCorrections.Builder setVerticalPositionUncertaintyMeters(double);
   }
@@ -3065,7 +3073,7 @@
     field public static final android.os.Parcelable.Creator<android.location.GnssReflectingPlane> CREATOR;
   }
 
-  public static class GnssReflectingPlane.Builder {
+  public static final class GnssReflectingPlane.Builder {
     ctor public GnssReflectingPlane.Builder();
     method public android.location.GnssReflectingPlane build();
     method public android.location.GnssReflectingPlane.Builder setAltitudeMeters(double);
@@ -3080,14 +3088,14 @@
     method public int getConstellationType();
     method public float getExcessPathLengthMeters();
     method public float getExcessPathLengthUncertaintyMeters();
-    method @FloatRange(from=0.0f, to=1.0f) public float getProbSatIsLos();
+    method @FloatRange(from=0.0f, to=1.0f) public float getProbabilityLineOfSight();
     method @Nullable public android.location.GnssReflectingPlane getReflectingPlane();
-    method public int getSatId();
-    method public int getSingleSatCorrectionFlags();
+    method public int getSatelliteId();
+    method public int getSingleSatelliteCorrectionFlags();
     method public boolean hasExcessPathLength();
     method public boolean hasExcessPathLengthUncertainty();
     method public boolean hasReflectingPlane();
-    method public boolean hasSatelliteLineOfSight();
+    method public boolean hasValidSatelliteLineOfSight();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.GnssSingleSatCorrection> CREATOR;
     field public static final int HAS_EXCESS_PATH_LENGTH_MASK = 2; // 0x2
@@ -3096,17 +3104,17 @@
     field public static final int HAS_REFLECTING_PLANE_MASK = 8; // 0x8
   }
 
-  public static class GnssSingleSatCorrection.Builder {
+  public static final class GnssSingleSatCorrection.Builder {
     ctor public GnssSingleSatCorrection.Builder();
     method public android.location.GnssSingleSatCorrection build();
     method public android.location.GnssSingleSatCorrection.Builder setCarrierFrequencyHz(float);
     method public android.location.GnssSingleSatCorrection.Builder setConstellationType(int);
     method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthMeters(float);
     method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthUncertaintyMeters(float);
-    method public android.location.GnssSingleSatCorrection.Builder setProbSatIsLos(@FloatRange(from=0.0f, to=1.0f) float);
+    method public android.location.GnssSingleSatCorrection.Builder setProbabilityLineOfSight(@FloatRange(from=0.0f, to=1.0f) float);
     method public android.location.GnssSingleSatCorrection.Builder setReflectingPlane(android.location.GnssReflectingPlane);
-    method public android.location.GnssSingleSatCorrection.Builder setSatId(int);
-    method public android.location.GnssSingleSatCorrection.Builder setSingleSatCorrectionFlags(int);
+    method public android.location.GnssSingleSatCorrection.Builder setSatelliteId(int);
+    method public android.location.GnssSingleSatCorrection.Builder setSingleSatelliteCorrectionFlags(int);
   }
 
   public class GpsClock implements android.os.Parcelable {
@@ -3590,10 +3598,10 @@
     method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>);
     method public int getFocusDuckingBehavior();
     method public int getStatus();
-    method public int removeUidDeviceAffinity(int);
+    method public boolean removeUidDeviceAffinity(int);
     method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public void setRegistration(String);
-    method public int setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
+    method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
     method public String toLogFriendlyString();
     field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0
     field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0
@@ -3617,6 +3625,7 @@
   }
 
   public abstract static class AudioPolicy.AudioPolicyVolumeCallback {
+    ctor public AudioPolicy.AudioPolicyVolumeCallback();
     method public void onVolumeAdjustment(int);
   }
 
@@ -4664,7 +4673,10 @@
   }
 
   public class WifiInfo implements android.os.Parcelable {
+    method @Nullable public String getFqdn();
+    method @Nullable public String getProviderFriendlyName();
     method public boolean isOsuAp();
+    method public boolean isPasspointAp();
   }
 
   public class WifiManager {
@@ -5121,6 +5133,7 @@
     method public void onError(int);
     method public void onFinished();
     method public void onProgress(float);
+    field public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; // 0x5
     field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1
     field public static final int BUGREPORT_ERROR_RUNTIME = 2; // 0x2
     field public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; // 0x4
@@ -5537,6 +5550,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.graphics.Bitmap getUserIcon();
     method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
     method public boolean hasRestrictedProfiles();
@@ -5547,6 +5561,8 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isPrimaryUser();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean removeUser(android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(android.graphics.Bitmap);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(String);
     field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
     field @Deprecated public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
     field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
@@ -5623,6 +5639,7 @@
     method @BinderThread public abstract void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream);
     method public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String);
     method @NonNull public abstract java.util.Map<java.lang.String,java.util.List<java.lang.String>> onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String);
+    method public abstract boolean onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int);
     field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
   }
 
@@ -7459,7 +7476,7 @@
     field public static final int VSNCP_TIMEOUT = 2236; // 0x8bc
   }
 
-  public class DisconnectCause {
+  public final class DisconnectCause {
     field public static final int ALREADY_DIALING = 72; // 0x48
     field public static final int ANSWERED_ELSEWHERE = 52; // 0x34
     field public static final int BUSY = 4; // 0x4
@@ -7637,18 +7654,18 @@
 
   public class PhoneStateListener {
     method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
-    method public void onCallDisconnectCauseChanged(int, int);
-    method public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
-    method public void onPreciseCallStateChanged(android.telephony.PreciseCallState);
-    method public void onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState);
+    method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onCallDisconnectCauseChanged(int, int);
+    method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
+    method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
+    method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState);
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
     method public void onVoiceActivationStateChanged(int);
     field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
-    field public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
-    field public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
-    field public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
-    field public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
+    field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
+    field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
+    field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
+    field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
     field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
     field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
@@ -7683,7 +7700,7 @@
     field public static final android.os.Parcelable.Creator<android.telephony.PreciseDataConnectionState> CREATOR;
   }
 
-  public class PreciseDisconnectCause {
+  public final class PreciseDisconnectCause {
     field public static final int ACCESS_CLASS_BLOCKED = 260; // 0x104
     field public static final int ACCESS_INFORMATION_DISCARDED = 43; // 0x2b
     field public static final int ACM_LIMIT_EXCEEDED = 68; // 0x44
@@ -7901,7 +7918,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
     method public int getSimApplicationState();
     method public int getSimCardState();
-    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getSimLocale();
+    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Locale getSimLocale();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.UiccSlotInfo[] getUiccSlotsInfo();
@@ -8010,7 +8027,7 @@
   }
 
   public class UiccSlotInfo implements android.os.Parcelable {
-    ctor public UiccSlotInfo(boolean, boolean, String, int, int, boolean);
+    ctor @Deprecated public UiccSlotInfo(boolean, boolean, String, int, int, boolean);
     method public int describeContents();
     method public String getCardId();
     method public int getCardStateInfo();
@@ -8018,6 +8035,7 @@
     method public boolean getIsEuicc();
     method public boolean getIsExtendedApduSupported();
     method public int getLogicalSlotIdx();
+    method public boolean isRemovable();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
     field public static final int CARD_STATE_INFO_ERROR = 3; // 0x3
@@ -8813,12 +8831,12 @@
     method public int describeContents();
     method public int getAudioDirection();
     method public int getAudioQuality();
-    method public boolean getRttAudioSpeech();
     method public int getRttMode();
     method public int getVideoDirection();
     method public int getVideoQuality();
+    method public boolean isReceivingRttAudio();
     method public boolean isRttCall();
-    method public void setRttAudioSpeech(boolean);
+    method public void setReceivingRttAudio(boolean);
     method public void setRttMode(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int AUDIO_QUALITY_AMR = 1; // 0x1
@@ -9394,11 +9412,13 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
     field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6
-    field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
-    field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
+    field public static final int TYPE_SESSION_PAUSED = 8; // 0x8
+    field public static final int TYPE_SESSION_RESUMED = 7; // 0x7
     field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
     field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2
     field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3
+    field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5
+    field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4
   }
 
   public final class ContentCaptureManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index 2a45cd3..06e7550 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -17,6 +17,10 @@
     field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
   }
 
+  public static final class R.bool {
+    field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
+  }
+
   public static final class R.string {
     field public static final int config_defaultAssistant = 17039393; // 0x1040021
     field public static final int config_defaultDialer = 17039395; // 0x1040023
@@ -48,6 +52,7 @@
     method public long getTotalRam();
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
+    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);
   }
 
@@ -94,6 +99,31 @@
     field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
   }
 
+  public class ActivityView extends android.view.ViewGroup {
+    ctor public ActivityView(android.content.Context);
+    ctor public ActivityView(android.content.Context, android.util.AttributeSet);
+    ctor public ActivityView(android.content.Context, android.util.AttributeSet, int);
+    ctor public ActivityView(android.content.Context, android.util.AttributeSet, int, boolean);
+    method public void onLayout(boolean, int, int, int, int);
+    method public void onLocationChanged();
+    method public void performBackPress();
+    method public void release();
+    method public void setCallback(android.app.ActivityView.StateCallback);
+    method public void setForwardedInsets(android.graphics.Insets);
+    method public void startActivity(@NonNull android.content.Intent);
+    method public void startActivity(@NonNull android.content.Intent, android.os.UserHandle);
+    method public void startActivity(@NonNull android.app.PendingIntent);
+  }
+
+  public abstract static class ActivityView.StateCallback {
+    ctor public ActivityView.StateCallback();
+    method public abstract void onActivityViewDestroyed(android.app.ActivityView);
+    method public abstract void onActivityViewReady(android.app.ActivityView);
+    method public void onTaskCreated(int, android.content.ComponentName);
+    method public void onTaskMovedToFront(int);
+    method public void onTaskRemovalStarted(int);
+  }
+
   public class AppDetailsActivity extends android.app.Activity {
     ctor public AppDetailsActivity();
   }
@@ -446,7 +476,6 @@
     method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
     method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback);
-    field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
   }
 
   public interface RoleManagerCallback {
@@ -483,6 +512,17 @@
 
 package android.content {
 
+  public final class AutofillOptions implements android.os.Parcelable {
+    ctor public AutofillOptions(int, boolean);
+    method public int describeContents();
+    method public static android.content.AutofillOptions forWhitelistingItself();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.AutofillOptions> CREATOR;
+    field public boolean augmentedEnabled;
+    field public final boolean compatModeEnabled;
+    field public final int loggingLevel;
+  }
+
   public final class ContentCaptureOptions implements android.os.Parcelable {
     ctor public ContentCaptureOptions(int, int, int, int, int, @Nullable android.util.ArraySet<android.content.ComponentName>);
     method public int describeContents();
@@ -507,12 +547,17 @@
 
   public abstract class Context {
     method public android.content.Context createPackageContextAsUser(String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract android.view.Display getDisplay();
     method public android.os.UserHandle getUser();
     method public int getUserId();
-    method public void setAutofillCompatibilityEnabled(boolean);
+    method public void setAutofillOptions(@Nullable android.content.AutofillOptions);
     method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions);
   }
 
+  public class ContextWrapper extends android.content.Context {
+    method public android.view.Display getDisplay();
+  }
+
 }
 
 package android.content.pm {
@@ -719,7 +764,7 @@
   public final class BrightnessConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
-    method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(String);
+    method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String);
     method public android.util.Pair<float[],float[]> getCurve();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
@@ -727,8 +772,8 @@
 
   public static class BrightnessConfiguration.Builder {
     ctor public BrightnessConfiguration.Builder(float[], float[]);
-    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection);
-    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(String, android.hardware.display.BrightnessCorrection);
+    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, @NonNull android.hardware.display.BrightnessCorrection);
+    method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(@NonNull String, @NonNull android.hardware.display.BrightnessCorrection);
     method public android.hardware.display.BrightnessConfiguration build();
     method public int getMaxCorrectionsByCategory();
     method public int getMaxCorrectionsByPackageName();
@@ -736,7 +781,7 @@
   }
 
   public final class BrightnessCorrection implements android.os.Parcelable {
-    method public float apply(float);
+    method @FloatRange(from=0.0) public float apply(@FloatRange(from=0.0) float);
     method @NonNull public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -958,6 +1003,50 @@
 
 }
 
+package android.metrics {
+
+  public class LogMaker {
+    ctor public LogMaker(int);
+    ctor public LogMaker(Object[]);
+    method public android.metrics.LogMaker addTaggedData(int, Object);
+    method public android.metrics.LogMaker clearCategory();
+    method public android.metrics.LogMaker clearPackageName();
+    method public android.metrics.LogMaker clearSubtype();
+    method public android.metrics.LogMaker clearTaggedData(int);
+    method public android.metrics.LogMaker clearType();
+    method public void deserialize(Object[]);
+    method public int getCategory();
+    method public long getCounterBucket();
+    method public String getCounterName();
+    method public int getCounterValue();
+    method public String getPackageName();
+    method public int getProcessId();
+    method public int getSubtype();
+    method public Object getTaggedData(int);
+    method public long getTimestamp();
+    method public int getType();
+    method public int getUid();
+    method public boolean isLongCounterBucket();
+    method public boolean isSubsetOf(android.metrics.LogMaker);
+    method public boolean isValidValue(Object);
+    method public Object[] serialize();
+    method public android.metrics.LogMaker setCategory(int);
+    method public android.metrics.LogMaker setPackageName(String);
+    method public android.metrics.LogMaker setSubtype(int);
+    method public android.metrics.LogMaker setType(int);
+  }
+
+  public class MetricsReader {
+    ctor public MetricsReader();
+    method public void checkpoint();
+    method public boolean hasNext();
+    method public android.metrics.LogMaker next();
+    method public void read(long);
+    method public void reset();
+  }
+
+}
+
 package android.net {
 
   public class CaptivePortal implements android.os.Parcelable {
@@ -1302,6 +1391,7 @@
 
   public class Build {
     method public static boolean is64BitAbi(String);
+    field public static final boolean IS_EMULATOR;
   }
 
   public static class Build.VERSION {
@@ -1861,6 +1951,7 @@
   public static final class Settings.Global extends android.provider.Settings.NameValueTable {
     field public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
     field public static final String AUTOMATIC_POWER_SAVER_MODE = "automatic_power_saver_mode";
+    field public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants";
     field public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = "captive_portal_fallback_probe_specs";
     field public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url";
     field public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url";
@@ -1882,6 +1973,7 @@
     field public static final String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch";
     field public static final String LOW_POWER_MODE = "low_power";
     field public static final String LOW_POWER_MODE_STICKY = "low_power_sticky";
+    field public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices";
     field public static final String SMS_ACCESS_RESTRICTION_ENABLED = "sms_access_restriction_enabled";
     field public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
   }
@@ -1900,6 +1992,7 @@
     field public static final String CONTENT_CAPTURE_ENABLED = "content_capture_enabled";
     field public static final String DISABLED_PRINT_SERVICES = "disabled_print_services";
     field @Deprecated public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
+    field public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
     field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
     field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
     field public static final String NOTIFICATION_BADGING = "notification_badging";
@@ -2380,6 +2473,10 @@
     method public E valueAtUnchecked(int);
   }
 
+  public class TimeUtils {
+    method public static String formatDuration(long);
+  }
+
 }
 
 package android.util.proto {
@@ -2601,6 +2698,10 @@
     field public static final int CALLBACK_ANIMATION = 1; // 0x1
   }
 
+  public final class Display {
+    method public boolean supportsSystemDecorations();
+  }
+
   public class FocusFinder {
     method public static void sort(android.view.View[], int, int, android.view.ViewGroup, boolean);
   }
@@ -2762,11 +2863,13 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
     field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6
-    field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
-    field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
+    field public static final int TYPE_SESSION_PAUSED = 8; // 0x8
+    field public static final int TYPE_SESSION_RESUMED = 7; // 0x7
     field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
     field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2
     field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3
+    field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5
+    field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4
   }
 
   public final class ContentCaptureManager {
diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc
index 469c964..3666d6a 100644
--- a/cmds/bootanimation/bootanim.rc
+++ b/cmds/bootanimation/bootanim.rc
@@ -2,6 +2,9 @@
     class core animation
     user graphics
     group graphics audio
+    # bootanimation depends on libandroidicu in the Runtime APEX.
+    # TODO(b/124939955): Remove this dependency on libandroidicu
+    updatable
     disabled
     oneshot
     writepid /dev/stune/top-app/tasks
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index f408655..ca48881 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -346,3 +346,9 @@
         javacflags: ["-XepDisableAllChecks"],
     },
 }
+
+// Filegroup for statsd config proto definition.
+filegroup {
+    name: "statsd-config-proto-def",
+    srcs: ["src/statsd_config.proto"],
+}
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 4deb8bd..69fbf1f 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1200,9 +1200,9 @@
 
     userid_t userId = multiuser_get_user_id(uid);
 
-    bool requiresStaging = options | IStatsManager::FLAG_REQUIRE_STAGING;
-    bool rollbackEnabled = options | IStatsManager::FLAG_ROLLBACK_ENABLED;
-    bool requiresLowLatencyMonitor = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
+    bool requiresStaging = options & IStatsManager::FLAG_REQUIRE_STAGING;
+    bool rollbackEnabled = options & IStatsManager::FLAG_ROLLBACK_ENABLED;
+    bool requiresLowLatencyMonitor = options & IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
 
     ProtoOutputStream proto;
     for (const auto& expId : experimentIds) {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 78e994f..1dd68df 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -45,6 +45,7 @@
 import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto";
 import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto";
 import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
+import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
 import "frameworks/base/core/proto/android/telecomm/enums.proto";
 import "frameworks/base/core/proto/android/telephony/enums.proto";
 import "frameworks/base/core/proto/android/view/enums.proto";
@@ -247,6 +248,7 @@
         AssistGestureProgressReported assist_gesture_progress_reported = 176;
         TouchGestureClassified touch_gesture_classified = 177;
         HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true];
+        StyleUIChanged style_ui_changed = 179;
     }
 
     // Pulled events will start at field 10000.
@@ -283,7 +285,7 @@
         CategorySize category_size = 10028;
         ProcStats proc_stats = 10029;
         BatteryVoltage battery_voltage = 10030;
-        NumBiometricsEnrolled num_fingerprints_enrolled = 10031;
+        NumFingerprintsEnrolled num_fingerprints_enrolled = 10031;
         DiskIo disk_io = 10032;
         PowerProfile power_profile = 10033;
         ProcStatsPkgProc proc_stats_pkg_proc = 10034;
@@ -300,7 +302,7 @@
         BatteryCycleCount battery_cycle_count = 10045;
         DebugElapsedClock debug_elapsed_clock = 10046;
         DebugFailingElapsedClock debug_failing_elapsed_clock = 10047;
-        NumBiometricsEnrolled num_faces_enrolled = 10048;
+        NumFacesEnrolled num_faces_enrolled = 10048;
         RoleHolder role_holder = 10049;
         DangerousPermissionState dangerous_permission_state = 10050;
         TrainInfo train_info = 10051;
@@ -2349,6 +2351,17 @@
     optional bool is_swipe_up_enabled = 5;
 }
 
+message StyleUIChanged {
+    optional android.stats.style.Action action = 1;
+    optional int32 color_package_hash = 2;
+    optional int32 font_package_hash  = 3;
+    optional int32 shape_package_hash = 4;
+    optional int32 clock_package_hash = 5;
+    optional int32 launcher_grid = 6;
+    optional int32 wallpaper_category_hash = 7;
+    optional int32 wallpaper_id_hash = 8;
+}
+
 /**
  * Logs when Settings UI has changed.
  *
@@ -4114,15 +4127,27 @@
  *
  * Pulled from StatsCompanionService, which queries <Biometric>Manager.
  */
-message NumBiometricsEnrolled {
+message NumFingerprintsEnrolled {
     // The associated user. Eg: 0 for owners, 10+ for others.
     // Defined in android/os/UserHandle.java
     optional int32 user = 1;
     // Number of fingerprints registered to that user.
-    optional int32 num_enrolled = 2;
+    optional int32 num_fingerprints_enrolled = 2;
 }
 
 /**
+ * Pulls the number of faces for each user.
+ *
+ * Pulled from StatsCompanionService, which queries <Biometric>Manager.
+ */
+message NumFacesEnrolled {
+    // The associated user. Eg: 0 for owners, 10+ for others.
+    // Defined in android/os/UserHandle.java
+    optional int32 user = 1;
+    // Number of faces registered to that user.
+    optional int32 num_faces_enrolled = 2;
+}
+/**
  * A mapping of role holder -> role
  */
 message RoleHolder {
diff --git a/cmds/statsd/src/external/PowerStatsPuller.cpp b/cmds/statsd/src/external/PowerStatsPuller.cpp
index 71e5fa0..c56f9a2 100644
--- a/cmds/statsd/src/external/PowerStatsPuller.cpp
+++ b/cmds/statsd/src/external/PowerStatsPuller.cpp
@@ -39,17 +39,40 @@
 namespace os {
 namespace statsd {
 
-sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr;
-std::mutex gPowerStatsHalMutex;
-bool gPowerStatsExist = true; // Initialized to ensure making a first attempt.
-std::vector<RailInfo> gRailInfo;
+static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr;
+static std::mutex gPowerStatsHalMutex;
+static bool gPowerStatsExist = true; // Initialized to ensure making a first attempt.
+static std::vector<RailInfo> gRailInfo;
 
-bool getPowerStatsHal() {
+struct PowerStatsPullerDeathRecipient : virtual public hardware::hidl_death_recipient {
+    virtual void serviceDied(uint64_t cookie,
+            const wp<android::hidl::base::V1_0::IBase>& who) override {
+        // The HAL just died. Reset all handles to HAL services.
+        std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
+        gPowerStatsHal = nullptr;
+    }
+};
+
+static sp<PowerStatsPullerDeathRecipient> gDeathRecipient = new PowerStatsPullerDeathRecipient();
+
+static bool getPowerStatsHalLocked() {
     if (gPowerStatsHal == nullptr && gPowerStatsExist) {
         gPowerStatsHal = android::hardware::power::stats::V1_0::IPowerStats::getService();
         if (gPowerStatsHal == nullptr) {
             ALOGW("Couldn't load power.stats HAL service");
             gPowerStatsExist = false;
+        } else {
+            // Link death recipient to power.stats service handle
+            hardware::Return<bool> linked = gPowerStatsHal->linkToDeath(gDeathRecipient, 0);
+            if (!linked.isOk()) {
+                ALOGE("Transaction error in linking to power.stats HAL death: %s",
+                        linked.description().c_str());
+                gPowerStatsHal = nullptr;
+                return false;
+            } else if (!linked) {
+                ALOGW("Unable to link to power.stats HAL death notifications");
+                // We should still continue even though linking failed
+            }
         }
     }
     return gPowerStatsHal != nullptr;
@@ -61,7 +84,7 @@
 bool PowerStatsPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
     std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
 
-    if (!getPowerStatsHal()) {
+    if (!getPowerStatsHalLocked()) {
         ALOGE("power.stats Hal not loaded");
         return false;
     }
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
index d822959..f6a4aea 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
+++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
@@ -60,41 +60,43 @@
 namespace os {
 namespace statsd {
 
-std::function<bool(vector<shared_ptr<LogEvent>>* data)> gPuller = {};
+static std::function<bool(vector<shared_ptr<LogEvent>>* data)> gPuller = {};
 
-sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
-sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
-sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
+static sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
+static sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
+static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
 
-std::unordered_map<uint32_t, std::string> gEntityNames = {};
-std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {};
+static std::unordered_map<uint32_t, std::string> gEntityNames = {};
+static std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {};
 
-std::mutex gPowerHalMutex;
+static std::mutex gPowerHalMutex;
 
 // The caller must be holding gPowerHalMutex.
-void deinitPowerStatsLocked() {
+static void deinitPowerStatsLocked() {
     gPowerHalV1_0 = nullptr;
     gPowerHalV1_1 = nullptr;
     gPowerStatsHalV1_0 = nullptr;
 }
 
-struct PowerHalDeathRecipient : virtual public hardware::hidl_death_recipient {
+struct SubsystemSleepStatePullerDeathRecipient : virtual public hardware::hidl_death_recipient {
     virtual void serviceDied(uint64_t cookie,
-        const wp<android::hidl::base::V1_0::IBase>& who) override {
+            const wp<android::hidl::base::V1_0::IBase>& who) override {
+
         // The HAL just died. Reset all handles to HAL services.
         std::lock_guard<std::mutex> lock(gPowerHalMutex);
         deinitPowerStatsLocked();
     }
 };
 
-sp<PowerHalDeathRecipient> gDeathRecipient = new PowerHalDeathRecipient();
+static sp<SubsystemSleepStatePullerDeathRecipient> gDeathRecipient =
+        new SubsystemSleepStatePullerDeathRecipient();
 
 SubsystemSleepStatePuller::SubsystemSleepStatePuller() :
     StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) {
 }
 
 // The caller must be holding gPowerHalMutex.
-bool checkResultLocked(const Return<void> &ret, const char* function) {
+static bool checkResultLocked(const Return<void> &ret, const char* function) {
     if (!ret.isOk()) {
         ALOGE("%s failed: requested HAL service not available. Description: %s",
             function, ret.description().c_str());
@@ -108,7 +110,7 @@
 
 // The caller must be holding gPowerHalMutex.
 // gPowerStatsHalV1_0 must not be null
-bool initializePowerStats() {
+static bool initializePowerStats() {
     using android::hardware::power::stats::V1_0::Status;
 
     // Clear out previous content if we are re-initializing
@@ -155,7 +157,7 @@
 }
 
 // The caller must be holding gPowerHalMutex.
-bool getPowerStatsHalLocked() {
+static bool getPowerStatsHalLocked() {
     if(gPowerStatsHalV1_0 == nullptr) {
         gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService();
         if (gPowerStatsHalV1_0 == nullptr) {
@@ -180,7 +182,7 @@
 }
 
 // The caller must be holding gPowerHalMutex.
-bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) {
+static bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) {
     using android::hardware::power::stats::V1_0::Status;
 
     if(!getPowerStatsHalLocked()) {
@@ -225,7 +227,7 @@
 }
 
 // The caller must be holding gPowerHalMutex.
-bool getPowerHalLocked() {
+static bool getPowerHalLocked() {
     if(gPowerHalV1_0 == nullptr) {
         gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
         if(gPowerHalV1_0 == nullptr) {
@@ -250,7 +252,7 @@
 }
 
 // The caller must be holding gPowerHalMutex.
-bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) {
+static bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) {
     using android::hardware::power::V1_0::Status;
 
     if(!getPowerHalLocked()) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index e7f7af2..9de62a2 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -155,7 +155,7 @@
     mCurrentBucketStartTimeNs = startTimeNs;
     // Kicks off the puller immediately if condition is true and diff based.
     if (mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
-        pullAndMatchEventsLocked(startTimeNs);
+        pullAndMatchEventsLocked(startTimeNs, mCondition);
     }
     VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mTimeBaseNs);
@@ -174,13 +174,17 @@
 }
 
 void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
-    flushIfNeededLocked(dropTimeNs);
     StatsdStats::getInstance().noteBucketDropped(mMetricId);
-    mPastBuckets.clear();
+    // We are going to flush the data without doing a pull first so we need to invalidte the data.
+    bool pullNeeded = mIsPulled && mCondition == ConditionState::kTrue;
+    if (pullNeeded) {
+        invalidateCurrentBucket();
+    }
+    flushIfNeededLocked(dropTimeNs);
+    clearPastBucketsLocked(dropTimeNs);
 }
 
 void ValueMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
-    flushIfNeededLocked(dumpTimeNs);
     mPastBuckets.clear();
     mSkippedBuckets.clear();
 }
@@ -192,7 +196,6 @@
                                              std::set<string> *str_set,
                                              ProtoOutputStream* protoOutput) {
     VLOG("metric %lld dump report now...", (long long)mMetricId);
-    flushIfNeededLocked(dumpTimeNs);
     if (include_current_partial_bucket) {
         // For pull metrics, we need to do a pull at bucket boundaries. If we do not do that the
         // current bucket will have incomplete data and the next will have the wrong snapshot to do
@@ -205,10 +208,10 @@
                     invalidateCurrentBucket();
                     break;
                 case NO_TIME_CONSTRAINTS:
-                    pullAndMatchEventsLocked(dumpTimeNs);
+                    pullAndMatchEventsLocked(dumpTimeNs, mCondition);
                     break;
             }
-        } 
+        }
         flushCurrentBucketLocked(dumpTimeNs, dumpTimeNs);
     }
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
@@ -325,12 +328,16 @@
     }
 }
 
-void ValueMetricProducer::invalidateCurrentBucket() {
+void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase() {
     if (!mCurrentBucketIsInvalid) {
         // Only report once per invalid bucket.
         StatsdStats::getInstance().noteInvalidatedBucket(mMetricId);
     }
     mCurrentBucketIsInvalid = true;
+}
+
+void ValueMetricProducer::invalidateCurrentBucket() {
+    invalidateCurrentBucketWithoutResetBase();
     resetBase();
 }
 
@@ -345,82 +352,112 @@
 
 void ValueMetricProducer::onConditionChangedLocked(const bool condition,
                                                    const int64_t eventTimeNs) {
-    if (eventTimeNs < mCurrentBucketStartTimeNs) {
+    bool isEventTooLate  = eventTimeNs < mCurrentBucketStartTimeNs;
+    if (!isEventTooLate) {
+        if (mCondition == ConditionState::kUnknown) {
+            // If the condition was unknown, we mark the bucket as invalid since the bucket will
+            // contain partial data. For instance, the condition change might happen close to the
+            // end of the bucket and we might miss lots of data.
+            //
+            // We still want to pull to set the base.
+            invalidateCurrentBucket();
+        }
+
+        // Pull on condition changes.
+        ConditionState newCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
+        bool conditionChanged =
+                (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse)
+                || (mCondition == ConditionState::kFalse && newCondition == ConditionState::kTrue);
+        // We do not need to pull when we go from unknown to false.
+        //
+        // We also pull if the condition was already true in order to be able to flush the bucket at
+        // the end if needed.
+        //
+        // onConditionChangedLocked might happen on bucket boundaries if this is called before
+        // #onDataPulled.
+        if (mIsPulled && (conditionChanged || condition)) {
+            pullAndMatchEventsLocked(eventTimeNs, newCondition);
+        }
+
+        // When condition change from true to false, clear diff base but don't
+        // reset other counters as we may accumulate more value in the bucket.
+        if (mUseDiff && mCondition == ConditionState::kTrue
+                && newCondition == ConditionState::kFalse) {
+            resetBase();
+        }
+        mCondition = newCondition;
+
+    } else {
         VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
              (long long)mCurrentBucketStartTimeNs);
         StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
         invalidateCurrentBucket();
-        return;
+        // Something weird happened. If we received another event if the future, the condition might
+        // be wrong.
+        mCondition = ConditionState::kUnknown;
     }
 
+    // This part should alway be called.
     flushIfNeededLocked(eventTimeNs);
-
-    // Pull on condition changes.
-    bool conditionChanged = mCondition != condition;
-    bool unknownToFalse = mCondition == ConditionState::kUnknown
-            && condition == ConditionState::kFalse;
-    // We do not need to pull when we go from unknown to false.
-    if (mIsPulled && conditionChanged && !unknownToFalse) {
-        pullAndMatchEventsLocked(eventTimeNs);
-    }
-
-    // when condition change from true to false, clear diff base but don't
-    // reset other counters as we may accumulate more value in the bucket.
-    if (mUseDiff && mCondition == ConditionState::kTrue && condition == ConditionState::kFalse) {
-        resetBase();
-    }
-
-    mCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
 }
 
-void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
+void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition) {
     vector<std::shared_ptr<LogEvent>> allData;
     if (!mPullerManager->Pull(mPullTagId, &allData)) {
-        ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
+        ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
         invalidateCurrentBucket();
         return;
     }
 
-    accumulateEvents(allData, timestampNs, timestampNs);
+    accumulateEvents(allData, timestampNs, timestampNs, condition);
 }
 
 int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) {
     return mTimeBaseNs + ((currentTimeNs - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs;
 }
 
+// By design, statsd pulls data at bucket boundaries using AlarmManager. These pulls are likely
+// to be delayed. Other events like condition changes or app upgrade which are not based on
+// AlarmManager might have arrived earlier and close the bucket.
 void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData,
                                        bool pullSuccess, int64_t originalPullTimeNs) {
     std::lock_guard<std::mutex> lock(mMutex);
-    if (mCondition == ConditionState::kTrue) {
-        if (!pullSuccess) {
+        if (mCondition == ConditionState::kTrue) {
             // If the pull failed, we won't be able to compute a diff.
-            invalidateCurrentBucket();
-            return;
+            if (!pullSuccess) {
+                invalidateCurrentBucket();
+            } else {
+                bool isEventLate = originalPullTimeNs < getCurrentBucketEndTimeNs();
+                if (isEventLate) {
+                    // If the event is late, we are in the middle of a bucket. Just
+                    // process the data without trying to snap the data to the nearest bucket.
+                    accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs, mCondition);
+                } else {
+                    // For scheduled pulled data, the effective event time is snap to the nearest
+                    // bucket end. In the case of waking up from a deep sleep state, we will
+                    // attribute to the previous bucket end. If the sleep was long but not very
+                    // long, we will be in the immediate next bucket. Previous bucket may get a
+                    // larger number as we pull at a later time than real bucket end.
+                    //
+                    // If the sleep was very long, we skip more than one bucket before sleep. In
+                    // this case, if the diff base will be cleared and this new data will serve as
+                    // new diff base.
+                    int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1;
+                    StatsdStats::getInstance().noteBucketBoundaryDelayNs(
+                            mMetricId, originalPullTimeNs - bucketEndTime);
+                    accumulateEvents(allData, originalPullTimeNs, bucketEndTime, mCondition);
+                }
+            }
         }
 
-        // For scheduled pulled data, the effective event time is snap to the nearest
-        // bucket end. In the case of waking up from a deep sleep state, we will
-        // attribute to the previous bucket end. If the sleep was long but not very long, we
-        // will be in the immediate next bucket. Previous bucket may get a larger number as
-        // we pull at a later time than real bucket end.
-        // If the sleep was very long, we skip more than one bucket before sleep. In this case,
-        // if the diff base will be cleared and this new data will serve as new diff base.
-        int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1;
-        StatsdStats::getInstance().noteBucketBoundaryDelayNs(
-                mMetricId, originalPullTimeNs - bucketEndTime);
-        accumulateEvents(allData, originalPullTimeNs, bucketEndTime);
-
-        // We can probably flush the bucket. Since we used bucketEndTime when calling
-        // #onMatchedLogEventInternalLocked, the current bucket will not have been flushed.
-        flushIfNeededLocked(originalPullTimeNs);
-
-    } else {
-        VLOG("No need to commit data on condition false.");
-    }
+    // We can probably flush the bucket. Since we used bucketEndTime when calling
+    // #onMatchedLogEventInternalLocked, the current bucket will not have been flushed.
+    flushIfNeededLocked(originalPullTimeNs);
 }
 
-void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, 
-                                           int64_t originalPullTimeNs, int64_t eventElapsedTimeNs) {
+void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
+                                           int64_t originalPullTimeNs, int64_t eventElapsedTimeNs,
+                                           ConditionState condition) {
     bool isEventLate = eventElapsedTimeNs < mCurrentBucketStartTimeNs;
     if (isEventLate) {
         VLOG("Skip bucket end pull due to late arrival: %lld vs %lld",
@@ -459,7 +496,7 @@
     // If the new pulled data does not contains some keys we track in our intervals, we need to
     // reset the base.
     for (auto& slice : mCurrentSlicedBucket) {
-        bool presentInPulledData = mMatchedMetricDimensionKeys.find(slice.first) 
+        bool presentInPulledData = mMatchedMetricDimensionKeys.find(slice.first)
                 != mMatchedMetricDimensionKeys.end();
         if (!presentInPulledData) {
             for (auto& interval : slice.second) {
@@ -583,7 +620,10 @@
     }
     mMatchedMetricDimensionKeys.insert(eventKey);
 
-    flushIfNeededLocked(eventTimeNs);
+    if (!mIsPulled) {
+        // We cannot flush without doing a pull first.
+        flushIfNeededLocked(eventTimeNs);
+    }
 
     // For pulled data, we already check condition when we decide to pull or
     // in onDataPulled. So take all of them.
@@ -718,26 +758,26 @@
     }
 }
 
+// For pulled metrics, we always need to make sure we do a pull before flushing the bucket
+// if mCondition is true!
 void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
     int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
-
     if (eventTimeNs < currentBucketEndTimeNs) {
         VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
              (long long)(currentBucketEndTimeNs));
         return;
     }
-
-    int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
+    int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
     int64_t nextBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
     flushCurrentBucketLocked(eventTimeNs, nextBucketStartTimeNs);
+}
 
-    mCurrentBucketNum += numBucketsForward;
-    if (numBucketsForward > 1) {
-        VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
-        StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId);
-        // take base again in future good bucket.
-        resetBase();
+int64_t ValueMetricProducer::calcBucketsForwardCount(const int64_t& eventTimeNs) const {
+    int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
+    if (eventTimeNs < currentBucketEndTimeNs) {
+        return 0;
     }
+    return 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
 }
 
 void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
@@ -746,6 +786,16 @@
         StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
     }
 
+    int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
+    mCurrentBucketNum += numBucketsForward;
+    if (numBucketsForward > 1) {
+        VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
+        StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId);
+        // Something went wrong. Maybe the device was sleeping for a long time. It is better
+        // to mark the current bucket as invalid. The last pull might have been successful through.
+        invalidateCurrentBucketWithoutResetBase();
+    }
+
     VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
          (int)mCurrentSlicedBucket.size());
     int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
@@ -769,12 +819,7 @@
     if (!mCurrentBucketIsInvalid) {
         appendToFullBucket(eventTimeNs, fullBucketEndTimeNs);
     }
-    StatsdStats::getInstance().noteBucketCount(mMetricId);
-    initCurrentSlicedBucket();
-    mCurrentBucketIsInvalid = false;
-    mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
-    VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
-         (long long)mCurrentBucketStartTimeNs);
+    initCurrentSlicedBucket(nextBucketStartTimeNs);
 }
 
 ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
@@ -801,7 +846,9 @@
     return bucket;
 }
 
-void ValueMetricProducer::initCurrentSlicedBucket() {
+void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs) {
+    StatsdStats::getInstance().noteBucketCount(mMetricId);
+    // Cleanup data structure to aggregate values.
     for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
         bool obsolete = true;
         for (auto& interval : it->second) {
@@ -819,6 +866,16 @@
             it++;
         }
     }
+
+    mCurrentBucketIsInvalid = false;
+    // If we do not have a global base when the condition is true,
+    // we will have incomplete bucket for the next bucket.
+    if (mUseDiff && !mHasGlobalBase && mCondition) {
+        mCurrentBucketIsInvalid = false;
+    }
+    mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
+    VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
+         (long long)mCurrentBucketStartTimeNs);
 }
 
 void ValueMetricProducer::appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 696d4fa..f317c37 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -39,6 +39,13 @@
     std::vector<Value> values;
 };
 
+
+// Aggregates values within buckets.
+//
+// There are different events that might complete a bucket
+// - a condition change
+// - an app upgrade
+// - an alarm set to the end of the bucket
 class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
 public:
     ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
@@ -61,9 +68,8 @@
         if (!mSplitBucketForAppUpgrade) {
             return;
         }
-        flushIfNeededLocked(eventTimeNs - 1);
         if (mIsPulled && mCondition) {
-            pullAndMatchEventsLocked(eventTimeNs - 1);
+            pullAndMatchEventsLocked(eventTimeNs, mCondition);
         }
         flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
     };
@@ -94,9 +100,12 @@
 
     void dumpStatesLocked(FILE* out, bool verbose) const override;
 
-    // Util function to flush the old packet.
+    // For pulled metrics, this method should only be called if a pull has be done. Else we will
+    // not have complete data for the bucket.
     void flushIfNeededLocked(const int64_t& eventTime) override;
 
+    // For pulled metrics, this method should only be called if a pulled have be done. Else we will
+    // not have complete data for the bucket.
     void flushCurrentBucketLocked(const int64_t& eventTimeNs,
                                   const int64_t& nextBucketStartTimeNs) override;
 
@@ -105,8 +114,12 @@
     // Calculate previous bucket end time based on current time.
     int64_t calcPreviousBucketEndTime(const int64_t currentTimeNs);
 
+    // Calculate how many buckets are present between the current bucket and eventTimeNs.
+    int64_t calcBucketsForwardCount(const int64_t& eventTimeNs) const;
+
     // Mark the data as invalid.
     void invalidateCurrentBucket();
+    void invalidateCurrentBucketWithoutResetBase();
 
     const int mWhatMatcherIndex;
 
@@ -163,14 +176,15 @@
 
     bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey);
 
-    void pullAndMatchEventsLocked(const int64_t timestampNs);
+    void pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition);
 
     void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, 
-                          int64_t originalPullTimeNs, int64_t eventElapsedTimeNs);
+                          int64_t originalPullTimeNs, int64_t eventElapsedTimeNs,
+                          ConditionState condition);
 
     ValueBucket buildPartialBucket(int64_t bucketEndTime,
                                    const std::vector<Interval>& intervals);
-    void initCurrentSlicedBucket();
+    void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs);
     void appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs);
 
     // Reset diff base and mHasGlobalBase
@@ -214,47 +228,55 @@
 
     const bool mSplitBucketForAppUpgrade;
 
-    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
-    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
-    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
-    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
-    FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
-    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
-    FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
-    FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated);
-    FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse);
-    FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled);
-    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
-    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
+    FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange);
+    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade);
+    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange);
     FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2);
-    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin);
-    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax);
-    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
-    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
-    FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
+    FRIEND_TEST(ValueMetricProducerTest, TestBucketIncludingUnknownConditionIsInvalid);
+    FRIEND_TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet);
     FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime);
-    FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
-    FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue);
-    FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
-    FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
-    FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
-    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange);
-    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange);
-    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket);
-    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
-    FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed);
+    FRIEND_TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged);
+    FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary);
+    FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged);
+    FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled);
+    FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
+    FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
     FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit);
     FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed);
     FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed);
-    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded);
-    FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange);
-    FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled);
-    FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged);
-    FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary);
+    FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed);
+    FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff);
+    FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff);
+    FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated);
     FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries);
+    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
+    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
+    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
+    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
+    FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
+    FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse);
+    FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled);
+    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
+    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax);
+    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin);
+    FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
+    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition);
+    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
+    FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
+    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded);
+    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange);
+    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket);
+    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange);
+    FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
+    FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
+    FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue);
+    FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
+    FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
+    FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
+    friend class ValueMetricProducerTestHelper;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index a9d2c88..e5e4534 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -52,15 +52,100 @@
 const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs;
 double epsilon = 0.001;
 
+static void assertPastBucketValuesSingleKey(
+        const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets,
+        const std::initializer_list<int>& expectedValuesList) {
+    std::vector<int> expectedValues(expectedValuesList);
+    if (expectedValues.size() == 0) {
+        ASSERT_EQ(0, mPastBuckets.size());
+        return;
+    }
+
+    ASSERT_EQ(1, mPastBuckets.size());
+    ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size());
+
+    auto buckets = mPastBuckets.begin()->second;
+    for (int i = 0; i < expectedValues.size(); i++) {
+        EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value)
+                << "Values differ at index " << i;
+    }
+}
+
+
+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 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;
+    }
+};
+
+
 /*
  * Tests that the first bucket works correctly
  */
 TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
     int64_t startTimeBase = 11;
     UidMap uidMap;
@@ -90,11 +175,7 @@
  * Tests that the first bucket works correctly
  */
 TEST(ValueMetricProducerTest, TestFirstBucket) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -120,23 +201,8 @@
  * Tests pulled atoms with no conditions
  */
 TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
-    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);
-
-    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>();
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 
     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();
@@ -148,9 +214,8 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -160,17 +225,17 @@
     event->init();
     allData.push_back(event);
 
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
 
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(11, curInterval.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(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -178,19 +243,19 @@
     event->write(23);
     event->init();
     allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
 
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(23, curInterval.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(12, valueProducer.mPastBuckets.begin()->second.back().values[0].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(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -198,39 +263,24 @@
     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];
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
 
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(36, curInterval.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(12, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value);
-    EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second[2].values[0].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(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
+    EXPECT_EQ(13, valueProducer->mPastBuckets.begin()->second[2].values[0].long_value);
 }
 
 TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
-    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);
-
-    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>();
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // Initialize bucket.
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
@@ -253,9 +303,8 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
 
     // First bucket ends.
     vector<shared_ptr<LogEvent>> allData;
@@ -265,14 +314,14 @@
     event->write(2);
     event->init();
     allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** success */ true, bucket2StartTimeNs);
+    valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs);
 
     // Partial buckets created in 2nd bucket.
-    valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
+    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(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);
@@ -284,12 +333,7 @@
  * Tests pulled atoms with filtering
  */
 TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -315,9 +359,10 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+    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();
@@ -327,18 +372,18 @@
     event->init();
     allData.push_back(event);
 
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
 
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(11, curInterval.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(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -346,16 +391,16 @@
     event->write(23);
     event->init();
     allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // No new data seen, so data has been cleared.
-    EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
 
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(11, curInterval.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(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -363,46 +408,30 @@
     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];
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
 
     // the base was reset
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(36, curInterval.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(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
+    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
 }
 
 /*
  * Tests pulled atoms with no conditions and take absolute value after reset
  */
 TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_use_absolute_value_on_reset(true);
-    metric.set_max_pull_delay_sec(INT_MAX);
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
-
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -412,15 +441,15 @@
     event->init();
     allData.push_back(event);
 
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
 
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(11, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -428,16 +457,16 @@
     event->write(10);
     event->init();
     allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(10, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(10, curInterval.value.long_value);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -445,45 +474,28 @@
     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];
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(36, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(26, curInterval.value.long_value);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
-    EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value);
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
+    EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
 }
 
 /*
  * Tests pulled atoms with no conditions and take zero value after reset
  */
 TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
-    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);
-
-    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>();
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 
     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(Return(false));
-
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -493,15 +505,15 @@
     event->init();
     allData.push_back(event);
 
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
 
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(11, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -509,14 +521,14 @@
     event->write(10);
     event->init();
     allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(10, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -524,39 +536,24 @@
     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];
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(36, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(26, curInterval.value.long_value);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
 }
 
 /*
  * Test pulled event with non sliced condition.
  */
 TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
 
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
@@ -572,25 +569,25 @@
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
                 event->write(tagId);
-                event->write(120);
+                event->write(130);
                 event->init();
                 data->push_back(event);
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+    sp<ValueMetricProducer> valueProducer =
+            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];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     // startUpdated:false sum:0 start:100
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(100, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -599,34 +596,30 @@
     event->write(110);
     event->init();
     allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10});
 
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(110, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(10, curInterval.value.long_value);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
 
-    valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
+    valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10});
 
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasValue);
-    EXPECT_EQ(10, curInterval.value.long_value);
+    EXPECT_EQ(20, curInterval.value.long_value);
     EXPECT_EQ(false, curInterval.hasBase);
 }
 
 TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -670,12 +663,7 @@
 }
 
 TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -733,12 +721,7 @@
 }
 
 TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 
     metric.set_split_bucket_for_app_upgrade(false);
 
     UidMap uidMap;
@@ -773,24 +756,9 @@
 }
 
 TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    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>();
-    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();
@@ -810,29 +778,25 @@
                 data->push_back(event);
                 return true;
             }));
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    valueProducer.onConditionChanged(false, bucket2StartTimeNs-100);
-    EXPECT_FALSE(valueProducer.mCondition);
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
 
-    valueProducer.notifyAppUpgrade(bucket2StartTimeNs-50, "ANY.APP", 1, 1);
+    valueProducer->onConditionChanged(false, bucket2StartTimeNs-100);
+    EXPECT_FALSE(valueProducer->mCondition);
+
+    valueProducer->notifyAppUpgrade(bucket2StartTimeNs-50, "ANY.APP", 1, 1);
     // Expect one full buckets already done and starting a partial bucket.
-    EXPECT_EQ(bucket2StartTimeNs-50, valueProducer.mCurrentBucketStartTimeNs);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-    EXPECT_EQ(bucketStartTimeNs, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
-    EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
-    EXPECT_FALSE(valueProducer.mCondition);
+    EXPECT_EQ(bucket2StartTimeNs-50, valueProducer->mCurrentBucketStartTimeNs);
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(bucketStartTimeNs, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
+    EXPECT_EQ(20L, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
+    EXPECT_FALSE(valueProducer->mCondition);
 }
 
 TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -869,18 +833,12 @@
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(30, curInterval.value.long_value);
 
-    valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+    valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30});
 }
 
 TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -894,6 +852,7 @@
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
                                       eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
+    valueProducer.mCondition = ConditionState::kFalse;
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -939,10 +898,8 @@
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(50, curInterval.value.long_value);
 
-    valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(50, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+    valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50});
 }
 
 TEST(ValueMetricProducerTest, TestAnomalyDetection) {
@@ -955,11 +912,7 @@
     const int32_t refPeriodSec = 3;
     alert.set_refractory_period_secs(refPeriodSec);
 
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -1036,28 +989,11 @@
 
 // Test value metric no condition, the pull on bucket boundary come in time and too late
 TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
-    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);
-
-    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>();
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 
     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(Return(true));
-
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
 
     vector<shared_ptr<LogEvent>> allData;
     // pull 1
@@ -1068,16 +1004,16 @@
     event->init();
     allData.push_back(event);
 
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
 
     // startUpdated:true sum:0 start:11
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(11, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
     // pull 2 at correct time
     allData.clear();
@@ -1086,16 +1022,15 @@
     event->write(23);
     event->init();
     allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     // tartUpdated:false sum:12
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(23, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12});
 
     // pull 3 come late.
     // The previous bucket gets closed with error. (Has start value 23, no ending)
@@ -1107,16 +1042,14 @@
     event->write(36);
     event->init();
     allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     // startUpdated:false sum:12
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(36, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12});
 }
 
 /*
@@ -1124,25 +1057,9 @@
  * was delivered late.
  */
 TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // condition becomes true
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
@@ -1164,44 +1081,35 @@
                 data->push_back(event);
                 return true;
             }));
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
 
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(100, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
     // pull on bucket boundary come late, condition change happens before it
-    valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
     EXPECT_EQ(false, curInterval.hasBase);
-    EXPECT_EQ(true, curInterval.hasValue);
-    EXPECT_EQ(20, curInterval.value.long_value);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // Now the alarm is delivered.
     // since the condition turned to off before this pull finish, it has no effect
     vector<shared_ptr<LogEvent>> allData;
-    allData.clear();
-    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 30);
-    event->write(1);
-    event->write(110);
-    event->init();
-    allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 110));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
 
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(false, curInterval.hasBase);
-    EXPECT_EQ(true, curInterval.hasValue);
-    EXPECT_EQ(20, curInterval.value.long_value);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(false, curInterval.hasValue);
 }
 
 /*
@@ -1209,25 +1117,9 @@
  * change to false, and then true again. This is due to alarm delivered late.
  */
 TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillRepeatedly(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // condition becomes true
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
@@ -1260,61 +1152,57 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+    sp<ValueMetricProducer> valueProducer =
+            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];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     // startUpdated:false sum:0 start:100
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(100, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
     // pull on bucket boundary come late, condition change happens before it
-    valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(false, curInterval.hasBase);
-    EXPECT_EQ(true, curInterval.hasValue);
-    EXPECT_EQ(20, curInterval.value.long_value);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(false, curInterval.hasValue);
 
     // condition changed to true again, before the pull alarm is delivered
-    valueProducer.onConditionChanged(true, bucket2StartTimeNs + 25);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(130, curInterval.base.long_value);
-    EXPECT_EQ(true, curInterval.hasValue);
-    EXPECT_EQ(20, curInterval.value.long_value);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(false, curInterval.hasValue);
 
-    // Now the alarm is delivered, but it is considered late, the bucket is invalidated.
+    // Now the alarm is delivered, but it is considered late, the data will be used
+    // for the new bucket since it was just pulled.
     vector<shared_ptr<LogEvent>> allData;
-    allData.clear();
-    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50);
-    event->write(1);
-    event->write(110);
-    event->init();
-    allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 50, 140));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
 
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(false, curInterval.hasBase);
-    EXPECT_EQ(130, curInterval.base.long_value);
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(140, curInterval.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
-    EXPECT_EQ(20, curInterval.value.long_value);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(10, curInterval.value.long_value);
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20});
+
+    allData.clear();
+    allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs, 160));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30});
 }
 
 TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_aggregation_type(ValueMetric::MIN);
 
     UidMap uidMap;
@@ -1352,18 +1240,12 @@
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(10, curInterval.value.long_value);
 
-    valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+    valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10});
 }
 
 TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_aggregation_type(ValueMetric::MAX);
 
     UidMap uidMap;
@@ -1402,17 +1284,13 @@
     EXPECT_EQ(20, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+    /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); */
+    /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); */
+    /* EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); */
 }
 
 TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_aggregation_type(ValueMetric::AVG);
 
     UidMap uidMap;
@@ -1453,18 +1331,14 @@
     EXPECT_EQ(25, curInterval.value.long_value);
     EXPECT_EQ(2, curInterval.sampleSize);
 
-    valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+    valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
     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 - 12.5) < epsilon);
 }
 
 TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_aggregation_type(ValueMetric::SUM);
 
     UidMap uidMap;
@@ -1502,18 +1376,12 @@
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(25, curInterval.value.long_value);
 
-    valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(25, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+    valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25});
 }
 
 TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_aggregation_type(ValueMetric::MIN);
     metric.set_use_diff(true);
 
@@ -1584,11 +1452,7 @@
 }
 
 TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.mutable_value_field()->add_child()->set_field(3);
     metric.set_aggregation_type(ValueMetric::MIN);
     metric.set_use_diff(true);
@@ -1694,26 +1558,12 @@
  * Tests zero default base.
  */
 TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.mutable_dimensions_in_what()->set_field(tagId);
     metric.mutable_dimensions_in_what()->add_child()->set_field(1);
     metric.set_use_zero_default_base(true);
-    metric.set_max_pull_delay_sec(INT_MAX);
 
-    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>();
-    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();
@@ -1725,19 +1575,18 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
 
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    auto iter = valueProducer.mCurrentSlicedBucket.begin();
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    auto iter = valueProducer->mCurrentSlicedBucket.begin();
     auto& interval1 = iter->second[0];
     EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, interval1.hasBase);
     EXPECT_EQ(3, interval1.base.long_value);
     EXPECT_EQ(false, interval1.hasValue);
-    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
     vector<shared_ptr<LogEvent>> allData;
 
     allData.clear();
@@ -1752,15 +1601,15 @@
     allData.push_back(event1);
     allData.push_back(event2);
 
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-    EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
     EXPECT_EQ(true, interval1.hasBase);
     EXPECT_EQ(11, interval1.base.long_value);
     EXPECT_EQ(false, interval1.hasValue);
     EXPECT_EQ(8, interval1.value.long_value);
 
-    auto it = valueProducer.mCurrentSlicedBucket.begin();
-    for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) {
+    auto it = valueProducer->mCurrentSlicedBucket.begin();
+    for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
         if (it != iter) {
             break;
         }
@@ -1773,8 +1622,8 @@
     EXPECT_EQ(false, interval2.hasValue);
     EXPECT_EQ(4, interval2.value.long_value);
 
-    EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
-    auto iterator = valueProducer.mPastBuckets.begin();
+    EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
+    auto iterator = valueProducer->mPastBuckets.begin();
     EXPECT_EQ(8, iterator->second[0].values[0].long_value);
     iterator++;
     EXPECT_EQ(4, iterator->second[0].values[0].long_value);
@@ -1784,26 +1633,12 @@
  * Tests using zero default base with failed pull.
  */
 TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.mutable_dimensions_in_what()->set_field(tagId);
     metric.mutable_dimensions_in_what()->add_child()->set_field(1);
     metric.set_use_zero_default_base(true);
-    metric.set_max_pull_delay_sec(INT_MAX);
 
-    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>();
-    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();
@@ -1815,19 +1650,18 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
 
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    auto iter = valueProducer.mCurrentSlicedBucket.begin();
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    auto iter = valueProducer->mCurrentSlicedBucket.begin();
     auto& interval1 = iter->second[0];
     EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, interval1.hasBase);
     EXPECT_EQ(3, interval1.base.long_value);
     EXPECT_EQ(false, interval1.hasValue);
-    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
     vector<shared_ptr<LogEvent>> allData;
 
     allData.clear();
@@ -1842,15 +1676,15 @@
     allData.push_back(event1);
     allData.push_back(event2);
 
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-    EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
     EXPECT_EQ(true, interval1.hasBase);
     EXPECT_EQ(11, interval1.base.long_value);
     EXPECT_EQ(false, interval1.hasValue);
     EXPECT_EQ(8, interval1.value.long_value);
 
-    auto it = valueProducer.mCurrentSlicedBucket.begin();
-    for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) {
+    auto it = valueProducer->mCurrentSlicedBucket.begin();
+    for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
         if (it != iter) {
             break;
         }
@@ -1862,7 +1696,7 @@
     EXPECT_EQ(4, interval2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
     EXPECT_EQ(4, interval2.value.long_value);
-    EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
 
     // next pull somehow did not happen, skip to end of bucket 3
     allData.clear();
@@ -1871,16 +1705,14 @@
     event1->write(5);
     event1->init();
     allData.push_back(event1);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
 
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     EXPECT_EQ(true, interval2.hasBase);
-    EXPECT_EQ(4, interval2.base.long_value);
+    EXPECT_EQ(5, interval2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
-    EXPECT_EQ(true, interval1.hasBase);
-    EXPECT_EQ(false, interval1.hasValue);
-    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
-    EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+    EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
 
     allData.clear();
     event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
@@ -1893,44 +1725,32 @@
     event2->write(5);
     event2->init();
     allData.push_back(event2);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
 
-    EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
-    EXPECT_EQ(true, interval2.hasBase);
-    EXPECT_EQ(5, interval2.base.long_value);
-    EXPECT_EQ(false, interval2.hasValue);
-    EXPECT_EQ(5, interval2.value.long_value);
-    EXPECT_EQ(true, interval1.hasBase);
-    EXPECT_EQ(13, interval1.base.long_value);
-    EXPECT_EQ(false, interval1.hasValue);
-    EXPECT_EQ(8, interval1.value.long_value);
-    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
-    EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    auto it1 = std::next(valueProducer->mCurrentSlicedBucket.begin())->second[0];
+    EXPECT_EQ(true, it1.hasBase);
+    EXPECT_EQ(13, it1.base.long_value);
+    EXPECT_EQ(false, it1.hasValue);
+    EXPECT_EQ(8, it1.value.long_value);
+    auto it2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(true, it2.hasBase);
+    EXPECT_EQ(5, it2.base.long_value);
+    EXPECT_EQ(false, it2.hasValue);
+    EXPECT_EQ(5, it2.value.long_value);
+    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+    EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
 }
 
 /*
  * Tests trim unused dimension key if no new data is seen in an entire bucket.
  */
 TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.mutable_dimensions_in_what()->set_field(tagId);
     metric.mutable_dimensions_in_what()->add_child()->set_field(1);
-    metric.set_max_pull_delay_sec(INT_MAX);
 
-    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>();
-    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();
@@ -1942,18 +1762,17 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
 
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    auto iter = valueProducer.mCurrentSlicedBucket.begin();
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    auto iter = valueProducer->mCurrentSlicedBucket.begin();
     auto& interval1 = iter->second[0];
     EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, interval1.hasBase);
     EXPECT_EQ(3, interval1.base.long_value);
     EXPECT_EQ(false, interval1.hasValue);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
     vector<shared_ptr<LogEvent>> allData;
 
     allData.clear();
@@ -1968,18 +1787,17 @@
     allData.push_back(event1);
     allData.push_back(event2);
 
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-    EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
     EXPECT_EQ(true, interval1.hasBase);
     EXPECT_EQ(11, interval1.base.long_value);
     EXPECT_EQ(false, interval1.hasValue);
     EXPECT_EQ(8, interval1.value.long_value);
     EXPECT_FALSE(interval1.seenNewData);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8});
 
-    auto it = valueProducer.mCurrentSlicedBucket.begin();
-    for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) {
+    auto it = valueProducer->mCurrentSlicedBucket.begin();
+    for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
         if (it != iter) {
             break;
         }
@@ -1991,7 +1809,7 @@
     EXPECT_EQ(4, interval2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
     EXPECT_FALSE(interval2.seenNewData);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8});
 
     // next pull somehow did not happen, skip to end of bucket 3
     allData.clear();
@@ -2000,17 +1818,16 @@
     event1->write(5);
     event1->init();
     allData.push_back(event1);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
-
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
 	// Only one interval left. One was trimmed.
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    interval2 = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, interval2.hasBase);
     EXPECT_EQ(5, interval2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
     EXPECT_FALSE(interval2.seenNewData);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8});
 
     allData.clear();
     event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
@@ -2018,40 +1835,24 @@
     event1->write(14);
     event1->init();
     allData.push_back(event1);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
 
-    interval2 = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, interval2.hasBase);
     EXPECT_EQ(14, interval2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
     EXPECT_FALSE(interval2.seenNewData);
-    EXPECT_EQ(2UL, valueProducer.mPastBuckets.size());
-    auto iterator = valueProducer.mPastBuckets.begin();
+    ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
+    auto iterator = valueProducer->mPastBuckets.begin();
     EXPECT_EQ(9, iterator->second[0].values[0].long_value);
     iterator++;
     EXPECT_EQ(8, iterator->second[0].values[0].long_value);
 }
 
 TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     // Used by onConditionChanged.
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
@@ -2064,47 +1865,30 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(100, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
 
     vector<shared_ptr<LogEvent>> allData;
-    valueProducer.onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     EXPECT_EQ(false, curInterval.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(false, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 }
 
 TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
@@ -2117,50 +1901,33 @@
             }))
             .WillOnce(Return(false));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
 
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(100, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
-    valueProducer.onConditionChanged(false, bucketStartTimeNs + 20);
+    valueProducer->onConditionChanged(false, bucketStartTimeNs + 20);
 
     // has one slice
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, curInterval.hasBase);
-    EXPECT_EQ(false, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 }
 
 TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
@@ -2172,45 +1939,30 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    valueProducer.mCondition = ConditionState::kTrue;
+    valueProducer->mCondition = ConditionState::kTrue;
 
     vector<shared_ptr<LogEvent>> allData;
-    valueProducer.onDataPulled(allData, /** succeed */ false, bucketStartTimeNs);
-    EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
+    valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs);
+    EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
 
-    valueProducer.onConditionChanged(false, bucketStartTimeNs + 1);
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    valueProducer->onConditionChanged(false, bucketStartTimeNs + 1);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(false, curInterval.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(false, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 }
 
 TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_condition(StringToId("SCREEN_ON"));
     metric.set_max_pull_delay_sec(0);
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
@@ -2222,25 +1974,18 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    valueProducer.mCondition = ConditionState::kFalse;
+    valueProducer->mCondition = ConditionState::kFalse;
 
     // Max delay is set to 0 so pull will exceed max delay.
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
-    EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
+    EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
 }
 
 TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -2266,24 +2011,9 @@
 }
 
 TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
@@ -2295,44 +2025,27 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    valueProducer.mCondition = ConditionState::kFalse;
-    valueProducer.mHasGlobalBase = false;
+    valueProducer->mCondition = ConditionState::kFalse;
+    valueProducer->mHasGlobalBase = false;
 
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
-    valueProducer.mHasGlobalBase = true;
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
+    valueProducer->mHasGlobalBase = true;
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(100, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 }
 
 TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // First onConditionChanged
             .WillOnce(Return(false))
@@ -2347,11 +2060,10 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    valueProducer.mCondition = ConditionState::kTrue;
+    valueProducer->mCondition = ConditionState::kTrue;
 
     // Bucket start.
     vector<shared_ptr<LogEvent>> allData;
@@ -2361,12 +2073,12 @@
     event->write(110);
     event->init();
     allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
 
     // This will fail and should invalidate the whole bucket since we do not have all the data
     // needed to compute the metric value when the screen was on.
-    valueProducer.onConditionChanged(false, bucketStartTimeNs + 2);
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 3);
+    valueProducer->onConditionChanged(false, bucketStartTimeNs + 2);
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 3);
 
     // Bucket end.
     allData.clear();
@@ -2375,48 +2087,33 @@
     event2->write(140);
     event2->init();
     allData.push_back(event2);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
 
-    valueProducer.flushIfNeededLocked(bucket2StartTimeNs + 1);
-    
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
+
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
     // Contains base from last pull which was successful.
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(140, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 }
 
 TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.mutable_dimensions_in_what()->set_field(tagId);
     metric.mutable_dimensions_in_what()->add_child()->set_field(1);
     metric.set_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // First onConditionChanged
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 for (int i = 0; i < 2000; i++) {
-                    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+                    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
                     event->write(i);
                     event->write(i);
                     event->init();
@@ -2425,36 +2122,19 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+    valueProducer->mCondition = ConditionState::kFalse;
 
-    valueProducer.mCondition = ConditionState::kFalse;
-    valueProducer.onConditionChanged(true, bucket2StartTimeNs + 2);
-    EXPECT_EQ(true, valueProducer.mCurrentBucketIsInvalid);
-    EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 2);
+    EXPECT_EQ(true, valueProducer->mCurrentBucketIsInvalid);
+    EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
 }
 
 TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // First onConditionChanged
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
@@ -2477,11 +2157,10 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    valueProducer.mCondition = ConditionState::kTrue;
+    valueProducer->mCondition = ConditionState::kTrue;
 
     // Bucket start.
     vector<shared_ptr<LogEvent>> allData;
@@ -2491,10 +2170,10 @@
     event->write(110);
     event->init();
     allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ false, bucketStartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs);
 
-    valueProducer.onConditionChanged(false, bucketStartTimeNs + 2);
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 3);
+    valueProducer->onConditionChanged(false, bucketStartTimeNs + 2);
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 3);
 
     // Bucket end.
     allData.clear();
@@ -2503,41 +2182,25 @@
     event2->write(140);
     event2->init();
     allData.push_back(event2);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
 
-    valueProducer.flushIfNeededLocked(bucket2StartTimeNs + 1);
-    
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
+
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
     // Contains base from last pull which was successful.
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(140, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 }
 
 TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // First onConditionChanged
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
@@ -2560,11 +2223,10 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    valueProducer.mCondition = ConditionState::kTrue;
+    valueProducer->mCondition = ConditionState::kTrue;
 
     // Bucket start.
     vector<shared_ptr<LogEvent>> allData;
@@ -2574,12 +2236,12 @@
     event->write(110);
     event->init();
     allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
 
     // This will fail and should invalidate the whole bucket since we do not have all the data
     // needed to compute the metric value when the screen was on.
-    valueProducer.onConditionChanged(false, bucketStartTimeNs + 2);
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 3);
+    valueProducer->onConditionChanged(false, bucketStartTimeNs + 2);
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 3);
 
     // Bucket end.
     allData.clear();
@@ -2588,39 +2250,23 @@
     event2->write(140);
     event2->init();
     allData.push_back(event2);
-    valueProducer.onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
 
-    valueProducer.flushIfNeededLocked(bucket2StartTimeNs + 1);
-    
-    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+    valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
+
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
     // Last pull failed so based has been reset.
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(false, curInterval.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(false, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 }
 
 TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
-    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);
-
-    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>();
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // Start bucket.
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
@@ -2633,9 +2279,8 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
 
     // Bucket 2 start.
     vector<shared_ptr<LogEvent>> allData;
@@ -2645,41 +2290,25 @@
     event->write(110);
     event->init();
     allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
 
     // Bucket 3 empty.
     allData.clear();
     shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
     event2->init();
     allData.push_back(event2);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // Data has been trimmed.
-    EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+    EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
 }
 
 TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // First onConditionChanged
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
@@ -2696,47 +2325,30 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 10);
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 
     // Empty pull.
-    valueProducer.onConditionChanged(false, bucketStartTimeNs + 10);
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(false, curInterval.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(false, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 }
 
 TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
-    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_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // First onConditionChanged
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
@@ -2767,58 +2379,42 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 10);
-    valueProducer.onConditionChanged(false, bucketStartTimeNs + 11);
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 12);
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
+    valueProducer->onConditionChanged(false, bucketStartTimeNs + 11);
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 12);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasBase);
     EXPECT_EQ(true, curInterval.hasValue);
-    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 
     // End of bucket
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
     // Data is empty, base should be reset.
     EXPECT_EQ(false, curInterval.hasBase);
     EXPECT_EQ(5, curInterval.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(1, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
 }
 
 TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.mutable_dimensions_in_what()->set_field(tagId);
     metric.mutable_dimensions_in_what()->add_child()->set_field(1);
     metric.set_condition(StringToId("SCREEN_ON"));
-    metric.set_max_pull_delay_sec(INT_MAX);
 
-    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>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-
     EXPECT_CALL(*pullerManager, Pull(tagId, _))
             // First onConditionChanged
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
@@ -2832,12 +2428,11 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
 
-    valueProducer.onConditionChanged(true, bucketStartTimeNs + 10);
-    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 
     // End of bucket
     vector<shared_ptr<LogEvent>> allData;
@@ -2847,11 +2442,11 @@
     event->write(2);
     event->init();
     allData.push_back(event);
-    valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
 
     // Key 1 should be reset since in not present in the most pull.
-    EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size());
-    auto iterator = valueProducer.mCurrentSlicedBucket.begin();
+    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    auto iterator = valueProducer->mCurrentSlicedBucket.begin();
     EXPECT_EQ(true, iterator->second[0].hasBase);
     EXPECT_EQ(2, iterator->second[0].base.long_value);
     EXPECT_EQ(false, iterator->second[0].hasValue);
@@ -2860,7 +2455,244 @@
     EXPECT_EQ(1, iterator->second[0].base.long_value);
     EXPECT_EQ(false, iterator->second[0].hasValue);
 
-    EXPECT_EQ(true, valueProducer.mHasGlobalBase);
+    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+}
+
+TEST(ValueMetricProducerTest, TestBucketIncludingUnknownConditionIsInvalid) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+    metric.mutable_dimensions_in_what()->set_field(tagId);
+    metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+    metric.set_condition(StringToId("SCREEN_ON"));
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            // Second onConditionChanged.
+            .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(2);
+                event->write(2);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+    valueProducer->mCondition = ConditionState::kUnknown;
+
+    valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 20);
+
+    // End of bucket
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    event->write(4);
+    event->write(4);
+    event->init();
+    allData.push_back(event);
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+    // Bucket is incomplete so it is mark as invalid, however the base is fine since the last pull
+    // succeeded.
+    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+}
+
+TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            // Second onConditionChanged.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 10, 5));
+                return true;
+            }))
+            // Third onConditionChanged.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs + 10, 7));
+                return true;
+            }));
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+    valueProducer->mCondition = ConditionState::kUnknown;
+
+    valueProducer->onConditionChanged(false, bucketStartTimeNs);
+    ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+    // End of first bucket
+    vector<shared_ptr<LogEvent>> allData;
+    allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 1, 4));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
+    ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+    valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
+    ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(true, curInterval.hasBase);
+    EXPECT_EQ(5, curInterval.base.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
+
+    valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10);
+
+    // Bucket should have been completed.
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2});
+}
+
+TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+    metric.set_use_diff(false);
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
+
+    allData.clear();
+    allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+    // Bucket should have been completed.
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30});
+}
+
+TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            // Initialization.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
+                return true;
+            }));
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
+
+    allData.clear();
+    allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+    // Bucket should have been completed.
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19});
+}
+
+TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            // Initialization.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
+                return true;
+            }))
+            // notifyAppUpgrade.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 2, 10));
+                return true;
+            }));
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+    valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
+
+    // Bucket should have been completed.
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9});
+}
+
+TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            // First on condition changed.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
+                return true;
+            }))
+            // Second on condition changed.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 3));
+                return true;
+            }));
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
+    valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
+    valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
+
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(2, curInterval.value.long_value);
+}
+
+TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            // First condition change.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
+                return true;
+            }))
+            // 2nd condition change.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                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;
+            }));
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+    valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 3, 10));
+    valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs + 3);
+
+    allData.clear();
+    allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20));
+    valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
+
+    valueProducer->onConditionChanged(false, bucket2StartTimeNs + 8);
+    valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
+
+    allData.clear();
+    allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs, 30));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+    // There was not global base available so all buckets are invalid.
+    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {});
 }
 
 static StatsLogReport outputStreamToProto(ProtoOutputStream* proto) {
@@ -2881,12 +2713,7 @@
 }
 
 TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -2918,7 +2745,7 @@
 
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer.onDumpReport(bucketStartTimeNs + 10, 
+    valueProducer.onDumpReport(bucketStartTimeNs + 10,
                                true /* include recent buckets */, true,
                                FAST, &strSet, &output);
 
@@ -2928,12 +2755,7 @@
 }
 
 TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -2975,7 +2797,7 @@
 
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer.onDumpReport(bucket4StartTimeNs, 
+    valueProducer.onDumpReport(bucket4StartTimeNs,
                                false /* include recent buckets */, true,
                                FAST, &strSet, &output);
 
@@ -2986,12 +2808,7 @@
 }
 
 TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
-    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);
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 
 
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
@@ -3033,7 +2850,7 @@
 
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer.onDumpReport(bucketStartTimeNs + 10, 
+    valueProducer.onDumpReport(bucketStartTimeNs + 10,
                                true /* include recent buckets */, true,
                                NO_TIME_CONSTRAINTS, &strSet, &output);
 
diff --git a/cmds/svc/src/com/android/commands/svc/Svc.java b/cmds/svc/src/com/android/commands/svc/Svc.java
index 62225df..68fb8e6 100644
--- a/cmds/svc/src/com/android/commands/svc/Svc.java
+++ b/cmds/svc/src/com/android/commands/svc/Svc.java
@@ -98,5 +98,6 @@
             new UsbCommand(),
             new NfcCommand(),
             new BluetoothCommand(),
+            new SystemServerCommand(),
     };
 }
diff --git a/cmds/svc/src/com/android/commands/svc/SystemServerCommand.java b/cmds/svc/src/com/android/commands/svc/SystemServerCommand.java
new file mode 100644
index 0000000..b9104d1
--- /dev/null
+++ b/cmds/svc/src/com/android/commands/svc/SystemServerCommand.java
@@ -0,0 +1,67 @@
+/*
+ * 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.commands.svc;
+
+import android.app.ActivityManager;
+import android.os.ParcelFileDescriptor;
+
+import java.io.FileInputStream;
+
+public class SystemServerCommand extends Svc.Command {
+    public SystemServerCommand() {
+        super("system-server");
+    }
+
+    @Override
+    public String shortHelp() {
+        return "System server process related command";
+    }
+
+    @Override
+    public String longHelp() {
+        return shortHelp() + "\n"
+                + "\n"
+                + "usage: system-server wait-for-crash\n"
+                + "         Wait until the system server process crashes.\n\n";
+    }
+
+    private void waitForCrash() throws Exception {
+        ParcelFileDescriptor fd = ActivityManager.getService().getLifeMonitor();
+        if (fd == null) {
+            System.err.println("Unable to get life monitor.");
+            return;
+        }
+        System.out.println("Waiting for the system server process to die...");
+        new FileInputStream(fd.getFileDescriptor()).read();
+    }
+
+    @Override
+    public void run(String[] args) {
+        try {
+            if (args.length > 1) {
+                switch (args[1]) {
+                    case "wait-for-crash":
+                        waitForCrash();
+                        return;
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        System.err.println(longHelp());
+    }
+}
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 0a26bfb..d2535c9 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -32983,7 +32983,7 @@
 HSPLandroid/view/IWindowSession$Stub$Proxy;->getInTouchMode()Z
 HSPLandroid/view/IWindowSession$Stub$Proxy;->getWindowId(Landroid/os/IBinder;)Landroid/view/IWindowId;
 HSPLandroid/view/IWindowSession$Stub$Proxy;->onRectangleOnScreenRequested(Landroid/os/IBinder;Landroid/graphics/Rect;)V
-HSPLandroid/view/IWindowSession$Stub$Proxy;->performHapticFeedback(Landroid/view/IWindow;IZ)Z
+HSPLandroid/view/IWindowSession$Stub$Proxy;->performHapticFeedback(IZ)Z
 HSPLandroid/view/IWindowSession$Stub$Proxy;->pokeDrawLock(Landroid/os/IBinder;)V
 HSPLandroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
 HSPLandroid/view/IWindowSession$Stub$Proxy;->remove(Landroid/view/IWindow;)V
@@ -33007,7 +33007,7 @@
 HSPLandroid/view/IWindowSession;->onRectangleOnScreenRequested(Landroid/os/IBinder;Landroid/graphics/Rect;)V
 HSPLandroid/view/IWindowSession;->outOfMemory(Landroid/view/IWindow;)Z
 HSPLandroid/view/IWindowSession;->performDrag(Landroid/view/IWindow;ILandroid/view/SurfaceControl;IFFFFLandroid/content/ClipData;)Landroid/os/IBinder;
-HSPLandroid/view/IWindowSession;->performHapticFeedback(Landroid/view/IWindow;IZ)Z
+HSPLandroid/view/IWindowSession;->performHapticFeedback(IZ)Z
 HSPLandroid/view/IWindowSession;->pokeDrawLock(Landroid/os/IBinder;)V
 HSPLandroid/view/IWindowSession;->prepareToReplaceWindows(Landroid/os/IBinder;Z)V
 HSPLandroid/view/IWindowSession;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index a836e8e..e25f463 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -688,7 +688,6 @@
 Landroid/os/Build;->getLong(Ljava/lang/String;)J
 Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
 Landroid/os/Build;->IS_DEBUGGABLE:Z
-Landroid/os/Build;->IS_EMULATOR:Z
 Landroid/os/Bundle;->filterValues()Landroid/os/Bundle;
 Landroid/os/Bundle;->forPair(Ljava/lang/String;Ljava/lang/String;)Landroid/os/Bundle;
 Landroid/os/Bundle;->getIBinder(Ljava/lang/String;)Landroid/os/IBinder;
@@ -1524,7 +1523,7 @@
 Landroid/view/IWindowSession;->finishDrawing(Landroid/view/IWindow;)V
 Landroid/view/IWindowSession;->getInTouchMode()Z
 Landroid/view/IWindowSession;->performDrag(Landroid/view/IWindow;ILandroid/view/SurfaceControl;IFFFFLandroid/content/ClipData;)Landroid/os/IBinder;
-Landroid/view/IWindowSession;->performHapticFeedback(Landroid/view/IWindow;IZ)Z
+Landroid/view/IWindowSession;->performHapticFeedback(IZ)Z
 Landroid/view/IWindowSession;->remove(Landroid/view/IWindow;)V
 Landroid/view/IWindowSession;->setInTouchMode(Z)V
 Landroid/view/IWindowSession;->setTransparentRegion(Landroid/view/IWindow;Landroid/graphics/Region;)V
@@ -2910,15 +2909,8 @@
 Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->loge(Ljava/lang/String;)V
 Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mAdnCache:Lcom/android/internal/telephony/uicc/AdnRecordCache;
 Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mBaseHandler:Landroid/os/Handler;
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mCurrentApp:Lcom/android/internal/telephony/uicc/UiccCardApplication;
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mIs3gCard:Z
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mLock:Ljava/lang/Object;
 Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mPhone:Lcom/android/internal/telephony/Phone;
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mRecords:Ljava/util/List;
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mRecordSize:[I
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mSuccess:Z
 Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->updateEfForIccType(I)I
-Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->waitForResult(Ljava/util/concurrent/atomic/AtomicBoolean;)V
 Lcom/android/internal/telephony/IccProvider;-><init>()V
 Lcom/android/internal/telephony/IccProvider;->ADDRESS_BOOK_COLUMN_NAMES:[Ljava/lang/String;
 Lcom/android/internal/telephony/IccProvider;->DBG:Z
@@ -3107,10 +3099,6 @@
 Lcom/android/internal/telephony/ISms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISms;
 Lcom/android/internal/telephony/ISub$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/telephony/ISub$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISub;
-Lcom/android/internal/telephony/ISub;->getActiveSubIdList()[I
-Lcom/android/internal/telephony/ISub;->getDefaultDataSubId()I
-Lcom/android/internal/telephony/ISub;->getDefaultSubId()I
-Lcom/android/internal/telephony/ISub;->setDefaultDataSubId(I)V
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String;
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->isRadioOn(Ljava/lang/String;)Z
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 89e848b..dc8863a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -127,7 +127,6 @@
 import android.view.autofill.IAutofillWindowPresenter;
 import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.ContentCaptureManager;
-import android.view.contentcapture.ContentCaptureSession;
 import android.widget.AdapterView;
 import android.widget.Toast;
 import android.widget.Toolbar;
@@ -1038,15 +1037,15 @@
     }
 
     /** @hide */ private static final int CONTENT_CAPTURE_START = 1;
-    /** @hide */ private static final int CONTENT_CAPTURE_PAUSE = 2;
-    /** @hide */ private static final int CONTENT_CAPTURE_RESUME = 3;
+    /** @hide */ private static final int CONTENT_CAPTURE_RESUME = 2;
+    /** @hide */ private static final int CONTENT_CAPTURE_PAUSE = 3;
     /** @hide */ private static final int CONTENT_CAPTURE_STOP = 4;
 
     /** @hide */
     @IntDef(prefix = { "CONTENT_CAPTURE_" }, value = {
             CONTENT_CAPTURE_START,
-            CONTENT_CAPTURE_PAUSE,
             CONTENT_CAPTURE_RESUME,
+            CONTENT_CAPTURE_PAUSE,
             CONTENT_CAPTURE_STOP
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -1056,10 +1055,10 @@
         switch (type) {
             case CONTENT_CAPTURE_START:
                 return "START";
-            case CONTENT_CAPTURE_PAUSE:
-                return "PAUSE";
             case CONTENT_CAPTURE_RESUME:
                 return "RESUME";
+            case CONTENT_CAPTURE_PAUSE:
+                return "PAUSE";
             case CONTENT_CAPTURE_STOP:
                 return "STOP";
             default:
@@ -1086,16 +1085,16 @@
                             & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
                         flags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE;
                     }
-                    cm.onActivityStarted(mToken, getComponentName(), flags);
-                    break;
-                case CONTENT_CAPTURE_PAUSE:
-                    cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_PAUSED);
+                    cm.onActivityCreated(mToken, getComponentName(), flags);
                     break;
                 case CONTENT_CAPTURE_RESUME:
-                    cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_RESUMED);
+                    cm.onActivityResumed();
+                    break;
+                case CONTENT_CAPTURE_PAUSE:
+                    cm.onActivityPaused();
                     break;
                 case CONTENT_CAPTURE_STOP:
-                    cm.onActivityStopped();
+                    cm.onActivityDestroyed();
                     break;
                 default:
                     Log.wtf(TAG, "Invalid @ContentCaptureNotificationType: " + type);
@@ -1654,6 +1653,8 @@
         }
 
         mCalled = true;
+
+        notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_START);
     }
 
     /**
@@ -1697,7 +1698,6 @@
         if (mAutoFillResetNeeded) {
             getAutofillManager().onVisibleForAutofill();
         }
-        notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_START);
     }
 
     /**
@@ -1780,8 +1780,10 @@
                 }
             }
         }
-        mCalled = true;
+
         notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_RESUME);
+
+        mCalled = true;
     }
 
     /**
@@ -2197,8 +2199,9 @@
                 mAutoFillIgnoreFirstResumePause = false;
             }
         }
-        mCalled = true;
+
         notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_PAUSE);
+        mCalled = true;
     }
 
     /**
@@ -2387,8 +2390,8 @@
                 getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
                         mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
             }
-            notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP);
         }
+        notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP);
         mEnterAnimationComplete = false;
     }
 
@@ -2459,6 +2462,8 @@
         }
 
         dispatchActivityDestroyed();
+
+        notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP);
     }
 
     /**
@@ -7608,7 +7613,7 @@
 
         mWindow.setColorMode(info.colorMode);
 
-        setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
+        setAutofillOptions(application.getAutofillOptions());
         setContentCaptureOptions(application.getContentCaptureOptions());
     }
 
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 5d4f988..ee7288f 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3925,6 +3925,14 @@
     /**
      * @hide
      */
+    @TestApi
+    public static void resumeAppSwitches() throws RemoteException {
+        getService().resumeAppSwitches();
+    }
+
+    /**
+     * @hide
+     */
     public static void noteWakeupAlarm(PendingIntent ps, WorkSource workSource, int sourceUid,
             String sourcePkg, String tag) {
         try {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 001cd69..92302c5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -43,6 +43,7 @@
 import android.app.servertransaction.PendingTransactionActions.StopInfo;
 import android.app.servertransaction.TransactionExecutor;
 import android.app.servertransaction.TransactionExecutorHelper;
+import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
@@ -745,7 +746,7 @@
         /** Initial values for {@link Profiler}. */
         ProfilerInfo initProfilerInfo;
 
-        boolean autofillCompatibilityEnabled;
+        AutofillOptions autofillOptions;
 
         /**
          * Content capture options for the application - when null, it means ContentCapture is not
@@ -975,9 +976,8 @@
                 boolean enableBinderTracking, boolean trackAllocation,
                 boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                 CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
-                String buildSerial, boolean autofillCompatibilityEnabled,
+                String buildSerial, AutofillOptions autofillOptions,
                 ContentCaptureOptions contentCaptureOptions) {
-
             if (services != null) {
                 if (false) {
                     // Test code to make sure the app could see the passed-in services.
@@ -1023,7 +1023,7 @@
             data.compatInfo = compatInfo;
             data.initProfilerInfo = profilerInfo;
             data.buildSerial = buildSerial;
-            data.autofillCompatibilityEnabled = autofillCompatibilityEnabled;
+            data.autofillOptions = autofillOptions;
             data.contentCaptureOptions = contentCaptureOptions;
             sendMessage(H.BIND_APPLICATION, data);
         }
@@ -6164,7 +6164,7 @@
             app = data.info.makeApplication(data.restrictedBackupMode, null);
 
             // Propagate autofill compat state
-            app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);
+            app.setAutofillOptions(data.autofillOptions);
 
             // Propagate Content Capture options
             app.setContentCaptureOptions(data.contentCaptureOptions);
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index a4b763d..2ef0856 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -21,7 +21,7 @@
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.TestApi;
 import android.app.ActivityManager.StackInfo;
 import android.content.ComponentName;
 import android.content.Context;
@@ -59,6 +59,7 @@
  * on VirtualDisplays.
  * @hide
  */
+@TestApi
 public class ActivityView extends ViewGroup {
 
     private static final String DISPLAY_NAME = "ActivityViewVirtualDisplay";
@@ -92,7 +93,6 @@
 
     private Insets mForwardedInsets;
 
-    @UnsupportedAppUsage
     public ActivityView(Context context) {
         this(context, null /* attrs */);
     }
@@ -151,7 +151,7 @@
          * Called when a task is moved to the front of the stack inside the container.
          * This is a filtered version of {@link TaskStackListener}
          */
-        public void onTaskMovedToFront(ActivityManager.StackInfo stackInfo) { }
+        public void onTaskMovedToFront(int taskId) { }
 
         /**
          * Called when a task is about to be removed from the stack inside the container.
@@ -195,7 +195,6 @@
      * @see StateCallback
      * @see #startActivity(PendingIntent)
      */
-    @UnsupportedAppUsage
     public void startActivity(@NonNull Intent intent) {
         final ActivityOptions options = prepareActivityOptions();
         getContext().startActivity(intent, options.toBundle());
@@ -238,7 +237,6 @@
      * @see StateCallback
      * @see #startActivity(Intent)
      */
-    @UnsupportedAppUsage
     public void startActivity(@NonNull PendingIntent pendingIntent) {
         final ActivityOptions options = prepareActivityOptions();
         try {
@@ -272,7 +270,6 @@
      *
      * @see StateCallback
      */
-    @UnsupportedAppUsage
     public void release() {
         if (mVirtualDisplay == null) {
             throw new IllegalStateException(
@@ -556,7 +553,7 @@
             // notifying the callback
             if (stackInfo != null
                     && taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
-                mActivityViewCallback.onTaskMovedToFront(stackInfo);
+                mActivityViewCallback.onTaskMovedToFront(taskInfo.taskId);
             }
         }
 
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 98032dc..d3e3507 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -696,7 +696,9 @@
             int flagMask, int flagValues, UserHandle user) {
         try {
             mPM.updatePermissionFlags(permissionName, packageName, flagMask,
-                    flagValues, user.getIdentifier());
+                    flagValues,
+                    mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.Q,
+                    user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2467,6 +2469,33 @@
     }
 
     @Override
+    public void setAppDetailsActivityEnabled(String packageName, boolean enabled) {
+        try {
+            ComponentName componentName = new ComponentName(packageName,
+                    PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME);
+            mPM.setComponentEnabledSetting(componentName, enabled
+                    ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                    : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                    PackageManager.DONT_KILL_APP, getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    public boolean getAppDetailsActivityEnabled(String packageName) {
+        try {
+            ComponentName componentName = new ComponentName(packageName,
+                    PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME);
+            int state = mPM.getComponentEnabledSetting(componentName, getUserId());
+            return state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                    || state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public void setComponentEnabledSetting(ComponentName componentName,
                                            int newState, int flags) {
         try {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 3a1e80d..b607f9a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
+import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
@@ -215,8 +216,8 @@
     // The name of the split this Context is representing. May be null.
     private @Nullable String mSplitName = null;
 
-    private AutofillClient mAutofillClient = null;
-    private boolean mIsAutofillCompatEnabled;
+    private @Nullable AutofillClient mAutofillClient = null;
+    private @Nullable AutofillOptions mAutofillOptions;
 
     private ContentCaptureOptions mContentCaptureOptions = null;
 
@@ -2283,8 +2284,8 @@
         return (mFlags & Context.CONTEXT_IGNORE_SECURITY) != 0;
     }
 
+    @TestApi
     @Override
-    @UnsupportedAppUsage
     public Display getDisplay() {
         if (mDisplay == null) {
             return mResourcesManager.getAdjustedDisplay(Display.DEFAULT_DISPLAY,
@@ -2376,15 +2377,14 @@
 
     /** @hide */
     @Override
-    public boolean isAutofillCompatibilityEnabled() {
-        return mIsAutofillCompatEnabled;
+    public AutofillOptions getAutofillOptions() {
+        return mAutofillOptions;
     }
 
     /** @hide */
-    @TestApi
     @Override
-    public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) {
-        mIsAutofillCompatEnabled = autofillCompatEnabled;
+    public void setAutofillOptions(AutofillOptions options) {
+        mAutofillOptions = options;
     }
 
     /** @hide */
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 3aa9fa7..780dd63 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -486,4 +486,13 @@
      * started from the shell.
      */
     void stopDelegateShellPermissionIdentity();
+
+    /** Returns a file descriptor that'll be closed when the system server process dies. */
+    ParcelFileDescriptor getLifeMonitor();
+
+    /**
+     * Start user, if it us not already running, and bring it to foreground.
+     * unlockProgressListener can be null if monitoring progress is not necessary.
+     */
+    boolean startUserInForegroundWithListener(int userid, IProgressListener unlockProgressListener);
 }
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 497d5ba..b01cd0e 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -277,12 +277,8 @@
      *
      * @param showingKeyguard True if the keyguard is showing, false otherwise.
      * @param showingAod True if AOD is showing, false otherwise.
-     * @param secondaryDisplaysShowing The displayId's of the secondary displays on which the
-     * keyguard is showing, or {@code null} if there is no such display. Only meaningful if showing
-     * is {@code true}.
      */
-    void setLockScreenShown(boolean showingKeyguard, boolean showingAod,
-            in int[] secondaryDisplaysShowing);
+    void setLockScreenShown(boolean showingKeyguard, boolean showingAod);
     Bundle getAssistContextExtras(int requestType);
     boolean launchAssistIntent(in Intent intent, int requestType, in String hint, int userHandle,
             in Bundle args);
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index b73092a..b8af898 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -21,6 +21,7 @@
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
 import android.app.servertransaction.ClientTransaction;
+import android.content.AutofillOptions;
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
 import android.content.IIntentReceiver;
@@ -69,7 +70,7 @@
             int debugMode, boolean enableBinderTracking, boolean trackAllocation,
             boolean restrictedBackupMode, boolean persistent, in Configuration config,
             in CompatibilityInfo compatInfo, in Map services,
-            in Bundle coreSettings, in String buildSerial, boolean isAutofillCompatEnabled,
+            in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions,
             in ContentCaptureOptions contentCaptureOptions);
     void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs);
     void scheduleExit();
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0166f52..2e7093d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -8409,7 +8409,6 @@
 
         private PendingIntent mPendingIntent;
         private PendingIntent mDeleteIntent;
-        private CharSequence mTitle;
         private Icon mIcon;
         private int mDesiredHeight;
         private int mFlags;
@@ -8438,9 +8437,8 @@
         private static final int FLAG_SUPPRESS_INITIAL_NOTIFICATION = 0x00000002;
 
         private BubbleMetadata(PendingIntent expandIntent, PendingIntent deleteIntent,
-                CharSequence title, Icon icon, int height) {
+                Icon icon, int height) {
             mPendingIntent = expandIntent;
-            mTitle = title;
             mIcon = icon;
             mDesiredHeight = height;
             mDeleteIntent = deleteIntent;
@@ -8448,7 +8446,6 @@
 
         private BubbleMetadata(Parcel in) {
             mPendingIntent = PendingIntent.CREATOR.createFromParcel(in);
-            mTitle = in.readCharSequence();
             mIcon = Icon.CREATOR.createFromParcel(in);
             mDesiredHeight = in.readInt();
             mFlags = in.readInt();
@@ -8474,11 +8471,13 @@
         /**
          * @return the title that will appear along with the app content defined by
          * {@link #getIntent()} for this bubble.
+         *
+         * @deprecated titles are no longer required or shown.
          */
+        @Deprecated
         public CharSequence getTitle() {
-            return mTitle;
+            return "";
         }
-
         /**
          * @return the icon that will be displayed for this bubble when it is collapsed.
          */
@@ -8534,7 +8533,6 @@
         @Override
         public void writeToParcel(Parcel out, int flags) {
             mPendingIntent.writeToParcel(out, 0);
-            out.writeCharSequence(mTitle);
             mIcon.writeToParcel(out, 0);
             out.writeInt(mDesiredHeight);
             out.writeInt(mFlags);
@@ -8554,7 +8552,6 @@
         public static class Builder {
 
             private PendingIntent mPendingIntent;
-            private CharSequence mTitle;
             private Icon mIcon;
             private int mDesiredHeight;
             private int mFlags;
@@ -8583,12 +8580,11 @@
              *
              * <p>A title is required and should expect to fit on a single line and make sense when
              * shown with the content defined by {@link #setIntent(PendingIntent)}.</p>
+             *
+             * @deprecated titles are no longer required or shown.
              */
+            @Deprecated
             public BubbleMetadata.Builder setTitle(CharSequence title) {
-                if (TextUtils.isEmpty(title)) {
-                    throw new IllegalArgumentException("Bubbles require non-null or empty title");
-                }
-                mTitle = title;
                 return this;
             }
 
@@ -8667,13 +8663,10 @@
                 if (mPendingIntent == null) {
                     throw new IllegalStateException("Must supply pending intent to bubble");
                 }
-                if (TextUtils.isEmpty(mTitle)) {
-                    throw new IllegalStateException("Must supply a title for the bubble");
-                }
                 if (mIcon == null) {
                     throw new IllegalStateException("Must supply an icon for the bubble");
                 }
-                BubbleMetadata data = new BubbleMetadata(mPendingIntent, mDeleteIntent, mTitle,
+                BubbleMetadata data = new BubbleMetadata(mPendingIntent, mDeleteIntent,
                         mIcon, mDesiredHeight);
                 data.setFlags(mFlags);
                 return data;
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 31521a3..7e07446 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -128,7 +128,13 @@
                 : InputManager.INJECT_INPUT_EVENT_MODE_ASYNC;
         final long identity = Binder.clearCallingIdentity();
         try {
-            return InputManager.getInstance().injectInputEvent(event, mode);
+            IWindowManager manager = IWindowManager.Stub.asInterface(
+                    ServiceManager.getService(Context.WINDOW_SERVICE));
+            try {
+                return manager.injectInputAfterTransactionsApplied(event, mode);
+            } catch (RemoteException e) {
+            }
+            return false;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3587c68..a32e01f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -59,6 +59,7 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
+import android.os.ParcelableException;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteCallback;
@@ -115,6 +116,8 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 
 /**
@@ -1576,6 +1579,19 @@
     public static final int PERMISSION_POLICY_AUTO_DENY = 2;
 
     /**
+     * Possible policy values for permissions.
+     *
+     * @hide
+     */
+    @IntDef(prefix = { "PERMISSION_GRANT_STATE_" }, value = {
+            PERMISSION_GRANT_STATE_DEFAULT,
+            PERMISSION_GRANT_STATE_GRANTED,
+            PERMISSION_GRANT_STATE_DENIED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PermissionGrantState {}
+
+    /**
      * Runtime permission state: The user can manage the permission
      * through the UI.
      */
@@ -8667,8 +8683,15 @@
      * Setting the grant state to {@link #PERMISSION_GRANT_STATE_DEFAULT default} does not revoke
      * the permission. It retains the previous grant, if any.
      * <p/>
-     * Permissions can be granted or revoked only for applications built with a
-     * {@code targetSdkVersion} of {@link android.os.Build.VERSION_CODES#M} or later.
+     * Device admins with a {@code targetSdkVersion} &lt; {@link android.os.Build.VERSION_CODES#Q}
+     * cannot grant and revoke permissions for applications built with a {@code targetSdkVersion}
+     * &lt; {@link android.os.Build.VERSION_CODES#M}.
+     * <p/>
+     * Admins with a {@code targetSdkVersion} &ge; {@link android.os.Build.VERSION_CODES#Q} can
+     * grant and revoke permissions of all apps. Similar to the user revoking a permission from a
+     * application built with a {@code targetSdkVersion} &lt;
+     * {@link android.os.Build.VERSION_CODES#M} the app-op matching the permission is set to
+     * {@link android.app.AppOpsManager#MODE_IGNORED}, but the permission stays granted.
      *
      * @param admin Which profile or device owner this request is associated with.
      * @param packageName The application to grant or revoke a permission to.
@@ -8684,14 +8707,21 @@
      * @see #setDelegatedScopes
      * @see #DELEGATION_PERMISSION_GRANT
      */
-    public boolean setPermissionGrantState(@NonNull ComponentName admin, String packageName,
-            String permission, int grantState) {
+    public boolean setPermissionGrantState(@NonNull ComponentName admin,
+            @NonNull String packageName, @NonNull String permission,
+            @PermissionGrantState int grantState) {
         throwIfParentInstance("setPermissionGrantState");
         try {
-            return mService.setPermissionGrantState(admin, mContext.getPackageName(), packageName,
-                    permission, grantState);
+            CompletableFuture<Boolean> result = new CompletableFuture<>();
+
+            mService.setPermissionGrantState(admin, mContext.getPackageName(), packageName,
+                    permission, grantState, new RemoteCallback((b) -> result.complete(b != null)));
+
+            return result.get();
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
+        } catch (InterruptedException | ExecutionException e) {
+            throw new RuntimeException(e);
         }
     }
 
@@ -8719,8 +8749,8 @@
      * @see #setDelegatedScopes
      * @see #DELEGATION_PERMISSION_GRANT
      */
-    public int getPermissionGrantState(@Nullable ComponentName admin, String packageName,
-            String permission) {
+    public @PermissionGrantState int getPermissionGrantState(@Nullable ComponentName admin,
+            @NonNull String packageName, @NonNull String permission) {
         throwIfParentInstance("getPermissionGrantState");
         try {
             return mService.getPermissionGrantState(admin, mContext.getPackageName(), packageName,
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5790fda..9478a3c 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -311,8 +311,8 @@
 
     void setPermissionPolicy(in ComponentName admin, in String callerPackage, int policy);
     int  getPermissionPolicy(in ComponentName admin);
-    boolean setPermissionGrantState(in ComponentName admin, in String callerPackage, String packageName,
-            String permission, int grantState);
+    void setPermissionGrantState(in ComponentName admin, in String callerPackage, String packageName,
+            String permission, int grantState, in RemoteCallback resultReceiver);
     int getPermissionGrantState(in ComponentName admin, in String callerPackage, String packageName, String permission);
     boolean isProvisioningAllowed(String action, String packageName);
     int checkProvisioningPreCondition(String action, String packageName);
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index edd3ef98..7ec21f6 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -71,10 +71,8 @@
     /**
      * The name of the assistant app role.
      *
-     * @hide
+     * @see android.service.voice.VoiceInteractionService
      */
-    @SystemApi
-    @TestApi
     public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
 
     /**
@@ -149,9 +147,6 @@
      * place the call through a call redirection service.
      *
      * @see android.telecom.CallRedirectionService
-     *
-     * TODO: STOPSHIP: Make name of required roles public API
-     * @hide
      */
     public static final String ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION";
 
@@ -159,9 +154,6 @@
      * The name of the call screening and caller id role.
      *
      * @see android.telecom.CallScreeningService
-     *
-     * TODO: STOPSHIP: Make name of required roles public API
-     * @hide
      */
     public static final String ROLE_CALL_SCREENING = "android.app.role.CALL_SCREENING";
 
diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java
index e86d348..38dd1eb 100644
--- a/core/java/android/app/timezone/RulesState.java
+++ b/core/java/android/app/timezone/RulesState.java
@@ -33,7 +33,7 @@
  *
  * <p>The following properties are included:
  * <dl>
- *     <dt>systemRulesVersion</dt>
+ *     <dt>baseRulesVersion</dt>
  *     <dd>the IANA rules version that shipped with the OS. Always present. e.g. "2017a".</dd>
  *     <dt>distroFormatVersionSupported</dt>
  *     <dd>the distro format version supported by this device. Always present.</dd>
@@ -98,7 +98,7 @@
     private static final byte BYTE_FALSE = 0;
     private static final byte BYTE_TRUE = 1;
 
-    private final String mSystemRulesVersion;
+    private final String mBaseRulesVersion;
     private final DistroFormatVersion mDistroFormatVersionSupported;
     private final boolean mOperationInProgress;
     @StagedOperationType private final int mStagedOperationType;
@@ -106,13 +106,13 @@
     @DistroStatus private final int mDistroStatus;
     @Nullable private final DistroRulesVersion mInstalledDistroRulesVersion;
 
-    public RulesState(String systemRulesVersion, DistroFormatVersion distroFormatVersionSupported,
+    public RulesState(String baseRulesVersion, DistroFormatVersion distroFormatVersionSupported,
             boolean operationInProgress,
             @StagedOperationType int stagedOperationType,
             @Nullable DistroRulesVersion stagedDistroRulesVersion,
             @DistroStatus int distroStatus,
             @Nullable DistroRulesVersion installedDistroRulesVersion) {
-        this.mSystemRulesVersion = validateRulesVersion("systemRulesVersion", systemRulesVersion);
+        this.mBaseRulesVersion = validateRulesVersion("baseRulesVersion", baseRulesVersion);
         this.mDistroFormatVersionSupported =
                 validateNotNull("distroFormatVersionSupported", distroFormatVersionSupported);
         this.mOperationInProgress = operationInProgress;
@@ -132,8 +132,8 @@
                 "installedDistroRulesVersion", installedDistroRulesVersion);
     }
 
-    public String getSystemRulesVersion() {
-        return mSystemRulesVersion;
+    public String getBaseRulesVersion() {
+        return mBaseRulesVersion;
     }
 
     public boolean isOperationInProgress() {
@@ -172,14 +172,14 @@
     }
 
     /**
-     * Returns true if the system image data files contain IANA rules data that are newer than the
+     * Returns true if the base data files contain IANA rules data that are newer than the
      * distro IANA rules version supplied, i.e. true when the version specified would be "worse"
-     * than the one that is in the system image. Returns false if the system image version is the
+     * than the one that is in the base data. Returns false if the base version is the
      * same or older, i.e. false when the version specified would be "better" than the one that is
-     * in the system image.
+     * in the base set.
      */
-    public boolean isSystemVersionNewerThan(DistroRulesVersion distroRulesVersion) {
-        return mSystemRulesVersion.compareTo(distroRulesVersion.getRulesVersion()) > 0;
+    public boolean isBaseVersionNewerThan(DistroRulesVersion distroRulesVersion) {
+        return mBaseRulesVersion.compareTo(distroRulesVersion.getRulesVersion()) > 0;
     }
 
     public static final Parcelable.Creator<RulesState> CREATOR =
@@ -194,14 +194,14 @@
     };
 
     private static RulesState createFromParcel(Parcel in) {
-        String systemRulesVersion = in.readString();
+        String baseRulesVersion = in.readString();
         DistroFormatVersion distroFormatVersionSupported = in.readParcelable(null);
         boolean operationInProgress = in.readByte() == BYTE_TRUE;
         int distroStagedState = in.readByte();
         DistroRulesVersion stagedDistroRulesVersion = in.readParcelable(null);
         int installedDistroStatus = in.readByte();
         DistroRulesVersion installedDistroRulesVersion = in.readParcelable(null);
-        return new RulesState(systemRulesVersion, distroFormatVersionSupported, operationInProgress,
+        return new RulesState(baseRulesVersion, distroFormatVersionSupported, operationInProgress,
                 distroStagedState, stagedDistroRulesVersion,
                 installedDistroStatus, installedDistroRulesVersion);
     }
@@ -213,7 +213,7 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeString(mSystemRulesVersion);
+        out.writeString(mBaseRulesVersion);
         out.writeParcelable(mDistroFormatVersionSupported, 0);
         out.writeByte(mOperationInProgress ? BYTE_TRUE : BYTE_FALSE);
         out.writeByte((byte) mStagedOperationType);
@@ -242,7 +242,7 @@
         if (mDistroStatus != that.mDistroStatus) {
             return false;
         }
-        if (!mSystemRulesVersion.equals(that.mSystemRulesVersion)) {
+        if (!mBaseRulesVersion.equals(that.mBaseRulesVersion)) {
             return false;
         }
         if (!mDistroFormatVersionSupported.equals(that.mDistroFormatVersionSupported)) {
@@ -259,7 +259,7 @@
 
     @Override
     public int hashCode() {
-        int result = mSystemRulesVersion.hashCode();
+        int result = mBaseRulesVersion.hashCode();
         result = 31 * result + mDistroFormatVersionSupported.hashCode();
         result = 31 * result + (mOperationInProgress ? 1 : 0);
         result = 31 * result + mStagedOperationType;
@@ -275,7 +275,7 @@
     @Override
     public String toString() {
         return "RulesState{"
-                + "mSystemRulesVersion='" + mSystemRulesVersion + '\''
+                + "mBaseRulesVersion='" + mBaseRulesVersion + '\''
                 + ", mDistroFormatVersionSupported=" + mDistroFormatVersionSupported
                 + ", mOperationInProgress=" + mOperationInProgress
                 + ", mStagedOperationType=" + mStagedOperationType
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index dc5bdc6..9c6bd92 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -779,7 +779,7 @@
             android.Manifest.permission.SUSPEND_APPS,
             android.Manifest.permission.OBSERVE_APP_USAGE})
     public void registerAppUsageLimitObserver(int observerId, @NonNull String[] observedEntities,
-            long timeLimit, @NonNull TimeUnit timeUnit, PendingIntent callbackIntent) {
+            long timeLimit, @NonNull TimeUnit timeUnit, @Nullable PendingIntent callbackIntent) {
         try {
             mService.registerAppUsageLimitObserver(observerId, observedEntities,
                     timeUnit.toMillis(timeLimit), callbackIntent, mContext.getOpPackageName());
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 3e9dd28..85f0e23 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -30,7 +30,6 @@
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Rect;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Parcelable;
@@ -178,40 +177,29 @@
      */
     public static Rect getDefaultPaddingForWidget(Context context, ComponentName component,
             Rect padding) {
-        ApplicationInfo appInfo = null;
-        try {
-            appInfo = context.getPackageManager().getApplicationInfo(component.getPackageName(), 0);
-        } catch (NameNotFoundException e) {
-            // if we can't find the package, ignore
-        }
-        return getDefaultPaddingForWidget(context, appInfo, padding);
+        return getDefaultPaddingForWidget(context, padding);
     }
 
-    @UnsupportedAppUsage
-    private static Rect getDefaultPaddingForWidget(Context context, ApplicationInfo appInfo,
-            Rect padding) {
+    private static Rect getDefaultPaddingForWidget(Context context, Rect padding) {
         if (padding == null) {
             padding = new Rect(0, 0, 0, 0);
         } else {
             padding.set(0, 0, 0, 0);
         }
-        if (appInfo != null && appInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            Resources r = context.getResources();
-            padding.left = r.getDimensionPixelSize(com.android.internal.
-                    R.dimen.default_app_widget_padding_left);
-            padding.right = r.getDimensionPixelSize(com.android.internal.
-                    R.dimen.default_app_widget_padding_right);
-            padding.top = r.getDimensionPixelSize(com.android.internal.
-                    R.dimen.default_app_widget_padding_top);
-            padding.bottom = r.getDimensionPixelSize(com.android.internal.
-                    R.dimen.default_app_widget_padding_bottom);
-        }
+        Resources r = context.getResources();
+        padding.left = r.getDimensionPixelSize(
+                com.android.internal.R.dimen.default_app_widget_padding_left);
+        padding.right = r.getDimensionPixelSize(
+                com.android.internal.R.dimen.default_app_widget_padding_right);
+        padding.top = r.getDimensionPixelSize(
+                com.android.internal.R.dimen.default_app_widget_padding_top);
+        padding.bottom = r.getDimensionPixelSize(
+                com.android.internal.R.dimen.default_app_widget_padding_bottom);
         return padding;
     }
 
     private Rect getDefaultPadding() {
-        return getDefaultPaddingForWidget(mContext,
-                mInfo == null ? null : mInfo.providerInfo.applicationInfo, null);
+        return getDefaultPaddingForWidget(mContext, null);
     }
 
     public int getAppWidgetId() {
diff --git a/core/java/android/content/AutofillOptions.aidl b/core/java/android/content/AutofillOptions.aidl
new file mode 100644
index 0000000..7e4fed2
--- /dev/null
+++ b/core/java/android/content/AutofillOptions.aidl
@@ -0,0 +1,19 @@
+/*
+** 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.content;
+
+parcelable AutofillOptions;
diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java
new file mode 100644
index 0000000..fd7e52a
--- /dev/null
+++ b/core/java/android/content/AutofillOptions.java
@@ -0,0 +1,130 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.app.ActivityThread;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.autofill.AutofillManager;
+
+import java.io.PrintWriter;
+
+/**
+ * Autofill options for a given package.
+ *
+ * <p>This object is created by the Autofill System Service and passed back to the app when the
+ * application is created.
+ *
+ * @hide
+ */
+@TestApi
+public final class AutofillOptions implements Parcelable {
+
+    private static final String TAG = AutofillOptions.class.getSimpleName();
+
+    /**
+     * Logging level for {@code logcat} statements.
+     */
+    public final int loggingLevel;
+
+    /**
+     * Whether compatibility mode is enabled for the package.
+     */
+    public final boolean compatModeEnabled;
+
+    /**
+     * Whether package is whitelisted for augmented autofill.
+     */
+    public boolean augmentedEnabled;
+    // TODO(b/123100824): add (optional) list of activities
+
+    public AutofillOptions(int loggingLevel, boolean compatModeEnabled) {
+        this.loggingLevel = loggingLevel;
+        this.compatModeEnabled = compatModeEnabled;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public static AutofillOptions forWhitelistingItself() {
+        final ActivityThread at = ActivityThread.currentActivityThread();
+        if (at == null) {
+            throw new IllegalStateException("No ActivityThread");
+        }
+
+        final String packageName = at.getApplication().getPackageName();
+
+        if (!"android.autofillservice.cts".equals(packageName)) {
+            Log.e(TAG, "forWhitelistingItself(): called by " + packageName);
+            throw new SecurityException("Thou shall not pass!");
+        }
+
+        final AutofillOptions options = new AutofillOptions(
+                AutofillManager.FLAG_ADD_CLIENT_VERBOSE, /* compatModeAllowed= */ true);
+        options.augmentedEnabled = true;
+        // Always log, as it's used by test only
+        Log.i(TAG, "forWhitelistingItself(" + packageName + "): " + options);
+
+        return options;
+    }
+
+    @Override
+    public String toString() {
+        return "AutofillOptions [loggingLevel=" + loggingLevel + ", compatMode="
+                + compatModeEnabled + ", augmentedEnabled=" + augmentedEnabled + "]";
+    }
+
+    /** @hide */
+    public void dumpShort(@NonNull PrintWriter pw) {
+        pw.print("logLvl="); pw.print(loggingLevel);
+        pw.print(", compatMode="); pw.print(compatModeEnabled);
+        pw.print(", augmented="); pw.print(augmentedEnabled);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(loggingLevel);
+        parcel.writeBoolean(compatModeEnabled);
+        parcel.writeBoolean(augmentedEnabled);
+    }
+
+    public static final Parcelable.Creator<AutofillOptions> CREATOR =
+            new Parcelable.Creator<AutofillOptions>() {
+
+                @Override
+                public AutofillOptions createFromParcel(Parcel parcel) {
+                    final int loggingLevel = parcel.readInt();
+                    final boolean compatMode = parcel.readBoolean();
+                    final AutofillOptions options = new AutofillOptions(loggingLevel, compatMode);
+                    options.augmentedEnabled = parcel.readBoolean();
+                    return options;
+                }
+
+                @Override
+                public AutofillOptions[] newArray(int size) {
+                    return new AutofillOptions[size];
+                }
+    };
+}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index c44520a..1e4b1e7 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -3159,6 +3159,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.CACHE_CONTENT)
     public void putCache(@NonNull Uri key, @Nullable Bundle value) {
         try {
             getContentService().putCache(mContext.getPackageName(), key, value,
@@ -3178,6 +3179,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.CACHE_CONTENT)
     public @Nullable Bundle getCache(@NonNull Uri key) {
         try {
             final Bundle bundle = getContentService().getCache(mContext.getPackageName(), key,
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index fdb0041..29added 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5241,9 +5241,10 @@
     public abstract DisplayAdjustments getDisplayAdjustments(int displayId);
 
     /**
+     * @return Returns the {@link Display} object this context is associated with.
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
     public abstract Display getDisplay();
 
     /**
@@ -5340,16 +5341,24 @@
     /**
      * @hide
      */
-    public boolean isAutofillCompatibilityEnabled() {
-        return false;
+    public final boolean isAutofillCompatibilityEnabled() {
+        final AutofillOptions options = getAutofillOptions();
+        return options != null && options.compatModeEnabled;
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public AutofillOptions getAutofillOptions() {
+        return null;
     }
 
     /**
      * @hide
      */
     @TestApi
-    public void setAutofillCompatibilityEnabled(
-            @SuppressWarnings("unused") boolean autofillCompatEnabled) {
+    public void setAutofillOptions(@SuppressWarnings("unused") @Nullable AutofillOptions options) {
     }
 
     /**
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 68b4320..40559d3 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -919,11 +919,9 @@
         return mBase.getDisplayAdjustments(displayId);
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
+    @TestApi
     @Override
-    @UnsupportedAppUsage
     public Display getDisplay() {
         return mBase.getDisplay();
     }
@@ -1031,22 +1029,17 @@
         mBase.setAutofillClient(client);
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @Override
-    public boolean isAutofillCompatibilityEnabled() {
-        return mBase != null && mBase.isAutofillCompatibilityEnabled();
+    public AutofillOptions getAutofillOptions() {
+        return mBase == null ? null : mBase.getAutofillOptions();
     }
 
-    /**
-     * @hide
-     */
-    @TestApi
+    /** @hide */
     @Override
-    public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) {
+    public void setAutofillOptions(AutofillOptions options) {
         if (mBase != null) {
-            mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled);
+            mBase.setAutofillOptions(options);
         }
     }
 
diff --git a/core/java/android/content/pm/AndroidTestBaseUpdater.java b/core/java/android/content/pm/AndroidTestBaseUpdater.java
index 2aaac02..6a1778c 100644
--- a/core/java/android/content/pm/AndroidTestBaseUpdater.java
+++ b/core/java/android/content/pm/AndroidTestBaseUpdater.java
@@ -19,11 +19,12 @@
 import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
 
 import android.content.pm.PackageParser.Package;
+import android.os.Build;
 
 import com.android.internal.annotations.VisibleForTesting;
 
 /**
- * Updates a package to ensure that if it targets < P that the android.test.base library is
+ * Updates a package to ensure that if it targets <= P that the android.test.base library is
  * included by default.
  *
  * <p>This is separated out so that it can be conditionally included at build time depending on
@@ -37,12 +38,17 @@
 @VisibleForTesting
 public class AndroidTestBaseUpdater extends PackageSharedLibraryUpdater {
 
+    private static boolean apkTargetsApiLevelLessThanOrEqualToP(Package pkg) {
+        int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
+        return targetSdkVersion <= Build.VERSION_CODES.P;
+    }
+
     @Override
     public void updatePackage(Package pkg) {
-        // Packages targeted at <= O_MR1 expect the classes in the android.test.base library
+        // Packages targeted at <= P expect the classes in the android.test.base library
         // to be accessible so this maintains backward compatibility by adding the
         // android.test.base library to those packages.
-        if (apkTargetsApiLevelLessThanOrEqualToOMR1(pkg)) {
+        if (apkTargetsApiLevelLessThanOrEqualToP(pkg)) {
             prefixRequiredLibrary(pkg, ANDROID_TEST_BASE);
         } else {
             // If a package already depends on android.test.runner then add a dependency on
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index a251c00..0cf83fd 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -50,5 +50,8 @@
     void uninstall(in VersionedPackage versionedPackage, String callerPackageName, int flags,
             in IntentSender statusReceiver, int userId);
 
+    void installExistingPackage(String packageName, int installFlags, int installReason,
+            in IntentSender statusReceiver, int userId);
+
     void setPermissionsResult(int sessionId, boolean accepted);
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 14e7725..dcbb4ac 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -111,7 +111,7 @@
     int getPermissionFlags(String permissionName, String packageName, int userId);
 
     void updatePermissionFlags(String permissionName, String packageName, int flagMask,
-            int flagValues, int userId);
+            int flagValues, boolean checkAdjustPolicyFlagPermission, int userId);
 
     void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId);
 
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index b0d16cd..bf556ba 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -514,7 +514,8 @@
 
     /**
      * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and
-     * {@link Intent#CATEGORY_LAUNCHER}, for a specified user.
+     * {@link Intent#CATEGORY_LAUNCHER}, for a specified user. Result may include
+     * synthesized activities like app details Activity injected by system.
      *
      * @param packageName The specific package to query. If null, it checks all installed packages
      *            in the profile.
@@ -794,7 +795,8 @@
      * @throws SecurityException when the caller is not the active launcher.
      */
     @Nullable
-    public LauncherApps.AppUsageLimit getAppUsageLimit(String packageName, UserHandle user) {
+    public LauncherApps.AppUsageLimit getAppUsageLimit(@NonNull String packageName,
+            @NonNull UserHandle user) {
         try {
             return mService.getAppUsageLimit(mContext.getPackageName(), packageName, user);
         } catch (RemoteException re) {
diff --git a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java b/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
index 7790067..707443b 100644
--- a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
+++ b/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
@@ -18,6 +18,7 @@
 import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
 
 import android.content.pm.PackageParser.Package;
+import android.os.Build;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -30,6 +31,11 @@
 @VisibleForTesting
 public class OrgApacheHttpLegacyUpdater extends PackageSharedLibraryUpdater {
 
+    private static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(Package pkg) {
+        int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
+        return targetSdkVersion < Build.VERSION_CODES.P;
+    }
+
     @Override
     public void updatePackage(Package pkg) {
         // Packages targeted at <= O_MR1 expect the classes in the org.apache.http.legacy library
diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java
index b19196a..4331bd4 100644
--- a/core/java/android/content/pm/PackageBackwardCompatibility.java
+++ b/core/java/android/content/pm/PackageBackwardCompatibility.java
@@ -116,7 +116,7 @@
 
     private final PackageSharedLibraryUpdater[] mPackageUpdaters;
 
-    public PackageBackwardCompatibility(
+    private PackageBackwardCompatibility(
             boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) {
         this.mBootClassPathContainsATB = bootClassPathContainsATB;
         this.mPackageUpdaters = packageUpdaters;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 8095473..0304f19 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -590,6 +590,30 @@
         }
     }
 
+    /**
+     * Install the given package, which already exists on the device, for the user for which this
+     * installer was created.
+     *
+     * @param packageName The package to install.
+     * @param installReason Reason for install.
+     * @param statusReceiver Where to deliver the result.
+     */
+    @RequiresPermission(allOf = {
+            Manifest.permission.INSTALL_PACKAGES,
+            Manifest.permission.INSTALL_EXISTING_PACKAGES})
+    public void installExistingPackage(@NonNull String packageName,
+            @InstallReason int installReason,
+            @Nullable IntentSender statusReceiver) {
+        Preconditions.checkNotNull(packageName, "packageName cannot be null");
+        try {
+            mInstaller.installExistingPackage(packageName, 0, installReason, statusReceiver,
+                    mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
     /** {@hide} */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)
@@ -1545,7 +1569,11 @@
 
         /**
          * Set this session to be installing an APEX package.
+         *
+         * {@hide}
          */
+        @SystemApi
+        @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
         public void setInstallAsApex() {
             installFlags |= PackageManager.INSTALL_APEX;
         }
@@ -1715,11 +1743,11 @@
         public int[] childSessionIds = NO_SESSIONS;
 
         /** {@hide} */
-        public boolean isSessionApplied;
+        public boolean isStagedSessionApplied;
         /** {@hide} */
-        public boolean isSessionReady;
+        public boolean isStagedSessionReady;
         /** {@hide} */
-        public boolean isSessionFailed;
+        public boolean isStagedSessionFailed;
         private int mStagedSessionErrorCode;
         private String mStagedSessionErrorMessage;
 
@@ -1758,9 +1786,9 @@
             if (childSessionIds == null) {
                 childSessionIds = NO_SESSIONS;
             }
-            isSessionApplied = source.readBoolean();
-            isSessionReady = source.readBoolean();
-            isSessionFailed = source.readBoolean();
+            isStagedSessionApplied = source.readBoolean();
+            isStagedSessionReady = source.readBoolean();
+            isStagedSessionFailed = source.readBoolean();
             mStagedSessionErrorCode = source.readInt();
             mStagedSessionErrorMessage = source.readString();
         }
@@ -2054,36 +2082,46 @@
             return childSessionIds;
         }
 
+        private void checkSessionIsStaged() {
+            if (!isStaged) {
+                throw new IllegalStateException("Session is not marked as staged.");
+            }
+        }
+
         /**
          * Whether the staged session has been applied successfully, meaning that all of its
          * packages have been activated and no further action is required.
          * Only meaningful if {@code isStaged} is true.
          */
-        public boolean isSessionApplied() {
-            return isSessionApplied;
+        public boolean isStagedSessionApplied() {
+            checkSessionIsStaged();
+            return isStagedSessionApplied;
         }
 
         /**
          * Whether the staged session is ready to be applied at next reboot. Only meaningful if
          * {@code isStaged} is true.
          */
-        public boolean isSessionReady() {
-            return isSessionReady;
+        public boolean isStagedSessionReady() {
+            checkSessionIsStaged();
+            return isStagedSessionReady;
         }
 
         /**
          * Whether something went wrong and the staged session is declared as failed, meaning that
          * it will be ignored at next reboot. Only meaningful if {@code isStaged} is true.
          */
-        public boolean isSessionFailed() {
-            return isSessionFailed;
+        public boolean isStagedSessionFailed() {
+            checkSessionIsStaged();
+            return isStagedSessionFailed;
         }
 
         /**
          * If something went wrong with a staged session, clients can check this error code to
          * understand which kind of failure happened. Only meaningful if {@code isStaged} is true.
          */
-        public int getStagedSessionErrorCode() {
+        public @StagedSessionErrorCode int getStagedSessionErrorCode() {
+            checkSessionIsStaged();
             return mStagedSessionErrorCode;
         }
 
@@ -2092,6 +2130,7 @@
          * empty string if no error was encountered.
          */
         public String getStagedSessionErrorMessage() {
+            checkSessionIsStaged();
             return mStagedSessionErrorMessage;
         }
 
@@ -2134,9 +2173,9 @@
             dest.writeBoolean(isStaged);
             dest.writeInt(parentSessionId);
             dest.writeIntArray(childSessionIds);
-            dest.writeBoolean(isSessionApplied);
-            dest.writeBoolean(isSessionReady);
-            dest.writeBoolean(isSessionFailed);
+            dest.writeBoolean(isStagedSessionApplied);
+            dest.writeBoolean(isStagedSessionReady);
+            dest.writeBoolean(isStagedSessionFailed);
             dest.writeInt(mStagedSessionErrorCode);
             dest.writeString(mStagedSessionErrorMessage);
         }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 0041921..a5464c2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -33,6 +33,7 @@
 import android.annotation.UserIdInt;
 import android.annotation.XmlRes;
 import android.app.ActivityManager;
+import android.app.AppDetailsActivity;
 import android.app.PackageDeleteObserver;
 import android.app.PackageInstallObserver;
 import android.app.admin.DevicePolicyManager;
@@ -2564,6 +2565,14 @@
     public static final String FEATURE_PC = "android.hardware.type.pc";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: This is a foldable device. Properties such as
+     * the display size may change in response to being folded.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_FOLDABLE = "android.hardware.type.foldable";
+
+    /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
      * The device supports printing.
      */
@@ -3030,6 +3039,13 @@
     public static final int MASK_PERMISSION_FLAGS = 0xFF;
 
     /**
+     * Injected activity in app that forwards user to setting activity of that app.
+     *
+     * @hide
+     */
+    public static final String APP_DETAILS_ACTIVITY_CLASS_NAME = AppDetailsActivity.class.getName();
+
+    /**
      * This is a library that contains components apps can invoke. For
      * example, a services for apps to bind to, or standard chooser UI,
      * etc. This library is versioned and backwards compatible. Clients
@@ -5114,7 +5130,10 @@
      * If there is already an application with the given package name installed
      * on the system for other users, also install it for the calling user.
      * @hide
+     *
+     * @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
      */
+    @Deprecated
     @SystemApi
     public abstract int installExistingPackage(String packageName) throws NameNotFoundException;
 
@@ -5122,7 +5141,10 @@
      * If there is already an application with the given package name installed
      * on the system for other users, also install it for the calling user.
      * @hide
+     *
+     * @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
      */
+    @Deprecated
     @SystemApi
     public abstract int installExistingPackage(String packageName, @InstallReason int installReason)
             throws NameNotFoundException;
@@ -5131,7 +5153,10 @@
      * If there is already an application with the given package name installed
      * on the system for other users, also install it for the specified user.
      * @hide
+     *
+     * @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
      */
+    @Deprecated
     @RequiresPermission(anyOf = {
             Manifest.permission.INSTALL_EXISTING_PACKAGES,
             Manifest.permission.INSTALL_PACKAGES,
@@ -5792,6 +5817,37 @@
             @NonNull ComponentName componentName);
 
     /**
+     * Set the enabled setting for a package app settings activity.
+     *
+     * @param packageName The package name of the app
+     * @param enabled The new enabled state for app details activity
+     *
+     * @hide
+     */
+    @RequiresPermission(value = android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE,
+            conditional = true)
+    @SystemApi
+    public void setAppDetailsActivityEnabled(@NonNull String packageName, boolean enabled) {
+        throw new UnsupportedOperationException(
+                "setAppDetailsActivityEnabled not implemented");
+    }
+
+
+    /**
+     * Return the enabled setting for a package app settings activity.
+     *
+     * @param packageName The package name of the app
+     * @return Returns the current enabled state for app settings activity.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean getAppDetailsActivityEnabled(@NonNull String packageName) {
+        throw new UnsupportedOperationException(
+                "getAppDetailsActivityEnabled not implemented");
+    }
+
+    /**
      * Set the enabled setting for an application
      * This setting will override any enabled state which may have been set by the application in
      * its manifest.  It also overrides the enabled state set in the manifest for any of the
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0f67262..4db7d0a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -48,7 +48,6 @@
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityTaskManager;
-import android.app.AppDetailsActivity;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -725,6 +724,9 @@
                 for (int i = 0; i < N; i++) {
                     final Activity a = p.activities.get(i);
                     if (state.isMatch(a.info, flags)) {
+                        if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(a.className)) {
+                            continue;
+                        }
                         res[num++] = generateActivityInfo(a, flags, state, userId);
                     }
                 }
@@ -4311,7 +4313,7 @@
         } else {
             String outInfoName
                 = buildClassName(owner.applicationInfo.packageName, name, outError);
-            if (AppDetailsActivity.class.getName().equals(outInfoName)) {
+            if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
                 outError[0] = tag + " invalid android:name";
                 return false;
             }
@@ -4364,13 +4366,14 @@
             boolean hardwareAccelerated) {
 
         // Build custom App Details activity info instead of parsing it from xml
-        Activity a = new Activity(owner, AppDetailsActivity.class.getName(), new ActivityInfo());
+        Activity a = new Activity(owner, PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME,
+                new ActivityInfo());
         a.owner = owner;
         a.setPackageName(owner.packageName);
 
         a.info.theme = android.R.style.Theme_NoDisplay;
         a.info.exported = true;
-        a.info.name = AppDetailsActivity.class.getName();
+        a.info.name = PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME;
         a.info.processName = owner.applicationInfo.processName;
         a.info.uiOptions = a.info.applicationInfo.uiOptions;
         a.info.taskAffinity = buildTaskAffinityName(owner.packageName, owner.packageName,
diff --git a/core/java/android/content/pm/PackageSharedLibraryUpdater.java b/core/java/android/content/pm/PackageSharedLibraryUpdater.java
index b14b321..1565d9c 100644
--- a/core/java/android/content/pm/PackageSharedLibraryUpdater.java
+++ b/core/java/android/content/pm/PackageSharedLibraryUpdater.java
@@ -17,7 +17,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.Build;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
@@ -60,11 +59,6 @@
                 || ArrayUtils.contains(usesOptionalLibraries, apacheHttpLegacy);
     }
 
-    static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(PackageParser.Package pkg) {
-        int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
-        return targetSdkVersion < Build.VERSION_CODES.P;
-    }
-
     /**
      * Add an implicit dependency.
      *
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index b155325..e674b8b 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -18,6 +18,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.UserIdInt;
@@ -1482,6 +1483,7 @@
      * @hide
      */
     @Nullable
+    @SystemApi
     public Person[] getPersons() {
         return clonePersons(mPersons);
     }
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 849fd03..bd327b0 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -644,6 +644,7 @@
      * @return True if the package has any share target definitions, False otherwise.
      * @hide
      */
+    @SystemApi
     public boolean hasShareTargets(@NonNull String packageName) {
         try {
             return mService.hasShareTargets(mContext.getPackageName(), packageName,
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 8c74ddc..ed8a97c 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -16,6 +16,7 @@
 
 package android.hardware.display;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -93,7 +94,7 @@
      *
      */
     @Nullable
-    public BrightnessCorrection getCorrectionByPackageName(String packageName) {
+    public BrightnessCorrection getCorrectionByPackageName(@NonNull String packageName) {
         return mCorrectionsByPackageName.get(packageName);
     }
 
@@ -106,7 +107,7 @@
      * @return The matching brightness correction, or null.
      */
     @Nullable
-    public BrightnessCorrection getCorrectionByCategory(int category) {
+    public BrightnessCorrection getCorrectionByCategory(@ApplicationInfo.Category int category) {
         return mCorrectionsByCategory.get(category);
     }
 
@@ -238,7 +239,7 @@
      *
      * @hide
      */
-    public void saveToXml(XmlSerializer serializer) throws IOException {
+    public void saveToXml(@NonNull XmlSerializer serializer) throws IOException {
         serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
         if (mDescription != null) {
             serializer.attribute(null, ATTR_DESCRIPTION, mDescription);
@@ -284,7 +285,7 @@
      *
      * @hide
      */
-    public static BrightnessConfiguration loadFromXml(XmlPullParser parser)
+    public static BrightnessConfiguration loadFromXml(@NonNull XmlPullParser parser)
             throws IOException, XmlPullParserException {
         String description = null;
         List<Float> luxList = new ArrayList<>();
@@ -443,8 +444,10 @@
          *      {@link #getMaxCorrectionsByPackageName}).
          *
          */
-        public Builder addCorrectionByPackageName(String packageName,
-                BrightnessCorrection correction) {
+        public Builder addCorrectionByPackageName(@NonNull String packageName,
+                @NonNull BrightnessCorrection correction) {
+            Objects.requireNonNull(packageName, "packageName must not be null");
+            Objects.requireNonNull(correction, "correction must not be null");
             if (mCorrectionsByPackageName.size() >= getMaxCorrectionsByPackageName()) {
                 throw new IllegalArgumentException("Too many corrections by package name");
             }
@@ -470,7 +473,8 @@
          *
          */
         public Builder addCorrectionByCategory(@ApplicationInfo.Category int category,
-                BrightnessCorrection correction) {
+                @NonNull BrightnessCorrection correction) {
+            Objects.requireNonNull(correction, "correction must not be null");
             if (mCorrectionsByCategory.size() >= getMaxCorrectionsByCategory()) {
                 throw new IllegalArgumentException("Too many corrections by category");
             }
diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java
index 6a073ff..ee8d846 100644
--- a/core/java/android/hardware/display/BrightnessCorrection.java
+++ b/core/java/android/hardware/display/BrightnessCorrection.java
@@ -16,6 +16,7 @@
 
 package android.hardware.display;
 
+import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -59,15 +60,15 @@
 
     /**
      * Creates a BrightnessCorrection that given {@code brightness}, corrects it to be
-     * {@code exp(scale * log(brightness) + translate)}.
+     * {@code exp(scale * ln(brightness) + translate)}.
      *
      * @param scale
-     *      How much to scale the log brightness.
+     *      How much to scale the log (base e) brightness.
      * @param translate
-     *      How much to translate the log brightness.
+     *      How much to translate the log (base e) brightness.
      *
      * @return A BrightnessCorrection that given {@code brightness}, corrects it to be
-     * {@code exp(scale * log(brightness) + translate)}.
+     * {@code exp(scale * ln(brightness) + translate)}.
      *
      * @throws IllegalArgumentException
      *      - scale or translate are NaN.
@@ -87,7 +88,8 @@
      *
      * @return The corrected brightness.
      */
-    public float apply(float brightness) {
+    @FloatRange(from = 0.0)
+    public float apply(@FloatRange(from = 0.0) float brightness) {
         return mImplementation.apply(brightness);
     }
 
@@ -202,7 +204,7 @@
 
     /**
      * A BrightnessCorrection that given {@code brightness}, corrects it to be
-     * {@code exp(scale * log(brightness) + translate)}.
+     * {@code exp(scale * ln(brightness) + translate)}.
      */
     private static class ScaleAndTranslateLog implements BrightnessCorrectionImplementation {
         private static final float MIN_SCALE = 0.5f;
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index 19848ee..5496e17 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -16,6 +16,7 @@
 package android.metrics;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.util.Log;
 import android.util.SparseArray;
@@ -31,6 +32,7 @@
  * @hide
  */
 @SystemApi
+@TestApi
 public class LogMaker {
     private static final String TAG = "LogBuilder";
 
diff --git a/core/java/android/metrics/MetricsReader.java b/core/java/android/metrics/MetricsReader.java
index 5f356ca..27f9a5d 100644
--- a/core/java/android/metrics/MetricsReader.java
+++ b/core/java/android/metrics/MetricsReader.java
@@ -16,6 +16,7 @@
 package android.metrics;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.util.EventLog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -35,6 +36,7 @@
  * @hide
  */
 @SystemApi
+@TestApi
 public class MetricsReader {
     private Queue<LogMaker> mPendingQueue = new LinkedList<>();
     private Queue<LogMaker> mSeenQueue = new LinkedList<>();
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 6d195ae..c1bce5e 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3147,9 +3147,9 @@
 
         /**
          * Called if no network is found in the timeout time specified in
-         * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} call. This callback is not
-         * called for the version of {@link #requestNetwork(NetworkRequest, NetworkCallback)}
-         * without timeout. When this callback is invoked the associated
+         * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} call or if the
+         * requested network request cannot be fulfilled (whether or not a timeout was
+         * specified). When this callback is invoked the associated
          * {@link NetworkRequest} will have already been removed and released, as if
          * {@link #unregisterNetworkCallback(NetworkCallback)} had been called.
          */
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
index 0dfe7a4..5b1d12c 100644
--- a/core/java/android/net/NetworkFactory.java
+++ b/core/java/android/net/NetworkFactory.java
@@ -27,11 +27,13 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Protocol;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -113,7 +115,16 @@
      */
     private static final int CMD_SET_FILTER = BASE + 3;
 
+    /**
+     * Sent by NetworkFactory to ConnectivityService to indicate that a request is
+     * unfulfillable.
+     * @see #releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest).
+     */
+    public static final int EVENT_UNFULFILLABLE_REQUEST = BASE + 4;
+
     private final Context mContext;
+    private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>();
+    private AsyncChannel mAsyncChannel;
     private final String LOG_TAG;
 
     private final SparseArray<NetworkRequestInfo> mNetworkRequests =
@@ -155,6 +166,36 @@
     @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
+            case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
+                if (mAsyncChannel != null) {
+                    log("Received new connection while already connected!");
+                    break;
+                }
+                if (VDBG) log("NetworkFactory fully connected");
+                AsyncChannel ac = new AsyncChannel();
+                ac.connected(null, this, msg.replyTo);
+                ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+                        AsyncChannel.STATUS_SUCCESSFUL);
+                mAsyncChannel = ac;
+                for (Message m : mPreConnectedQueue) {
+                    ac.sendMessage(m);
+                }
+                mPreConnectedQueue.clear();
+                break;
+            }
+            case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
+                if (VDBG) log("CMD_CHANNEL_DISCONNECT");
+                if (mAsyncChannel != null) {
+                    mAsyncChannel.disconnect();
+                    mAsyncChannel = null;
+                }
+                break;
+            }
+            case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+                if (DBG) log("NetworkFactory channel lost");
+                mAsyncChannel = null;
+                break;
+            }
             case CMD_REQUEST_NETWORK: {
                 handleAddRequest((NetworkRequest) msg.obj, msg.arg1, msg.arg2);
                 break;
@@ -355,6 +396,27 @@
         });
     }
 
+    /**
+     * Can be called by a factory to release a request as unfulfillable: the request will be
+     * removed, and the caller will get a
+     * {@link ConnectivityManager.NetworkCallback#onUnavailable()} callback after this function
+     * returns.
+     *
+     * Note: this should only be called by factory which KNOWS that it is the ONLY factory which
+     * is able to fulfill this request!
+     */
+    protected void releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest r) {
+        post(() -> {
+            if (DBG) log("releaseRequestAsUnfulfillableByAnyFactory: " + r);
+            Message msg = obtainMessage(EVENT_UNFULFILLABLE_REQUEST, r);
+            if (mAsyncChannel != null) {
+                mAsyncChannel.sendMessage(msg);
+            } else {
+                mPreConnectedQueue.add(msg);
+            }
+        });
+    }
+
     // override to do simple mode (request independent)
     protected void startNetwork() { }
     protected void stopNetwork() { }
diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java
index 42e8aa6..a43dc60 100644
--- a/core/java/android/net/metrics/ValidationProbeEvent.java
+++ b/core/java/android/net/metrics/ValidationProbeEvent.java
@@ -79,7 +79,7 @@
     }
 
     /**
-     * Utility to create an instance of {@link ApfProgramEvent}.
+     * Utility to create an instance of {@link ValidationProbeEvent}.
      */
     public static class Builder {
         private long mDurationMs;
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 27f7e22..684369a 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -59,7 +59,8 @@
                 BUGREPORT_ERROR_INVALID_INPUT,
                 BUGREPORT_ERROR_RUNTIME,
                 BUGREPORT_ERROR_USER_DENIED_CONSENT,
-                BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT
+                BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT,
+                BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS
         })
 
         /** Possible error codes taking a bugreport can encounter */
@@ -81,6 +82,10 @@
         public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT =
                 IDumpstateListener.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT;
 
+        /** There is currently a bugreport running. The caller should try again later. */
+        public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS =
+                IDumpstateListener.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS;
+
         /**
          * Called when there is a progress update.
          * @param progress the progress in [0.0, 100.0]
@@ -96,6 +101,9 @@
          * <p>If {@code BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT} is passed, then the consent timed
          * out, but the bugreport could be available in the internal directory of dumpstate for
          * manual retrieval.
+         *
+         * <p> If {@code BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS} is passed, then the
+         * caller should try later, as only one bugreport can be in progress at a time.
          */
         public void onError(@BugreportErrorCode int errorCode) {}
 
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 83a7654..0425c62 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -107,6 +107,7 @@
      * Whether this build was for an emulator device.
      * @hide
      */
+    @TestApi
     public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1");
 
     /**
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index da03895..330bde5 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -16,11 +16,14 @@
 
 package android.os;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.util.Log;
 
 import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -45,6 +48,24 @@
  * keep a reference to the FileObserver instance from some other live object.</p>
  */
 public abstract class FileObserver {
+    /** @hide */
+    @IntDef(flag = true, value = {
+            ACCESS,
+            MODIFY,
+            ATTRIB,
+            CLOSE_WRITE,
+            CLOSE_NOWRITE,
+            OPEN,
+            MOVED_FROM,
+            MOVED_TO,
+            CREATE,
+            DELETE,
+            DELETE_SELF,
+            MOVE_SELF
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NotifyEventType {}
+
     /** Event type: Data was read from a file */
     public static final int ACCESS = 0x00000001;
     /** Event type: Data was written to a file */
@@ -71,6 +92,7 @@
     public static final int MOVE_SELF = 0x00000800;
 
     /** Event mask: All valid event types, combined */
+    @NotifyEventType
     public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE
             | CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE
             | DELETE_SELF | MOVE_SELF;
@@ -90,7 +112,8 @@
             observe(m_fd);
         }
 
-        public int[] startWatching(List<File> files, int mask, FileObserver observer) {
+        public int[] startWatching(List<File> files,
+                @NotifyEventType int mask, FileObserver observer) {
             final int count = files.size();
             final String[] paths = new String[count];
             for (int i = 0; i < count; ++i) {
@@ -118,7 +141,7 @@
             stopWatching(m_fd, descriptors);
         }
 
-        public void onEvent(int wfd, int mask, String path) {
+        public void onEvent(int wfd, @NotifyEventType int mask, String path) {
             // look up our observer, fixing up the map if necessary...
             FileObserver observer = null;
 
@@ -144,7 +167,8 @@
 
         private native int init();
         private native void observe(int fd);
-        private native void startWatching(int fd, String[] paths, int mask, int[] wfds);
+        private native void startWatching(int fd, String[] paths,
+                @NotifyEventType int mask, int[] wfds);
         private native void stopWatching(int fd, int[] wfds);
     }
 
@@ -197,7 +221,7 @@
      * @deprecated use {@link #FileObserver(File, int)} instead.
      */
     @Deprecated
-    public FileObserver(String path, int mask) {
+    public FileObserver(String path, @NotifyEventType int mask) {
         this(new File(path), mask);
     }
 
@@ -209,7 +233,7 @@
      * @param file The file or directory to monitor
      * @param mask The event or events (added together) to watch for
      */
-    public FileObserver(@NonNull File file, int mask) {
+    public FileObserver(@NonNull File file, @NotifyEventType int mask) {
         this(Arrays.asList(file), mask);
     }
 
@@ -220,7 +244,7 @@
      * @param files The files or directories to monitor
      * @param mask The event or events (added together) to watch for
      */
-    public FileObserver(@NonNull List<File> files, int mask) {
+    public FileObserver(@NonNull List<File> files, @NotifyEventType int mask) {
         mFiles = files;
         mMask = mask;
     }
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 47b1eef..650d2178 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -664,8 +664,15 @@
           .append(abi);
         final String paths = sb.toString();
 
-        if (DEBUG) Log.v(TAG, "gfx driver package libs: " + paths);
-        setDriverPath(paths);
+        final String sphalLibraries =
+                coreSettings.getString(Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES);
+
+        if (DEBUG) {
+            Log.v(TAG,
+                    "gfx driver package search path: " + paths
+                            + ", required sphal libraries: " + sphalLibraries);
+        }
+        setDriverPathAndSphalLibraries(paths, sphalLibraries);
 
         if (driverAppInfo.metaData == null) {
             throw new NullPointerException("apk's meta-data cannot be null");
@@ -700,7 +707,7 @@
     private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
     private static native void setDebugLayers(String layers);
     private static native void setDebugLayersGLES(String layers);
-    private static native void setDriverPath(String path);
+    private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries);
     private static native void setGpuStats(String driverPackageName, String driverVersionName,
             long driverVersionCode, long driverBuildTime, String appPackageName);
     private static native void setAngleInfo(String path, String appPackage, String devOptIn,
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 6bd1f2b..91ddf82 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1569,7 +1569,7 @@
     @LocationPowerSaveMode
     public int getLocationPowerSaveMode() {
         final PowerSaveState powerSaveState = getPowerSaveState(ServiceType.LOCATION);
-        if (!powerSaveState.globalBatterySaverEnabled) {
+        if (!powerSaveState.batterySaverEnabled) {
             return LOCATION_MODE_NO_CHANGE;
         }
         return powerSaveState.locationMode;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 9e97e37..cd43b42 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -505,7 +505,6 @@
      * @param invokeWith null-ok the command to invoke with.
      * @param packageName null-ok the name of the package this process belongs to.
      * @param packagesForUid null-ok all the packages with the same uid as this process.
-     * @param visibleVols null-ok storage volumes that can be accessed by this process.
      * @param zygoteArgs Additional arguments to supply to the zygote process.
      * 
      * @return An object that describes the result of the attempt to start the process.
@@ -525,13 +524,12 @@
                                   @Nullable String invokeWith,
                                   @Nullable String packageName,
                                   @Nullable String[] packagesForUid,
-                                  @Nullable String[] visibleVols,
                                   @Nullable String sandboxId,
                                   @Nullable String[] zygoteArgs) {
         return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, packageName,
-                    packagesForUid, visibleVols, sandboxId, /*useBlastulaPool=*/ true, zygoteArgs);
+                    packagesForUid, sandboxId, /*useBlastulaPool=*/ true, zygoteArgs);
     }
 
     /** @hide */
@@ -547,13 +545,12 @@
                                   @Nullable String invokeWith,
                                   @Nullable String packageName,
                                   @Nullable String[] packagesForUid,
-                                  @Nullable String[] visibleVols,
                                   @Nullable String sandboxId,
                                   @Nullable String[] zygoteArgs) {
         return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, packageName,
-                    packagesForUid, visibleVols, sandboxId, /*useBlastulaPool=*/ false, zygoteArgs);
+                    packagesForUid, sandboxId, /*useBlastulaPool=*/ false, zygoteArgs);
     }
 
     /**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index e2b5730..0673755 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2697,6 +2697,19 @@
     }
 
     /**
+     * Updates the calling user's name.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * @param name the new name for the user
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public void setUserName(String name) {
+        setUserName(getUserHandle(), name);
+    }
+
+    /**
      * Sets the user's photo.
      * @param userHandle the user for whom to change the photo.
      * @param icon the bitmap to set as the photo.
@@ -2711,6 +2724,19 @@
     }
 
     /**
+     * Sets the calling user's photo.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * @param icon the bitmap to set as the photo.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public void setUserIcon(Bitmap icon) {
+        setUserIcon(getUserHandle(), icon);
+    }
+
+    /**
      * Returns a file descriptor for the user's photo. PNG data can be read from this file.
      * @param userHandle the user whose photo we want to read.
      * @return a {@link Bitmap} of the user's photo, or null if there's no photo.
@@ -2737,6 +2763,20 @@
     }
 
     /**
+     * Returns a Bitmap for the calling user's photo.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     *
+     * @return a {@link Bitmap} of the user's photo, or null if there's no photo.
+     * @see com.android.internal.util.UserIcons#getDefaultUserIcon for a default.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public Bitmap getUserIcon() {
+        return getUserIcon(getUserHandle());
+    }
+
+    /**
      * Returns the maximum number of users that can be created on this device. A return value
      * of 1 means that it is a single user device.
      * @hide
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index de378b0..e49b65e 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -27,7 +27,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.Zygote;
-import com.android.internal.util.Preconditions;
 
 import java.io.BufferedWriter;
 import java.io.DataInputStream;
@@ -122,8 +121,9 @@
                 new LocalSocketAddress(Zygote.BLASTULA_POOL_SECONDARY_SOCKET_NAME,
                                        LocalSocketAddress.Namespace.RESERVED);
 
-        // TODO (chriswailes): Uncomment when the blastula pool can be enabled.
-//        fetchBlastulaPoolEnabledProp();
+        if (fetchBlastulaPoolEnabledProp()) {
+            informZygotesOfBlastulaPoolStatus();
+        }
     }
 
     public ZygoteProcess(LocalSocketAddress primarySocketAddress,
@@ -305,7 +305,6 @@
      * @param invokeWith null-ok the command to invoke with.
      * @param packageName null-ok the name of the package this process belongs to.
      * @param packagesForUid null-ok all the packages with the same uid as this process.
-     * @param visibleVols null-ok storage volumes that can be accessed by this process.
      * @param zygoteArgs Additional arguments to supply to the zygote process.
      *
      * @return An object that describes the result of the attempt to start the process.
@@ -323,23 +322,19 @@
                                                   @Nullable String invokeWith,
                                                   @Nullable String packageName,
                                                   @Nullable String[] packagesForUid,
-                                                  @Nullable String[] visibleVols,
                                                   @Nullable String sandboxId,
                                                   boolean useBlastulaPool,
                                                   @Nullable String[] zygoteArgs) {
-        if (fetchBlastulaPoolEnabledProp()) {
-            // TODO (chriswailes): Send the appropriate command to the zygotes
-            Log.i(LOG_TAG, "Blastula pool enabled property set to: " + mBlastulaPoolEnabled);
-
-            // This can't be enabled yet, but we do want to test this code path.
-            mBlastulaPoolEnabled = false;
+        // TODO (chriswailes): Is there a better place to check this value?
+        if (fetchBlastulaPoolEnabledPropWithMinInterval()) {
+            informZygotesOfBlastulaPoolStatus();
         }
 
         try {
             return startViaZygote(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/false,
-                    packageName, packagesForUid, visibleVols, sandboxId,
+                    packageName, packagesForUid, sandboxId,
                     useBlastulaPool, zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
@@ -387,7 +382,7 @@
      * @throws ZygoteStartFailedEx if process start failed for any reason
      */
     @GuardedBy("mLock")
-    private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
+    private Process.ProcessStartResult zygoteSendArgsAndGetResult(
             ZygoteState zygoteState, boolean useBlastulaPool, ArrayList<String> args)
             throws ZygoteStartFailedEx {
         // Throw early if any of the arguments are malformed. This means we can
@@ -415,7 +410,7 @@
         Process.ProcessStartResult result = new Process.ProcessStartResult();
 
         // TODO (chriswailes): Move branch body into separate function.
-        if (useBlastulaPool && isValidBlastulaCommand(args)) {
+        if (useBlastulaPool && mBlastulaPoolEnabled && isValidBlastulaCommand(args)) {
             LocalSocket blastulaSessionSocket = null;
 
             try {
@@ -444,7 +439,7 @@
                 // If there was an IOException using the blastula pool we will log the error and
                 // attempt to start the process through the Zygote.
                 Log.e(LOG_TAG, "IO Exception while communicating with blastula pool - "
-                               + ex.toString());
+                               + ex.getMessage());
             } finally {
                 try {
                     blastulaSessionSocket.close();
@@ -531,7 +526,6 @@
      * that has its state cloned from this zygote process.
      * @param packageName null-ok the name of the package this process belongs to.
      * @param packagesForUid null-ok all the packages with the same uid as this process.
-     * @param visibleVols null-ok storage volumes that can be accessed by this process.
      * @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
@@ -550,7 +544,6 @@
                                                       boolean startChildZygote,
                                                       @Nullable String packageName,
                                                       @Nullable String[] packagesForUid,
-                                                      @Nullable String[] visibleVols,
                                                       @Nullable String sandboxId,
                                                       boolean useBlastulaPool,
                                                       @Nullable String[] extraArgs)
@@ -638,19 +631,6 @@
             argsForZygote.add(sb.toString());
         }
 
-        if (visibleVols != null && visibleVols.length > 0) {
-            final StringBuilder sb = new StringBuilder();
-            sb.append("--visible-vols=");
-
-            for (int i = 0; i < visibleVols.length; ++i) {
-                if (i != 0) {
-                    sb.append(',');
-                }
-                sb.append(visibleVols[i]);
-            }
-            argsForZygote.add(sb.toString());
-        }
-
         if (sandboxId != null) {
             argsForZygote.add("--sandbox-id=" + sandboxId);
         }
@@ -665,7 +645,7 @@
 
         synchronized(mLock) {
             return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
-                                              useBlastulaPool && mBlastulaPoolEnabled,
+                                              useBlastulaPool,
                                               argsForZygote);
         }
     }
@@ -685,6 +665,10 @@
                             Boolean.parseBoolean(BLASTULA_POOL_ENABLED_DEFAULT));
         }
 
+        if (origVal != mBlastulaPoolEnabled) {
+            Log.i(LOG_TAG, "blastulaPoolEnabled = " + mBlastulaPoolEnabled);
+        }
+
         return origVal != mBlastulaPoolEnabled;
     }
 
@@ -847,49 +831,57 @@
     }
 
     /**
+     * Creates a ZygoteState for the primary zygote if it doesn't exist or has been disconnected.
+     */
+    @GuardedBy("mLock")
+    private void attemptConnectionToPrimaryZygote() throws IOException {
+        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
+            primaryZygoteState =
+                    ZygoteState.connect(mZygoteSocketAddress, mBlastulaPoolSocketAddress);
+
+            maybeSetApiBlacklistExemptions(primaryZygoteState, false);
+            maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
+        }
+    }
+
+    /**
+     * Creates a ZygoteState for the secondary zygote if it doesn't exist or has been disconnected.
+     */
+    @GuardedBy("mLock")
+    private void attemptConnectionToSecondaryZygote() throws IOException {
+        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
+            secondaryZygoteState =
+                    ZygoteState.connect(mZygoteSecondarySocketAddress,
+                            mBlastulaPoolSecondarySocketAddress);
+
+            maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
+            maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
+        }
+    }
+
+    /**
      * Tries to open a session socket to a Zygote process with a compatible ABI if one is not
      * already open. If a compatible session socket is already open that session socket is returned.
      * This function may block and may have to try connecting to multiple Zygotes to find the
      * appropriate one.  Requires that mLock be held.
      */
     @GuardedBy("mLock")
-    private ZygoteState openZygoteSocketIfNeeded(String abi)
-            throws ZygoteStartFailedEx {
+    private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
+        try {
+            attemptConnectionToPrimaryZygote();
 
-        Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
-
-        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
-            try {
-                primaryZygoteState =
-                    ZygoteState.connect(mZygoteSocketAddress, mBlastulaPoolSocketAddress);
-            } catch (IOException ioe) {
-                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
+            if (primaryZygoteState.matches(abi)) {
+                return primaryZygoteState;
             }
 
-            maybeSetApiBlacklistExemptions(primaryZygoteState, false);
-            maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
-        }
+            // The primary zygote didn't match. Try the secondary.
+            attemptConnectionToSecondaryZygote();
 
-        if (primaryZygoteState.matches(abi)) {
-            return primaryZygoteState;
-        }
-
-        // The primary zygote didn't match. Try the secondary.
-        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
-            try {
-                secondaryZygoteState =
-                    ZygoteState.connect(mZygoteSecondarySocketAddress,
-                                        mBlastulaPoolSecondarySocketAddress);
-            } catch (IOException ioe) {
-                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
+            if (secondaryZygoteState.matches(abi)) {
+                return secondaryZygoteState;
             }
-
-            maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
-            maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
-        }
-
-        if (secondaryZygoteState.matches(abi)) {
-            return secondaryZygoteState;
+        } catch (IOException ioe) {
+            throw new ZygoteStartFailedEx("Error connecting to zygote", ioe);
         }
 
         throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
@@ -1015,6 +1007,57 @@
     }
 
     /**
+     * Sends messages to the zygotes telling them to change the status of their blastula pools.  If
+     * this notification fails the ZygoteProcess will fall back to the previous behavior.
+     */
+    private void informZygotesOfBlastulaPoolStatus() {
+        final String command = "1\n--blastula-pool-enabled=" + mBlastulaPoolEnabled + "\n";
+
+        synchronized (mLock) {
+            try {
+                attemptConnectionToPrimaryZygote();
+
+                primaryZygoteState.mZygoteOutputWriter.write(command);
+                primaryZygoteState.mZygoteOutputWriter.flush();
+            } catch (IOException ioe) {
+                mBlastulaPoolEnabled = !mBlastulaPoolEnabled;
+                Log.w(LOG_TAG, "Failed to inform zygotes of blastula pool status: "
+                        + ioe.getMessage());
+                return;
+            }
+
+            try {
+                attemptConnectionToSecondaryZygote();
+
+                try {
+                    secondaryZygoteState.mZygoteOutputWriter.write(command);
+                    secondaryZygoteState.mZygoteOutputWriter.flush();
+
+                    // Wait for the secondary Zygote to finish its work.
+                    secondaryZygoteState.mZygoteInputStream.readInt();
+                } catch (IOException ioe) {
+                    throw new IllegalStateException(
+                            "Blastula pool state change cause an irrecoverable error",
+                            ioe);
+                }
+            } catch (IOException ioe) {
+                // No secondary zygote present.  This is expected on some devices.
+            }
+
+            // Wait for the response from the primary zygote here so the primary/secondary zygotes
+            // can work concurrently.
+            try {
+                // Wait for the primary zygote to finish its work.
+                primaryZygoteState.mZygoteInputStream.readInt();
+            } catch (IOException ioe) {
+                throw new IllegalStateException(
+                        "Blastula pool state change cause an irrecoverable error",
+                        ioe);
+            }
+        }
+    }
+
+    /**
      * Starts a new zygote process as a child of this zygote. This is used to create
      * secondary zygotes that inherit data from the zygote that this object
      * communicates with. This returns a new ZygoteProcess representing a connection
@@ -1061,7 +1104,7 @@
                     gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
                     abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
                     true /* startChildZygote */, null /* packageName */,
-                    null /* packagesForUid */, null /* visibleVolumes */, null /* sandboxId */,
+                    null /* packagesForUid */, null /* sandboxId */,
                     false /* useBlastulaPool */, 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 03b2c2c..f1c3138 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -109,11 +109,6 @@
             @Nullable String sharedUserId, int userId);
 
     /**
-     * @return Labels of storage volumes that are visible to the given userId.
-     */
-    public abstract String[] getVisibleVolumesForUser(int userId);
-
-    /**
      * A listener for reset events in the StorageManagerService.
      */
     public interface ResetListener {
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 7e9ba5d..cb2517e 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -40,4 +40,6 @@
     void getPermissionUsages(boolean countSystem, long numMillis, in RemoteCallback callback);
     void isApplicationQualifiedForRole(String roleName, String packageName,
             in RemoteCallback callback);
+    void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName, String packageName,
+            String permission, int grantState, in RemoteCallback callback);
 }
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index d62bc6c5..9d58064 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -16,8 +16,12 @@
 
 package android.permission;
 
+import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED;
+import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED;
 import static android.permission.PermissionControllerService.SERVICE_INTERFACE;
 
+import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
 import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
 import static com.android.internal.util.Preconditions.checkFlagsArgument;
@@ -35,6 +39,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.app.admin.DevicePolicyManager.PermissionGrantState;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -268,6 +273,40 @@
     }
 
     /**
+     * Set the runtime permission state from a device admin.
+     *
+     * @param callerPackageName The package name of the admin requesting the change
+     * @param packageName Package the permission belongs to
+     * @param permission Permission to change
+     * @param grantState State to set the permission into
+     * @param executor Executor to run the {@code callback} on
+     * @param callback The callback
+     *
+     * @hide
+     */
+    @RequiresPermission(allOf = {Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+            Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+            Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY},
+            conditional = true)
+    public void setRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName,
+            @NonNull String packageName, @NonNull String permission,
+            @PermissionGrantState int grantState, @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Boolean> callback) {
+        checkStringNotEmpty(callerPackageName);
+        checkStringNotEmpty(packageName);
+        checkStringNotEmpty(permission);
+        checkArgument(grantState == PERMISSION_GRANT_STATE_GRANTED
+                || grantState == PERMISSION_GRANT_STATE_DENIED
+                || grantState == PERMISSION_GRANT_STATE_DEFAULT);
+        checkNotNull(executor);
+        checkNotNull(callback);
+
+        mRemoteService.scheduleRequest(new PendingSetRuntimePermissionGrantStateByDeviceAdmin(
+                mRemoteService, callerPackageName, packageName, permission, grantState, executor,
+                callback));
+    }
+
+    /**
      * Create a backup of the runtime permissions.
      *
      * @param user The user to be backed up
@@ -480,7 +519,7 @@
         }
 
         @Override
-        public void scheduleRequest(@NonNull PendingRequest<RemoteService,
+        public void scheduleRequest(@NonNull BasePendingRequest<RemoteService,
                 IPermissionController> pendingRequest) {
             super.scheduleRequest(pendingRequest);
         }
@@ -797,6 +836,67 @@
     }
 
     /**
+     * Request for {@link #getRuntimePermissionBackup}
+     */
+    private static final class PendingSetRuntimePermissionGrantStateByDeviceAdmin extends
+            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
+        private final @NonNull String mCallerPackageName;
+        private final @NonNull String mPackageName;
+        private final @NonNull String mPermission;
+        private final @PermissionGrantState int mGrantState;
+
+        private final @NonNull Executor mExecutor;
+        private final @NonNull Consumer<Boolean> mCallback;
+        private final @NonNull RemoteCallback mRemoteCallback;
+
+        private PendingSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull RemoteService service,
+                @NonNull String callerPackageName, @NonNull String packageName,
+                @NonNull String permission, @PermissionGrantState int grantState,
+                @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+            super(service);
+
+            mCallerPackageName = callerPackageName;
+            mPackageName = packageName;
+            mPermission = permission;
+            mGrantState = grantState;
+            mExecutor = executor;
+            mCallback = callback;
+
+            mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    callback.accept(result.getBoolean(KEY_RESULT, false));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+
+                    finish();
+                }
+            }), null);
+        }
+
+        @Override
+        protected void onTimeout(RemoteService remoteService) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.accept(false));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void run() {
+            try {
+                getService().getServiceInterface().setRuntimePermissionGrantStateByDeviceAdmin(
+                        mCallerPackageName, mPackageName, mPermission, mGrantState, mRemoteCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error setting permissions state for device admin " + mPackageName,
+                        e);
+            }
+        }
+    }
+
+    /**
      * Request for {@link #restoreRuntimePermissionBackup}
      */
     private static final class PendingRestoreRuntimePermissionBackup implements
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index fb6c061..e883d25 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -16,6 +16,9 @@
 
 package android.permission;
 
+import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED;
+import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED;
 import static android.permission.PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED;
 import static android.permission.PermissionControllerManager.COUNT_WHEN_SYSTEM;
 
@@ -32,6 +35,7 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.app.Service;
+import android.app.admin.DevicePolicyManager.PermissionGrantState;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
@@ -180,6 +184,18 @@
     public abstract boolean onIsApplicationQualifiedForRole(@NonNull String roleName,
             @NonNull String packageName);
 
+    /**
+     * Set the runtime permission state from a device admin.
+     *
+     * @param callerPackageName The package name of the admin requesting the change
+     * @param packageName Package the permission belongs to
+     * @param permission Permission to change
+     * @param grantState State to set the permission into
+     */
+    public abstract boolean onSetRuntimePermissionGrantStateByDeviceAdmin(
+            @NonNull String callerPackageName, @NonNull String packageName,
+            @NonNull String permission, @PermissionGrantState int grantState);
+
     @Override
     public final IBinder onBind(Intent intent) {
         return new IPermissionController.Stub() {
@@ -326,6 +342,35 @@
                         PermissionControllerService::isApplicationQualifiedForRole,
                         PermissionControllerService.this, roleName, packageName, callback));
             }
+
+            @Override
+            public void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName,
+                    String packageName, String permission, int grantState,
+                    RemoteCallback callback) {
+                checkStringNotEmpty(callerPackageName);
+                checkStringNotEmpty(packageName);
+                checkStringNotEmpty(permission);
+                checkArgument(grantState == PERMISSION_GRANT_STATE_GRANTED
+                        || grantState == PERMISSION_GRANT_STATE_DENIED
+                        || grantState == PERMISSION_GRANT_STATE_DEFAULT);
+                checkNotNull(callback);
+
+                if (grantState == PERMISSION_GRANT_STATE_DENIED) {
+                    enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null);
+                }
+
+                if (grantState == PERMISSION_GRANT_STATE_DENIED) {
+                    enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null);
+                }
+
+                enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
+                        null);
+
+                mHandler.sendMessage(obtainMessage(
+                        PermissionControllerService::setRuntimePermissionGrantStateByDeviceAdmin,
+                        PermissionControllerService.this, callerPackageName, packageName,
+                        permission, grantState, callback));
+            }
         };
     }
 
@@ -399,4 +444,15 @@
         result.putBoolean(PermissionControllerManager.KEY_RESULT, qualified);
         callback.sendResult(result);
     }
+
+    private void setRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName,
+            @NonNull String packageName, @NonNull String permission,
+            @PermissionGrantState int grantState, @NonNull RemoteCallback callback) {
+        boolean wasSet = onSetRuntimePermissionGrantStateByDeviceAdmin(callerPackageName,
+                packageName, permission, grantState);
+
+        Bundle result = new Bundle();
+        result.putBoolean(PermissionControllerManager.KEY_RESULT, wasSet);
+        callback.sendResult(result);
+    }
 }
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index f63c0adb..44adc1c 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -783,7 +783,7 @@
                 String postDialDigits, String viaNumber, int presentation, int callType,
                 int features, PhoneAccountHandle accountHandle, long start, int duration,
                 Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo,
-                boolean isRead, int callBlockReason, String callScreeningAppName,
+                boolean isRead, int callBlockReason, CharSequence callScreeningAppName,
                 String callScreeningComponentName, CallIdentification callIdentification) {
             if (VERBOSE_LOG) {
                 Log.v(LOG_TAG, String.format("Add call: number=%s, user=%s, for all=%s",
@@ -836,15 +836,19 @@
             }
 
             values.put(BLOCK_REASON, callBlockReason);
-            values.put(CALL_SCREENING_APP_NAME, callScreeningAppName);
+            values.put(CALL_SCREENING_APP_NAME, charSequenceToString(callScreeningAppName));
             values.put(CALL_SCREENING_COMPONENT_NAME, callScreeningComponentName);
 
             if (callIdentification != null) {
                 values.put(CALL_ID_PACKAGE_NAME, callIdentification.getCallScreeningPackageName());
-                values.put(CALL_ID_APP_NAME, callIdentification.getCallScreeningAppName());
-                values.put(CALL_ID_NAME, callIdentification.getName());
-                values.put(CALL_ID_DESCRIPTION, callIdentification.getDescription());
-                values.put(CALL_ID_DETAILS, callIdentification.getDetails());
+                values.put(CALL_ID_APP_NAME,
+                        charSequenceToString(callIdentification.getCallScreeningAppName()));
+                values.put(CALL_ID_NAME,
+                        charSequenceToString(callIdentification.getName()));
+                values.put(CALL_ID_DESCRIPTION,
+                        charSequenceToString(callIdentification.getDescription()));
+                values.put(CALL_ID_DETAILS,
+                        charSequenceToString(callIdentification.getDetails()));
                 values.put(CALL_ID_NUISANCE_CONFIDENCE, callIdentification.getNuisanceConfidence());
             } else {
                 values.putNull(CALL_ID_PACKAGE_NAME);
@@ -987,6 +991,10 @@
             return result;
         }
 
+        private static String charSequenceToString(CharSequence sequence) {
+            return sequence == null ? null : sequence.toString();
+        }
+
         /** @hide */
         public static boolean shouldHaveSharedCallLogEntries(Context context,
                 UserManager userManager, int userId) {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index f6a8388..ce28c30 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -169,7 +169,7 @@
          *
          * @hide for internal use only
          */
-        String BLASTULA_POOL_SIZE_MIN = "blastula_pool_size_max";
+        String BLASTULA_POOL_SIZE_MIN = "blastula_pool_size_min";
 
         /**
          * The threshold used to determine if the pool should be refilled.
@@ -197,6 +197,7 @@
      */
     @SystemApi
     public interface MediaNative {
+        /** The flag namespace for media native features. */
         String NAMESPACE = "media_native";
     }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a465b32..8acdc8c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1204,6 +1204,21 @@
     public static final String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS";
 
     /**
+     * Activity Action: Show Notification assistant settings.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     * @see android.service.notification.NotificationAssistantService
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_NOTIFICATION_ASSISTANT_SETTINGS =
+            "android.settings.NOTIFICATION_ASSISTANT_SETTINGS";
+
+    /**
      * Activity Action: Show Notification listener settings.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -8131,6 +8146,7 @@
          *
          * @hide
          */
+        @TestApi
         public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
 
         private static final Validator ENABLED_VR_LISTENERS_VALIDATOR =
@@ -9028,7 +9044,6 @@
          * Whether applying ramping ringer on incoming phone call ringtone.
          * <p>1 = apply ramping ringer
          * <p>0 = do not apply ramping ringer
-         * @hide
          */
         public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer";
 
@@ -10780,6 +10795,7 @@
         *
         * @hide
         */
+       @TestApi
        public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices";
 
         /**
@@ -11506,6 +11522,7 @@
          * @hide
          * @see com.android.server.power.batterysaver.BatterySaverPolicy
          */
+        @TestApi
         public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants";
 
         /**
@@ -11929,6 +11946,7 @@
          * bcast_deferral               (long)
          * bcast_deferral_decay_factor  (float)
          * bcast_deferral_floor         (long)
+         * bcast_allow_bg_activity_start_timeout    (long)
          * </pre>
          *
          * @hide
@@ -13045,6 +13063,15 @@
          */
         public static final String LTE_SERVICE_FORCED = "lte_service_forced";
 
+
+        /**
+         * Specifies the behaviour the lid triggers when closed
+         * <p>
+         * See WindowManagerPolicy.WindowManagerFuncs
+         * @hide
+         */
+        public static final String LID_BEHAVIOR = "lid_behavior";
+
         /**
          * Ephemeral app cookie max size in bytes.
          * <p>
@@ -14096,18 +14123,17 @@
         public static final String SHOW_TEMPERATURE_WARNING = "show_temperature_warning";
 
         /**
+         * Whether to show the usb high temperature alarm notification.
+         * @hide
+         */
+        public static final String SHOW_USB_TEMPERATURE_ALARM = "show_usb_temperature_alarm";
+
+        /**
          * Temperature at which the high temperature warning notification should be shown.
          * @hide
          */
         public static final String WARNING_TEMPERATURE = "warning_temperature";
 
-
-        /**
-         * USB Temperature at which the high temperature alarm notification should be shown.
-         * @hide
-         */
-        public static final String USB_ALARM_TEMPERATURE = "usb_alarm_temperature";
-
         /**
          * Whether the diskstats logging task is enabled/disabled.
          * @hide
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index 1d0c987..cbd0cd9 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -71,6 +71,14 @@
      */
     public static final int FLAG_COMPATIBILITY_MODE_REQUEST = 0x2;
 
+    // Private flags below start from the highest-significative bit (0x80000000)
+    /**
+     * Request was only triggered for augmented autofill.
+     *
+     * @hide
+     */
+    public static final int FLAG_AUGMENTED_AUTOFILL_REQUEST = 0x80000000;
+
     /** @hide */
     public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
 
@@ -115,7 +123,8 @@
     /**
      * Gets the flags associated with this request.
      *
-     * @see #FLAG_MANUAL_REQUEST
+     * @return any combination of {@link #FLAG_MANUAL_REQUEST} and
+     *         {@link #FLAG_COMPATIBILITY_MODE_REQUEST}.
      */
     public @RequestFlags int getFlags() {
         return mFlags;
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 792eda7..463eae68 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -209,7 +209,7 @@
         } else {
             // TODO(b/123099468): figure out if it's ok to reuse the proxy; add logging
             if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
-            proxy.update(focusedId, focusedValue);
+            proxy.update(focusedId, focusedValue, callback);
         }
         // TODO(b/123101711): set cancellation signal
         final CancellationSignal cancellationSignal = null;
@@ -299,13 +299,14 @@
         private final Object mLock = new Object();
         private final IAugmentedAutofillManagerClient mClient;
         private final int mSessionId;
-        private final IFillCallback mCallback;
         public final int taskId;
         public final ComponentName componentName;
         @GuardedBy("mLock")
         private AutofillId mFocusedId;
         @GuardedBy("mLock")
         private AutofillValue mFocusedValue;
+        @GuardedBy("mLock")
+        private IFillCallback mCallback;
 
         /**
          * Id of the last field that cause the Autofill UI to be shown.
@@ -316,8 +317,8 @@
         private AutofillId mLastShownId;
 
         // Objects used to log metrics
-        private final long mRequestTime;
-        private long mOnSuccessTime;
+        private final long mFirstRequestTime;
+        private long mFirstOnSuccessTime;
         private long mUiFirstShownTime;
         private long mUiFirstDestroyedTime;
 
@@ -338,7 +339,7 @@
             this.componentName = componentName;
             this.mFocusedId = focusedId;
             this.mFocusedValue = focusedValue;
-            this.mRequestTime = requestTime;
+            this.mFirstRequestTime = requestTime;
             // TODO(b/123099468): linkToDeath
         }
 
@@ -400,11 +401,18 @@
             mClient.requestHideFillUi(mSessionId, mFocusedId);
         }
 
-        private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue) {
+        private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue,
+                @NonNull IFillCallback callback) {
             synchronized (mLock) {
                 // TODO(b/123099468): should we close the popupwindow if the focused id changed?
                 mFocusedId = focusedId;
                 mFocusedValue = focusedValue;
+                if (mCallback != null) {
+                    // TODO(b/123101711): we need to check whether the previous request was
+                    //  completed or not, and if not, cancel it first.
+                    Slog.d(TAG, "mCallback is updated.");
+                }
+                mCallback = callback;
             }
         }
 
@@ -426,11 +434,11 @@
         public void report(@ReportEvent int event) {
             switch (event) {
                 case REPORT_EVENT_ON_SUCCESS:
-                    if (mOnSuccessTime == 0) {
-                        mOnSuccessTime = SystemClock.elapsedRealtime();
+                    if (mFirstOnSuccessTime == 0) {
+                        mFirstOnSuccessTime = SystemClock.elapsedRealtime();
                         if (DEBUG) {
-                            Slog.d(TAG, "Service responsed in "
-                                    + TimeUtils.formatDuration(mOnSuccessTime - mRequestTime));
+                            Slog.d(TAG, "Service responded in " + TimeUtils.formatDuration(
+                                    mFirstOnSuccessTime - mFirstRequestTime));
                         }
                     }
                     try {
@@ -443,8 +451,8 @@
                     if (mUiFirstShownTime == 0) {
                         mUiFirstShownTime = SystemClock.elapsedRealtime();
                         if (DEBUG) {
-                            Slog.d(TAG, "UI shown in "
-                                    + TimeUtils.formatDuration(mUiFirstShownTime - mRequestTime));
+                            Slog.d(TAG, "UI shown in " + TimeUtils.formatDuration(
+                                    mUiFirstShownTime - mFirstRequestTime));
                         }
                     }
                     break;
@@ -452,9 +460,8 @@
                     if (mUiFirstDestroyedTime == 0) {
                         mUiFirstDestroyedTime = SystemClock.elapsedRealtime();
                         if (DEBUG) {
-                            Slog.d(TAG, "UI destroyed in "
-                                    + TimeUtils.formatDuration(
-                                            mUiFirstDestroyedTime - mRequestTime));
+                            Slog.d(TAG, "UI destroyed in " + TimeUtils.formatDuration(
+                                    mUiFirstDestroyedTime - mFirstRequestTime));
                         }
                     }
                     break;
@@ -486,20 +493,20 @@
                 pw.print(prefix); pw.println("smartSuggestion:");
                 mSmartSuggestion.dump(prefix2, pw);
             }
-            if (mOnSuccessTime > 0) {
-                final long responseTime = mOnSuccessTime - mRequestTime;
+            if (mFirstOnSuccessTime > 0) {
+                final long responseTime = mFirstOnSuccessTime - mFirstRequestTime;
                 pw.print(prefix); pw.print("response time: ");
                 TimeUtils.formatDuration(responseTime, pw); pw.println();
             }
 
             if (mUiFirstShownTime > 0) {
-                final long uiRenderingTime = mUiFirstShownTime - mRequestTime;
+                final long uiRenderingTime = mUiFirstShownTime - mFirstRequestTime;
                 pw.print(prefix); pw.print("UI rendering time: ");
                 TimeUtils.formatDuration(uiRenderingTime, pw); pw.println();
             }
 
             if (mUiFirstDestroyedTime > 0) {
-                final long uiTotalTime = mUiFirstDestroyedTime - mRequestTime;
+                final long uiTotalTime = mUiFirstDestroyedTime - mFirstRequestTime;
                 pw.print(prefix); pw.print("UI life time: ");
                 TimeUtils.formatDuration(uiTotalTime, pw); pw.println();
             }
@@ -510,6 +517,7 @@
                 if (mFillWindow != null) {
                     if (DEBUG) Log.d(TAG, "destroying window");
                     mFillWindow.destroy();
+                    mFillWindow = null;
                 }
             }
         }
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 6e06754..bd532a3 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -82,6 +82,8 @@
     private Rect mBounds;
 
     @GuardedBy("mLock")
+    private boolean mUpdateCalled;
+    @GuardedBy("mLock")
     private boolean mDestroyed;
 
     private AutofillProxy mProxy;
@@ -103,6 +105,7 @@
         }
         // TODO(b/123100712): add test case for null
         Preconditions.checkNotNull(area);
+        Preconditions.checkNotNull(area.proxy);
         Preconditions.checkNotNull(rootView);
         // TODO(b/123100712): must check the area is a valid object returned by
         // SmartSuggestionParams, throw IAE if not
@@ -149,6 +152,7 @@
             if (DEBUG) {
                 Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
             }
+            mUpdateCalled = true;
             mDestroyed = false;
             mProxy.setFillWindow(this);
             return true;
@@ -237,8 +241,10 @@
         }
         synchronized (mLock) {
             if (mDestroyed) return;
-            hide();
-            mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
+            if (mUpdateCalled) {
+                hide();
+                mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
+            }
             mDestroyed = true;
             mCloseGuard.close();
         }
@@ -266,6 +272,7 @@
     public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
         synchronized (this) {
             pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
+            pw.print(prefix); pw.print("updateCalled: "); pw.println(mUpdateCalled);
             if (mFillView != null) {
                 pw.print(prefix); pw.print("fill window: ");
                 pw.println(mShowing ? "shown" : "hidden");
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 1d89628..140c34f 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -51,7 +51,7 @@
         DEFAULT_FLAGS.put("settings_slice_injection", "true");
         DEFAULT_FLAGS.put("settings_systemui_theme", "true");
         DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "true");
-        DEFAULT_FLAGS.put("settings_mainline_module", "false");
+        DEFAULT_FLAGS.put("settings_mainline_module", "true");
         DEFAULT_FLAGS.put("settings_dynamic_android", "false");
         DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
         DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index e4c8eeb..f8b38e9 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.SystemClock;
@@ -302,6 +303,7 @@
     }
 
     /** @hide Just for debugging; not internationalized. */
+    @TestApi
     public static String formatDuration(long duration) {
         synchronized (sFormatSync) {
             int len = formatDurationLocked(duration, 0);
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index cb5100a..94a9a1c 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.KeyguardManager;
 import android.content.res.CompatibilityInfo;
@@ -911,6 +912,7 @@
      * @see #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
      * @hide
      */
+    @TestApi
     // TODO (b/114338689): Remove the method and use IWindowManager#shouldShowSystemDecors
     public boolean supportsSystemDecorations() {
         return (mDisplayInfo.flags & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0;
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 3e8002f..60daddd 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -156,6 +156,17 @@
     }
 
     /**
+     * Called when a display config changed event is received.
+     *
+     * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
+     * timebase.
+     * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
+     * @param configId The new config Id
+     */
+    public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+    }
+
+    /**
      * Schedules a single vertical sync pulse to be delivered when the next
      * display frame begins.
      */
@@ -182,4 +193,11 @@
     private void dispatchHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
         onHotplug(timestampNanos, physicalDisplayId, connected);
     }
+
+    // Called from native code.
+    @SuppressWarnings("unused")
+    private void dispatchConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+        onConfigChanged(timestampNanos, physicalDisplayId, configId);
+    }
+
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 2ef7c4b..5dc54a5 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -582,4 +582,13 @@
      *        display should be re-parented to.
      */
     void reparentDisplayContent(int displayId, in SurfaceControl sc);
+
+    /**
+     * Waits for transactions to get applied before injecting input.
+     * This includes waiting for the input windows to get sent to InputManager.
+     *
+     * This is needed for testing since the system add windows and injects input
+     * quick enough that the windows don't have time to get sent to InputManager.
+     */
+     boolean injectInputAfterTransactionsApplied(in InputEvent ev, int mode);
 }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 658f06a..240aad5 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -148,7 +148,7 @@
     void setInTouchMode(boolean showFocus);
     boolean getInTouchMode();
 
-    boolean performHapticFeedback(IWindow window, int effectId, boolean always);
+    boolean performHapticFeedback(int effectId, boolean always);
 
     /**
      * Initiate the drag operation itself
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index d0194f9..a15a20a 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -922,7 +922,7 @@
             if (mCanvas != null) {
                 throw new IllegalStateException("Surface was already locked!");
             }
-            mCanvas = mRenderNode.start(width, height);
+            mCanvas = mRenderNode.beginRecording(width, height);
             return mCanvas;
         }
 
@@ -931,7 +931,7 @@
                 throw new IllegalArgumentException("canvas object must be the same instance that "
                         + "was previously returned by lockCanvas");
             }
-            mRenderNode.end(mCanvas);
+            mRenderNode.endRecording();
             mCanvas = null;
             nHwuiDraw(mHwuiRenderer);
         }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index e5db44e..ea7f31d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -88,7 +88,8 @@
     private static native void nativeDisconnect(long nativeObject);
 
     private static native GraphicBuffer nativeScreenshot(IBinder displayToken,
-            Rect sourceCrop, int width, int height, boolean useIdentityTransform, int rotation);
+            Rect sourceCrop, int width, int height, boolean useIdentityTransform, int rotation,
+            boolean captureSecureLayers);
     private static native GraphicBuffer nativeCaptureLayers(IBinder layerHandleToken,
             Rect sourceCrop, float frameScale);
 
@@ -157,6 +158,8 @@
             IBinder displayToken, long numFrames, long timestamp);
     private static native int nativeGetActiveConfig(IBinder displayToken);
     private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
+    private static native boolean nativeSetAllowedDisplayConfigs(IBinder displayToken,
+                                                                 int[] allowedConfigs);
     private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
     private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries(
             IBinder displayToken);
@@ -189,6 +192,7 @@
             IBinder toToken);
     private static native boolean nativeGetProtectedContentSupport();
     private static native void nativeSetMetadata(long transactionObj, int key, Parcel data);
+    private static native void nativeSyncInputWindows(long transactionObj);
 
     private final CloseGuard mCloseGuard = CloseGuard.get();
     private String mName;
@@ -1520,6 +1524,20 @@
     /**
      * @hide
      */
+    public static boolean setAllowedDisplayConfigs(IBinder displayToken, int[] allowedConfigs) {
+        if (displayToken == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+        if (allowedConfigs == null) {
+            throw new IllegalArgumentException("allowedConfigs must not be null");
+        }
+
+        return nativeSetAllowedDisplayConfigs(displayToken, allowedConfigs);
+    }
+
+    /**
+     * @hide
+     */
     public static int[] getDisplayColorModes(IBinder displayToken) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
@@ -1852,7 +1870,29 @@
             throw new IllegalArgumentException("displayToken must not be null");
         }
 
-        return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation);
+        return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation,
+                false /* captureSecureLayers */);
+    }
+
+    /**
+     * Like screenshotToBuffer, but if the caller is AID_SYSTEM, allows
+     * for the capture of secure layers. This is used for the screen rotation
+     * animation where the system server takes screenshots but does
+     * not persist them or allow them to leave the server. However in other
+     * cases in the system server, we mostly want to omit secure layers
+     * like when we take a screenshot on behalf of the assistant.
+     *
+     * @hide
+     */
+    public static GraphicBuffer screenshotToBufferWithSecureLayersUnsafe(IBinder display,
+            Rect sourceCrop, int width, int height, boolean useIdentityTransform,
+            int rotation) {
+        if (display == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+
+        return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation,
+                true /* captureSecureLayers */);
     }
 
     private static void rotateCropForSF(Rect crop, int rot) {
@@ -2109,6 +2149,17 @@
         }
 
         /**
+         * Waits until any changes to input windows have been sent from SurfaceFlinger to
+         * InputFlinger before returning.
+         *
+         * @hide
+         */
+        public Transaction syncInputWindows() {
+            nativeSyncInputWindows(mNativeObject);
+            return this;
+        }
+
+        /**
          * Specify how the buffer assosciated with this Surface is mapped in to the
          * parent coordinate space. The source frame will be scaled to fit the destination
          * frame, after being rotated according to the orientation parameter.
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 2097812..3d3d5dc 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -590,7 +590,7 @@
         }
 
         if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) {
-            RecordingCanvas canvas = mRootNode.startRecording(mSurfaceWidth, mSurfaceHeight);
+            RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight);
             try {
                 final int saveCount = canvas.save();
                 canvas.translate(mInsetLeft, mInsetTop);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 278b9ff..1a9d3a0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -40,6 +40,7 @@
 import android.annotation.TestApi;
 import android.annotation.UiThread;
 import android.annotation.UnsupportedAppUsage;
+import android.content.AutofillOptions;
 import android.content.ClipData;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -9408,20 +9409,13 @@
             }
             setNotifiedContentCaptureAppeared();
 
-            // TODO(b/123307965): instead of post, we should queue it on AttachInfo and then
-            // dispatch on RootImpl, as we're doing with the removed ones (in that case, we should
-            // merge the delayNotifyContentCaptureDisappeared() into a more generic method that
-            // takes a session and a command, where the command is either view added or removed
-
-            // The code below doesn't take much for a unique view, but it's called for all views
-            // the first time the view hiearchy is laid off, which could acccumulative delay the
-            // initial layout. Hence, we're postponing it to a later stage - it might still cost a
-            // lost frame (or more), but that jank cost would only happen after the 1st layout.
-            Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
-                final ViewStructure structure = session.newViewStructure(this);
-                onProvideContentCaptureStructure(structure, /* flags= */ 0);
-                session.notifyViewAppeared(structure);
-            }, /* token= */ null);
+            if (ai != null) {
+                ai.delayNotifyContentCaptureEvent(session, this, appeared);
+            } else {
+                if (DEBUG_CONTENT_CAPTURE) {
+                    Log.w(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on appeared for " + this);
+                }
+            }
         } else {
             if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0
                     || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) {
@@ -9440,13 +9434,11 @@
             mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
 
             if (ai != null) {
-                ai.delayNotifyContentCaptureDisappeared(session, getAutofillId());
+                ai.delayNotifyContentCaptureEvent(session, this, appeared);
             } else {
                 if (DEBUG_CONTENT_CAPTURE) {
-                    Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on gone for " + this);
+                    Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on disappeared for " + this);
                 }
-                Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT,
-                        () -> session.notifyViewDisappeared(getAutofillId()), /* token= */ null);
             }
         }
     }
@@ -9533,8 +9525,22 @@
     }
 
     private boolean isAutofillable() {
-        return getAutofillType() != AUTOFILL_TYPE_NONE && isImportantForAutofill()
-                && getAutofillViewId() > LAST_APP_AUTOFILL_ID;
+        if (getAutofillType() == AUTOFILL_TYPE_NONE) return false;
+
+        if (!isImportantForAutofill()) {
+            // View is not important for "regular" autofill, so we must check if Augmented Autofill
+            // is enabled for the activity
+            final AutofillOptions options = mContext.getAutofillOptions();
+            if (options == null || !options.augmentedEnabled) {
+                // TODO(b/123100824): should also check if activity is whitelisted
+                return false;
+            }
+            final AutofillManager afm = getAutofillManager();
+            if (afm == null) return false;
+            afm.notifyViewEnteredForAugmentedAutofill(this);
+        }
+
+        return getAutofillViewId() > LAST_APP_AUTOFILL_ID;
     }
 
     /** @hide */
@@ -9703,13 +9709,19 @@
      *
      * @hide
      */
-    public void dispatchInitialProvideContentCaptureStructure(@NonNull ContentCaptureManager ccm) {
+    public void dispatchInitialProvideContentCaptureStructure() {
         AttachInfo ai = mAttachInfo;
         if (ai == null) {
             Log.w(CONTENT_CAPTURE_LOG_TAG,
                     "dispatchProvideContentCaptureStructure(): no AttachInfo for " + this);
             return;
         }
+        ContentCaptureManager ccm = ai.mContentCaptureManager;
+        if (ccm == null) {
+            Log.w(CONTENT_CAPTURE_LOG_TAG, "dispatchProvideContentCaptureStructure(): "
+                    + "no ContentCaptureManager for " + this);
+            return;
+        }
 
         // We must set it before checkign if the view itself is important, because it might
         // initially not be (for example, if it's empty), although that might change later (for
@@ -9735,11 +9747,11 @@
             return;
         }
 
-        session.internalNotifyViewHierarchyEvent(/* started= */ true);
+        session.internalNotifyViewTreeEvent(/* started= */ true);
         try {
             dispatchProvideContentCaptureStructure();
         } finally {
-            session.internalNotifyViewHierarchyEvent(/* started= */ false);
+            session.internalNotifyViewTreeEvent(/* started= */ false);
         }
     }
 
@@ -16043,7 +16055,7 @@
     @ViewDebug.ExportedProperty(category = "drawing")
     @InspectableProperty
     public float getRotation() {
-        return mRenderNode.getRotation();
+        return mRenderNode.getRotationZ();
     }
 
     /**
@@ -16064,7 +16076,7 @@
         if (rotation != getRotation()) {
             // Double-invalidation is necessary to capture view's old and new areas
             invalidateViewProperty(true, false);
-            mRenderNode.setRotation(rotation);
+            mRenderNode.setRotationZ(rotation);
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
@@ -20578,7 +20590,7 @@
             int height = mBottom - mTop;
             int layerType = getLayerType();
 
-            final RecordingCanvas canvas = renderNode.startRecording(width, height);
+            final RecordingCanvas canvas = renderNode.beginRecording(width, height);
 
             try {
                 if (layerType == LAYER_TYPE_SOFTWARE) {
@@ -21233,7 +21245,7 @@
         } else {
             mClipBounds = null;
         }
-        mRenderNode.setClipBounds(mClipBounds);
+        mRenderNode.setClipRect(mClipBounds);
         invalidateViewProperty(false, false);
     }
 
@@ -21978,7 +21990,7 @@
         final Rect bounds = drawable.getBounds();
         final int width = bounds.width();
         final int height = bounds.height();
-        final RecordingCanvas canvas = renderNode.startRecording(width, height);
+        final RecordingCanvas canvas = renderNode.beginRecording(width, height);
 
         // Reverse left/top translation done by drawable canvas, which will
         // instead be applied by rendernode's LTRB bounds below. This way, the
@@ -28358,11 +28370,12 @@
         boolean mReadyForContentCaptureUpdates;
 
         /**
-         * Map of ids (per session) that need to be notified after as gone the view hierchy is
-         * traversed.
+         * Map(keyed by session) of content capture events that need to be notified after the view
+         * hierarchy is traversed: value is either the view itself for appearead events, or its
+         * autofill id for disappeared.
          */
         // TODO(b/121197119): use SparseArray once session id becomes integer
-        ArrayMap<String, ArrayList<AutofillId>> mContentCaptureRemovedIds;
+        ArrayMap<String, ArrayList<Object>> mContentCaptureEvents;
 
         /**
          * Cached reference to the {@link ContentCaptureManager}.
@@ -28388,24 +28401,24 @@
             mTreeObserver = new ViewTreeObserver(context);
         }
 
-        private void delayNotifyContentCaptureDisappeared(@NonNull ContentCaptureSession session,
-                @NonNull AutofillId id) {
-            if (mContentCaptureRemovedIds == null) {
+        private void delayNotifyContentCaptureEvent(@NonNull ContentCaptureSession session,
+                @NonNull View view, boolean appeared) {
+            if (mContentCaptureEvents == null) {
                 // Most of the time there will be just one session, so intial capacity is 1
-                mContentCaptureRemovedIds = new ArrayMap<>(1);
+                mContentCaptureEvents = new ArrayMap<>(1);
             }
             String sessionId = session.getId();
             // TODO: life would be much easier if we provided a MultiMap implementation somwhere...
-            ArrayList<AutofillId> ids = mContentCaptureRemovedIds.get(sessionId);
-            if (ids == null) {
-                ids = new ArrayList<>();
-                mContentCaptureRemovedIds.put(sessionId, ids);
+            ArrayList<Object> events = mContentCaptureEvents.get(sessionId);
+            if (events == null) {
+                events = new ArrayList<>();
+                mContentCaptureEvents.put(sessionId, events);
             }
-            ids.add(id);
+            events.add(appeared ? view : view.getAutofillId());
         }
 
         @Nullable
-        private ContentCaptureManager getContentCaptureManager(@NonNull Context context) {
+        ContentCaptureManager getContentCaptureManager(@NonNull Context context) {
             if (mContentCaptureManager != null) {
                 return mContentCaptureManager;
             }
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index bb29ed6..c030ac2 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -928,8 +928,8 @@
     }
 
     /**
-     * If a MotionEvent has CLASSIFICATION_AMBIGUOUS_GESTURE set, then certain actions, such as
-     * scrolling, will be inhibited.
+     * If a MotionEvent has {@link android.view.MotionEvent#CLASSIFICATION_AMBIGUOUS_GESTURE} set,
+     * then certain actions, such as scrolling, will be inhibited.
      * However, to account for the possibility of incorrect classification,
      * the default scrolling will only be inhibited if the pointer travels less than
      * (getScaledTouchSlop() * this factor).
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 6f9ee4b..a390db2 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -611,11 +611,11 @@
         }
 
         if (view.isHardwareAccelerated()) {
-            RecordingCanvas canvas = node.start(dm.widthPixels, dm.heightPixels);
+            RecordingCanvas canvas = node.beginRecording(dm.widthPixels, dm.heightPixels);
             try {
                 return profileViewOperation(view, () -> view.draw(canvas));
             } finally {
-                node.end(canvas);
+                node.endRecording();
             }
         } else {
             Bitmap bitmap = Bitmap.createBitmap(
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 68c0d9e..afee1e5 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -985,7 +985,7 @@
                 renderNode.setTranslationZ(value);
                 break;
             case ROTATION:
-                renderNode.setRotation(value);
+                renderNode.setRotationZ(value);
                 break;
             case ROTATION_X:
                 renderNode.setRotationX(value);
@@ -1031,7 +1031,7 @@
             case TRANSLATION_Z:
                 return node.getTranslationZ();
             case ROTATION:
-                return node.getRotation();
+                return node.getRotationZ();
             case ROTATION_X:
                 return node.getRotationX();
             case ROTATION_Y:
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ab4847d..ad0aaa6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -29,6 +29,7 @@
 
 import android.Manifest;
 import android.animation.LayoutTransition;
+import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
@@ -107,6 +108,7 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
 import android.view.contentcapture.MainContentCaptureSession;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
@@ -222,10 +224,25 @@
      */
     static final int MAX_TRACKBALL_DELAY = 250;
 
+    /**
+     * Initial value for {@link #mContentCaptureEnabled}.
+     */
+    private static final int CONTENT_CAPTURE_ENABLED_NOT_CHECKED = 0;
+
+    /**
+     * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code true}.
+     */
+    private static final int CONTENT_CAPTURE_ENABLED_TRUE = 1;
+
+    /**
+     * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code false}.
+     */
+    private static final int CONTENT_CAPTURE_ENABLED_FALSE = 2;
+
     @UnsupportedAppUsage
     static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
 
-    static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
+    static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<>();
     static boolean sFirstDrawComplete = false;
 
     /**
@@ -417,7 +434,11 @@
     boolean mApplyInsetsRequested;
     boolean mLayoutRequested;
     boolean mFirst;
+
+    @Nullable
+    int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED;
     boolean mPerformContentCapture;
+
     boolean mReportNextDraw;
     boolean mFullRedrawNeeded;
     boolean mNewSurfaceNeeded;
@@ -1041,10 +1062,22 @@
         return mHeight;
     }
 
+    /**
+     * Destroys hardware rendering resources for this ViewRootImpl
+     *
+     * May be called on any thread
+     */
+    @AnyThread
     void destroyHardwareResources() {
-        if (mAttachInfo.mThreadedRenderer != null) {
-            mAttachInfo.mThreadedRenderer.destroyHardwareResources(mView);
-            mAttachInfo.mThreadedRenderer.destroy();
+        final ThreadedRenderer renderer = mAttachInfo.mThreadedRenderer;
+        if (renderer != null) {
+            // This is called by WindowManagerGlobal which may or may not be on the right thread
+            if (Looper.myLooper() != mAttachInfo.mHandler.getLooper()) {
+                mAttachInfo.mHandler.postAtFrontOfQueue(this::destroyHardwareResources);
+                return;
+            }
+            renderer.destroyHardwareResources(mView);
+            renderer.destroy();
         }
     }
 
@@ -2763,27 +2796,56 @@
             }
         }
 
-        if (mAttachInfo.mContentCaptureRemovedIds != null) {
-            MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
-                    .getMainContentCaptureSession();
-            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureViewsGone");
-            try {
-                for (int i = 0; i < mAttachInfo.mContentCaptureRemovedIds.size(); i++) {
-                    String sessionId = mAttachInfo.mContentCaptureRemovedIds
-                            .keyAt(i);
-                    ArrayList<AutofillId> ids = mAttachInfo.mContentCaptureRemovedIds
-                            .valueAt(i);
-                    mainSession.notifyViewsDisappeared(sessionId, ids);
-                }
-                mAttachInfo.mContentCaptureRemovedIds = null;
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-            }
+        if (mAttachInfo.mContentCaptureEvents != null) {
+            notifyContentCatpureEvents();
         }
 
         mIsInTraversal = false;
     }
 
+    private void notifyContentCatpureEvents() {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
+        try {
+            MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
+                    .getMainContentCaptureSession();
+            for (int i = 0; i < mAttachInfo.mContentCaptureEvents.size(); i++) {
+                String sessionId = mAttachInfo.mContentCaptureEvents
+                        .keyAt(i);
+                mainSession.notifyViewTreeEvent(sessionId, /* started= */ true);
+                ArrayList<Object> events = mAttachInfo.mContentCaptureEvents
+                        .valueAt(i);
+                for_each_event: for (int j = 0; j < events.size(); j++) {
+                    Object event = events.get(j);
+                    if (event instanceof AutofillId) {
+                        mainSession.notifyViewDisappeared(sessionId, (AutofillId) event);
+                    } else if (event instanceof View) {
+                        View view = (View) event;
+                        ContentCaptureSession session = view.getContentCaptureSession();
+                        if (session == null) {
+                            Log.w(mTag, "no content capture session on view: " + view);
+                            continue for_each_event;
+                        }
+                        String actualId = session.getId().toString();
+                        if (!actualId.equals(sessionId)) {
+                            Log.w(mTag, "content capture session mismatch for view (" + view
+                                    + "): was " + sessionId + " before, it's " + actualId + " now");
+                            continue for_each_event;
+                        }
+                        ViewStructure structure = session.newViewStructure(view);
+                        view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
+                        session.notifyViewAppeared(structure);
+                    } else {
+                        Log.w(mTag, "invalid content capture event: " + event);
+                    }
+                }
+                mainSession.notifyViewTreeEvent(sessionId, /* started= */ false);
+            }
+            mAttachInfo.mContentCaptureEvents = null;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
+    }
+
     private void notifySurfaceDestroyed() {
         mSurfaceHolder.ungetCallbacks();
         SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks();
@@ -2914,6 +2976,13 @@
             }
         }
         mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
+
+        // NOTE: there's no view visibility (appeared / disapparead) events when the windows focus
+        // is lost, so we don't need to to force a flush - there might be other events such as
+        // text changes, but these should be flushed independently.
+        if (hasWindowFocus) {
+            handleContentCaptureFlush();
+        }
     }
 
     private void fireAccessibilityFocusEventIfHasFocusedNode() {
@@ -3481,31 +3550,82 @@
             }
         }
         if (mPerformContentCapture) {
-            performContentCapture();
+            performContentCaptureInitialReport();
         }
     }
 
-    private void performContentCapture() {
+    /**
+     * Checks (and caches) if content capture is enabled for this context.
+     */
+    private boolean isContentCaptureEnabled() {
+        switch (mContentCaptureEnabled) {
+            case CONTENT_CAPTURE_ENABLED_TRUE:
+                return true;
+            case CONTENT_CAPTURE_ENABLED_FALSE:
+                return false;
+            case CONTENT_CAPTURE_ENABLED_NOT_CHECKED:
+                final boolean reallyEnabled = isContentCaptureReallyEnabled();
+                mContentCaptureEnabled = reallyEnabled ? CONTENT_CAPTURE_ENABLED_TRUE
+                        : CONTENT_CAPTURE_ENABLED_FALSE;
+                return reallyEnabled;
+            default:
+                Log.w(TAG, "isContentCaptureEnabled(): invalid state " + mContentCaptureEnabled);
+                return false;
+        }
+
+    }
+
+    /**
+     * Checks (without caching) if content capture is enabled for this context.
+     */
+    private boolean isContentCaptureReallyEnabled() {
+        // First check if context supports it, so it saves a service lookup when it doesn't
+        if (mContext.getContentCaptureOptions() == null) return false;
+
+        final ContentCaptureManager ccm = mAttachInfo.getContentCaptureManager(mContext);
+        // Then check if it's enabled in the contex itself.
+        if (ccm == null || !ccm.isContentCaptureEnabled()) return false;
+
+        return true;
+    }
+
+    private void performContentCaptureInitialReport() {
         mPerformContentCapture = false; // One-time offer!
         final View rootView = mView;
         if (DEBUG_CONTENT_CAPTURE) {
-            Log.v(mTag, "dispatchContentCapture() on " + rootView);
+            Log.v(mTag, "performContentCaptureInitialReport() on " + rootView);
         }
         if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
             Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for "
                     + getClass().getSimpleName());
         }
         try {
-            // First check if context supports it, so it saves a service lookup when it doesn't
-            if (mContext.getContentCaptureOptions() == null) return;
-
-            // Then check if it's enabled in the contex itself.
-            final ContentCaptureManager ccm = mContext
-                    .getSystemService(ContentCaptureManager.class);
-            if (ccm == null || !ccm.isContentCaptureEnabled()) return;
+            if (!isContentCaptureEnabled()) return;
 
             // Content capture is a go!
-            rootView.dispatchInitialProvideContentCaptureStructure(ccm);
+            rootView.dispatchInitialProvideContentCaptureStructure();
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
+    }
+
+    private void handleContentCaptureFlush() {
+        if (DEBUG_CONTENT_CAPTURE) {
+            Log.v(mTag, "handleContentCaptureFlush()");
+        }
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for "
+                    + getClass().getSimpleName());
+        }
+        try {
+            if (!isContentCaptureEnabled()) return;
+
+            final ContentCaptureManager ccm = mAttachInfo.mContentCaptureManager;
+            if (ccm == null) {
+                Log.w(TAG, "No ContentCapture on AttachInfo");
+                return;
+            }
+            ccm.flush(ContentCaptureSession.FLUSH_REASON_VIEW_ROOT_ENTERED);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
         }
@@ -5150,11 +5270,8 @@
         protected int onProcess(QueuedInputEvent q) {
             if (q.mEvent instanceof KeyEvent) {
                 return processKeyEvent(q);
-            } else {
-                final int source = q.mEvent.getSource();
-                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-                    return processPointerEvent(q);
-                }
+            } else if (q.mEvent instanceof MotionEvent) {
+                return processMotionEvent(q);
             }
             return FORWARD;
         }
@@ -5178,6 +5295,23 @@
             return FORWARD;
         }
 
+        private int processMotionEvent(QueuedInputEvent q) {
+            final MotionEvent event = (MotionEvent) q.mEvent;
+
+            if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
+                return processPointerEvent(q);
+            }
+
+            // If the motion event is from an absolute position device, exit touch mode
+            final int action = event.getActionMasked();
+            if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
+                if (event.isFromSource(InputDevice.SOURCE_CLASS_POSITION)) {
+                    ensureTouchMode(false);
+                }
+            }
+            return FORWARD;
+        }
+
         private int processPointerEvent(QueuedInputEvent q) {
             final MotionEvent event = (MotionEvent)q.mEvent;
 
@@ -5510,6 +5644,12 @@
         private int processGenericMotionEvent(QueuedInputEvent q) {
             final MotionEvent event = (MotionEvent)q.mEvent;
 
+            if (event.isFromSource(InputDevice.SOURCE_TOUCHPAD)) {
+                if (hasPointerCapture() && mView.dispatchCapturedPointerEvent(event)) {
+                    return FINISH_HANDLED;
+                }
+            }
+
             // Deliver the event to the view.
             if (mView.dispatchGenericMotionEvent(event)) {
                 return FINISH_HANDLED;
@@ -6983,7 +7123,7 @@
     @Override
     public boolean performHapticFeedback(int effectId, boolean always) {
         try {
-            return mWindowSession.performHapticFeedback(mWindow, effectId, always);
+            return mWindowSession.performHapticFeedback(effectId, always);
         } catch (RemoteException e) {
             return false;
         }
@@ -7080,7 +7220,7 @@
         RenderNode renderNode = view.mRenderNode;
         info[0]++;
         if (renderNode != null) {
-            info[1] += renderNode.computeApproximateMemoryUsage();
+            info[1] += (int) renderNode.computeApproximateMemoryUsage();
         }
 
         if (view instanceof ViewGroup) {
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index 5d59e42..87e18b7 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -424,28 +424,20 @@
      *
      * @param nodes The nodes in the hosting window.
      * @param rootNodeId The id of the root to evict.
-     *
-     * @return {@code true} if the cache was cleared
      */
-    private boolean clearSubTreeRecursiveLocked(LongSparseArray<AccessibilityNodeInfo> nodes,
+    private void clearSubTreeRecursiveLocked(LongSparseArray<AccessibilityNodeInfo> nodes,
             long rootNodeId) {
         AccessibilityNodeInfo current = nodes.get(rootNodeId);
         if (current == null) {
-            // The node isn't in the cache, but its descendents might be.
-            clear();
-            return true;
+            return;
         }
         nodes.remove(rootNodeId);
         final int childCount = current.getChildCount();
         for (int i = 0; i < childCount; i++) {
             final long childNodeId = current.getChildId(i);
-            if (clearSubTreeRecursiveLocked(nodes, childNodeId)) {
-                current.recycle();
-                return true;
-            }
+            clearSubTreeRecursiveLocked(nodes, childNodeId);
         }
         current.recycle();
-        return false;
     }
 
     /**
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index e9b1683..70fe230 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -16,6 +16,7 @@
 
 package android.view.autofill;
 
+import static android.service.autofill.FillRequest.FLAG_AUGMENTED_AUTOFILL_REQUEST;
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
 import static android.view.autofill.Helper.sDebug;
 import static android.view.autofill.Helper.sVerbose;
@@ -28,6 +29,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.content.AutofillOptions;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -369,6 +371,24 @@
     public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES =
             "smart_suggestion_supported_modes";
 
+    /**
+     * Sets how long (in ms) the augmented autofill service is bound while idle.
+     *
+     * <p>Use {@code 0} to keep it permanently bound.
+     *
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT =
+            "augmented_service_idle_unbind_timeout";
+
+    /**
+     * Sets how long (in ms) the augmented autofill service request is killed if not replied.
+     *
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT =
+            "augmented_service_request_timeout";
+
     /** @hide */
     public static final int RESULT_OK = 0;
     /** @hide */
@@ -466,6 +486,13 @@
     @GuardedBy("mLock")
     @Nullable private ArraySet<AutofillId> mEnteredIds;
 
+    /**
+     * Views that were otherwised not important for autofill but triggered a session because the
+     * context is whitelisted for augmented autofill.
+     */
+    @GuardedBy("mLock")
+    @Nullable private Set<AutofillId> mEnteredForAugmentedAutofillIds;
+
     /** If set, session is commited when the field is clicked. */
     @GuardedBy("mLock")
     @Nullable private AutofillId mSaveTriggerId;
@@ -482,6 +509,9 @@
     @GuardedBy("mLock")
     private CompatibilityBridge mCompatibilityBridge;
 
+    @Nullable
+    private final AutofillOptions mOptions;
+
     /** @hide */
     public interface AutofillClient {
         /**
@@ -618,6 +648,12 @@
     public AutofillManager(Context context, IAutoFillManager service) {
         mContext = Preconditions.checkNotNull(context, "context cannot be null");
         mService = service;
+        mOptions = context.getAutofillOptions();
+
+        if (mOptions != null) {
+            sDebug = (mOptions.loggingLevel & FLAG_ADD_CLIENT_DEBUG) != 0;
+            sVerbose = (mOptions.loggingLevel & FLAG_ADD_CLIENT_VERBOSE) != 0;
+        }
     }
 
     /**
@@ -1616,6 +1652,11 @@
     @GuardedBy("mLock")
     private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
             @NonNull AutofillValue value, int flags) {
+        if (mEnteredForAugmentedAutofillIds != null
+                && mEnteredForAugmentedAutofillIds.contains(id)) {
+            if (sVerbose) Log.v(TAG, "Starting session for augmented autofill on " + id);
+            flags |= FLAG_AUGMENTED_AUTOFILL_REQUEST;
+        }
         if (sVerbose) {
             Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
                     + ", flags=" + flags + ", state=" + getStateAsStringLocked()
@@ -1851,6 +1892,25 @@
         return set == null ? null : new ArrayList<T>(set);
     }
 
+    /**
+     * Notifies that a non-autofillable view was entered because the activity is whitelisted for
+     * augmented autofill.
+     *
+     * <p>This method is necessary to set the right flag on start, so the server-side session
+     * doesn't trigger the standard autofill workflow, but the augmented's instead.
+     *
+     * @hide
+     */
+    public void notifyViewEnteredForAugmentedAutofill(@NonNull View view) {
+        final AutofillId id = view.getAutofillId();
+        synchronized (mLock) {
+            if (mEnteredForAugmentedAutofillIds == null) {
+                mEnteredForAugmentedAutofillIds = new ArraySet<>(1);
+            }
+            mEnteredForAugmentedAutofillIds.add(id);
+        }
+    }
+
     private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
             Rect anchorBounds, IAutofillWindowPresenter presenter) {
         final View anchor = findView(id);
@@ -2350,8 +2410,15 @@
         }
         pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
         pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
+        if (mEnteredForAugmentedAutofillIds != null) {
+            pw.print(pfx); pw.print("entered ids for augmented autofill: ");
+            pw.println(mEnteredForAugmentedAutofillIds);
+        }
         pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
         pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
+        if (mOptions != null) {
+            pw.print(pfx); pw.print("options: "); mOptions.dumpShort(pw); pw.println();
+        }
         pw.print(pfx); pw.print("compat mode enabled: ");
         synchronized (mLock) {
             if (mCompatibilityBridge != null) {
diff --git a/core/java/android/view/autofill/AutofillManagerInternal.java b/core/java/android/view/autofill/AutofillManagerInternal.java
index 155fe72..d5862bd 100644
--- a/core/java/android/view/autofill/AutofillManagerInternal.java
+++ b/core/java/android/view/autofill/AutofillManagerInternal.java
@@ -16,7 +16,9 @@
 package android.view.autofill;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.content.AutofillOptions;
 
 /**
  * Autofill Manager local system service interface.
@@ -31,12 +33,13 @@
     public abstract void onBackKeyPressed();
 
     /**
-     * Gets whether compatibility mode is enabled for a package
+     * Gets autofill options for a package
      *
      * @param packageName The package for which to query.
      * @param versionCode The package version code.
      * @param userId The user id for which to query.
      */
-    public abstract boolean isCompatibilityModeRequested(@NonNull String packageName,
+    @Nullable
+    public abstract AutofillOptions getAutofillOptions(@NonNull String packageName,
             long versionCode, @UserIdInt int userId);
 }
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 13e8a65..b3b0b72 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -84,8 +84,8 @@
     }
 
     @Override
-    public void internalNotifyViewHierarchyEvent(boolean started) {
-        getMainCaptureSession().notifyInitialViewHierarchyEvent(mId, started);
+    public void internalNotifyViewTreeEvent(boolean started) {
+        getMainCaptureSession().notifyViewTreeEvent(mId, started);
     }
 
     @Override
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 9cdbefa..2585b74 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -72,24 +72,24 @@
     public static final int TYPE_VIEW_TEXT_CHANGED = 3;
 
     /**
-     * Called before events (such as {@link #TYPE_VIEW_APPEARED}) representing the initial view
-     * hierarchy are sent.
+     * Called before events (such as {@link #TYPE_VIEW_APPEARED} and/or
+     * {@link #TYPE_VIEW_DISAPPEARED}) representing a view hierarchy are sent.
      *
      * <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent
      * if the initial view hierarchy doesn't initially have any view that's important for content
      * capture.
      */
-    public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4;
+    public static final int TYPE_VIEW_TREE_APPEARING = 4;
 
     /**
-     * Called after events (such as {@link #TYPE_VIEW_APPEARED}) representing the initial view
-     * hierarchy are sent.
+     * Called after events (such as {@link #TYPE_VIEW_APPEARED} and/or
+     * {@link #TYPE_VIEW_DISAPPEARED}) representing a view hierarchy were sent.
      *
      * <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent
      * if the initial view hierarchy doesn't initially have any view that's important for content
      * capture.
      */
-    public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5;
+    public static final int TYPE_VIEW_TREE_APPEARED = 5;
 
     /**
      * Called after a call to
@@ -99,14 +99,29 @@
      */
     public static final int TYPE_CONTEXT_UPDATED = 6;
 
+    /**
+     * Called after the session is ready, typically after the activity resumed and the
+     * initial views appeared
+     */
+    public static final int TYPE_SESSION_RESUMED = 7;
+
+    /**
+     * Called after the session is paused, typically after the activity paused and the
+     * views disappeared.
+     */
+    public static final int TYPE_SESSION_PAUSED = 8;
+
+
     /** @hide */
     @IntDef(prefix = { "TYPE_" }, value = {
             TYPE_VIEW_APPEARED,
             TYPE_VIEW_DISAPPEARED,
             TYPE_VIEW_TEXT_CHANGED,
-            TYPE_INITIAL_VIEW_TREE_APPEARING,
-            TYPE_INITIAL_VIEW_TREE_APPEARED,
-            TYPE_CONTEXT_UPDATED
+            TYPE_VIEW_TREE_APPEARING,
+            TYPE_VIEW_TREE_APPEARED,
+            TYPE_CONTEXT_UPDATED,
+            TYPE_SESSION_PAUSED,
+            TYPE_SESSION_RESUMED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType{}
@@ -230,8 +245,9 @@
      * Gets the type of the event.
      *
      * @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED},
-     * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING},
-     * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}, or {@link #TYPE_CONTEXT_UPDATED}.
+     * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_VIEW_TREE_APPEARING},
+     * {@link #TYPE_VIEW_TREE_APPEARED}, {@link #TYPE_CONTEXT_UPDATED},
+     * {@link #TYPE_SESSION_RESUMED}, or {@link #TYPE_SESSION_PAUSED}.
      */
     public @EventType int getType() {
         return mType;
@@ -411,16 +427,20 @@
                 return "SESSION_STARTED";
             case TYPE_SESSION_FINISHED:
                 return "SESSION_FINISHED";
+            case TYPE_SESSION_RESUMED:
+                return "SESSION_RESUMED";
+            case TYPE_SESSION_PAUSED:
+                return "SESSION_PAUSED";
             case TYPE_VIEW_APPEARED:
                 return "VIEW_APPEARED";
             case TYPE_VIEW_DISAPPEARED:
                 return "VIEW_DISAPPEARED";
             case TYPE_VIEW_TEXT_CHANGED:
                 return "VIEW_TEXT_CHANGED";
-            case TYPE_INITIAL_VIEW_TREE_APPEARING:
-                return "INITIAL_VIEW_HIERARCHY_STARTED";
-            case TYPE_INITIAL_VIEW_TREE_APPEARED:
-                return "INITIAL_VIEW_HIERARCHY_FINISHED";
+            case TYPE_VIEW_TREE_APPEARING:
+                return "VIEW_TREE_APPEARING";
+            case TYPE_VIEW_TREE_APPEARED:
+                return "VIEW_TREE_APPEARED";
             case TYPE_CONTEXT_UPDATED:
                 return "CONTEXT_UPDATED";
             default:
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 336d997..885bd2a 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -129,6 +129,14 @@
     @TestApi
     public static final String DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL = "logging_level";
 
+    /**
+     * Sets how long (in ms) the service is bound while idle.
+     *
+     * <p>Use {@code 0} to keep it permanently bound.
+     *
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT = "idle_unbind_timeout";
 
     /** @hide */
     @TestApi
@@ -224,7 +232,7 @@
 
     /** @hide */
     @UiThread
-    public void onActivityStarted(@NonNull IBinder applicationToken,
+    public void onActivityCreated(@NonNull IBinder applicationToken,
             @NonNull ComponentName activityComponent, int flags) {
         synchronized (mLock) {
             mFlags |= flags;
@@ -234,7 +242,19 @@
 
     /** @hide */
     @UiThread
-    public void onActivityStopped() {
+    public void onActivityResumed() {
+        getMainContentCaptureSession().notifySessionLifecycle(/* started= */ true);
+    }
+
+    /** @hide */
+    @UiThread
+    public void onActivityPaused() {
+        getMainContentCaptureSession().notifySessionLifecycle(/* started= */ false);
+    }
+
+    /** @hide */
+    @UiThread
+    public void onActivityDestroyed() {
         getMainContentCaptureSession().destroy();
     }
 
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 1e051a43..ab8f346 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -132,27 +132,24 @@
     /** @hide */
     public static final int FLUSH_REASON_FULL = 1;
     /** @hide */
-    public static final int FLUSH_REASON_ACTIVITY_PAUSED = 2;
+    public static final int FLUSH_REASON_VIEW_ROOT_ENTERED = 2;
     /** @hide */
-    public static final int FLUSH_REASON_ACTIVITY_RESUMED = 3;
+    public static final int FLUSH_REASON_SESSION_STARTED = 3;
     /** @hide */
-    public static final int FLUSH_REASON_SESSION_STARTED = 4;
+    public static final int FLUSH_REASON_SESSION_FINISHED = 4;
     /** @hide */
-    public static final int FLUSH_REASON_SESSION_FINISHED = 5;
-    /** @hide */
-    public static final int FLUSH_REASON_IDLE_TIMEOUT = 6;
+    public static final int FLUSH_REASON_IDLE_TIMEOUT = 5;
 
     /** @hide */
     @IntDef(prefix = { "FLUSH_REASON_" }, value = {
             FLUSH_REASON_FULL,
-            FLUSH_REASON_ACTIVITY_PAUSED,
-            FLUSH_REASON_ACTIVITY_RESUMED,
+            FLUSH_REASON_VIEW_ROOT_ENTERED,
             FLUSH_REASON_SESSION_STARTED,
             FLUSH_REASON_SESSION_FINISHED,
             FLUSH_REASON_IDLE_TIMEOUT
     })
     @Retention(RetentionPolicy.SOURCE)
-    @interface FlushReason{}
+    public @interface FlushReason{}
 
     private final Object mLock = new Object();
 
@@ -414,7 +411,7 @@
             @Nullable CharSequence text);
 
     /** @hide */
-    public abstract void internalNotifyViewHierarchyEvent(boolean started);
+    public abstract void internalNotifyViewTreeEvent(boolean started);
 
     /**
      * Creates a {@link ViewStructure} for a "standard" view.
@@ -500,14 +497,12 @@
 
     /** @hide */
     @NonNull
-    static String getflushReasonAsString(@FlushReason int reason) {
+    public static String getFlushReasonAsString(@FlushReason int reason) {
         switch (reason) {
             case FLUSH_REASON_FULL:
                 return "FULL";
-            case FLUSH_REASON_ACTIVITY_PAUSED:
-                return "PAUSED";
-            case FLUSH_REASON_ACTIVITY_RESUMED:
-                return "RESUMED";
+            case FLUSH_REASON_VIEW_ROOT_ENTERED:
+                return "VIEW_ROOT";
             case FLUSH_REASON_SESSION_STARTED:
                 return "STARTED";
             case FLUSH_REASON_SESSION_FINISHED:
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 0abf689..dce8ebe 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -16,13 +16,15 @@
 package android.view.contentcapture;
 
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARING;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_PAUSED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_RESUMED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING;
 import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
 import static android.view.contentcapture.ContentCaptureHelper.sDebug;
 import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
@@ -126,7 +128,6 @@
     @Nullable
     private final LocalLog mFlushHistory;
 
-    /** @hide */
     protected MainContentCaptureSession(@NonNull Context context,
             @NonNull ContentCaptureManager manager, @NonNull Handler handler,
             @NonNull IContentCaptureManager systemServerInterface) {
@@ -153,8 +154,6 @@
 
     /**
      * Starts this session.
-     *
-     * @hide
      */
     @UiThread
     void start(@NonNull IBinder token, @NonNull ComponentName component,
@@ -451,7 +450,7 @@
         }
 
         final int numberEvents = mEvents.size();
-        final String reasonString = getflushReasonAsString(reason);
+        final String reasonString = getFlushReasonAsString(reason);
         if (sDebug) {
             Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState(reason));
         }
@@ -546,8 +545,8 @@
     }
 
     @Override
-    public void internalNotifyViewHierarchyEvent(boolean started) {
-        notifyInitialViewHierarchyEvent(mId, started);
+    public void internalNotifyViewTreeEvent(boolean started) {
+        notifyViewTreeEvent(mId, started);
     }
 
     @Override
@@ -581,21 +580,9 @@
                 .setViewNode(node.mNode));
     }
 
-    void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) {
-        sendEvent(
-                new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id));
-    }
-
-    /** @hide */
-    public void notifyViewsDisappeared(@NonNull String sessionId,
-            @NonNull ArrayList<AutofillId> ids) {
-        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED);
-        if (ids.size() == 1) {
-            event.setAutofillId(ids.get(0));
-        } else {
-            event.setAutofillIds(ids);
-        }
-        sendEvent(event);
+    /** Public because is also used by ViewRootImpl */
+    public void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) {
+        sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id));
     }
 
     void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id,
@@ -604,13 +591,16 @@
                 .setText(text));
     }
 
-    void notifyInitialViewHierarchyEvent(@NonNull String sessionId, boolean started) {
-        if (started) {
-            sendEvent(new ContentCaptureEvent(sessionId, TYPE_INITIAL_VIEW_TREE_APPEARING));
-        } else {
-            sendEvent(new ContentCaptureEvent(sessionId, TYPE_INITIAL_VIEW_TREE_APPEARED),
-                    FORCE_FLUSH);
-        }
+    /** Public because is also used by ViewRootImpl */
+    public void notifyViewTreeEvent(@NonNull String sessionId, boolean started) {
+        final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED;
+        sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH);
+    }
+
+    /** Public because is also used by ViewRootImpl */
+    public void notifySessionLifecycle(boolean started) {
+        final int type = started ? TYPE_SESSION_RESUMED : TYPE_SESSION_PAUSED;
+        sendEvent(new ContentCaptureEvent(mId, type), FORCE_FLUSH);
     }
 
     void notifyContextUpdated(@NonNull String sessionId,
@@ -684,6 +674,6 @@
 
     @NonNull
     private String getDebugState(@FlushReason int reason) {
-        return getDebugState() + ", reason=" + getflushReasonAsString(reason);
+        return getDebugState() + ", reason=" + getFlushReasonAsString(reason);
     }
 }
diff --git a/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java b/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java
index 8faae1f..d4b7e85 100644
--- a/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java
+++ b/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java
@@ -40,8 +40,15 @@
             final Class<InspectionCompanion<T>> companionClass =
                     (Class<InspectionCompanion<T>>) cls.getClassLoader().loadClass(companionName);
             return companionClass.newInstance();
-        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+        } catch (ClassNotFoundException e) {
             return null;
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        } catch (InstantiationException e) {
+            Throwable cause = e.getCause();
+            if (cause instanceof RuntimeException) throw (RuntimeException) cause;
+            if (cause instanceof Error) throw (Error) cause;
+            throw new RuntimeException(cause);
         }
     }
 }
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
index 4d917a1..efdc968 100644
--- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
+++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
@@ -16,6 +16,7 @@
 
 package android.view.textclassifier;
 
+import android.annotation.Nullable;
 import android.app.Person;
 import android.content.Context;
 import android.text.TextUtils;
@@ -110,6 +111,19 @@
                 SelectionSessionLogger.CLASSIFIER_ID, modelName, hash);
     }
 
+    /**
+     * Returns a {@link android.view.textclassifier.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) -> resolveInfo.handleAllWebDataURI
+                    ? labeledIntent.titleWithEntity : labeledIntent.titleWithoutEntity;
+        }
+        return null;
+    }
+
     private static final class PersonEncoder {
         private final Map<Person, Integer> mMapping = new ArrayMap<>();
         private int mNextUserId = FIRST_NON_LOCAL_USER;
diff --git a/core/java/android/view/textclassifier/ExtrasUtils.java b/core/java/android/view/textclassifier/ExtrasUtils.java
index b0e7ad5..2ad17a8 100644
--- a/core/java/android/view/textclassifier/ExtrasUtils.java
+++ b/core/java/android/view/textclassifier/ExtrasUtils.java
@@ -94,7 +94,8 @@
         if (actionIntents != null) {
             final int size = actionIntents.size();
             for (int i = 0; i < size; i++) {
-                if (intentAction.equals(actionIntents.get(i).getAction())) {
+                final Intent intent = actionIntents.get(i);
+                if (intent != null && intentAction.equals(intent.getAction())) {
                     return classification.getActions().get(i);
                 }
             }
diff --git a/core/java/android/view/textclassifier/IntentFactory.java b/core/java/android/view/textclassifier/IntentFactory.java
index 9f3b97f..722c812 100644
--- a/core/java/android/view/textclassifier/IntentFactory.java
+++ b/core/java/android/view/textclassifier/IntentFactory.java
@@ -32,7 +32,7 @@
     /**
      * Return a list of LabeledIntent from the classification result.
      */
-    List<TextClassifierImpl.LabeledIntent> create(
+    List<LabeledIntent> create(
             Context context,
             String text,
             boolean foreignText,
@@ -43,9 +43,10 @@
      * Inserts translate action to the list if it is a foreign text.
      */
     static void insertTranslateAction(
-            List<TextClassifierImpl.LabeledIntent> actions, Context context, String text) {
-        actions.add(new TextClassifierImpl.LabeledIntent(
+            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),
                 new Intent(Intent.ACTION_TRANSLATE)
                         // TODO: Probably better to introduce a "translate" scheme instead of
diff --git a/core/java/android/view/textclassifier/LabeledIntent.java b/core/java/android/view/textclassifier/LabeledIntent.java
new file mode 100644
index 0000000..7544dc1
--- /dev/null
+++ b/core/java/android/view/textclassifier/LabeledIntent.java
@@ -0,0 +1,157 @@
+/*
+ * 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.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.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+/**
+ * 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;
+    // 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,
+            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 = Preconditions.checkNotNull(description);
+        this.intent = Preconditions.checkNotNull(intent);
+        this.requestCode = requestCode;
+    }
+
+    /**
+     * Return the resolved result.
+     */
+    @Nullable
+    public Result resolve(
+            Context context, @Nullable TitleChooser titleChooser) {
+        final PackageManager pm = context.getPackageManager();
+        final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
+        final String packageName = resolveInfo != null && resolveInfo.activityInfo != null
+                ? resolveInfo.activityInfo.packageName : null;
+        Icon icon = null;
+        Intent resolvedIntent = new Intent(intent);
+        boolean shouldShowIcon = false;
+        if (packageName != null && !"android".equals(packageName)) {
+            // There is a default activity handling the intent.
+            resolvedIntent.setComponent(
+                    new ComponentName(packageName, resolveInfo.activityInfo.name));
+            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);
+        if (pendingIntent == null) {
+            return null;
+        }
+        if (titleChooser == null) {
+            titleChooser = DEFAULT_TITLE_CHOOSER;
+        }
+        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, description, pendingIntent);
+        action.setShouldShowIcon(shouldShowIcon);
+        return new Result(resolvedIntent, action);
+    }
+
+    /**
+     * 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 = Preconditions.checkNotNull(resolvedIntent);
+            this.remoteAction = Preconditions.checkNotNull(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.
+         */
+        @Nullable
+        CharSequence chooseTitle(LabeledIntent labeledIntent, ResolveInfo resolveInfo);
+    }
+}
diff --git a/core/java/android/view/textclassifier/LegacyIntentFactory.java b/core/java/android/view/textclassifier/LegacyIntentFactory.java
index 2d0d032..ea9229d 100644
--- a/core/java/android/view/textclassifier/LegacyIntentFactory.java
+++ b/core/java/android/view/textclassifier/LegacyIntentFactory.java
@@ -29,7 +29,6 @@
 import android.provider.Browser;
 import android.provider.CalendarContract;
 import android.provider.ContactsContract;
-import android.view.textclassifier.TextClassifierImpl.LabeledIntent;
 
 import com.google.android.textclassifier.AnnotatorModel;
 
@@ -100,8 +99,7 @@
             IntentFactory.insertTranslateAction(actions, context, text);
         }
         actions.forEach(
-                action -> action.getIntent()
-                        .putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true));
+                action -> action.intent.putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true));
         return actions;
     }
 
@@ -110,12 +108,14 @@
         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),
                 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),
                 new Intent(Intent.ACTION_INSERT_OR_EDIT)
                         .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
@@ -133,6 +133,7 @@
         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),
                     new Intent(Intent.ACTION_DIAL).setData(
                             Uri.parse(String.format("tel:%s", text))),
@@ -140,6 +141,7 @@
         }
         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),
                 new Intent(Intent.ACTION_INSERT_OR_EDIT)
                         .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
@@ -148,6 +150,7 @@
         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),
                     new Intent(Intent.ACTION_SENDTO)
                             .setData(Uri.parse(String.format("smsto:%s", text))),
@@ -163,6 +166,7 @@
             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),
                     new Intent(Intent.ACTION_VIEW)
                             .setData(Uri.parse(String.format("geo:0,0?q=%s", encText))),
@@ -181,6 +185,7 @@
         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),
                 new Intent(Intent.ACTION_VIEW)
                         .setDataAndNormalize(Uri.parse(text))
@@ -211,6 +216,7 @@
         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),
                 new Intent(Intent.ACTION_WEB_SEARCH)
                         .putExtra(SearchManager.QUERY, text),
@@ -225,6 +231,7 @@
         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),
                 new Intent(Intent.ACTION_VIEW).setData(builder.build()),
                 LabeledIntent.DEFAULT_REQUEST_CODE);
@@ -236,6 +243,7 @@
         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),
                 new Intent(Intent.ACTION_INSERT)
                         .setData(CalendarContract.Events.CONTENT_URI)
@@ -252,6 +260,7 @@
         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),
                 new Intent(Intent.ACTION_DEFINE)
                         .putExtra(Intent.EXTRA_TEXT, text),
diff --git a/core/java/android/view/textclassifier/TemplateClassificationIntentFactory.java b/core/java/android/view/textclassifier/TemplateClassificationIntentFactory.java
index 2467802..ed0259f 100644
--- a/core/java/android/view/textclassifier/TemplateClassificationIntentFactory.java
+++ b/core/java/android/view/textclassifier/TemplateClassificationIntentFactory.java
@@ -48,12 +48,12 @@
     }
 
     /**
-     * Returns a list of {@link android.view.textclassifier.TextClassifierImpl.LabeledIntent}
+     * Returns a list of {@link android.view.textclassifier.LabeledIntent}
      * that are constructed from the classification result.
      */
     @NonNull
     @Override
-    public List<TextClassifierImpl.LabeledIntent> create(
+    public List<LabeledIntent> create(
             Context context,
             String text,
             boolean foreignText,
@@ -68,7 +68,7 @@
             Log.w(TAG, "RemoteActionTemplate is missing, fallback to LegacyIntentFactory.");
             return mFallback.create(context, text, foreignText, referenceTime, classification);
         }
-        final List<TextClassifierImpl.LabeledIntent> labeledIntents =
+        final List<LabeledIntent> labeledIntents =
                 mTemplateIntentFactory.create(remoteActionTemplates);
         if (foreignText) {
             IntentFactory.insertTranslateAction(labeledIntents, context, text.trim());
diff --git a/core/java/android/view/textclassifier/TemplateIntentFactory.java b/core/java/android/view/textclassifier/TemplateIntentFactory.java
index 95f88c7..0696d98 100644
--- a/core/java/android/view/textclassifier/TemplateIntentFactory.java
+++ b/core/java/android/view/textclassifier/TemplateIntentFactory.java
@@ -42,29 +42,29 @@
     private static final String TAG = TextClassifier.DEFAULT_LOG_TAG;
 
     @NonNull
-    public List<TextClassifierImpl.LabeledIntent> create(
+    public List<LabeledIntent> create(
             @Nullable RemoteActionTemplate[] remoteActionTemplates) {
         if (ArrayUtils.isEmpty(remoteActionTemplates)) {
             return Collections.emptyList();
         }
-        final List<TextClassifierImpl.LabeledIntent> labeledIntents = 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 TextClassifierImpl.LabeledIntent(
-                            remoteActionTemplate.title,
+                    new LabeledIntent(
+                            remoteActionTemplate.titleWithoutEntity,
+                            remoteActionTemplate.titleWithEntity,
                             remoteActionTemplate.description,
                             createIntent(remoteActionTemplate),
                             remoteActionTemplate.requestCode == null
-                                    ? TextClassifierImpl.LabeledIntent.DEFAULT_REQUEST_CODE
+                                    ? LabeledIntent.DEFAULT_REQUEST_CODE
                                     : remoteActionTemplate.requestCode));
         }
         labeledIntents.forEach(
-                action -> action.getIntent()
-                        .putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true));
+                action -> action.intent.putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true));
         return labeledIntents;
     }
 
@@ -73,7 +73,8 @@
             Log.w(TAG, "Invalid RemoteActionTemplate: is null");
             return false;
         }
-        if (TextUtils.isEmpty(remoteActionTemplate.title)) {
+        if (TextUtils.isEmpty(remoteActionTemplate.titleWithEntity)
+                && TextUtils.isEmpty(remoteActionTemplate.titleWithoutEntity)) {
             Log.w(TAG, "Invalid RemoteActionTemplate: title is null");
             return false;
         }
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index a059209..052ee95 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -54,6 +54,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * Information for generating a widget to handle classified text.
@@ -276,8 +277,8 @@
     @Override
     public String toString() {
         return String.format(Locale.US,
-                "TextClassification {text=%s, entities=%s, actions=%s, id=%s}",
-                mText, mEntityConfidence, mActions, mId);
+                "TextClassification {text=%s, entities=%s, actions=%s, id=%s, extras=%s}",
+                mText, mEntityConfidence, mActions, mId, mExtras);
     }
 
     /**
@@ -532,7 +533,7 @@
 
         private Bundle buildExtras() {
             final Bundle extras = mExtras == null ? new Bundle() : mExtras.deepCopy();
-            if (!mActionIntents.isEmpty()) {
+            if (mActionIntents.stream().anyMatch(Objects::nonNull)) {
                 ExtrasUtils.putActionsIntents(extras, mActionIntents);
             }
             if (mForeignLanguageExtra != null) {
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 295c8b7..632328b 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -19,21 +19,14 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.WorkerThread;
-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.icu.util.ULocale;
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.ParcelFileDescriptor;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 
@@ -240,9 +233,7 @@
                                                 refTime.getZone().getId(),
                                                 localesString),
                                         mContext,
-                                        // TODO: Pass the locale list once it is supported in
-                                        //  native side.
-                                        LocaleList.getDefault().get(0).toLanguageTag()
+                                        getResourceLocaleString()
                                 );
                 if (results.length > 0) {
                     return createClassificationResult(
@@ -403,8 +394,7 @@
                             nativeConversation,
                             null,
                             mContext,
-                            // TODO: Pass the locale list once it is supported in native side.
-                            LocaleList.getDefault().get(0).toLanguageTag());
+                            getResourceLocaleString());
             return createConversationActionResult(request, nativeSuggestions);
         } catch (Throwable t) {
             // Avoid throwing from this method. Log the error.
@@ -433,7 +423,12 @@
             // Given that we only support implicit intent here, we should expect there is just one
             // intent for each action type.
             if (!labeledIntents.isEmpty()) {
-                remoteAction = labeledIntents.get(0).asRemoteAction(mContext);
+                LabeledIntent.TitleChooser titleChooser =
+                        ActionsSuggestionsHelper.createTitleChooser(actionType);
+                LabeledIntent.Result result = labeledIntents.get(0).resolve(mContext, titleChooser);
+                if (result != null) {
+                    remoteAction = result.remoteAction;
+                }
             }
             conversationActions.add(
                     new ConversationAction.Builder(actionType)
@@ -456,10 +451,9 @@
         TextLanguage textLanguage = detectLanguage(request);
         int localeHypothesisCount = textLanguage.getLocaleHypothesisCount();
         List<String> languageTags = new ArrayList<>();
-        // TODO: Reconsider this and probably make the score threshold configurable.
         for (int i = 0; i < localeHypothesisCount; i++) {
             ULocale locale = textLanguage.getLocale(i);
-            if (textLanguage.getConfidenceScore(locale) < 0.5) {
+            if (textLanguage.getConfidenceScore(locale) < getForeignLanguageThreshold()) {
                 break;
             }
             languageTags.add(locale.toLanguageTag());
@@ -587,38 +581,36 @@
             }
         }
 
-        final float foreignTextThreshold = mSettings.getLangIdThresholdOverride() >= 0
-                ? mSettings.getLangIdThresholdOverride()
-                : 0.5f /* TODO: Load this from the langId model. */;
-        final Bundle foreignLanguageBundle =
-                detectForeignLanguage(classifiedText, foreignTextThreshold);
+        final Bundle foreignLanguageBundle = detectForeignLanguage(classifiedText);
         builder.setForeignLanguageExtra(foreignLanguageBundle);
 
         boolean isPrimaryAction = true;
-        final ArrayList<Intent> sourceIntents = new ArrayList<>();
         List<LabeledIntent> labeledIntents = mIntentFactory.create(
                 mContext,
                 classifiedText,
                 foreignLanguageBundle != null,
                 referenceTime,
                 highestScoringResult);
+        LabeledIntent.TitleChooser titleChooser =
+                (labeledIntent, resolveInfo) -> labeledIntent.titleWithoutEntity;
         for (LabeledIntent labeledIntent : labeledIntents) {
-            final RemoteAction action = labeledIntent.asRemoteAction(mContext);
-            if (action == null) {
+            LabeledIntent.Result result = labeledIntent.resolve(mContext, titleChooser);
+            if (result == null) {
                 continue;
             }
+            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(labeledIntent.getIntent());
+                builder.setIntent(result.resolvedIntent);
                 builder.setOnClickListener(TextClassification.createIntentOnClickListener(
                         TextClassification.createPendingIntent(mContext,
-                                labeledIntent.getIntent(), labeledIntent.getRequestCode())));
+                                result.resolvedIntent, labeledIntent.requestCode)));
                 isPrimaryAction = false;
             }
-            builder.addAction(action, labeledIntent.getIntent());
+            builder.addAction(action, result.resolvedIntent);
         }
 
         return builder.setId(createId(text, start, end)).build();
@@ -626,16 +618,20 @@
 
     /**
      * Returns a bundle with the language and confidence score if it finds the text to be
-     * in a foreign language. Otherwise returns null.
+     * in a foreign language. Otherwise returns null. This algorithm defines what the system thinks
+     * is a foreign language.
      */
+    // TODO: Revisit this algorithm.
+    // TODO: Consider making this public API.
     @Nullable
-    private Bundle detectForeignLanguage(String text, float threshold) {
-        if (threshold > 1) {
-            return null;
-        }
-
-        // TODO: Revisit this algorithm.
+    private Bundle detectForeignLanguage(String text) {
         try {
+            final float threshold = getForeignLanguageThreshold();
+            if (threshold > 1) {
+                Log.v(LOG_TAG, "Foreign language detection disabled.");
+                return null;
+            }
+
             final LangIdModel langId = getLangIdImpl();
             final LangIdModel.LanguageResult[] langResults = langId.detectLanguages(text);
             if (langResults.length <= 0) {
@@ -651,8 +647,8 @@
             if (highestScoringResult.getScore() < threshold) {
                 return null;
             }
-            // TODO: Remove
-            Log.d(LOG_TAG, String.format("Language detected: <%s:%s>",
+
+            Log.v(LOG_TAG, String.format("Language detected: <%s:%s>",
                     highestScoringResult.getLanguage(), highestScoringResult.getScore()));
 
             final Locale detected = new Locale(highestScoringResult.getLanguage());
@@ -671,6 +667,18 @@
         return null;
     }
 
+    private float getForeignLanguageThreshold() {
+        try {
+            return mSettings.getLangIdThresholdOverride() >= 0
+                    ? mSettings.getLangIdThresholdOverride()
+                    : getLangIdImpl().getTranslateThreshold();
+        } 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) {
@@ -719,86 +727,15 @@
     }
 
     /**
-     * Helper class to store the information from which RemoteActions are built.
+     * Returns the locale string for the current resources configuration.
      */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    public static final class LabeledIntent {
-
-        static final int DEFAULT_REQUEST_CODE = 0;
-
-        private final String mTitle;
-        private final String mDescription;
-        private final Intent mIntent;
-        private final int mRequestCode;
-
-        /**
-         * Initializes a LabeledIntent.
-         *
-         * <p>NOTE: {@code reqestCode} 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.
-        LabeledIntent(String title, String description, Intent intent, int requestCode) {
-            mTitle = title;
-            mDescription = description;
-            mIntent = intent;
-            mRequestCode = requestCode;
-        }
-
-        @VisibleForTesting
-        public String getTitle() {
-            return mTitle;
-        }
-
-        @VisibleForTesting
-        public String getDescription() {
-            return mDescription;
-        }
-
-        @VisibleForTesting
-        public Intent getIntent() {
-            return mIntent;
-        }
-
-        @VisibleForTesting
-        public int getRequestCode() {
-            return mRequestCode;
-        }
-
-        @Nullable
-        RemoteAction asRemoteAction(Context context) {
-            final PackageManager pm = context.getPackageManager();
-            final ResolveInfo resolveInfo = pm.resolveActivity(mIntent, 0);
-            final String packageName = resolveInfo != null && resolveInfo.activityInfo != null
-                    ? resolveInfo.activityInfo.packageName : null;
-            Icon icon = null;
-            boolean shouldShowIcon = false;
-            if (packageName != null && !"android".equals(packageName)) {
-                // There is a default activity handling the intent.
-                mIntent.setComponent(new ComponentName(packageName, resolveInfo.activityInfo.name));
-                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, mIntent, mRequestCode);
-            if (pendingIntent == null) {
-                return null;
-            }
-            final RemoteAction action = new RemoteAction(icon, mTitle, mDescription, pendingIntent);
-            action.setShouldShowIcon(shouldShowIcon);
-            return action;
+    private String getResourceLocaleString() {
+        // TODO: Pass the locale list once it is supported in native side.
+        try {
+            return mContext.getResources().getConfiguration().getLocales().get(0).toLanguageTag();
+        } catch (NullPointerException e) {
+            // NPE is unexpected. Erring on the side of caution.
+            return LocaleList.getDefault().get(0).toLanguageTag();
         }
     }
 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index ded3be4e..b6ec5f9 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1947,7 +1947,7 @@
 
             // Rebuild display list if it is invalid
             if (blockDisplayListIsInvalid) {
-                final RecordingCanvas recordingCanvas = blockDisplayList.start(
+                final RecordingCanvas recordingCanvas = blockDisplayList.beginRecording(
                         right - left, bottom - top);
                 try {
                     // drawText is always relative to TextView's origin, this translation
@@ -1958,7 +1958,7 @@
                     // No need to untranslate, previous context is popped after
                     // drawDisplayList
                 } finally {
-                    blockDisplayList.end(recordingCanvas);
+                    blockDisplayList.endRecording();
                     // Same as drawDisplayList below, handled by our TextView's parent
                     blockDisplayList.setClipToBounds(false);
                 }
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index a6129b0..f44c331 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -312,7 +312,7 @@
      * @return The view that is currently selected, if it happens to be in the
      *         range that we draw.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private View fillDown(int pos, int nextTop) {
         View selectedView = null;
 
@@ -412,7 +412,7 @@
      *
      * @return The view that is currently selected
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private View fillUp(int pos, int nextBottom) {
         View selectedView = null;
 
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 2aa019b..2f44d6e 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -782,7 +782,7 @@
      * @return The view that is currently selected, if it happens to be in the
      *         range that we draw.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private View fillDown(int pos, int nextTop) {
         View selectedView = null;
 
@@ -817,7 +817,7 @@
      *
      * @return The view that is currently selected
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private View fillUp(int pos, int nextBottom) {
         View selectedView = null;
 
@@ -1490,7 +1490,7 @@
      * @return The selected view, or null if the selected view is outside the
      *         visible area.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private View fillSpecific(int position, int top) {
         boolean tempIsSelected = position == mSelectedPosition;
         View temp = makeAndAddView(position, top, true, mListPadding.left, tempIsSelected);
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 249f499..b7cdad2 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -859,7 +859,7 @@
             );
             setupOverlay();
 
-            final RecordingCanvas canvas = mRenderer.getRootNode().start(width, height);
+            final RecordingCanvas canvas = mRenderer.getRootNode().beginRecording(width, height);
             try {
                 canvas.insertReorderBarrier();
                 canvas.drawRenderNode(mBitmapRenderNode);
@@ -867,7 +867,7 @@
                 canvas.drawRenderNode(mOverlayRenderNode);
                 canvas.insertInorderBarrier();
             } finally {
-                mRenderer.getRootNode().end(canvas);
+                mRenderer.getRootNode().endRecording();
             }
             if (mCallback != null) {
                 mCurrentContent =
@@ -898,11 +898,12 @@
             bitmapRenderNode.setClipToOutline(true);
 
             // Create a dummy draw, which will be replaced later with real drawing.
-            final RecordingCanvas canvas = bitmapRenderNode.start(mContentWidth, mContentHeight);
+            final RecordingCanvas canvas = bitmapRenderNode.beginRecording(
+                    mContentWidth, mContentHeight);
             try {
                 canvas.drawColor(0xFF00FF00);
             } finally {
-                bitmapRenderNode.end(canvas);
+                bitmapRenderNode.endRecording();
             }
 
             return bitmapRenderNode;
@@ -954,7 +955,7 @@
             // Draw the drawable to the render node. This happens once during
             // initialization and whenever the overlay drawable is invalidated.
             final RecordingCanvas canvas =
-                    mOverlayRenderNode.startRecording(mContentWidth, mContentHeight);
+                    mOverlayRenderNode.beginRecording(mContentWidth, mContentHeight);
             try {
                 mOverlay.setBounds(0, 0, mContentWidth, mContentHeight);
                 mOverlay.draw(canvas);
@@ -1035,7 +1036,7 @@
                 }
 
                 final RecordingCanvas canvas =
-                        mBitmapRenderNode.start(mContentWidth, mContentHeight);
+                        mBitmapRenderNode.beginRecording(mContentWidth, mContentHeight);
                 try {
                     final Rect srcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
                     final Rect dstRect = new Rect(0, 0, mContentWidth, mContentHeight);
@@ -1043,7 +1044,7 @@
                     paint.setFilterBitmap(true);
                     canvas.drawBitmap(mBitmap, srcRect, dstRect, paint);
                 } finally {
-                    mBitmapRenderNode.end(canvas);
+                    mBitmapRenderNode.endRecording();
                 }
 
                 if (mPendingWindowPositionUpdate || mFirstDraw) {
@@ -1061,7 +1062,6 @@
                                 return;
                             }
                             synchronized (mLock) {
-                                mRenderer.setLightCenter(mDisplay, pendingX, pendingY);
                                 // Show or move the window at the content draw frame.
                                 SurfaceControl.openTransaction();
                                 mSurfaceControl.deferTransactionUntil(mSurface, frame);
@@ -1076,6 +1076,7 @@
                             }
                         }
                     };
+                    mRenderer.setLightCenter(mDisplay, pendingX, pendingY);
                 } else {
                     callback = null;
                 }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 0fbd4dc..89e3d6b 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -381,6 +381,8 @@
         final long systemCost = mChooserShownTime - intentReceivedTime;
 
         getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)
+                .setSubtype(isWorkProfile() ? MetricsEvent.MANAGED_PROFILE :
+                        MetricsEvent.PARENT_PROFILE)
                 .addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType())
                 .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost));
 
@@ -418,6 +420,16 @@
     }
 
     /**
+     * Check if the profile currently used is a work profile.
+     * @return true if it is work profile, false if it is parent profile (or no work profile is
+     * set up)
+     */
+    protected boolean isWorkProfile() {
+        return ((UserManager) getSystemService(Context.USER_SERVICE))
+                .getUserInfo(UserHandle.myUserId()).isManagedProfile();
+    }
+
+    /**
      * Override method to add content preview area, specific to the chooser activity.
      */
     @Override
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index d13bcf2..64f0010 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -31,6 +31,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
+import android.metrics.LogMaker;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -39,6 +40,8 @@
 import android.widget.Toast;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
 import java.util.Arrays;
 import java.util.HashSet;
@@ -66,6 +69,8 @@
 
     private Injector mInjector;
 
+    private MetricsLogger mMetricsLogger;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -78,9 +83,17 @@
         if (className.equals(FORWARD_INTENT_TO_PARENT)) {
             userMessageId = com.android.internal.R.string.forward_intent_to_owner;
             targetUserId = getProfileParent();
+
+            getMetricsLogger().write(
+                    new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
+                    .setSubtype(MetricsEvent.PARENT_PROFILE));
         } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
             userMessageId = com.android.internal.R.string.forward_intent_to_work;
             targetUserId = getManagedProfile();
+
+            getMetricsLogger().write(
+                    new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
+                    .setSubtype(MetricsEvent.MANAGED_PROFILE));
         } else {
             Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
             userMessageId = -1;
@@ -257,6 +270,13 @@
         intent.setComponent(null);
     }
 
+    protected MetricsLogger getMetricsLogger() {
+        if (mMetricsLogger == null) {
+            mMetricsLogger = new MetricsLogger();
+        }
+        return mMetricsLogger;
+    }
+
     @VisibleForTesting
     protected Injector createInjector() {
         return new InjectorImpl();
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index e19a32e..87e048c 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -86,6 +86,10 @@
                 && wakeScreenGestureAvailable();
     }
 
+    public long getWakeLockScreenDebounce() {
+        return mContext.getResources().getInteger(R.integer.config_dozeWakeLockScreenDebounce);
+    }
+
     public String doubleTapSensorType() {
         return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType);
     }
diff --git a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
index 26cf180..293ffd3 100644
--- a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
@@ -39,7 +39,7 @@
 
     private final int mInitialCapacity;
 
-    protected ArrayList<PendingRequest<S, I>> mPendingRequests;
+    protected ArrayList<BasePendingRequest<S, I>> mPendingRequests;
 
     public AbstractMultiplePendingRequestsRemoteService(@NonNull Context context,
             @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
@@ -85,7 +85,7 @@
     }
 
     @Override // from AbstractRemoteService
-    void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest) {
+    void handlePendingRequestWhileUnBound(@NonNull BasePendingRequest<S, I> pendingRequest) {
         if (mPendingRequests == null) {
             mPendingRequests = new ArrayList<>(mInitialCapacity);
         }
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index a937aa7..f4c904d 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -67,7 +67,7 @@
     private static final int MSG_BIND = 1;
     private static final int MSG_UNBIND = 2;
 
-    protected static final long PERMANENT_BOUND_TIMEOUT_MS = 0;
+    public static final long PERMANENT_BOUND_TIMEOUT_MS = 0;
 
     protected static final int LAST_PRIVATE_MSG = MSG_UNBIND;
 
@@ -95,7 +95,7 @@
     private long mNextUnbind;
 
     /** Requests that have been scheduled, but that are not finished yet */
-    private final ArrayList<PendingRequest<S, I>> mUnfinishedRequests = new ArrayList<>();
+    private final ArrayList<BasePendingRequest<S, I>> mUnfinishedRequests = new ArrayList<>();
 
     /**
      * Callback called when the service dies.
@@ -183,8 +183,15 @@
 
     /**
      * Defines how long after we make a remote request to a fill service we timeout.
+     *
+     * <p>Just need to be overridden by subclasses that uses sync {@link PendingRequest}s.
+     *
+     * @throws UnsupportedOperationException if called when not overridden.
+     *
      */
-    protected abstract long getRemoteRequestMillis();
+    protected long getRemoteRequestMillis() {
+        throw new UnsupportedOperationException("not implemented by " + getClass());
+    }
 
     /**
      * Gets the currently registered service interface or {@code null} if the service is not
@@ -243,7 +250,7 @@
         pw.append(prefix).append(tab).append("destroyed=")
                 .append(String.valueOf(mDestroyed)).println();
         pw.append(prefix).append(tab).append("numUnfinishedRequests=")
-                .append(String.valueOf(mUnfinishedRequests.size()));
+                .append(String.valueOf(mUnfinishedRequests.size())).println();
         final boolean bound = handleIsBound();
         pw.append(prefix).append(tab).append("bound=")
                 .append(String.valueOf(bound));
@@ -260,9 +267,13 @@
         pw.println();
         pw.append(prefix).append("mBindInstantServiceAllowed=").println(mBindInstantServiceAllowed);
         pw.append(prefix).append("idleTimeout=")
-            .append(Long.toString(idleTimeout / 1000)).append("s").println();
-        pw.append(prefix).append("requestTimeout=")
-            .append(Long.toString(getRemoteRequestMillis() / 1000)).append("s").println();
+            .append(Long.toString(idleTimeout / 1000)).append("s\n");
+        pw.append(prefix).append("requestTimeout=");
+        try {
+            pw.append(Long.toString(getRemoteRequestMillis() / 1000)).append("s\n");
+        } catch (UnsupportedOperationException e) {
+            pw.append("not supported\n");
+        }
         pw.println();
     }
 
@@ -273,7 +284,7 @@
      * othewise it will trigger a {@link PendingRequest#onTimeout(AbstractRemoteService)} if the
      * service doesn't respond.
      */
-    protected void scheduleRequest(@NonNull PendingRequest<S, I> pendingRequest) {
+    protected void scheduleRequest(@NonNull BasePendingRequest<S, I> pendingRequest) {
         mHandler.sendMessage(obtainMessage(
                 AbstractRemoteService::handlePendingRequest, this, pendingRequest));
     }
@@ -283,12 +294,12 @@
      *
      * @param finshedRequest The request that is finished
      */
-    void finishRequest(@NonNull PendingRequest<S, I> finshedRequest) {
+    void finishRequest(@NonNull BasePendingRequest<S, I> finshedRequest) {
         mHandler.sendMessage(
                 obtainMessage(AbstractRemoteService::handleFinishRequest, this, finshedRequest));
     }
 
-    private void handleFinishRequest(@NonNull PendingRequest<S, I> finshedRequest) {
+    private void handleFinishRequest(@NonNull BasePendingRequest<S, I> finshedRequest) {
         mUnfinishedRequests.remove(finshedRequest);
 
         if (mUnfinishedRequests.isEmpty()) {
@@ -361,7 +372,7 @@
      * Handles a request, either processing it right now when bound, or saving it to be handled when
      * bound.
      */
-    protected final void handlePendingRequest(@NonNull PendingRequest<S, I> pendingRequest) {
+    protected final void handlePendingRequest(@NonNull BasePendingRequest<S, I> pendingRequest) {
         if (checkIfDestroyed() || mCompleted) return;
 
         if (!handleIsBound()) {
@@ -384,7 +395,8 @@
     /**
      * Defines what to do with a request that arrives while not bound to the service.
      */
-    abstract void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest);
+    abstract void handlePendingRequestWhileUnBound(
+            @NonNull BasePendingRequest<S, I> pendingRequest);
 
     private boolean handleIsBound() {
         return mService != null;
@@ -471,50 +483,28 @@
     /**
      * Base class for the requests serviced by the remote service.
      *
-     * <p><b>NOTE: </b> this class is typically used when the service needs to use a callback to
-     * communicate back with the system server. For cases where that's not needed, you should use
-     * {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} instead.
+     * <p><b>NOTE: </b> this class is not used directly, you should either override
+     * {@link com.android.internal.infra.AbstractRemoteService.PendingRequest} for sync requests, or
+     * use {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} for async requests.
      *
      * @param <S> the remote service class
      * @param <I> the interface of the binder service
      */
-    public abstract static class PendingRequest<S extends AbstractRemoteService<S, I>,
+    public abstract static class BasePendingRequest<S extends AbstractRemoteService<S, I>,
             I extends IInterface> implements Runnable {
         protected final String mTag = getClass().getSimpleName();
         protected final Object mLock = new Object();
 
-        private final WeakReference<S> mWeakService;
-        private final Runnable mTimeoutTrigger;
-        private final Handler mServiceHandler;
+        final WeakReference<S> mWeakService;
 
         @GuardedBy("mLock")
-        private boolean mCancelled;
+        boolean mCancelled;
 
         @GuardedBy("mLock")
-        private boolean mCompleted;
+        boolean mCompleted;
 
-        protected PendingRequest(@NonNull S service) {
+        BasePendingRequest(@NonNull S service) {
             mWeakService = new WeakReference<>(service);
-            mServiceHandler = service.mHandler;
-            mTimeoutTrigger = () -> {
-                synchronized (mLock) {
-                    if (mCancelled) {
-                        return;
-                    }
-                    mCompleted = true;
-                }
-
-                final S remoteService = mWeakService.get();
-                if (remoteService != null) {
-                    // TODO(b/117779333): we should probably ignore it if service is destroyed.
-                    Slog.w(mTag, "timed out after " + service.getRemoteRequestMillis() + " ms");
-                    onTimeout(remoteService);
-                } else {
-                    Slog.w(mTag, "timed out (no service)");
-                }
-            };
-            mServiceHandler.postAtTime(mTimeoutTrigger,
-                    SystemClock.uptimeMillis() + service.getRemoteRequestMillis());
         }
 
         /**
@@ -543,10 +533,13 @@
                 service.finishRequest(this);
             }
 
-            mServiceHandler.removeCallbacks(mTimeoutTrigger);
+            onFinished();
+
             return true;
         }
 
+        void onFinished() { }
+
         /**
          * Checks whether this request was cancelled.
          */
@@ -568,15 +561,11 @@
                 mCancelled = true;
             }
 
-            mServiceHandler.removeCallbacks(mTimeoutTrigger);
+            onCancel();
             return true;
         }
 
-        /**
-         * Called by the self-destruct timeout when the remote service didn't reply to the
-         * request on time.
-         */
-        protected abstract void onTimeout(S remoteService);
+        void onCancel() {}
 
         /**
          * Checks whether this request leads to a final state where no other requests can be made.
@@ -587,6 +576,67 @@
     }
 
     /**
+     * Base class for the requests serviced by the remote service.
+     *
+     * <p><b>NOTE: </b> this class is typically used when the service needs to use a callback to
+     * communicate back with the system server. For cases where that's not needed, you should use
+     * {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} instead.
+     *
+     * <p><b>NOTE: </b> you must override {@link AbstractRemoteService#getRemoteRequestMillis()},
+     * otherwise the constructor will throw an {@link UnsupportedOperationException}.
+     *
+     * @param <S> the remote service class
+     * @param <I> the interface of the binder service
+     */
+    public abstract static class PendingRequest<S extends AbstractRemoteService<S, I>,
+            I extends IInterface> extends BasePendingRequest<S, I> {
+
+        private final Runnable mTimeoutTrigger;
+        private final Handler mServiceHandler;
+
+        protected PendingRequest(S service) {
+            super(service);
+            mServiceHandler = service.mHandler;
+
+            mTimeoutTrigger = () -> {
+                synchronized (mLock) {
+                    if (mCancelled) {
+                        return;
+                    }
+                    mCompleted = true;
+                }
+
+                final S remoteService = mWeakService.get();
+                if (remoteService != null) {
+                    // TODO(b/117779333): we should probably ignore it if service is destroyed.
+                    Slog.w(mTag, "timed out after " + service.getRemoteRequestMillis() + " ms");
+                    onTimeout(remoteService);
+                } else {
+                    Slog.w(mTag, "timed out (no service)");
+                }
+            };
+            mServiceHandler.postAtTime(mTimeoutTrigger,
+                    SystemClock.uptimeMillis() + service.getRemoteRequestMillis());
+        }
+
+        @Override
+        final void onFinished() {
+            mServiceHandler.removeCallbacks(mTimeoutTrigger);
+        }
+
+        @Override
+        final void onCancel() {
+            mServiceHandler.removeCallbacks(mTimeoutTrigger);
+        }
+
+        /**
+         * Called by the self-destruct timeout when the remote service didn't reply to the
+         * request on time.
+         */
+        protected abstract void onTimeout(S remoteService);
+    }
+
+    /**
      * Represents a request that does not expect a callback from the remote service.
      *
      * @param <I> the interface of the binder service
@@ -600,7 +650,7 @@
     }
 
     private static final class MyAsyncPendingRequest<S extends AbstractRemoteService<S, I>,
-            I extends IInterface> extends PendingRequest<S, I> {
+            I extends IInterface> extends BasePendingRequest<S, I> {
         private static final String TAG = MyAsyncPendingRequest.class.getSimpleName();
 
         private final AsyncRequest<I> mRequest;
@@ -623,12 +673,5 @@
                 finish();
             }
         }
-
-        @Override
-        protected void onTimeout(S remoteService) {
-            // TODO(b/117779333): should not happen because we called finish() on run(), although
-            // currently it might be called if the service is destroyed while showing it.
-            Slog.w(TAG, "AsyncPending requested timed out");
-        }
     }
 }
diff --git a/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
index f0c2233..3e92a0b 100644
--- a/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
@@ -38,7 +38,7 @@
         extends AbstractSinglePendingRequestRemoteService<S, I>, I extends IInterface>
         extends AbstractRemoteService<S, I> {
 
-    protected PendingRequest<S, I> mPendingRequest;
+    protected BasePendingRequest<S, I> mPendingRequest;
 
     public AbstractSinglePendingRequestRemoteService(@NonNull Context context,
             @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
@@ -51,7 +51,7 @@
     @Override // from AbstractRemoteService
     void handlePendingRequests() {
         if (mPendingRequest != null) {
-            final PendingRequest<S, I> pendingRequest = mPendingRequest;
+            final BasePendingRequest<S, I> pendingRequest = mPendingRequest;
             mPendingRequest = null;
             handlePendingRequest(pendingRequest);
         }
@@ -73,7 +73,7 @@
     }
 
     @Override // from AbstractRemoteService
-    void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest) {
+    void handlePendingRequestWhileUnBound(@NonNull BasePendingRequest<S, I> pendingRequest) {
         if (mPendingRequest != null) {
             if (mVerbose) {
                 Slog.v(mTag, "handlePendingRequestWhileUnBound(): cancelling " + mPendingRequest
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index f4902d4..524a5cc 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -61,17 +61,19 @@
             SystemProperties.getBoolean("ro.fw.system_user_split", false);
 
     // ------ ro.crypto.* -------- //
-    public static final String CRYPTO_STATE = SystemProperties.get("ro.crypto.state");
-    public static final String CRYPTO_TYPE = CryptoProperties.type().orElse("");
+    public static final CryptoProperties.state_values CRYPTO_STATE =
+            CryptoProperties.state().orElse(CryptoProperties.state_values.UNSUPPORTED);
+    public static final CryptoProperties.type_values CRYPTO_TYPE =
+            CryptoProperties.type().orElse(CryptoProperties.type_values.NONE);
     // These are pseudo-properties
     public static final boolean CRYPTO_ENCRYPTABLE =
-            !CRYPTO_STATE.isEmpty() && !"unsupported".equals(CRYPTO_STATE);
+            CRYPTO_STATE != CryptoProperties.state_values.UNSUPPORTED;
     public static final boolean CRYPTO_ENCRYPTED =
-            "encrypted".equalsIgnoreCase(CRYPTO_STATE);
+            CRYPTO_STATE == CryptoProperties.state_values.ENCRYPTED;
     public static final boolean CRYPTO_FILE_ENCRYPTED =
-            "file".equalsIgnoreCase(CRYPTO_TYPE);
+            CRYPTO_TYPE == CryptoProperties.type_values.FILE;
     public static final boolean CRYPTO_BLOCK_ENCRYPTED =
-            "block".equalsIgnoreCase(CRYPTO_TYPE);
+            CRYPTO_TYPE == CryptoProperties.type_values.BLOCK;
 
     public static final boolean CONTROL_PRIVAPP_PERMISSIONS_LOG =
             "log".equalsIgnoreCase(CONTROL_PRIVAPP_PERMISSIONS);
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 0604ab2..58b48d8 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -162,9 +162,9 @@
     /**
      * The duration to wait before re-checking Zygote related system properties.
      *
-     * Five minutes in milliseconds.
+     * One minute in milliseconds.
      */
-    public static final long PROPERTY_CHECK_INTERVAL = 300000;
+    public static final long PROPERTY_CHECK_INTERVAL = 60000;
 
     /**
      * @hide for internal use only
@@ -236,14 +236,14 @@
     public 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,
-            String packageName, String[] packagesForUID, String[] visibleVolIDs, String sandboxId) {
+            String packageName, String[] packagesForUID, String sandboxId) {
         ZygoteHooks.preFork();
         // Resets nice priority for zygote process.
         resetNicePriority();
         int pid = nativeForkAndSpecialize(
                 uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                 fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName,
-                packagesForUID, visibleVolIDs, sandboxId);
+                packagesForUID, sandboxId);
         // Enable tracing as soon as possible for the child process.
         if (pid == 0) {
             Trace.setTracingEnabled(true, runtimeFlags);
@@ -258,7 +258,7 @@
     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, String packageName, String[] packagesForUID, String[] visibleVolIDs,
+            String appDataDir, String packageName, String[] packagesForUID,
             String sandboxId);
 
     /**
@@ -285,11 +285,11 @@
     public static void specializeBlastula(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, int mountExternal, String seInfo, String niceName,
             boolean startChildZygote, String instructionSet, String appDataDir, String packageName,
-            String[] packagesForUID, String[] visibleVolIDs, String sandboxId) {
+            String[] packagesForUID, String sandboxId) {
 
         nativeSpecializeBlastula(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
                                  niceName, startChildZygote, instructionSet, appDataDir,
-                                 packageName, packagesForUID, visibleVolIDs, sandboxId);
+                                 packageName, packagesForUID, sandboxId);
 
         // Enable tracing as soon as possible for the child process.
         Trace.setTracingEnabled(true, runtimeFlags);
@@ -309,7 +309,7 @@
     private static native void nativeSpecializeBlastula(int uid, int gid, int[] gids,
             int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
             boolean startChildZygote, String instructionSet, String appDataDir, String packageName,
-            String[] packagesForUID, String[] visibleVolIDs, String sandboxId);
+            String[] packagesForUID, String sandboxId);
 
     /**
      * Called to do any initialization before starting an application.
@@ -427,6 +427,12 @@
                 defaultValue);
     }
 
+    protected static void emptyBlastulaPool() {
+        nativeEmptyBlastulaPool();
+    }
+
+    private static native void nativeEmptyBlastulaPool();
+
     /**
      * Returns the value of a system property converted to a boolean using specific logic.
      *
@@ -520,7 +526,7 @@
         LocalSocket sessionSocket = null;
         DataOutputStream blastulaOutputStream = null;
         Credentials peerCredentials = null;
-        String[] argStrings = null;
+        ZygoteArguments args = null;
 
         while (true) {
             try {
@@ -533,25 +539,24 @@
 
                 peerCredentials = sessionSocket.getPeerCredentials();
 
-                argStrings = readArgumentList(blastulaReader);
+                String[] argStrings = readArgumentList(blastulaReader);
 
                 if (argStrings != null) {
+                    args = new ZygoteArguments(argStrings);
+
+                    // TODO (chriswailes): Should this only be run for debug builds?
+                    validateBlastulaCommand(args);
                     break;
                 } else {
                     Log.e("Blastula", "Truncated command received.");
                     IoUtils.closeQuietly(sessionSocket);
                 }
-            } catch (IOException ioEx) {
-                Log.e("Blastula", "Failed to read command: " + ioEx.getMessage());
+            } catch (Exception ex) {
+                Log.e("Blastula", ex.getMessage());
                 IoUtils.closeQuietly(sessionSocket);
             }
         }
 
-        ZygoteArguments args = new ZygoteArguments(argStrings);
-
-        // TODO (chriswailes): Should this only be run for debug builds?
-        validateBlastulaCommand(args);
-
         applyUidSecurityPolicy(args, peerCredentials);
         applyDebuggerSystemProperty(args);
 
@@ -600,7 +605,7 @@
                            args.mRuntimeFlags, rlimits, args.mMountExternal,
                            args.mSeInfo, args.mNiceName, args.mStartChildZygote,
                            args.mInstructionSet, args.mAppDataDir, args.mPackageName,
-                           args.mPackagesForUid, args.mVisibleVolIds, args.mSandboxId);
+                           args.mPackagesForUid, args.mSandboxId);
 
         if (args.mNiceName != null) {
             Process.setArgV0(args.mNiceName);
@@ -740,8 +745,8 @@
 
         if (args.mInvokeWith != null && peerUid != 0
                 && (args.mRuntimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
-            throw new ZygoteSecurityException("Peer is permitted to specify an"
-                + "explicit invoke-with wrapper command only for debuggable"
+            throw new ZygoteSecurityException("Peer is permitted to specify an "
+                + "explicit invoke-with wrapper command only for debuggable "
                 + "applications.");
         }
     }
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index c24a9e0..9cb5820 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -101,6 +101,12 @@
     String mSeInfo;
 
     /**
+     *
+     */
+    boolean mBlastulaPoolEnabled;
+    boolean mBlastulaPoolStatusSpecified = false;
+
+    /**
      * from all --rlimit=r,c,m
      */
     ArrayList<int[]> mRLimits;
@@ -116,9 +122,6 @@
     /** from --packages-for-uid */
     String[] mPackagesForUid;
 
-    /** from --visible-vols */
-    String[] mVisibleVolIds;
-
     /** from --sandbox-id */
     String mSandboxId;
 
@@ -395,13 +398,15 @@
                 mPackageName = arg.substring(arg.indexOf('=') + 1);
             } else if (arg.startsWith("--packages-for-uid=")) {
                 mPackagesForUid = arg.substring(arg.indexOf('=') + 1).split(",");
-            } else if (arg.startsWith("--visible-vols=")) {
-                mVisibleVolIds = arg.substring(arg.indexOf('=') + 1).split(",");
             } else if (arg.startsWith("--sandbox-id=")) {
                 if (mSandboxId != null) {
                     throw new IllegalArgumentException("Duplicate arg specified");
                 }
                 mSandboxId = arg.substring(arg.indexOf('=') + 1);
+            } else if (arg.startsWith("--blastula-pool-enabled=")) {
+                mBlastulaPoolStatusSpecified = true;
+                mBlastulaPoolEnabled = Boolean.parseBoolean(arg.substring(arg.indexOf('=') + 1));
+                expectRuntimeArgs = false;
             } else {
                 break;
             }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 8a878e2..c7ba22d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -158,6 +158,10 @@
             return null;
         }
 
+        if (parsedArgs.mBlastulaPoolStatusSpecified) {
+            return handleBlastulaPoolStatusChange(zygoteServer, parsedArgs.mBlastulaPoolEnabled);
+        }
+
         if (parsedArgs.mPreloadDefault) {
             handlePreload();
             return null;
@@ -185,13 +189,12 @@
         }
 
         if (parsedArgs.mApiBlacklistExemptions != null) {
-            handleApiBlacklistExemptions(parsedArgs.mApiBlacklistExemptions);
-            return null;
+            return handleApiBlacklistExemptions(zygoteServer, parsedArgs.mApiBlacklistExemptions);
         }
 
         if (parsedArgs.mHiddenApiAccessLogSampleRate != -1) {
-            handleHiddenApiAccessLogSampleRate(parsedArgs.mHiddenApiAccessLogSampleRate);
-            return null;
+            return handleHiddenApiAccessLogSampleRate(zygoteServer,
+                    parsedArgs.mHiddenApiAccessLogSampleRate);
         }
 
         if (parsedArgs.mPermittedCapabilities != 0 || parsedArgs.mEffectiveCapabilities != 0) {
@@ -258,7 +261,7 @@
                 parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
                 parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
                 parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mPackageName,
-                parsedArgs.mPackagesForUid, parsedArgs.mVisibleVolIds, parsedArgs.mSandboxId);
+                parsedArgs.mPackagesForUid, parsedArgs.mSandboxId);
 
         try {
             if (pid == 0) {
@@ -325,10 +328,64 @@
         }
     }
 
-    private void handleApiBlacklistExemptions(String[] exemptions) {
+    private Runnable stateChangeWithBlastulaPoolReset(ZygoteServer zygoteServer,
+            Runnable stateChangeCode) {
         try {
-            ZygoteInit.setApiBlacklistExemptions(exemptions);
+            if (zygoteServer.isBlastulaPoolEnabled()) {
+                Zygote.emptyBlastulaPool();
+            }
+
+            stateChangeCode.run();
+
+            if (zygoteServer.isBlastulaPoolEnabled()) {
+                Runnable fpResult =
+                        zygoteServer.fillBlastulaPool(
+                                new int[]{mSocket.getFileDescriptor().getInt$()});
+
+                if (fpResult != null) {
+                    zygoteServer.setForkChild();
+                    return fpResult;
+                }
+            }
+
             mSocketOutStream.writeInt(0);
+
+            return null;
+        } catch (IOException ioe) {
+            throw new IllegalStateException("Error writing to command socket", ioe);
+        }
+    }
+
+    /**
+     * Makes the necessary changes to implement a new API blacklist exemption policy, and then
+     * responds to the system server, letting it know that the task has been completed.
+     *
+     * This necessitates a change to the internal state of the Zygote.  As such, if the blastula
+     * pool is enabled all existing blastulas have an incorrect API blacklist exemption list.  To
+     * properly handle this request the pool must be emptied and refilled.  This process can return
+     * a Runnable object that must be returned to ZygoteServer.runSelectLoop to be invoked.
+     *
+     * @param zygoteServer  The server object that received the request
+     * @param exemptions  The new exemption list.
+     * @return A Runnable object representing a new app in any blastulas spawned from here; the
+     *         zygote process will always receive a null value from this function.
+     */
+    private Runnable handleApiBlacklistExemptions(ZygoteServer zygoteServer, String[] exemptions) {
+        return stateChangeWithBlastulaPoolReset(zygoteServer,
+                () -> ZygoteInit.setApiBlacklistExemptions(exemptions));
+    }
+
+    private Runnable handleBlastulaPoolStatusChange(ZygoteServer zygoteServer, boolean newStatus) {
+        try {
+            Runnable fpResult = zygoteServer.setBlastulaPoolStatus(newStatus, mSocket);
+
+            if (fpResult == null) {
+                mSocketOutStream.writeInt(0);
+            } else {
+                zygoteServer.setForkChild();
+            }
+
+            return fpResult;
         } catch (IOException ioe) {
             throw new IllegalStateException("Error writing to command socket", ioe);
         }
@@ -384,15 +441,26 @@
         }
     }
 
-    private void handleHiddenApiAccessLogSampleRate(int samplingRate) {
-        try {
+    /**
+     * Changes the API access log sample rate for the Zygote and processes spawned from it.
+     *
+     * This necessitates a change to the internal state of the Zygote.  As such, if the blastula
+     * pool is enabled all existing blastulas have an incorrect API access log sample rate.  To
+     * properly handle this request the pool must be emptied and refilled.  This process can return
+     * a Runnable object that must be returned to ZygoteServer.runSelectLoop to be invoked.
+     *
+     * @param zygoteServer  The server object that received the request
+     * @param samplingRate  The new sample rate
+     * @return A Runnable object representing a new app in any blastulas spawned from here; the
+     *         zygote process will always receive a null value from this function.
+     */
+    private Runnable handleHiddenApiAccessLogSampleRate(ZygoteServer zygoteServer,
+            int samplingRate) {
+        return stateChangeWithBlastulaPoolReset(zygoteServer, () -> {
             ZygoteInit.setHiddenApiAccessLogSampleRate(samplingRate);
             HiddenApiUsageLogger.setHiddenApiAccessLogSampleRate(samplingRate);
             ZygoteInit.setHiddenApiUsageLogger(HiddenApiUsageLogger.getInstance());
-            mSocketOutStream.writeInt(0);
-        } catch (IOException ioe) {
-            throw new IllegalStateException("Error writing to command socket", ioe);
-        }
+        });
     }
 
     protected void preload() {
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index 2c17540..c4c98ba 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -68,6 +68,15 @@
     private static final String BLASTULA_POOL_SIZE_MIN_DEFAULT = "1";
 
     /**
+     * Indicates if this Zygote server can support a blastula pool.  Currently this should only be
+     * true for the primary and secondary Zygotes, and not the App Zygotes or the WebView Zygote.
+     *
+     * TODO (chriswailes): Make this an explicit argument to the constructor
+     */
+
+    private final boolean mBlastulaPoolSupported;
+
+    /**
      * If the blastula pool should be created and used to start applications.
      *
      * Setting this value to false will disable the creation, maintenance, and use of the blastula
@@ -127,6 +136,8 @@
         mBlastulaPoolEventFD = null;
         mZygoteSocket = null;
         mBlastulaPoolSocket = null;
+
+        mBlastulaPoolSupported = false;
     }
 
     /**
@@ -151,12 +162,18 @@
         }
 
         fetchBlastulaPoolPolicyProps();
+
+        mBlastulaPoolSupported = true;
     }
 
     void setForkChild() {
         mIsForkChild = true;
     }
 
+    public boolean isBlastulaPoolEnabled() {
+        return mBlastulaPoolEnabled;
+    }
+
     /**
      * Registers a server socket for zygote command connections. This opens the server socket
      * at the specified name in the abstract socket namespace.
@@ -224,42 +241,43 @@
     }
 
     private void fetchBlastulaPoolPolicyProps() {
-        final String blastulaPoolSizeMaxPropString =
-                Zygote.getSystemProperty(
-                        DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MAX,
-                        BLASTULA_POOL_SIZE_MAX_DEFAULT);
+        if (mBlastulaPoolSupported) {
+            final String blastulaPoolSizeMaxPropString =
+                    Zygote.getSystemProperty(
+                            DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MAX,
+                            BLASTULA_POOL_SIZE_MAX_DEFAULT);
 
-        if (!blastulaPoolSizeMaxPropString.isEmpty()) {
-            mBlastulaPoolSizeMax =
-                    Integer.min(
-                            Integer.parseInt(blastulaPoolSizeMaxPropString),
-                            BLASTULA_POOL_SIZE_MAX_LIMIT);
+            if (!blastulaPoolSizeMaxPropString.isEmpty()) {
+                mBlastulaPoolSizeMax =
+                        Integer.min(
+                                Integer.parseInt(blastulaPoolSizeMaxPropString),
+                                BLASTULA_POOL_SIZE_MAX_LIMIT);
+            }
+
+            final String blastulaPoolSizeMinPropString =
+                    Zygote.getSystemProperty(
+                            DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MIN,
+                            BLASTULA_POOL_SIZE_MIN_DEFAULT);
+
+            if (!blastulaPoolSizeMinPropString.isEmpty()) {
+                mBlastulaPoolSizeMin =
+                        Integer.max(
+                                Integer.parseInt(blastulaPoolSizeMinPropString),
+                                BLASTULA_POOL_SIZE_MIN_LIMIT);
+            }
+
+            final String blastulaPoolRefillThresholdPropString =
+                    Zygote.getSystemProperty(
+                            DeviceConfig.RuntimeNative.BLASTULA_POOL_REFILL_THRESHOLD,
+                            Integer.toString(mBlastulaPoolSizeMax / 2));
+
+            if (!blastulaPoolRefillThresholdPropString.isEmpty()) {
+                mBlastulaPoolRefillThreshold =
+                        Integer.min(
+                                Integer.parseInt(blastulaPoolRefillThresholdPropString),
+                                mBlastulaPoolSizeMax);
+            }
         }
-
-        final String blastulaPoolSizeMinPropString =
-                Zygote.getSystemProperty(
-                        DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MIN,
-                        BLASTULA_POOL_SIZE_MIN_DEFAULT);
-
-        if (!blastulaPoolSizeMinPropString.isEmpty()) {
-            mBlastulaPoolSizeMin =
-                    Integer.max(
-                            Integer.parseInt(blastulaPoolSizeMinPropString),
-                            BLASTULA_POOL_SIZE_MIN_LIMIT);
-        }
-
-        final String blastulaPoolRefillThresholdPropString =
-                Zygote.getSystemProperty(
-                        DeviceConfig.RuntimeNative.BLASTULA_POOL_REFILL_THRESHOLD,
-                        Integer.toString(mBlastulaPoolSizeMax / 2));
-
-        if (!blastulaPoolRefillThresholdPropString.isEmpty()) {
-            mBlastulaPoolRefillThreshold =
-                    Integer.min(
-                        Integer.parseInt(blastulaPoolRefillThresholdPropString),
-                        mBlastulaPoolSizeMax);
-        }
-
     }
 
     private long mLastPropCheckTimestamp = 0;
@@ -282,44 +300,65 @@
      *         this function will return a Runnable object representing the new application that is
      *         passed up from blastulaMain.
      */
-    private Runnable fillBlastulaPool(int[] sessionSocketRawFDs) {
-        if (mBlastulaPoolEnabled) {
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool");
 
-            int blastulaPoolCount = Zygote.getBlastulaPoolCount();
-            int numBlastulasToSpawn = mBlastulaPoolSizeMax - blastulaPoolCount;
+    Runnable fillBlastulaPool(int[] sessionSocketRawFDs) {
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool");
 
-            if (blastulaPoolCount < mBlastulaPoolSizeMin
-                    || numBlastulasToSpawn >= mBlastulaPoolRefillThreshold) {
+        int blastulaPoolCount = Zygote.getBlastulaPoolCount();
+        int numBlastulasToSpawn = mBlastulaPoolSizeMax - blastulaPoolCount;
 
-                // Disable some VM functionality and reset some system values
-                // before forking.
-                ZygoteHooks.preFork();
-                Zygote.resetNicePriority();
+        if (blastulaPoolCount < mBlastulaPoolSizeMin
+                || numBlastulasToSpawn >= mBlastulaPoolRefillThreshold) {
 
-                while (blastulaPoolCount++ < mBlastulaPoolSizeMax) {
-                    Runnable caller = Zygote.forkBlastula(mBlastulaPoolSocket, sessionSocketRawFDs);
+            // Disable some VM functionality and reset some system values
+            // before forking.
+            ZygoteHooks.preFork();
+            Zygote.resetNicePriority();
 
-                    if (caller != null) {
-                        return caller;
-                    }
+            while (blastulaPoolCount++ < mBlastulaPoolSizeMax) {
+                Runnable caller = Zygote.forkBlastula(mBlastulaPoolSocket, sessionSocketRawFDs);
+
+                if (caller != null) {
+                    return caller;
                 }
-
-                // Re-enable runtime services for the Zygote.  Blastula services
-                // are re-enabled in specializeBlastula.
-                ZygoteHooks.postForkCommon();
-
-                Log.i("zygote",
-                        "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn);
             }
 
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            // Re-enable runtime services for the Zygote.  Blastula services
+            // are re-enabled in specializeBlastula.
+            ZygoteHooks.postForkCommon();
+
+            Log.i("zygote",
+                    "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn);
         }
 
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
         return null;
     }
 
     /**
+     * Empty or fill the blastula pool as dictated by the current and new blastula pool statuses.
+     */
+    Runnable setBlastulaPoolStatus(boolean newStatus, LocalSocket sessionSocket) {
+        if (!mBlastulaPoolSupported) {
+            Log.w(TAG,
+                    "Attempting to enable a blastula pool for a Zygote that doesn't support it.");
+            return null;
+        } else if (mBlastulaPoolEnabled == newStatus) {
+            return null;
+        }
+
+        mBlastulaPoolEnabled = newStatus;
+
+        if (newStatus) {
+            return fillBlastulaPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() });
+        } else {
+            Zygote.emptyBlastulaPool();
+            return null;
+        }
+    }
+
+    /**
      * Runs the zygote process's select loop. Accepts new connections as
      * they happen, and reads commands from connections one spawn-request's
      * worth at a time.
@@ -334,12 +373,26 @@
         while (true) {
             fetchBlastulaPoolPolicyPropsWithMinInterval();
 
-            int[] blastulaPipeFDs = Zygote.getBlastulaPipeFDs();
+            int[] blastulaPipeFDs = null;
+            StructPollfd[] pollFDs = null;
 
-            // Space for all of the socket FDs, the Blastula Pool Event FD, and
-            // all of the open blastula read pipe FDs.
-            StructPollfd[] pollFDs =
-                new StructPollfd[socketFDs.size() + 1 + blastulaPipeFDs.length];
+            // Allocate enough space for the poll structs, taking into account
+            // the state of the blastula pool for this Zygote (could be a
+            // regular Zygote, a WebView Zygote, or an AppZygote).
+            if (mBlastulaPoolEnabled) {
+                blastulaPipeFDs = Zygote.getBlastulaPipeFDs();
+                pollFDs = new StructPollfd[socketFDs.size() + 1 + blastulaPipeFDs.length];
+            } else {
+                pollFDs = new StructPollfd[socketFDs.size()];
+            }
+
+            /*
+             * For reasons of correctness the blastula pool pipe and event FDs
+             * must be processed before the session and server sockets.  This
+             * is to ensure that the blastula pool accounting information is
+             * accurate when handling other requests like API blacklist
+             * exemptions.
+             */
 
             int pollIndex = 0;
             for (FileDescriptor socketFD : socketFDs) {
@@ -350,19 +403,22 @@
             }
 
             final int blastulaPoolEventFDIndex = pollIndex;
-            pollFDs[pollIndex] = new StructPollfd();
-            pollFDs[pollIndex].fd = mBlastulaPoolEventFD;
-            pollFDs[pollIndex].events = (short) POLLIN;
-            ++pollIndex;
 
-            for (int blastulaPipeFD : blastulaPipeFDs) {
-                FileDescriptor managedFd = new FileDescriptor();
-                managedFd.setInt$(blastulaPipeFD);
-
+            if (mBlastulaPoolEnabled) {
                 pollFDs[pollIndex] = new StructPollfd();
-                pollFDs[pollIndex].fd = managedFd;
+                pollFDs[pollIndex].fd = mBlastulaPoolEventFD;
                 pollFDs[pollIndex].events = (short) POLLIN;
                 ++pollIndex;
+
+                for (int blastulaPipeFD : blastulaPipeFDs) {
+                    FileDescriptor managedFd = new FileDescriptor();
+                    managedFd.setInt$(blastulaPipeFD);
+
+                    pollFDs[pollIndex] = new StructPollfd();
+                    pollFDs[pollIndex].fd = managedFd;
+                    pollFDs[pollIndex].events = (short) POLLIN;
+                    ++pollIndex;
+                }
             }
 
             try {
@@ -371,6 +427,8 @@
                 throw new RuntimeException("poll failed", ex);
             }
 
+            boolean blastulaPoolFDRead = false;
+
             while (--pollIndex >= 0) {
                 if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
                     continue;
@@ -390,6 +448,7 @@
                         ZygoteConnection connection = peers.get(pollIndex);
                         final Runnable command = connection.processOneCommand(this);
 
+                        // TODO (chriswailes): Is this extra check necessary?
                         if (mIsForkChild) {
                             // We're in the child. We should always have a command to run at this
                             // stage if processOneCommand hasn't called "exec".
@@ -480,17 +539,22 @@
                         Zygote.removeBlastulaTableEntry((int) messagePayload);
                     }
 
-                    int[] sessionSocketRawFDs =
-                            socketFDs.subList(1, socketFDs.size())
+                    blastulaPoolFDRead = true;
+                }
+            }
+
+            // Check to see if the blastula pool needs to be refilled.
+            if (blastulaPoolFDRead) {
+                int[] sessionSocketRawFDs =
+                        socketFDs.subList(1, socketFDs.size())
                                 .stream()
                                 .mapToInt(fd -> fd.getInt$())
                                 .toArray();
 
-                    final Runnable command = fillBlastulaPool(sessionSocketRawFDs);
+                final Runnable command = fillBlastulaPool(sessionSocketRawFDs);
 
-                    if (command != null) {
-                        return command;
-                    }
+                if (command != null) {
+                    return command;
                 }
             }
         }
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index cc958f4..fa73758 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -339,7 +339,7 @@
         mFrameAndBackdropNode.setLeftTopRightBottom(left, top, left + width, top + height);
 
         // Draw the caption and content backdrops in to our render node.
-        RecordingCanvas canvas = mFrameAndBackdropNode.start(width, height);
+        RecordingCanvas canvas = mFrameAndBackdropNode.beginRecording(width, height);
         final Drawable drawable = mUserCaptionBackgroundDrawable != null
                 ? mUserCaptionBackgroundDrawable : mCaptionBackgroundDrawable;
 
@@ -353,7 +353,7 @@
             mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
             mResizingBackgroundDrawable.draw(canvas);
         }
-        mFrameAndBackdropNode.end(canvas);
+        mFrameAndBackdropNode.endRecording();
 
         drawColorViews(left, top, width, height, fullscreen, systemInsets, stableInsets);
 
@@ -368,7 +368,7 @@
         if (mSystemBarBackgroundNode == null) {
             return;
         }
-        RecordingCanvas canvas = mSystemBarBackgroundNode.start(width, height);
+        RecordingCanvas canvas = mSystemBarBackgroundNode.beginRecording(width, height);
         mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height);
         final int topInset = DecorView.getColorViewTopInset(mStableInsets.top, mSystemInsets.top);
         if (mStatusBarColor != null) {
@@ -384,7 +384,7 @@
             mNavigationBarColor.setBounds(mTmpRect);
             mNavigationBarColor.draw(canvas);
         }
-        mSystemBarBackgroundNode.end(canvas);
+        mSystemBarBackgroundNode.endRecording();
         mRenderer.drawRenderNode(mSystemBarBackgroundNode);
     }
 
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 0938e56..16517bf 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -244,6 +244,11 @@
 // Copying (CC) garbage collector.
 static const char* kNoGenerationalCCRuntimeOption = "-Xgc:nogenerational_cc";
 
+// Feature flag name for running the JIT in Zygote experiment, b/119800099.
+static const char* ENABLE_APEX_IMAGE = "enable_apex_image";
+// Flag to pass to the runtime when using the apex image.
+static const char* kApexImageOption = "-Ximage:/system/framework/apex.art";
+
 static AndroidRuntime* gCurRuntime = NULL;
 
 /*
@@ -678,8 +683,17 @@
     char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX];
     char bootImageBuf[sizeof("-Ximage:") - 1 + PROPERTY_VALUE_MAX];
 
-    if (parseRuntimeOption("dalvik.vm.boot-image", bootImageBuf, "-Ximage:")) {
-        ALOGI("Boot image: '%s'\n", bootImageBuf);
+    std::string use_apex_image =
+        server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
+                                                             ENABLE_APEX_IMAGE,
+                                                             /*default_value=*/ "");
+    if (use_apex_image == "true") {
+        addOption(kApexImageOption);
+        ALOGI("Using Apex boot image: '%s'\n", kApexImageOption);
+    } else if (parseRuntimeOption("dalvik.vm.boot-image", bootImageBuf, "-Ximage:")) {
+        ALOGI("Using dalvik.vm.boot-image: '%s'\n", bootImageBuf);
+    } else {
+        ALOGI("Using default boot image");
     }
 
     bool checkJni = false;
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index a1f2377..1163b86 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -33,14 +33,16 @@
 #include <unistd.h>
 #include <sys/ioctl.h>
 
+#include <android-base/cmsg.h>
+#include <android-base/macros.h>
 #include <cutils/sockets.h>
 #include <netinet/tcp.h>
 #include <nativehelper/ScopedUtfChars.h>
 
-namespace android {
+using android::base::ReceiveFileDescriptorVector;
+using android::base::SendFileDescriptorVector;
 
-template <typename T>
-void UNUSED(T t) {}
+namespace android {
 
 static jfieldID field_inboundFileDescriptors;
 static jfieldID field_outboundFileDescriptors;
@@ -118,67 +120,6 @@
 }
 
 /**
- * Processes ancillary data, handling only
- * SCM_RIGHTS. Creates appropriate objects and sets appropriate
- * fields in the LocalSocketImpl object. Returns 0 on success
- * or -1 if an exception was thrown.
- */
-static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg)
-{
-    struct cmsghdr *cmsgptr;
-
-    for (cmsgptr = CMSG_FIRSTHDR(pMsg);
-            cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(pMsg, cmsgptr)) {
-
-        if (cmsgptr->cmsg_level != SOL_SOCKET) {
-            continue;
-        }
-
-        if (cmsgptr->cmsg_type == SCM_RIGHTS) {
-            int *pDescriptors = (int *)CMSG_DATA(cmsgptr);
-            jobjectArray fdArray;
-            int count
-                = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int));
-
-            if (count < 0) {
-                jniThrowException(env, "java/io/IOException",
-                    "invalid cmsg length");
-                return -1;
-            }
-
-            fdArray = env->NewObjectArray(count, class_FileDescriptor, NULL);
-
-            if (fdArray == NULL) {
-                return -1;
-            }
-
-            for (int i = 0; i < count; i++) {
-                jobject fdObject
-                        = jniCreateFileDescriptor(env, pDescriptors[i]);
-
-                if (env->ExceptionCheck()) {
-                    return -1;
-                }
-
-                env->SetObjectArrayElement(fdArray, i, fdObject);
-
-                if (env->ExceptionCheck()) {
-                    return -1;
-                }
-            }
-
-            env->SetObjectField(thisJ, field_inboundFileDescriptors, fdArray);
-
-            if (env->ExceptionCheck()) {
-                return -1;
-            }
-        }
-    }
-
-    return 0;
-}
-
-/**
  * Reads data from a socket into buf, processing any ancillary data
  * and adding it to thisJ.
  *
@@ -189,47 +130,48 @@
         void *buffer, size_t len)
 {
     ssize_t ret;
-    struct msghdr msg;
-    struct iovec iv;
-    unsigned char *buf = (unsigned char *)buffer;
-    // Enough buffer for a pile of fd's. We throw an exception if
-    // this buffer is too small.
-    struct cmsghdr cmsgbuf[2*sizeof(cmsghdr) + 0x100];
+    std::vector<android::base::unique_fd> received_fds;
 
-    memset(&msg, 0, sizeof(msg));
-    memset(&iv, 0, sizeof(iv));
-
-    iv.iov_base = buf;
-    iv.iov_len = len;
-
-    msg.msg_iov = &iv;
-    msg.msg_iovlen = 1;
-    msg.msg_control = cmsgbuf;
-    msg.msg_controllen = sizeof(cmsgbuf);
-
-    ret = TEMP_FAILURE_RETRY(recvmsg(fd, &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC));
-
-    if (ret < 0 && errno == EPIPE) {
-        // Treat this as an end of stream
-        return 0;
-    }
+    ret = ReceiveFileDescriptorVector(fd, buffer, len, 64, &received_fds);
 
     if (ret < 0) {
+        if (errno == EPIPE) {
+            // Treat this as an end of stream
+            return 0;
+        }
+
         jniThrowIOException(env, errno);
         return -1;
     }
 
-    if ((msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) {
-        // To us, any of the above flags are a fatal error
+    if (received_fds.size() > 0) {
+        jobjectArray fdArray = env->NewObjectArray(received_fds.size(), class_FileDescriptor, NULL);
 
-        jniThrowException(env, "java/io/IOException",
-                "Unexpected error or truncation during recvmsg()");
+        if (fdArray == NULL) {
+            // NewObjectArray has thrown.
+            return -1;
+        }
 
-        return -1;
-    }
+        for (size_t i = 0; i < received_fds.size(); i++) {
+            jobject fdObject = jniCreateFileDescriptor(env, received_fds[i].get());
 
-    if (ret >= 0) {
-        socket_process_cmsg(env, thisJ, &msg);
+            if (env->ExceptionCheck()) {
+                return -1;
+            }
+
+            env->SetObjectArrayElement(fdArray, i, fdObject);
+
+            if (env->ExceptionCheck()) {
+                return -1;
+            }
+        }
+
+        for (auto &fd : received_fds) {
+            // The fds are stored in java.io.FileDescriptors now.
+            static_cast<void>(fd.release());
+        }
+
+        env->SetObjectField(thisJ, field_inboundFileDescriptors, fdArray);
     }
 
     return ret;
@@ -243,7 +185,6 @@
 static int socket_write_all(JNIEnv *env, jobject object, int fd,
         void *buf, size_t len)
 {
-    ssize_t ret;
     struct msghdr msg;
     unsigned char *buffer = (unsigned char *)buf;
     memset(&msg, 0, sizeof(msg));
@@ -256,14 +197,11 @@
         return -1;
     }
 
-    struct cmsghdr *cmsg;
     int countFds = outboundFds == NULL ? 0 : env->GetArrayLength(outboundFds);
-    int fds[countFds];
-    char msgbuf[CMSG_SPACE(countFds)];
+    std::vector<int> fds;
 
     // Add any pending outbound file descriptors to the message
     if (outboundFds != NULL) {
-
         if (env->ExceptionCheck()) {
             return -1;
         }
@@ -274,47 +212,25 @@
                 return -1;
             }
 
-            fds[i] = jniGetFDFromFileDescriptor(env, fdObject);
+            fds.push_back(jniGetFDFromFileDescriptor(env, fdObject));
             if (env->ExceptionCheck()) {
                 return -1;
             }
         }
-
-        // See "man cmsg" really
-        msg.msg_control = msgbuf;
-        msg.msg_controllen = sizeof msgbuf;
-        cmsg = CMSG_FIRSTHDR(&msg);
-        cmsg->cmsg_level = SOL_SOCKET;
-        cmsg->cmsg_type = SCM_RIGHTS;
-        cmsg->cmsg_len = CMSG_LEN(sizeof fds);
-        memcpy(CMSG_DATA(cmsg), fds, sizeof fds);
     }
 
-    // We only write our msg_control during the first write
-    while (len > 0) {
-        struct iovec iv;
-        memset(&iv, 0, sizeof(iv));
+    ssize_t rc = SendFileDescriptorVector(fd, buffer, len, fds);
 
-        iv.iov_base = buffer;
-        iv.iov_len = len;
-
-        msg.msg_iov = &iv;
-        msg.msg_iovlen = 1;
-
-        do {
-            ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
-        } while (ret < 0 && errno == EINTR);
-
-        if (ret < 0) {
+    while (rc != len) {
+        if (rc == -1) {
             jniThrowIOException(env, errno);
             return -1;
         }
 
-        buffer += ret;
-        len -= ret;
+        buffer += rc;
+        len -= rc;
 
-        // Wipes out any msg_control too
-        memset(&msg, 0, sizeof(msg));
+        rc = send(fd, buffer, len, MSG_NOSIGNAL);
     }
 
     return 0;
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 15d1944..de5e3a5 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -35,8 +35,6 @@
 
 #include <ui/ANativeObjectBase.h>
 
-static int initialized = 0;
-
 static jclass egldisplayClass;
 static jclass eglcontextClass;
 static jclass eglsurfaceClass;
@@ -239,7 +237,7 @@
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
-        return false;
+        return JNI_FALSE;
     }
     return (jboolean)_returnValue;
 }
@@ -337,7 +335,7 @@
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
-        return false;
+        return JNI_FALSE;
     }
     return (jboolean)_returnValue;
 }
@@ -457,7 +455,7 @@
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
-        return false;
+        return JNI_FALSE;
     }
     return (jboolean)_returnValue;
 }
@@ -513,7 +511,7 @@
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
-        return false;
+        return JNI_FALSE;
     }
     return (jboolean)_returnValue;
 }
@@ -808,7 +806,7 @@
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
-        return false;
+        return JNI_FALSE;
     }
     return (jboolean)_returnValue;
 }
@@ -1163,7 +1161,7 @@
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
-        return false;
+        return JNI_FALSE;
     }
     return (jboolean)_returnValue;
 }
@@ -1209,7 +1207,7 @@
   (JNIEnv *_env, jobject _this, jobject dpy, jobject surface, jint target) {
     jniThrowException(_env, "java/lang/UnsupportedOperationException",
         "eglCopyBuffers");
-    return (EGLBoolean) 0;
+        return JNI_FALSE;
 }
 
 static const char *classPathName = "android/opengl/EGL14";
diff --git a/core/jni/android_opengl_EGL15.cpp b/core/jni/android_opengl_EGL15.cpp
index 2abd950..99bdce2 100644
--- a/core/jni/android_opengl_EGL15.cpp
+++ b/core/jni/android_opengl_EGL15.cpp
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+// This source file is automatically generated
+
 #pragma GCC diagnostic ignored "-Wunused-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
@@ -27,8 +29,6 @@
 
 #include <ui/ANativeObjectBase.h>
 
-static int initialized = 0;
-
 // classes from EGL 1.4
 static jclass egldisplayClass;
 static jclass eglsurfaceClass;
@@ -92,7 +92,6 @@
     egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J");
     eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J");
 
-
     eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V");
     eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V");
     egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V");
@@ -105,7 +104,6 @@
     jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor, reinterpret_cast<jlong>(EGL_NO_SURFACE));
     eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject);
 
-
     jclass eglClass = _env->FindClass("android/opengl/EGL15");
     jfieldID noContextFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;");
     _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject);
@@ -171,8 +169,6 @@
         *array = NULL;
         return reinterpret_cast<void*>(pointer);
     }
-    eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
-    eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
 
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
@@ -191,7 +187,7 @@
 
 static void *
 fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) {
-    if (obj == NULL){
+    if (obj == NULL) {
         jniThrowException(_env, "java/lang/IllegalArgumentException",
                           "Object is set to null.");
         return nullptr;
@@ -202,10 +198,9 @@
 }
 
 static jobject
-toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void * handle) {
-    if (cls == eglimageClass &&
-       (EGLImage)handle == EGL_NO_IMAGE) {
-           return eglNoImageObject;
+toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void *handle) {
+    if (cls == eglimageClass && (EGLImage)handle == EGL_NO_IMAGE) {
+        return eglNoImageObject;
     }
 
     return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle));
@@ -337,7 +332,7 @@
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
-        return false;
+        return JNI_FALSE;
     }
     return (jboolean)_returnValue;
 }
@@ -461,12 +456,9 @@
 static jobject
 android_eglCreatePlatformPixmapSurface
   (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_pixmap_buf, jlongArray attrib_list_ref, jint offset) {
-    if ((true)) {
-        jniThrowException(_env, "java/lang/UnsupportedOperationException",
-            "eglCreatePlatformPixmapSurface");
+    jniThrowException(_env, "java/lang/UnsupportedOperationException",
+        "eglCreatePlatformPixmapSurface");
         return nullptr;
-    }
-    return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, (EGLSurface) 0);
 }
 
 /* EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags ) */
diff --git a/core/jni/android_opengl_EGLExt.cpp b/core/jni/android_opengl_EGLExt.cpp
index 75a25fe..fef8116 100644
--- a/core/jni/android_opengl_EGLExt.cpp
+++ b/core/jni/android_opengl_EGLExt.cpp
@@ -36,8 +36,6 @@
 
 #include <ui/ANativeObjectBase.h>
 
-static int initialized = 0;
-
 static jclass egldisplayClass;
 static jclass eglcontextClass;
 static jclass eglsurfaceClass;
@@ -104,6 +102,7 @@
     if (obj == NULL){
         jniThrowException(_env, "java/lang/IllegalArgumentException",
                           "Object is set to null.");
+        return nullptr;
     }
 
     return reinterpret_cast<void*>(_env->CallLongMethod(obj, mid));
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index ee5b594..a4ab5db 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -29,8 +29,6 @@
 #include <utils/misc.h>
 #include <assert.h>
 
-static int initialized = 0;
-
 static jclass nioAccessClass;
 static jclass bufferClass;
 static jmethodID getBasePointerID;
diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp
index da7d0f0..a5dcbf7 100644
--- a/core/jni/android_opengl_GLES10Ext.cpp
+++ b/core/jni/android_opengl_GLES10Ext.cpp
@@ -29,8 +29,6 @@
 #include <utils/misc.h>
 #include <assert.h>
 
-static int initialized = 0;
-
 static jclass nioAccessClass;
 static jclass bufferClass;
 static jmethodID getBasePointerID;
@@ -531,6 +529,7 @@
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
+        return (jint)0;
     }
     return (jint)_returnValue;
 }
@@ -600,6 +599,7 @@
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
+        return (jint)0;
     }
     return (jint)_returnValue;
 }
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index 391ae53..be86a03 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -29,8 +29,6 @@
 #include <utils/misc.h>
 #include <assert.h>
 
-static int initialized = 0;
-
 static jclass nioAccessClass;
 static jclass bufferClass;
 static jmethodID getBasePointerID;
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 09dce32..d28d9a3 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -29,8 +29,6 @@
 #include <utils/misc.h>
 #include <assert.h>
 
-static int initialized = 0;
-
 static jclass nioAccessClass;
 static jclass bufferClass;
 static jmethodID getBasePointerID;
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index 99922cf..0e20d47 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -29,8 +29,6 @@
 #include <utils/misc.h>
 #include <assert.h>
 
-static int initialized = 0;
-
 static jclass nioAccessClass;
 static jclass bufferClass;
 static jmethodID getBasePointerID;
@@ -2684,6 +2682,7 @@
 
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
+        return (jint)0;
     }
     return (jint)_returnValue;
 }
@@ -3909,6 +3908,7 @@
 
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
+        return (jint)0;
     }
     return (jint)_returnValue;
 }
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index adc635e..9922398 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -29,8 +29,6 @@
 #include <utils/misc.h>
 #include <assert.h>
 
-static int initialized = 0;
-
 static jclass nioAccessClass;
 static jclass bufferClass;
 static jmethodID getBasePointerID;
@@ -3012,6 +3010,7 @@
 
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
+        return (jint)0;
     }
     return (jint)_returnValue;
 }
@@ -3981,6 +3980,7 @@
 
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
+        return (jint)0;
     }
     return (jint)_returnValue;
 }
diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp
index 512f562..27dbd39 100644
--- a/core/jni/android_opengl_GLES31.cpp
+++ b/core/jni/android_opengl_GLES31.cpp
@@ -27,8 +27,6 @@
 #include <utils/misc.h>
 #include <assert.h>
 
-static int initialized = 0;
-
 static jclass nioAccessClass;
 static jclass bufferClass;
 static jmethodID getBasePointerID;
@@ -708,6 +706,7 @@
 
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
+        return (jint)0;
     }
     return (jint)_returnValue;
 }
@@ -919,6 +918,7 @@
 
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
+        return (jint)0;
     }
     return (jint)_returnValue;
 }
diff --git a/core/jni/android_opengl_GLES31Ext.cpp b/core/jni/android_opengl_GLES31Ext.cpp
index 5543fca..5b671c8 100644
--- a/core/jni/android_opengl_GLES31Ext.cpp
+++ b/core/jni/android_opengl_GLES31Ext.cpp
@@ -28,8 +28,6 @@
 #include <utils/misc.h>
 #include <assert.h>
 
-static int initialized = 0;
-
 static jclass nioAccessClass;
 static jclass bufferClass;
 static jmethodID getBasePointerID;
diff --git a/core/jni/android_opengl_GLES32.cpp b/core/jni/android_opengl_GLES32.cpp
index 2f1e31e..d59d25c 100644
--- a/core/jni/android_opengl_GLES32.cpp
+++ b/core/jni/android_opengl_GLES32.cpp
@@ -27,8 +27,6 @@
 #include <utils/misc.h>
 #include <assert.h>
 
-static int initialized = 0;
-
 static jclass nioAccessClass;
 static jclass bufferClass;
 static jmethodID getBasePointerID;
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index d9d3cdf..72e3d349 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -27,9 +27,12 @@
     return android::GraphicsEnv::getInstance().getCanLoadSystemLibraries();
 }
 
-void setDriverPath(JNIEnv* env, jobject clazz, jstring path) {
+void setDriverPathAndSphalLibraries_native(JNIEnv* env, jobject clazz, jstring path,
+                                           jstring sphalLibraries) {
     ScopedUtfChars pathChars(env, path);
-    android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
+    ScopedUtfChars sphalLibrariesChars(env, sphalLibraries);
+    android::GraphicsEnv::getInstance().setDriverPathAndSphalLibraries(pathChars.c_str(),
+                                                                       sphalLibrariesChars.c_str());
 }
 
 void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName,
@@ -84,7 +87,7 @@
 
 const JNINativeMethod g_methods[] = {
     { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) },
-    { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
+    { "setDriverPathAndSphalLibraries", "(Ljava/lang/String;Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPathAndSphalLibraries_native) },
     { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JJLjava/lang/String;)V", reinterpret_cast<void*>(setGpuStats_native) },
     { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) },
     { "getShouldUseAngle", "(Ljava/lang/String;)Z", reinterpret_cast<void*>(shouldUseAngle_native) },
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index a212f47..af2d413 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -343,7 +343,9 @@
   assetmanager->ForEachPackage([&](const std::string& this_package_name, uint8_t package_id) {
     if (this_package_name == std_package_name) {
       map = assetmanager->GetOverlayableMapForPackage(package_id);
+      return false;
     }
+    return true;
   });
 
   if (map == nullptr) {
@@ -521,15 +523,16 @@
     return nullptr;
   }
 
-  assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) {
+  assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) -> bool {
     jstring jpackage_name = env->NewStringUTF(package_name.c_str());
     if (jpackage_name == nullptr) {
       // An exception is pending.
-      return;
+      return false;
     }
 
     env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id),
                         jpackage_name);
+    return true;
   });
   return sparse_array;
 }
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 191472d..8d702d1 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -41,6 +41,7 @@
 
     jmethodID dispatchVsync;
     jmethodID dispatchHotplug;
+    jmethodID dispatchConfigChanged;
 } gDisplayEventReceiverClassInfo;
 
 
@@ -61,6 +62,8 @@
 
     void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
     void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+    void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+                               int32_t configId) override;
 };
 
 
@@ -114,6 +117,23 @@
     mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
 }
 
+void NativeDisplayEventReceiver::dispatchConfigChanged(nsecs_t timestamp,
+                                                       PhysicalDisplayId displayId,
+                                                       int32_t configId) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+    ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
+    if (receiverObj.get()) {
+        ALOGV("receiver %p ~ Invoking config changed handler.", this);
+        env->CallVoidMethod(receiverObj.get(),
+                            gDisplayEventReceiverClassInfo.dispatchConfigChanged,
+                            timestamp, displayId, configId);
+        ALOGV("receiver %p ~ Returned from config changed handler.", this);
+    }
+
+    mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
+}
+
 
 static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
         jobject messageQueueObj, jint vsyncSource) {
@@ -180,6 +200,8 @@
             gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
     gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
             gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
+    gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env,
+           gDisplayEventReceiverClassInfo.clazz, "dispatchConfigChanged", "(JJI)V");
 
     return res;
 }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 6b8d8b1..4c25fd4 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -205,7 +205,7 @@
 
 static jobject nativeScreenshot(JNIEnv* env, jclass clazz,
         jobject displayTokenObj, jobject sourceCropObj, jint width, jint height,
-        bool useIdentityTransform, int rotation) {
+        bool useIdentityTransform, int rotation, bool captureSecureLayers) {
     sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj);
     if (displayToken == NULL) {
         return NULL;
@@ -213,9 +213,9 @@
     Rect sourceCrop = rectFromObj(env, sourceCropObj);
     sp<GraphicBuffer> buffer;
     status_t res = ScreenshotClient::capture(displayToken, ui::Dataspace::V0_SRGB,
-                                             ui::PixelFormat::RGBA_8888,
-                                             sourceCrop, width, height,
-                                             useIdentityTransform, rotation, &buffer);
+            ui::PixelFormat::RGBA_8888,
+            sourceCrop, width, height,
+            useIdentityTransform, rotation, captureSecureLayers, &buffer);
     if (res != NO_ERROR) {
         return NULL;
     }
@@ -406,6 +406,11 @@
     transaction->transferTouchFocus(fromToken, toToken);
 }
 
+static void nativeSyncInputWindows(JNIEnv* env, jclass clazz, jlong transactionObj) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    transaction->syncInputWindows();
+}
+
 static void nativeSetMetadata(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject, jint id, jobject parcelObj) {
     Parcel* parcel = parcelForJavaObject(env, parcelObj);
@@ -693,6 +698,25 @@
     return configArray;
 }
 
+static jboolean nativeSetAllowedDisplayConfigs(JNIEnv* env, jclass clazz,
+        jobject tokenObj, jintArray configArray) {
+    sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+    if (token == nullptr) return JNI_FALSE;
+
+    std::vector<int32_t> allowedConfigs;
+    jsize configArraySize = env->GetArrayLength(configArray);
+    allowedConfigs.reserve(configArraySize);
+
+    jint* configArrayElements = env->GetIntArrayElements(configArray, 0);
+    for (int i = 0; i < configArraySize; i++) {
+        allowedConfigs.push_back(configArrayElements[i]);
+    }
+    env->ReleaseIntArrayElements(configArray, configArrayElements, 0);
+
+    size_t result = SurfaceComposerClient::setAllowedDisplayConfigs(token, allowedConfigs);
+    return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
+}
+
 static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return -1;
@@ -1189,6 +1213,8 @@
             (void*)nativeGetActiveConfig },
     {"nativeSetActiveConfig", "(Landroid/os/IBinder;I)Z",
             (void*)nativeSetActiveConfig },
+    {"nativeSetAllowedDisplayConfigs", "(Landroid/os/IBinder;[I)Z",
+            (void*)nativeSetAllowedDisplayConfigs },
     {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I",
             (void*)nativeGetDisplayColorModes},
     {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;",
@@ -1227,7 +1253,7 @@
             (void*)nativeSetOverrideScalingMode },
     {"nativeGetHandle", "(J)Landroid/os/IBinder;",
             (void*)nativeGetHandle },
-    {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIZI)Landroid/graphics/GraphicBuffer;",
+    {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIZIZ)Landroid/graphics/GraphicBuffer;",
             (void*)nativeScreenshot },
     {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;",
             (void*)nativeCaptureLayers },
@@ -1246,7 +1272,9 @@
             "(Landroid/os/IBinder;JJ)Landroid/hardware/display/DisplayedContentSample;",
             (void*)nativeGetDisplayedContentSample },
     {"nativeSetGeometry", "(JJLandroid/graphics/Rect;Landroid/graphics/Rect;J)V",
-            (void*)nativeSetGeometry }
+            (void*)nativeSetGeometry },
+    {"nativeSyncInputWindows", "(J)V",
+            (void*)nativeSyncInputWindows }
 };
 
 int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 15ceca9..2ccb01a 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -155,10 +155,10 @@
 static int gBlastulaPoolEventFD = -1;
 
 /**
- * The maximum value that the gBlastulaPoolMax variable may take.  This value
- * is a mirror of Zygote.BLASTULA_POOL_MAX_LIMIT
+ * The maximum value that the gBlastulaPoolSizeMax variable may take.  This value
+ * is a mirror of ZygoteServer.BLASTULA_POOL_SIZE_MAX_LIMIT
  */
-static constexpr int BLASTULA_POOL_MAX_LIMIT = 10;
+static constexpr int BLASTULA_POOL_SIZE_MAX_LIMIT = 100;
 
 /**
  * A helper class containing accounting information for Blastulas.
@@ -216,6 +216,19 @@
     }
   }
 
+  void Clear() {
+    EntryStorage storage = mStorage.load();
+
+    if (storage != INVALID_ENTRY_VALUE) {
+      close(storage.read_pipe_fd);
+      mStorage.store(INVALID_ENTRY_VALUE);
+    }
+  }
+
+  void Invalidate() {
+    mStorage.store(INVALID_ENTRY_VALUE);
+  }
+
   /**
    * @return A copy of the data stored in this entry.
    */
@@ -257,7 +270,7 @@
  * the BlastulaTableEntry class prevent data races during these concurrent
  * operations.
  */
-static std::array<BlastulaTableEntry, BLASTULA_POOL_MAX_LIMIT> gBlastulaTable;
+static std::array<BlastulaTableEntry, BLASTULA_POOL_SIZE_MAX_LIMIT> gBlastulaTable;
 
 /**
  * The list of open zygote file descriptors.
@@ -572,214 +585,255 @@
 }
 
 static int UnmountTree(const char* path) {
-    size_t path_len = strlen(path);
+  size_t path_len = strlen(path);
 
-    FILE* fp = setmntent("/proc/mounts", "r");
-    if (fp == nullptr) {
-        ALOGE("Error opening /proc/mounts: %s", strerror(errno));
-        return -errno;
-    }
+  FILE* fp = setmntent("/proc/mounts", "r");
+  if (fp == nullptr) {
+    ALOGE("Error opening /proc/mounts: %s", strerror(errno));
+    return -errno;
+  }
 
-    // Some volumes can be stacked on each other, so force unmount in
-    // reverse order to give us the best chance of success.
-    std::list<std::string> toUnmount;
-    mntent* mentry;
-    while ((mentry = getmntent(fp)) != nullptr) {
-        if (strncmp(mentry->mnt_dir, path, path_len) == 0) {
-            toUnmount.push_front(std::string(mentry->mnt_dir));
-        }
+  // Some volumes can be stacked on each other, so force unmount in
+  // reverse order to give us the best chance of success.
+  std::list<std::string> to_unmount;
+  mntent* mentry;
+  while ((mentry = getmntent(fp)) != nullptr) {
+    if (strncmp(mentry->mnt_dir, path, path_len) == 0) {
+      to_unmount.push_front(std::string(mentry->mnt_dir));
     }
-    endmntent(fp);
+  }
+  endmntent(fp);
 
-    for (const auto& path : toUnmount) {
-        if (umount2(path.c_str(), MNT_DETACH)) {
-            ALOGW("Failed to unmount %s: %s", path.c_str(), strerror(errno));
-        }
+  for (const auto& path : to_unmount) {
+    if (umount2(path.c_str(), MNT_DETACH)) {
+      ALOGW("Failed to unmount %s: %s", path.c_str(), strerror(errno));
     }
-    return 0;
+  }
+  return 0;
 }
 
 static void CreateDir(const std::string& dir,
                       mode_t mode, uid_t uid, gid_t gid,
                       fail_fn_t fail_fn) {
-    if (TEMP_FAILURE_RETRY(access(dir.c_str(), F_OK)) == 0) {
-        return;
-    } else if (errno != ENOENT) {
-        fail_fn(CREATE_ERROR("Failed to stat %s: %s", dir.c_str(), strerror(errno)));
-    }
-    if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) {
-        fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s: %s",
-                dir.c_str(), strerror(errno)));
-    }
+  if (TEMP_FAILURE_RETRY(access(dir.c_str(), F_OK)) == 0) {
+    return;
+  } else if (errno != ENOENT) {
+    fail_fn(CREATE_ERROR("Failed to stat %s: %s", dir.c_str(), strerror(errno)));
+  }
+  if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) {
+    fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s: %s",
+                         dir.c_str(), strerror(errno)));
+  }
 }
 
-static void CreatePkgSandboxTarget(uid_t uid, const std::string& package_name, fail_fn_t fail_fn) {
-    // Create /mnt/user/0/package/<package-name>
-    userid_t user_id = multiuser_get_user_id(uid);
-    std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id);
-    CreateDir(pkg_sandbox_dir, 0751, AID_ROOT, AID_ROOT, fail_fn);
+static void CreatePkgSandboxTarget(userid_t user_id, fail_fn_t fail_fn) {
+  // Create /mnt/user/0/package
+  std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id);
+  CreateDir(pkg_sandbox_dir, 0751, AID_ROOT, AID_ROOT, fail_fn);
 
-    StringAppendF(&pkg_sandbox_dir, "/package");
-    CreateDir(pkg_sandbox_dir, 0700, AID_ROOT, AID_ROOT, fail_fn);
-
-    StringAppendF(&pkg_sandbox_dir, "/%s", package_name.c_str());
-    CreateDir(pkg_sandbox_dir, 0755, uid, uid, fail_fn);
+  StringAppendF(&pkg_sandbox_dir, "/package");
+  CreateDir(pkg_sandbox_dir, 0755, AID_ROOT, AID_ROOT, fail_fn);
 }
 
-static void BindMount(const std::string& sourceDir, const std::string& targetDir,
+static void BindMount(const std::string& source_dir, const std::string& target_dir,
                       fail_fn_t fail_fn) {
-    if (TEMP_FAILURE_RETRY(mount(sourceDir.c_str(), targetDir.c_str(), nullptr,
-                                 MS_BIND, nullptr)) == -1) {
-        fail_fn(CREATE_ERROR("Failed to mount %s to %s: %s",
-                             sourceDir.c_str(), targetDir.c_str(), strerror(errno)));
-    }
+  if (TEMP_FAILURE_RETRY(mount(source_dir.c_str(), target_dir.c_str(), nullptr,
+                               MS_BIND, nullptr)) == -1) {
+    fail_fn(CREATE_ERROR("Failed to mount %s to %s: %s",
+                         source_dir.c_str(), target_dir.c_str(), strerror(errno)));
+  }
 }
 
-static void MountPkgSpecificDir(const std::string& mntSourceRoot,
-                                const std::string& mntTargetRoot,
-                                const std::string& packageName,
+static void MountPkgSpecificDir(const std::string& mnt_source_root,
+                                const std::string& mnt_target_root,
+                                const std::string& package_name,
                                 uid_t uid,
-                                const char* dirName,
+                                const char* dir_name,
                                 fail_fn_t fail_fn) {
-    std::string mntSourceDir = StringPrintf("%s/Android/%s/%s",
-            mntSourceRoot.c_str(), dirName, packageName.c_str());
+  std::string mnt_source_dir = StringPrintf("%s/Android/%s/%s",
+      mnt_source_root.c_str(), dir_name, package_name.c_str());
 
-    std::string mntTargetDir = StringPrintf("%s/Android/%s/%s",
-            mntTargetRoot.c_str(), dirName, packageName.c_str());
+  std::string mnt_target_dir = StringPrintf("%s/Android/%s/%s",
+      mnt_target_root.c_str(), dir_name, package_name.c_str());
 
-    BindMount(mntSourceDir, mntTargetDir, fail_fn);
+  BindMount(mnt_source_dir, mnt_target_dir, fail_fn);
 }
 
-static void CreateSubDirs(int dirfd, const std::string& parentDirPath,
-                          const std::vector<std::string>& subDirs,
+static void CreateSubDirs(int parent_fd, const std::string& parent_path,
+                          const std::vector<std::string>& sub_dirs,
                           fail_fn_t fail_fn) {
-    for (auto& dirName : subDirs) {
-        struct stat sb;
-        if (TEMP_FAILURE_RETRY(fstatat(dirfd, dirName.c_str(), &sb, 0)) == 0) {
-            if (S_ISDIR(sb.st_mode)) {
-                continue;
-            } else if (TEMP_FAILURE_RETRY(unlinkat(dirfd, dirName.c_str(), 0)) == -1) {
-                fail_fn(CREATE_ERROR("Failed to unlinkat on %s/%s: %s",
-                        parentDirPath.c_str(), dirName.c_str(), strerror(errno)));
-            }
-        } else if (errno != ENOENT) {
-            fail_fn(CREATE_ERROR("Failed to fstatat on %s/%s: %s",
-                    parentDirPath.c_str(), dirName.c_str(), strerror(errno)));
-        }
-        if (TEMP_FAILURE_RETRY(mkdirat(dirfd, dirName.c_str(), 0700)) == -1 && errno != EEXIST) {
-            fail_fn(CREATE_ERROR("Failed to mkdirat on %s/%s: %s",
-                    parentDirPath.c_str(), dirName.c_str(), strerror(errno)));
-        }
+  for (auto& dir_name : sub_dirs) {
+    struct stat sb;
+    if (TEMP_FAILURE_RETRY(fstatat(parent_fd, dir_name.c_str(), &sb, 0)) == 0) {
+      if (S_ISDIR(sb.st_mode)) {
+        continue;
+      } else if (TEMP_FAILURE_RETRY(unlinkat(parent_fd, dir_name.c_str(), 0)) == -1) {
+        fail_fn(CREATE_ERROR("Failed to unlinkat on %s/%s: %s",
+                             parent_path.c_str(), dir_name.c_str(), strerror(errno)));
+      }
+    } else if (errno != ENOENT) {
+      fail_fn(CREATE_ERROR("Failed to fstatat on %s/%s: %s",
+                           parent_path.c_str(), dir_name.c_str(), strerror(errno)));
     }
+    if (TEMP_FAILURE_RETRY(mkdirat(parent_fd, dir_name.c_str(), 0700)) == -1 && errno != EEXIST) {
+      fail_fn(CREATE_ERROR("Failed to mkdirat on %s/%s: %s",
+                           parent_path.c_str(), dir_name.c_str(), strerror(errno)));
+    }
+  }
 }
 
 static void EnsurePkgSpecificDirs(const std::string& path,
-                                  const std::vector<std::string>& packageNames,
-                                  bool createSandboxDir,
+                                  const std::vector<std::string>& package_names,
+                                  bool create_sandbox_dir,
                                   fail_fn_t fail_fn) {
-    std::string androidDir = StringPrintf("%s/Android", path.c_str());
-    android::base::unique_fd androidFd(
-            open(androidDir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
-    if (androidFd.get() < 0) {
-        if (errno == ENOENT || errno == ENOTDIR) {
-            if (errno == ENOTDIR && TEMP_FAILURE_RETRY(unlink(androidDir.c_str())) == -1) {
-                fail_fn(CREATE_ERROR("Failed to unlink %s: %s",
-                        androidDir.c_str(), strerror(errno)));
-            }
-            if (TEMP_FAILURE_RETRY(mkdir(androidDir.c_str(), 0700)) == -1
-                    && errno != EEXIST) {
-                fail_fn(CREATE_ERROR("Failed to mkdir %s: %s",
-                        androidDir.c_str(), strerror(errno)));
-            }
-            androidFd.reset(open(androidDir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
-        }
-
-        if (androidFd.get() < 0) {
-            fail_fn(CREATE_ERROR("Failed to open %s: %s", androidDir.c_str(), strerror(errno)));
-        }
-    }
-
-    std::vector<std::string> dataMediaObbDirs = {"data", "media", "obb"};
-    if (createSandboxDir) {
-        dataMediaObbDirs.push_back("sandbox");
-    }
-    CreateSubDirs(androidFd.get(), androidDir, dataMediaObbDirs, fail_fn);
-    if (createSandboxDir) {
-        dataMediaObbDirs.pop_back();
-    }
-    for (auto& dirName : dataMediaObbDirs) {
-        std::string dataDir = StringPrintf("%s/%s", androidDir.c_str(), dirName.c_str());
-        android::base::unique_fd dataFd(
-                openat(androidFd, dirName.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
-        if (dataFd.get() < 0) {
-            fail_fn(CREATE_ERROR("Failed to openat %s/%s: %s",
-                    androidDir.c_str(), dirName.c_str(), strerror(errno)));
-        }
-        CreateSubDirs(dataFd.get(), dataDir, packageNames, fail_fn);
-    }
-}
-
-static void CreatePkgSandboxSource(const std::string& sandboxSource, fail_fn_t fail_fn) {
-
-    struct stat sb;
-    if (TEMP_FAILURE_RETRY(stat(sandboxSource.c_str(), &sb)) == 0) {
-        if (S_ISDIR(sb.st_mode)) {
-            return;
-        } else if (TEMP_FAILURE_RETRY(unlink(sandboxSource.c_str())) == -1) {
-            fail_fn(CREATE_ERROR("Failed to unlink %s: %s",
-                    sandboxSource.c_str(), strerror(errno)));
-        }
-    } else if (errno != ENOENT) {
-        fail_fn(CREATE_ERROR("Failed to stat %s: %s",
-                sandboxSource.c_str(), strerror(errno)));
-    }
-    if (TEMP_FAILURE_RETRY(mkdir(sandboxSource.c_str(), 0700)) == -1 && errno != EEXIST) {
+  std::string android_dir = StringPrintf("%s/Android", path.c_str());
+  android::base::unique_fd android_fd(open(android_dir.c_str(),
+                                           O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+  if (android_fd.get() < 0) {
+    if (errno == ENOENT || errno == ENOTDIR) {
+      if (errno == ENOTDIR && TEMP_FAILURE_RETRY(unlink(android_dir.c_str())) == -1) {
+        fail_fn(CREATE_ERROR("Failed to unlink %s: %s",
+                             android_dir.c_str(), strerror(errno)));
+      }
+      if (TEMP_FAILURE_RETRY(mkdir(android_dir.c_str(), 0700)) == -1
+          && errno != EEXIST) {
         fail_fn(CREATE_ERROR("Failed to mkdir %s: %s",
-                sandboxSource.c_str(), strerror(errno)));
+                             android_dir.c_str(), strerror(errno)));
+      }
+      android_fd.reset(open(android_dir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
     }
+
+    if (android_fd.get() < 0) {
+      fail_fn(CREATE_ERROR("Failed to open %s: %s", android_dir.c_str(), strerror(errno)));
+    }
+  }
+
+  std::vector<std::string> data_media_obb_dirs = {"data", "media", "obb"};
+  if (create_sandbox_dir) {
+    data_media_obb_dirs.push_back("sandbox");
+  }
+  CreateSubDirs(android_fd.get(), android_dir, data_media_obb_dirs, fail_fn);
+  if (create_sandbox_dir) {
+    data_media_obb_dirs.pop_back();
+  }
+  for (auto& dir_name : data_media_obb_dirs) {
+    std::string data_dir = StringPrintf("%s/%s", android_dir.c_str(), dir_name.c_str());
+    android::base::unique_fd data_fd(openat(android_fd, dir_name.c_str(),
+                                            O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+    if (data_fd.get() < 0) {
+      fail_fn(CREATE_ERROR("Failed to openat %s/%s: %s",
+                           android_dir.c_str(), dir_name.c_str(), strerror(errno)));
+    }
+    CreateSubDirs(data_fd.get(), data_dir, package_names, fail_fn);
+  }
 }
 
-static void PreparePkgSpecificDirs(const std::vector<std::string>& packageNames,
-                                   const std::vector<std::string>& volumeLabels,
-                                   bool mountAllObbs, const std::string& sandboxId,
-                                   userid_t userId, uid_t uid, fail_fn_t fail_fn) {
-    for (auto& label : volumeLabels) {
-        std::string mntSource = StringPrintf("/mnt/runtime/write/%s", label.c_str());
-        std::string mntTarget = StringPrintf("/storage/%s", label.c_str());
-        if (label == "emulated") {
-            StringAppendF(&mntSource, "/%d", userId);
-            StringAppendF(&mntTarget, "/%d", userId);
-        }
+static void CreatePkgSandboxSource(const std::string& sandbox_source, fail_fn_t fail_fn) {
 
-        if (TEMP_FAILURE_RETRY(access(mntSource.c_str(), F_OK)) == -1) {
-            ALOGE("Can't access %s: %s", mntSource.c_str(), strerror(errno));
-            continue;
-        }
-
-        // Ensure /mnt/runtime/write/emulated/0/Android/{data,media,obb}
-        EnsurePkgSpecificDirs(mntSource, packageNames, true, fail_fn);
-
-        std::string sandboxSource = StringPrintf("%s/Android/sandbox/%s",
-            mntSource.c_str(), sandboxId.c_str());
-        CreatePkgSandboxSource(sandboxSource, fail_fn);
-        BindMount(sandboxSource, mntTarget, fail_fn);
-
-        // Ensure /storage/emulated/0/Android/{data,media,obb}
-        EnsurePkgSpecificDirs(mntTarget, packageNames, false, fail_fn);
-        for (auto& package : packageNames) {
-            MountPkgSpecificDir(mntSource, mntTarget, package, uid, "data", fail_fn);
-            MountPkgSpecificDir(mntSource, mntTarget, package, uid, "media", fail_fn);
-            if (!mountAllObbs) {
-                MountPkgSpecificDir(mntSource, mntTarget, package, uid, "obb", fail_fn);
-            }
-        }
-
-        if (mountAllObbs) {
-            StringAppendF(&mntSource, "/Android/obb");
-            StringAppendF(&mntTarget, "/Android/obb");
-            BindMount(mntSource, mntTarget, fail_fn);
-        }
+  struct stat sb;
+  if (TEMP_FAILURE_RETRY(stat(sandbox_source.c_str(), &sb)) == 0) {
+    if (S_ISDIR(sb.st_mode)) {
+      return;
+    } else if (TEMP_FAILURE_RETRY(unlink(sandbox_source.c_str())) == -1) {
+      fail_fn(CREATE_ERROR("Failed to unlink %s: %s",
+                           sandbox_source.c_str(), strerror(errno)));
     }
+  } else if (errno != ENOENT) {
+    fail_fn(CREATE_ERROR("Failed to stat %s: %s",
+                         sandbox_source.c_str(), strerror(errno)));
+  }
+  if (TEMP_FAILURE_RETRY(mkdir(sandbox_source.c_str(), 0700)) == -1 && errno != EEXIST) {
+    fail_fn(CREATE_ERROR("Failed to mkdir %s: %s",
+                         sandbox_source.c_str(), strerror(errno)));
+  }
+}
+
+static void PreparePkgSpecificDirs(const std::vector<std::string>& package_names,
+                                   bool mount_all_obbs, const std::string& sandbox_id,
+                                   userid_t user_id, uid_t uid, fail_fn_t fail_fn) {
+  std::unique_ptr<DIR, decltype(&closedir)> dirp(opendir("/storage"), closedir);
+  if (!dirp) {
+    fail_fn(CREATE_ERROR("Failed to opendir /storage: %s", strerror(errno)));
+  }
+  struct dirent* ent;
+  while ((ent = readdir(dirp.get()))) {
+    if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "self")) {
+      continue;
+    }
+    std::string label(ent->d_name);
+
+    std::string mnt_source = StringPrintf("/mnt/runtime/write/%s", label.c_str());
+    std::string mnt_target = StringPrintf("/storage/%s", label.c_str());
+    if (label == "emulated") {
+      StringAppendF(&mnt_source, "/%d", user_id);
+      StringAppendF(&mnt_target, "/%d", user_id);
+    }
+
+    if (TEMP_FAILURE_RETRY(access(mnt_source.c_str(), F_OK)) == -1) {
+      ALOGE("Can't access %s: %s", mnt_source.c_str(), strerror(errno));
+      continue;
+    } else if (TEMP_FAILURE_RETRY(access(mnt_target.c_str(), F_OK)) == -1) {
+      ALOGE("Can't access %s: %s", mnt_target.c_str(), strerror(errno));
+      continue;
+    }
+
+    // Ensure /mnt/runtime/write/emulated/0/Android/{data,media,obb}
+    EnsurePkgSpecificDirs(mnt_source, package_names, true, fail_fn);
+
+    std::string sandbox_source = StringPrintf("%s/Android/sandbox/%s",
+        mnt_source.c_str(), sandbox_id.c_str());
+    CreatePkgSandboxSource(sandbox_source, fail_fn);
+    BindMount(sandbox_source, mnt_target, fail_fn);
+
+    // Ensure /storage/emulated/0/Android/{data,media,obb}
+    EnsurePkgSpecificDirs(mnt_target, package_names, false, fail_fn);
+    for (auto& package : package_names) {
+      MountPkgSpecificDir(mnt_source, mnt_target, package, uid, "data", fail_fn);
+      MountPkgSpecificDir(mnt_source, mnt_target, package, uid, "media", fail_fn);
+      if (!mount_all_obbs) {
+        MountPkgSpecificDir(mnt_source, mnt_target, package, uid, "obb", fail_fn);
+      }
+    }
+
+    if (mount_all_obbs) {
+      StringAppendF(&mnt_source, "/Android/obb");
+      StringAppendF(&mnt_target, "/Android/obb");
+      BindMount(mnt_source, mnt_target, fail_fn);
+    }
+  }
+}
+
+static void HandleMountModeInstaller(int mount_mode,
+                                     userid_t user_id,
+                                     const std::string& sandbox_id,
+                                     fail_fn_t fail_fn) {
+  std::string obb_mount_dir = StringPrintf("/mnt/user/%d/obb_mount", user_id);
+  std::string obb_mount_file = StringPrintf("%s/%s", obb_mount_dir.c_str(), sandbox_id.c_str());
+  if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
+    if (TEMP_FAILURE_RETRY(access(obb_mount_file.c_str(), F_OK)) != -1) {
+      return;
+    } else if (errno != ENOENT) {
+      fail_fn(CREATE_ERROR("Failed to access %s: %s", obb_mount_file.c_str(), strerror(errno)));
+    }
+    if (fs_prepare_dir(obb_mount_dir.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
+      fail_fn(CREATE_ERROR("Failed to fs_prepare_dir %s: %s",
+                           obb_mount_dir.c_str(), strerror(errno)));
+    }
+    const android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+        open(obb_mount_file.c_str(), O_RDWR | O_CREAT, 0600)));
+    if (fd.get() < 0) {
+      fail_fn(CREATE_ERROR("Failed to create %s: %s", obb_mount_file.c_str(), strerror(errno)));
+    }
+  } else {
+    if (TEMP_FAILURE_RETRY(access(obb_mount_file.c_str(), F_OK)) != -1) {
+      if (TEMP_FAILURE_RETRY(unlink(obb_mount_file.c_str())) == -1) {
+        fail_fn(CREATE_ERROR("Failed to unlink %s: %s",
+                             obb_mount_dir.c_str(), strerror(errno)));
+      }
+    } else if (errno != ENOENT) {
+      fail_fn(CREATE_ERROR("Failed to access %s: %s", obb_mount_file.c_str(), strerror(errno)));
+    }
+  }
 }
 
 // Create a private mount namespace and bind mount appropriate emulated
@@ -787,128 +841,99 @@
 static void MountEmulatedStorage(uid_t uid, jint mount_mode,
         bool force_mount_namespace, const std::string& package_name,
         const std::vector<std::string>& packages_for_uid,
-        const std::vector<std::string>& visible_vol_ids, const std::string& sandbox_id,
+        const std::string& sandbox_id,
         fail_fn_t fail_fn) {
-    // See storage config details at http://source.android.com/tech/storage/
+  // See storage config details at http://source.android.com/tech/storage/
 
-    String8 storageSource;
-    if (mount_mode == MOUNT_EXTERNAL_DEFAULT) {
-        storageSource = "/mnt/runtime/default";
-    } else if (mount_mode == MOUNT_EXTERNAL_READ) {
-        storageSource = "/mnt/runtime/read";
-    } else if (mount_mode == MOUNT_EXTERNAL_WRITE) {
-        storageSource = "/mnt/runtime/write";
-    } else if (mount_mode == MOUNT_EXTERNAL_NONE && !force_mount_namespace) {
-        // Sane default of no storage visible
-        return;
-    }
+  String8 storage_source;
+  if (mount_mode == MOUNT_EXTERNAL_DEFAULT) {
+    storage_source = "/mnt/runtime/default";
+  } else if (mount_mode == MOUNT_EXTERNAL_READ) {
+    storage_source = "/mnt/runtime/read";
+  } else if (mount_mode == MOUNT_EXTERNAL_WRITE) {
+    storage_source = "/mnt/runtime/write";
+  } else if (mount_mode == MOUNT_EXTERNAL_NONE && !force_mount_namespace) {
+    // Sane default of no storage visible
+    return;
+  }
 
-    // Create a second private mount namespace for our process
-    if (unshare(CLONE_NEWNS) == -1) {
-        fail_fn(CREATE_ERROR("Failed to unshare(): %s", strerror(errno)));
-    }
+  // Create a second private mount namespace for our process
+  if (unshare(CLONE_NEWNS) == -1) {
+    fail_fn(CREATE_ERROR("Failed to unshare(): %s", strerror(errno)));
+  }
 
-    // Handle force_mount_namespace with MOUNT_EXTERNAL_NONE.
-    if (mount_mode == MOUNT_EXTERNAL_NONE) {
-        return;
-    }
+  // Handle force_mount_namespace with MOUNT_EXTERNAL_NONE.
+  if (mount_mode == MOUNT_EXTERNAL_NONE) {
+    return;
+  }
 
-    if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, true))) {
-        if (mount_mode == MOUNT_EXTERNAL_FULL || mount_mode == MOUNT_EXTERNAL_LEGACY) {
-            storageSource = (mount_mode == MOUNT_EXTERNAL_FULL)
-                    ? "/mnt/runtime/full" : "/mnt/runtime/write";
-            if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
-                                         NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
-                fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s",
-                                     storageSource.string(),
-                                     strerror(errno)));
-            }
+  if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, true))) {
+    if (mount_mode == MOUNT_EXTERNAL_FULL || mount_mode == MOUNT_EXTERNAL_LEGACY) {
+      storage_source = (mount_mode == MOUNT_EXTERNAL_FULL)
+          ? "/mnt/runtime/full" : "/mnt/runtime/write";
+      if (TEMP_FAILURE_RETRY(mount(storage_source.string(), "/storage",
+                                   NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
+        fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s",
+                             storage_source.string(),
+                             strerror(errno)));
+      }
 
-            // Mount user-specific symlink helper into place
-            userid_t user_id = multiuser_get_user_id(uid);
-            const String8 userSource(String8::format("/mnt/user/%d", user_id));
-            if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
-                fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s (%s)",
-                                     userSource.string(), strerror(errno)));
-            }
+      // Mount user-specific symlink helper into place
+      userid_t user_id = multiuser_get_user_id(uid);
+      const String8 user_source(String8::format("/mnt/user/%d", user_id));
+      if (fs_prepare_dir(user_source.string(), 0751, 0, 0) == -1) {
+        fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s (%s)",
+                             user_source.string(), strerror(errno)));
+      }
 
-            if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self", nullptr, MS_BIND,
-                                         nullptr)) == -1) {
-                fail_fn(CREATE_ERROR("Failed to mount %s to /storage/self: %s",
-                                     userSource.string(),
-                                     strerror(errno)));
-            }
-        } else {
-            if (package_name.empty() || sandbox_id.empty()) {
-                return;
-            }
-
-            userid_t user_id = multiuser_get_user_id(uid);
-            std::string pkgSandboxDir =
-                StringPrintf("/mnt/user/%d/package/%s", user_id, package_name.c_str());
-            bool sandboxAlreadyCreated = true;
-            if (TEMP_FAILURE_RETRY(access(pkgSandboxDir.c_str(), F_OK)) == -1) {
-                if (errno == ENOENT) {
-                    ALOGD("Sandbox not yet created for %s", pkgSandboxDir.c_str());
-                    sandboxAlreadyCreated = false;
-                    CreatePkgSandboxTarget(uid, package_name, fail_fn);
-                } else {
-                    fail_fn(CREATE_ERROR("Failed to access %s: %s",
-                                         pkgSandboxDir.c_str(), strerror(errno)));
-                }
-            }
-
-            if (TEMP_FAILURE_RETRY(mount(pkgSandboxDir.c_str(), "/storage",
-                                         nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
-                fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s",
-                                     pkgSandboxDir.c_str(), strerror(errno)));
-            }
-
-            if (TEMP_FAILURE_RETRY(access("/storage/obb_mount", F_OK)) == 0) {
-                if (mount_mode != MOUNT_EXTERNAL_INSTALLER) {
-                    remove("/storage/obb_mount");
-                }
-            } else {
-                if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
-                    int fd =
-                        TEMP_FAILURE_RETRY(open("/storage/obb_mount", O_RDWR | O_CREAT, 0660));
-                    if (fd == -1) {
-                        fail_fn(CREATE_ERROR("Couldn't create /storage/obb_mount: %s",
-                                             strerror(errno)));
-                    }
-                    close(fd);
-                }
-            }
-            // If the sandbox was already created by vold, only then set up the bind mounts for
-            // pkg specific directories. Otherwise, leave as is and bind mounts will be taken
-            // care of by vold later.
-            if (sandboxAlreadyCreated) {
-                PreparePkgSpecificDirs(packages_for_uid, visible_vol_ids,
-                    mount_mode == MOUNT_EXTERNAL_INSTALLER, sandbox_id, user_id, uid, fail_fn);
-            }
-        }
+      if (TEMP_FAILURE_RETRY(mount(user_source.string(), "/storage/self", nullptr, MS_BIND,
+                                   nullptr)) == -1) {
+        fail_fn(CREATE_ERROR("Failed to mount %s to /storage/self: %s",
+                             user_source.string(),
+                             strerror(errno)));
+      }
     } else {
-        if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage", nullptr,
-                                     MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
-            fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s",
-                                 storageSource.string(),
-                                 strerror(errno)));
-        }
+      if (package_name.empty() || sandbox_id.empty()) {
+        return;
+      }
 
-        // Mount user-specific symlink helper into place
-        userid_t user_id = multiuser_get_user_id(uid);
-        const String8 userSource(String8::format("/mnt/user/%d", user_id));
-        if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
-          fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s",
-                               userSource.string()));
-        }
+      userid_t user_id = multiuser_get_user_id(uid);
+      CreatePkgSandboxTarget(user_id, fail_fn);
 
-        if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
-                               nullptr, MS_BIND, nullptr)) == -1) {
-          fail_fn(CREATE_ERROR("Failed to mount %s to /storage/self: %s",
-                               userSource.string(), strerror(errno)));
-        }
+      std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d/package", user_id);
+      if (TEMP_FAILURE_RETRY(mount(pkg_sandbox_dir.c_str(), "/storage",
+                                   nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
+        fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s",
+                             pkg_sandbox_dir.c_str(), strerror(errno)));
+      }
+
+      HandleMountModeInstaller(mount_mode, user_id, sandbox_id, fail_fn);
+
+      PreparePkgSpecificDirs(packages_for_uid,
+          mount_mode == MOUNT_EXTERNAL_INSTALLER, sandbox_id, user_id, uid, fail_fn);
     }
+  } else {
+    if (TEMP_FAILURE_RETRY(mount(storage_source.string(), "/storage", nullptr,
+                                 MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
+      fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s",
+                           storage_source.string(),
+                           strerror(errno)));
+    }
+
+    // Mount user-specific symlink helper into place
+    userid_t user_id = multiuser_get_user_id(uid);
+    const String8 user_source(String8::format("/mnt/user/%d", user_id));
+    if (fs_prepare_dir(user_source.string(), 0751, 0, 0) == -1) {
+      fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s",
+                           user_source.string()));
+    }
+
+    if (TEMP_FAILURE_RETRY(mount(user_source.string(), "/storage/self",
+                                 nullptr, MS_BIND, nullptr)) == -1) {
+      fail_fn(CREATE_ERROR("Failed to mount %s to /storage/self: %s",
+                           user_source.string(), strerror(errno)));
+    }
+  }
 }
 
 static bool NeedsNoRandomizeWorkaround() {
@@ -1156,6 +1181,14 @@
   }
 }
 
+static void ClearBlastulaTable() {
+  for (BlastulaTableEntry& entry : gBlastulaTable) {
+    entry.Clear();
+  }
+
+  gBlastulaPoolCount = 0;
+}
+
 // Utility routine to fork a process from the zygote.
 static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
                         const std::vector<int>& fds_to_close,
@@ -1198,12 +1231,17 @@
     // Clean up any descriptors which must be closed immediately
     DetachDescriptors(env, fds_to_close, fail_fn);
 
+    // Invalidate the entries in the blastula table.
+    ClearBlastulaTable();
+
     // Re-open all remaining open file descriptors so that they aren't shared
     // with the zygote across a fork.
     gOpenFdTable->ReopenOrDetach(fail_fn);
 
     // Turn fdsan back on.
     android_fdsan_set_error_level(fdsan_error_level);
+  } else {
+    ALOGD("Forked child process %d", pid);
   }
 
   // We blocked SIGCHLD prior to a fork, we unblock it here.
@@ -1221,7 +1259,7 @@
                              bool is_child_zygote, jstring managed_instruction_set,
                              jstring managed_app_data_dir, jstring managed_package_name,
                              jobjectArray managed_pacakges_for_uid,
-                             jobjectArray managed_visible_vol_ids, jstring managed_sandbox_id) {
+                             jstring managed_sandbox_id) {
   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);
@@ -1269,12 +1307,8 @@
       ExtractJStringArray(env, process_name, managed_nice_name, managed_pacakges_for_uid).
       value_or(std::vector<std::string>());
 
-  std::vector<std::string> visible_vol_ids =
-      ExtractJStringArray(env, process_name, managed_nice_name, managed_visible_vol_ids).
-      value_or(std::vector<std::string>());
-
   MountEmulatedStorage(uid, mount_external, use_native_bridge, package_name.value(),
-                       packages_for_uid, visible_vol_ids, sandbox_id.value_or(""), fail_fn);
+                       packages_for_uid, sandbox_id.value_or(""), fail_fn);
 
   // If this zygote isn't root, it won't be able to create a process group,
   // since the directory is owned by root.
@@ -1577,7 +1611,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, jstring package_name,
-        jobjectArray packages_for_uid, jobjectArray visible_vol_ids, jstring sandbox_id) {
+        jobjectArray packages_for_uid, jstring sandbox_id) {
     jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
 
     if (UNLIKELY(managed_fds_to_close == nullptr)) {
@@ -1609,7 +1643,7 @@
                        capabilities, capabilities,
                        mount_external, se_info, nice_name, false,
                        is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
-                       package_name, packages_for_uid, visible_vol_ids, sandbox_id);
+                       package_name, packages_for_uid, sandbox_id);
     }
     return pid;
 }
@@ -1635,7 +1669,7 @@
       SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
                        permitted_capabilities, effective_capabilities,
                        MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
-                       false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
+                       false, nullptr, nullptr, nullptr, nullptr, nullptr);
   } 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);
@@ -1789,7 +1823,7 @@
     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,
-    jstring package_name, jobjectArray packages_for_uid, jobjectArray visible_vol_ids,
+    jstring package_name, jobjectArray packages_for_uid,
     jstring sandbox_id) {
   jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
 
@@ -1797,7 +1831,7 @@
                    capabilities, capabilities,
                    mount_external, se_info, nice_name, false,
                    is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
-                   package_name, packages_for_uid, visible_vol_ids, sandbox_id);
+                   package_name, packages_for_uid, sandbox_id);
 }
 
 /**
@@ -1884,11 +1918,32 @@
   return gBlastulaPoolCount;
 }
 
+/**
+ * Kills all processes currently in the blastula pool.
+ *
+ * @param env  Managed runtime environment
+ * @return The number of blastulas currently in the blastula pool
+ */
+static void com_android_internal_os_Zygote_nativeEmptyBlastulaPool(JNIEnv* env, jclass) {
+  for (auto& entry : gBlastulaTable) {
+    auto entry_storage = entry.GetValues();
+
+    if (entry_storage.has_value()) {
+      kill(entry_storage.value().pid, SIGKILL);
+      close(entry_storage.value().read_pipe_fd);
+
+      // Avoid a second atomic load by invalidating instead of clearing.
+      entry.Invalidate();
+      --gBlastulaPoolCount;
+    }
+  }
+}
+
 static const JNINativeMethod gMethods[] = {
     { "nativeSecurityInit", "()V",
       (void *) com_android_internal_os_Zygote_nativeSecurityInit },
     { "nativeForkAndSpecialize",
-      "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)I",
+      "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)I",
       (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
     { "nativeForkSystemServer", "(II[II[[IJJ)I",
       (void *) com_android_internal_os_Zygote_nativeForkSystemServer },
@@ -1903,7 +1958,7 @@
     { "nativeForkBlastula", "(II[I)I",
       (void *) com_android_internal_os_Zygote_nativeForkBlastula },
     { "nativeSpecializeBlastula",
-      "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V",
+      "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V",
       (void *) com_android_internal_os_Zygote_nativeSpecializeBlastula },
     { "nativeGetSocketFDs", "(Z)V",
       (void *) com_android_internal_os_Zygote_nativeGetSocketFDs },
@@ -1914,7 +1969,9 @@
     { "nativeGetBlastulaPoolEventFD", "()I",
       (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolEventFD },
     { "nativeGetBlastulaPoolCount", "()I",
-      (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolCount }
+      (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolCount },
+    { "nativeEmptyBlastulaPool", "()V",
+      (void *) com_android_internal_os_Zygote_nativeEmptyBlastulaPool }
 };
 
 int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index 40ff7e4..c806162 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -65,8 +65,6 @@
         GLsizei stride, const GLvoid *pointer, GLsizei count);
 }
 
-static int initialized = 0;
-
 static jclass nioAccessClass;
 static jclass bufferClass;
 static jclass G11ImplClass;
@@ -4239,6 +4237,7 @@
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
+        return (jint)0;
     }
     return (jint)_returnValue;
 }
@@ -4308,6 +4307,7 @@
     }
     if (_exception) {
         jniThrowException(_env, _exceptionType, _exceptionMessage);
+        return (jint)0;
     }
     return (jint)_returnValue;
 }
@@ -4904,6 +4904,7 @@
   (JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) {
     jniThrowException(_env, "java/lang/UnsupportedOperationException",
         "glGetBufferParameteriv");
+    return;
 }
 
 /* void glGetBufferParameteriv ( GLenum target, GLenum pname, GLint *params ) */
@@ -4912,6 +4913,7 @@
   (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) {
     jniThrowException(_env, "java/lang/UnsupportedOperationException",
         "glGetBufferParameteriv");
+    return;
 }
 
 /* void glGetClipPlanef ( GLenum pname, GLfloat *eqn ) */
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 5497b86..efbe8ba 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -615,6 +615,40 @@
     // CATEGORY: SETTINGS
     // OS: Q
     ACTION_PANEL_INTERACTION = 1658;
+
+    // ACTION: Show Contextual homepage, log latency in loading cards
+    ACTION_CONTEXTUAL_HOME_SHOW = 1662;
+
+    // ACTION: Contextual card displays
+    ACTION_CONTEXTUAL_CARD_SHOW = 1663;
+
+    // ACTION: Contextual cards are eligible to be shown, but don't rank high
+    ACTION_CONTEXTUAL_CARD_NOT_SHOW = 1664;
+
+    // ACTION: Settings > long press a card, and click dismiss
+    // Contextual card is dismissed
+    ACTION_CONTEXTUAL_CARD_DISMISS = 1665;
+
+    // ACTION: Settings > click a card
+    // Contextual card is clicked
+    ACTION_CONTEXTUAL_CARD_CLICK = 1666;
+
+    // Mapping: go/at-mapping
+    ACTION_ATSG = 1674;
+
+    ACTION_ATPG = 1675;
+
+    ACTION_ATCLPB = 1676;
+
+    ACTION_ATCGIB = 1677;
+
+    ACTION_ATCPAB = 1678;
+
+    ACTION_ATCSAUC = 1679;
+
+    ACTION_ATCSCUC = 1680;
+
+    ACTION_ATCHNUC = 1681;
 }
 
 /**
@@ -1622,6 +1656,10 @@
     // OPEN: Settings > Apps > Default Apps > Default sms
     DEFAULT_SMS_PICKER = 789;
 
+    // OPEN: Settings > Apps > Notification > Notification Assistant
+    DEFAULT_NOTIFICATION_ASSISTANT = 790;
+
+
     // OPEN: Settings > Apps > Default Apps > Warning dialog to confirm selection
     DEFAULT_APP_PICKER_CONFIRMATION_DIALOG = 791;
 
@@ -2231,4 +2269,19 @@
 
     // Panel for Media Output
     PANEL_MEDIA_OUTPUT = 1657;
+
+    // Mapping: go/at-mapping
+    PAGE_ATSSI = 1667;
+
+    PAGE_ATSII = 1668;
+
+    PAGE_ATUS = 1669;
+
+    PAGE_ATSSP = 1670;
+
+    PAGE_ATSAP = 1671;
+
+    PAGE_ATSCP = 1672;
+
+    PAGE_ATHNP = 1673;
 }
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index cccb40d..c1cbd52 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -920,8 +920,7 @@
         // Temperature at which the high temperature warning notification should
         // be shown.
         optional SettingProto warning_temperature_level = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        // USB temperature at which the high temperature alarm notification should be shown.
-        optional SettingProto usb_alarm_temperature_level = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto show_usb_temperature_alarm = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional TemperatureWarning temperature_warning = 119;
 
diff --git a/core/proto/android/stats/style/Android.bp b/core/proto/android/stats/style/Android.bp
new file mode 100644
index 0000000..f085a52
--- /dev/null
+++ b/core/proto/android/stats/style/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library {
+    name: "styleprotosnano",
+    proto: {
+        type: "nano",
+        output_params: ["store_unknown_fields=true"],
+        include_dirs: ["external/protobuf/src"],
+    },
+
+    sdk_version: "current",
+    srcs: [
+        "*.proto",
+    ],
+}
diff --git a/core/proto/android/stats/style/style_enums.proto b/core/proto/android/stats/style/style_enums.proto
new file mode 100644
index 0000000..b0e4391
--- /dev/null
+++ b/core/proto/android/stats/style/style_enums.proto
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.stats.style;
+option java_multiple_files = true;
+
+enum Action {
+    DEFAULT_ACTION = 0;
+    ONRESUME = 1;
+    ONSTOP = 2;
+    PICKER_SELECT = 3;
+    PICKER_APPLIED = 4;
+    WALLPAPER_OPEN_CATEGORY = 5;
+    WALLPAPER_SELECT = 6;
+    WALLPAPER_APPLIED = 7;
+    WALLPAPER_EXPLORE = 8;
+    WALLPAPER_DOWNLOAD = 9;
+    WALLPAPER_REMOVE = 10;
+}
+
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fc896cf..0149365 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3418,6 +3418,11 @@
     <permission android:name="android.permission.GET_RUNTIME_PERMISSIONS"
                 android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows an application to change policy_fixed permissions.
+    @hide -->
+    <permission android:name="android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY"
+                android:protectionLevel="signature|installer" />
+
     <!-- @hide Allows an application to observe permission changes. -->
     <permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"
         android:protectionLevel="signature|privileged" />
diff --git a/core/res/res/drawable/ic_action_open.xml b/core/res/res/drawable/ic_action_open.xml
index 3d3d36e..ba0a38d 100644
--- a/core/res/res/drawable/ic_action_open.xml
+++ b/core/res/res/drawable/ic_action_open.xml
@@ -17,8 +17,10 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+    android:viewportHeight="24.0"
+    android:tint="?attr/colorControlNormal">
+  >
   <path
-      android:fillColor="#FF737373"
+      android:fillColor="@color/white"
       android:pathData="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 733878b..8f2d6c3 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -185,4 +185,20 @@
         <item>@string/app_info</item>
     </string-array>
 
+    <!-- Device-specific array of SIM slot indexes which are are embedded eUICCs.
+         e.g. If a device has two physical slots with indexes 0, 1, and slot 1 is an
+         eUICC, then the value of this array should be:
+             <integer-array name="non_removable_euicc_slots">
+                 <item>1</item>
+             </integer-array>
+         If a device has three physical slots and slot 1 and 2 are eUICCs, then the value of
+         this array should be:
+             <integer-array name="non_removable_euicc_slots">
+                <item>1</item>
+                <item>2</item>
+             </integer-array>
+         This is used to differentiate between removable eUICCs and built in eUICCs, and should
+         be set by OEMs for devices which use eUICCs. -->
+    <integer-array name="non_removable_euicc_slots"></integer-array>
+
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6571cd2..0041bfc 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2272,6 +2272,7 @@
 
     <!-- If the sensor that wakes up the lock screen is available or not. -->
     <bool name="config_dozeWakeLockScreenSensorAvailable">false</bool>
+    <integer name="config_dozeWakeLockScreenDebounce">3000</integer>
 
     <!-- Control whether the always on display mode is available. This should only be enabled on
          devices where the display has been tuned to be power efficient in DOZE and/or DOZE_SUSPEND
@@ -2673,11 +2674,6 @@
 
     </string-array>
 
-    <!-- Flag indicating that this device does not rotate and will always remain in its default
-         orientation. Activities that desire to run in a non-compatible orientation will be run
-         from an emulated display within the physical display. -->
-    <bool name="config_forceDefaultOrientation">false</bool>
-
     <!-- Default Gravity setting for the system Toast view. Equivalent to: Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM -->
     <integer name="config_toastDefaultGravity">0x00000051</integer>
 
@@ -3368,6 +3364,10 @@
     <!-- True if home app should be pinned via Pinner Service -->
     <bool name="config_pinnerHomeApp">false</bool>
 
+    <!-- List of files pinned by the Pinner Service with the apex boot image b/119800099 -->
+    <string-array translatable="false" name="config_apexBootImagePinnerServiceFiles">
+    </string-array>
+
     <!-- Number of days preloaded file cache should be preserved on a device before it can be
          deleted -->
     <integer name="config_keepPreloadsMinDays">7</integer>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e1ce2f6..5a3c536 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2998,6 +2998,8 @@
       <public name="config_showDefaultEmergency" />
       <!-- @hide @SystemApi -->
       <public name="config_showDefaultHome" />
+      <!-- @hide @TestApi -->
+      <public name="config_perDisplayFocusEnabled" />
     </public-group>
 
     <public-group type="dimen" first-id="0x01050007">
diff --git a/core/res/res/values/styles_car.xml b/core/res/res/values/styles_car.xml
index f6ff1b6..2129734 100644
--- a/core/res/res/values/styles_car.xml
+++ b/core/res/res/values/styles_car.xml
@@ -59,7 +59,7 @@
     <style name="CarAction1">
         <item name="textStyle">bold</item>
         <item name="textSize">@dimen/car_action1_size</item>
-        <item name="textColor">@color/car_highlight</item>
+        <item name="textColor">@color/control_default_material</item>
     </style>
 
     <style name="CarAction1.Dark">
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 93068ea9..3c7b36d 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -38,6 +38,8 @@
     <style name="Widget.DeviceDefault.Button.Inset" parent="Widget.Material.Button.Inset"/>
     <style name="Widget.DeviceDefault.Button.Toggle" parent="Widget.Material.Button.Toggle"/>
     <style name="Widget.DeviceDefault.Button.Colored" parent="Widget.Material.Button.Colored">
+        <item name="outlineAmbientShadowColor">@color/btn_colored_background_material</item>
+        <item name="outlineSpotShadowColor">@color/btn_colored_background_material</item>
         <item name="textAppearance">?attr/textAppearanceButton</item>
         <item name="textColor">@color/btn_colored_text_material</item>
     </style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4ae239e..33ca311 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -347,7 +347,6 @@
   <java-symbol type="bool" name="config_requireRadioPowerOffOnSimRefreshReset" />
   <java-symbol type="bool" name="config_speed_up_audio_on_mt_calls" />
   <java-symbol type="bool" name="config_useFixedVolume" />
-  <java-symbol type="bool" name="config_forceDefaultOrientation" />
   <java-symbol type="bool" name="config_wifi_batched_scan_supported" />
   <java-symbol type="bool" name="config_wifi_softap_acs_supported" />
   <java-symbol type="string" name="config_wifi_softap_acs_supported_channel_list" />
@@ -2988,6 +2987,8 @@
   <java-symbol type="array" name="resolver_target_actions_pin" />
   <java-symbol type="array" name="resolver_target_actions_unpin" />
 
+  <java-symbol type="array" name="non_removable_euicc_slots" />
+
   <java-symbol type="string" name="install_carrier_app_notification_title" />
   <java-symbol type="string" name="install_carrier_app_notification_text" />
   <java-symbol type="string" name="install_carrier_app_notification_text_app_name" />
@@ -3048,6 +3049,7 @@
   <java-symbol type="array" name="config_defaultPinnerServiceFiles" />
   <java-symbol type="bool" name="config_pinnerCameraApp" />
   <java-symbol type="bool" name="config_pinnerHomeApp" />
+  <java-symbol type="array" name="config_apexBootImagePinnerServiceFiles" />
 
   <java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
 
@@ -3404,6 +3406,7 @@
 
   <java-symbol type="string" name="config_dozeLongPressSensorType" />
   <java-symbol type="bool" name="config_dozeWakeLockScreenSensorAvailable" />
+  <java-symbol type="integer" name="config_dozeWakeLockScreenDebounce" />
 
   <java-symbol type="array" name="config_allowedGlobalInstantAppSettings" />
   <java-symbol type="array" name="config_allowedSystemInstantAppSettings" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
new file mode 100644
index 0000000..833c734
--- /dev/null
+++ b/core/tests/coretests/Android.bp
@@ -0,0 +1,113 @@
+android_test {
+    name: "FrameworksCoreTests",
+
+    srcs: [
+        "src/**/*.java",
+        "src/**/I*.aidl",
+        "DisabledTestApp/src/**/*.java",
+        "EnabledTestApp/src/**/*.java",
+        "BinderProxyCountingTestApp/src/**/*.java",
+        "BinderProxyCountingTestService/src/**/*.java",
+        "aidl/**/I*.aidl",
+    ],
+
+    aidl: {
+        local_include_dirs: ["aidl"],
+    },
+
+    dxflags: ["--core-library"],
+
+    aaptflags: [
+        "-0 .dat",
+        "-0 .gld",
+        "-c fa",
+    ],
+    static_libs: [
+        "frameworks-base-testutils",
+        "core-tests-support",
+        "android-common",
+        "frameworks-core-util-lib",
+        "mockwebserver",
+        "guava",
+        "androidx.test.espresso.core",
+        "androidx.test.ext.junit",
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "mockito-target-minus-junit4",
+        "ub-uiautomator",
+        "platform-test-annotations",
+        "truth-prebuilt",
+        "print-test-util-lib",
+        "testng",
+    ],
+
+    libs: [
+        "android.test.runner",
+        "telephony-common",
+        "testables",
+        "org.apache.http.legacy",
+        "android.test.base",
+        "android.test.mock",
+        "framework-atb-backward-compatibility",
+    ],
+
+    platform_apis: true,
+    test_suites: ["device-tests"],
+
+    certificate: "platform",
+
+    resource_dirs: ["res"],
+    resource_zips: [":FrameworksCoreTests_apks_as_resources"],
+}
+
+// Rules to copy all the test apks to the intermediate raw resource directory
+java_genrule {
+    name: "FrameworksCoreTests_apks_as_resources",
+    srcs: [
+        ":FrameworksCoreTests_install",
+        ":FrameworksCoreTests_install_bad_dex",
+        ":FrameworksCoreTests_install_complete_package_info",
+        ":FrameworksCoreTests_install_decl_perm",
+        ":FrameworksCoreTests_install_jni_lib_open_from_apk",
+        ":FrameworksCoreTests_install_loc_auto",
+        ":FrameworksCoreTests_install_loc_internal",
+        ":FrameworksCoreTests_install_loc_sdcard",
+        ":FrameworksCoreTests_install_loc_unspecified",
+        ":FrameworksCoreTests_install_multi_package",
+        ":FrameworksCoreTests_install_split_base",
+        ":FrameworksCoreTests_install_split_feature_a",
+        ":FrameworksCoreTests_install_use_perm_good",
+        ":FrameworksCoreTests_install_uses_feature",
+        ":FrameworksCoreTests_install_verifier_bad",
+        ":FrameworksCoreTests_install_verifier_good",
+        ":FrameworksCoreTests_keyset_permdef_sa_unone",
+        ":FrameworksCoreTests_keyset_permuse_sa_ua_ub",
+        ":FrameworksCoreTests_keyset_permuse_sb_ua_ub",
+        ":FrameworksCoreTests_keyset_sab_ua",
+        ":FrameworksCoreTests_keyset_sa_ua",
+        ":FrameworksCoreTests_keyset_sa_uab",
+        ":FrameworksCoreTests_keyset_sa_ua_ub",
+        ":FrameworksCoreTests_keyset_sa_ub",
+        ":FrameworksCoreTests_keyset_sa_unone",
+        ":FrameworksCoreTests_keyset_sau_ub",
+        ":FrameworksCoreTests_keyset_sb_ua",
+        ":FrameworksCoreTests_keyset_sb_ub",
+        ":FrameworksCoreTests_keyset_splata_api",
+        ":FrameworksCoreTests_keyset_splat_api",
+        ":FrameworksCoreTests_locales",
+        ":FrameworksCoreTests_version_1",
+        ":FrameworksCoreTests_version_1_diff",
+        ":FrameworksCoreTests_version_1_nosys",
+        ":FrameworksCoreTests_version_2",
+        ":FrameworksCoreTests_version_2_diff",
+        ":FrameworksCoreTests_version_3",
+    ],
+    out: ["FrameworkCoreTests_apks_as_resources.res.zip"],
+    tools: ["soong_zip"],
+
+    cmd: "mkdir -p $(genDir)/res/raw && " +
+        "for i in $(in); do " +
+        "  x=$${i##*FrameworksCoreTests_}; echo $${x}; cp $$i $(genDir)/res/raw/$${x%.apk};" +
+        "done && " +
+        "$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
+}
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
deleted file mode 100644
index 40ebd44..0000000
--- a/core/tests/coretests/Android.mk
+++ /dev/null
@@ -1,85 +0,0 @@
-ACTUAL_LOCAL_PATH := $(call my-dir)
-
-# this var will hold all the test apk module names later.
-FrameworkCoreTests_all_apks :=
-
-# We have to include the subdir makefiles first
-# so that FrameworkCoreTests_all_apks will be populated correctly.
-include $(call all-makefiles-under,$(ACTUAL_LOCAL_PATH))
-
-LOCAL_PATH := $(ACTUAL_LOCAL_PATH)
-
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
-	$(call all-java-files-under, src) \
-	$(call all-Iaidl-files-under, src) \
-	$(call all-java-files-under, DisabledTestApp/src) \
-	$(call all-java-files-under, EnabledTestApp/src) \
-	$(call all-java-files-under, BinderProxyCountingTestApp/src) \
-	$(call all-java-files-under, BinderProxyCountingTestService/src) \
-	$(call all-Iaidl-files-under, aidl)
-
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
-
-LOCAL_DX_FLAGS := --core-library
-LOCAL_JACK_FLAGS := --multi-dex native
-LOCAL_AAPT_FLAGS = -0 dat -0 gld -c fa
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    frameworks-base-testutils \
-    core-tests-support \
-    android-common \
-    frameworks-core-util-lib \
-    mockwebserver \
-    guava \
-    androidx.test.espresso.core \
-    androidx.test.ext.junit \
-    androidx.test.runner \
-    androidx.test.rules \
-    mockito-target-minus-junit4 \
-    ub-uiautomator \
-    platform-test-annotations \
-    truth-prebuilt \
-    print-test-util-lib \
-    testng # TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows
-
-LOCAL_JAVA_LIBRARIES := \
-    android.test.runner \
-    telephony-common \
-    testables \
-    org.apache.http.legacy \
-    android.test.base \
-    android.test.mock \
-    framework-atb-backward-compatibility \
-
-LOCAL_PACKAGE_NAME := FrameworksCoreTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-
-# intermediate dir to include all the test apks as raw resource
-FrameworkCoreTests_intermediates := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/test_apks/res
-LOCAL_RESOURCE_DIR := $(FrameworkCoreTests_intermediates) $(LOCAL_PATH)/res
-
-# Disable AAPT2 because the hacks below depend on the AAPT rules implementation
-LOCAL_USE_AAPT2 := false
-
-include $(BUILD_PACKAGE)
-# Rules to copy all the test apks to the intermediate raw resource directory
-FrameworkCoreTests_all_apks_res := $(addprefix $(FrameworkCoreTests_intermediates)/raw/, \
-    $(foreach a, $(FrameworkCoreTests_all_apks), $(patsubst FrameworkCoreTests_%,%,$(a))))
-
-$(FrameworkCoreTests_all_apks_res): $(FrameworkCoreTests_intermediates)/raw/%: $(call intermediates-dir-for,APPS,FrameworkCoreTests_%)/package.apk
-	$(call copy-file-to-new-target)
-
-# Use R_file_stamp as dependency because we want the test apks in place before the R.java is generated.
-$(R_file_stamp) : $(FrameworkCoreTests_all_apks_res)
-
-FrameworkCoreTests_all_apks :=
-FrameworkCoreTests_intermediates :=
-FrameworkCoreTests_all_apks_res :=
diff --git a/core/tests/coretests/BinderProxyCountingTestApp/Android.bp b/core/tests/coretests/BinderProxyCountingTestApp/Android.bp
new file mode 100644
index 0000000..6279a48
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestApp/Android.bp
@@ -0,0 +1,25 @@
+// 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.
+
+android_test_helper_app {
+    name: "BinderProxyCountingTestApp",
+
+    static_libs: ["coretests-aidl"],
+    srcs: ["**/*.java"],
+
+    sdk_version: "current",
+    certificate: "platform",
+
+    test_suites: ["device-tests"],
+}
diff --git a/core/tests/coretests/BinderProxyCountingTestApp/Android.mk b/core/tests/coretests/BinderProxyCountingTestApp/Android.mk
deleted file mode 100644
index 4642694..0000000
--- a/core/tests/coretests/BinderProxyCountingTestApp/Android.mk
+++ /dev/null
@@ -1,29 +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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := BinderProxyCountingTestApp
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-include $(BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/BinderProxyCountingTestService/Android.bp b/core/tests/coretests/BinderProxyCountingTestService/Android.bp
new file mode 100644
index 0000000..22718cb
--- /dev/null
+++ b/core/tests/coretests/BinderProxyCountingTestService/Android.bp
@@ -0,0 +1,25 @@
+// 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.
+
+android_test_helper_app {
+    name: "BinderProxyCountingTestService",
+
+    static_libs: ["coretests-aidl"],
+    srcs: ["**/*.java"],
+
+    platform_apis: true,
+    certificate: "platform",
+
+    test_suites: ["device-tests"],
+}
diff --git a/core/tests/coretests/BinderProxyCountingTestService/Android.mk b/core/tests/coretests/BinderProxyCountingTestService/Android.mk
deleted file mode 100644
index f852c7a..0000000
--- a/core/tests/coretests/BinderProxyCountingTestService/Android.mk
+++ /dev/null
@@ -1,29 +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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := BinderProxyCountingTestService
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-include $(BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/BstatsTestApp/Android.bp b/core/tests/coretests/BstatsTestApp/Android.bp
new file mode 100644
index 0000000..424c71a
--- /dev/null
+++ b/core/tests/coretests/BstatsTestApp/Android.bp
@@ -0,0 +1,34 @@
+// 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.
+
+android_test_helper_app {
+    name: "BstatsTestApp",
+
+    test_suites: [
+        "device-tests",
+    ],
+
+    static_libs: ["coretests-aidl"],
+
+    srcs: ["**/*.java"],
+
+    sdk_version: "current",
+    certificate: "platform",
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/core/tests/coretests/BstatsTestApp/Android.mk b/core/tests/coretests/BstatsTestApp/Android.mk
deleted file mode 100644
index a5872a5..0000000
--- a/core/tests/coretests/BstatsTestApp/Android.mk
+++ /dev/null
@@ -1,34 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := BstatsTestApp
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-LOCAL_DEX_PREOPT := false
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-include $(BUILD_PACKAGE)
diff --git a/core/tests/coretests/DisabledTestApp/Android.bp b/core/tests/coretests/DisabledTestApp/Android.bp
new file mode 100644
index 0000000..419816e
--- /dev/null
+++ b/core/tests/coretests/DisabledTestApp/Android.bp
@@ -0,0 +1,8 @@
+android_test_helper_app {
+    name: "DisabledTestApp",
+
+    srcs: ["**/*.java"],
+
+    sdk_version: "current",
+    certificate: "platform",
+}
diff --git a/core/tests/coretests/DisabledTestApp/Android.mk b/core/tests/coretests/DisabledTestApp/Android.mk
deleted file mode 100644
index e4304f7..0000000
--- a/core/tests/coretests/DisabledTestApp/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := DisabledTestApp
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/EnabledTestApp/Android.bp b/core/tests/coretests/EnabledTestApp/Android.bp
new file mode 100644
index 0000000..bc4f4bd
--- /dev/null
+++ b/core/tests/coretests/EnabledTestApp/Android.bp
@@ -0,0 +1,8 @@
+android_test_helper_app {
+    name: "EnabledTestApp",
+
+    srcs: ["**/*.java"],
+
+    sdk_version: "current",
+    certificate: "platform",
+}
diff --git a/core/tests/coretests/EnabledTestApp/Android.mk b/core/tests/coretests/EnabledTestApp/Android.mk
deleted file mode 100644
index cd37f08..0000000
--- a/core/tests/coretests/EnabledTestApp/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := EnabledTestApp
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/aidl/Android.bp b/core/tests/coretests/aidl/Android.bp
new file mode 100644
index 0000000..6e442db
--- /dev/null
+++ b/core/tests/coretests/aidl/Android.bp
@@ -0,0 +1,19 @@
+// 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.
+
+java_test {
+    name: "coretests-aidl",
+    sdk_version: "current",
+    srcs: ["**/*.aidl"],
+}
diff --git a/core/tests/coretests/aidl/Android.mk b/core/tests/coretests/aidl/Android.mk
deleted file mode 100644
index 86e36b6..0000000
--- a/core/tests/coretests/aidl/Android.mk
+++ /dev/null
@@ -1,22 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-subdir-Iaidl-files)
-LOCAL_MODULE := coretests-aidl
-include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/Android.bp b/core/tests/coretests/apks/Android.bp
new file mode 100644
index 0000000..20c87b2
--- /dev/null
+++ b/core/tests/coretests/apks/Android.bp
@@ -0,0 +1,7 @@
+java_defaults {
+    name: "FrameworksCoreTests_apks_defaults",
+    sdk_version: "current",
+
+    // Every package should have a native library
+    jni_libs: ["libframeworks_coretests_jni"],
+}
diff --git a/core/tests/coretests/apks/Android.mk b/core/tests/coretests/apks/Android.mk
deleted file mode 100644
index 98c0c2a..0000000
--- a/core/tests/coretests/apks/Android.mk
+++ /dev/null
@@ -1,7 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-FrameworkCoreTests_BUILD_PACKAGE := $(LOCAL_PATH)/FrameworkCoreTests_apk.mk
-
-# build sub packages
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/coretests/apks/FrameworkCoreTests_apk.mk b/core/tests/coretests/apks/FrameworkCoreTests_apk.mk
deleted file mode 100644
index 8a7d72a5..0000000
--- a/core/tests/coretests/apks/FrameworkCoreTests_apk.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-
-LOCAL_MODULE_TAGS := tests
-
-# Disable dexpreopt.
-LOCAL_DEX_PREOPT := false
-
-# Make sure every package name gets the FrameworkCoreTests_ prefix.
-LOCAL_PACKAGE_NAME := FrameworkCoreTests_$(LOCAL_PACKAGE_NAME)
-LOCAL_SDK_VERSION := current
-
-# Every package should have a native library
-LOCAL_JNI_SHARED_LIBRARIES := libframeworks_coretests_jni
-
-FrameworkCoreTests_all_apks += $(LOCAL_PACKAGE_NAME)
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install-split-base/Android.bp b/core/tests/coretests/apks/install-split-base/Android.bp
new file mode 100644
index 0000000..ddf75b2
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-base/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_split_base",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install-split-base/Android.mk b/core/tests/coretests/apks/install-split-base/Android.mk
deleted file mode 100644
index 5b60e31..0000000
--- a/core/tests/coretests/apks/install-split-base/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_split_base
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/install-split-feature-a/Android.bp b/core/tests/coretests/apks/install-split-feature-a/Android.bp
new file mode 100644
index 0000000..9ec9893
--- /dev/null
+++ b/core/tests/coretests/apks/install-split-feature-a/Android.bp
@@ -0,0 +1,11 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_split_feature_a",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+
+    aaptflags: [
+        "--custom-package com.google.android.dexapis.splitapp.feature_a",
+        "--package-id 0x80",
+    ],
+}
diff --git a/core/tests/coretests/apks/install-split-feature-a/Android.mk b/core/tests/coretests/apks/install-split-feature-a/Android.mk
deleted file mode 100644
index 0f37d16..0000000
--- a/core/tests/coretests/apks/install-split-feature-a/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_split_feature_a
-
-LOCAL_USE_AAPT2 := true
-LOCAL_AAPT_FLAGS += --custom-package com.google.android.dexapis.splitapp.feature_a
-LOCAL_AAPT_FLAGS += --package-id 0x80
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/install/Android.bp b/core/tests/coretests/apks/install/Android.bp
new file mode 100644
index 0000000..e783fe2
--- /dev/null
+++ b/core/tests/coretests/apks/install/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install/Android.mk b/core/tests/coretests/apks/install/Android.mk
deleted file mode 100644
index b38dc20..0000000
--- a/core/tests/coretests/apks/install/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_bad_dex/Android.bp b/core/tests/coretests/apks/install_bad_dex/Android.bp
new file mode 100644
index 0000000..d156793
--- /dev/null
+++ b/core/tests/coretests/apks/install_bad_dex/Android.bp
@@ -0,0 +1,23 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_bad_dex_",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["src/**/*.java"],
+}
+
+// Inject bad classes.dex file.
+java_genrule {
+    name: "FrameworksCoreTests_install_bad_dex",
+    tools: [
+        "soong_zip",
+        "merge_zips",
+    ],
+    srcs: [
+        ":FrameworksCoreTests_install_bad_dex_",
+        "classes.dex",
+    ],
+    out: ["FrameworksCoreTests_install_bad_dex.apk"],
+    cmd: "$(location soong_zip) -o $(genDir)/classes.dex.zip -j -f $(location classes.dex) && " +
+        "$(location merge_zips) -ignore-duplicates $(out) $(genDir)/classes.dex.zip " +
+        "$(location :FrameworksCoreTests_install_bad_dex_)",
+}
diff --git a/core/tests/coretests/apks/install_bad_dex/Android.mk b/core/tests/coretests/apks/install_bad_dex/Android.mk
deleted file mode 100644
index 05983aa..0000000
--- a/core/tests/coretests/apks/install_bad_dex/Android.mk
+++ /dev/null
@@ -1,11 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_bad_dex
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-# Override target specific variable PRIVATE_DEX_FILE to inject bad classes.dex file.
-$(LOCAL_BUILT_MODULE): PRIVATE_DEX_FILE := $(LOCAL_PATH)/classes.dex
diff --git a/core/tests/coretests/apks/install_complete_package_info/Android.bp b/core/tests/coretests/apks/install_complete_package_info/Android.bp
new file mode 100644
index 0000000..123558bd
--- /dev/null
+++ b/core/tests/coretests/apks/install_complete_package_info/Android.bp
@@ -0,0 +1,7 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_complete_package_info",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
+
diff --git a/core/tests/coretests/apks/install_complete_package_info/Android.mk b/core/tests/coretests/apks/install_complete_package_info/Android.mk
deleted file mode 100644
index 19bf356..0000000
--- a/core/tests/coretests/apks/install_complete_package_info/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_complete_package_info
-#LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-#include $(BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/apks/install_decl_perm/Android.bp b/core/tests/coretests/apks/install_decl_perm/Android.bp
new file mode 100644
index 0000000..868e8b5
--- /dev/null
+++ b/core/tests/coretests/apks/install_decl_perm/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_decl_perm",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_decl_perm/Android.mk b/core/tests/coretests/apks/install_decl_perm/Android.mk
deleted file mode 100644
index 86370c8..0000000
--- a/core/tests/coretests/apks/install_decl_perm/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_decl_perm
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_jni_lib/Android.bp b/core/tests/coretests/apks/install_jni_lib/Android.bp
index c1a6bd0..f20f599 100644
--- a/core/tests/coretests/apks/install_jni_lib/Android.bp
+++ b/core/tests/coretests/apks/install_jni_lib/Android.bp
@@ -14,6 +14,7 @@
 
 cc_test_library {
     name: "libframeworks_coretests_jni",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
 
     srcs: ["com_android_frameworks_coretests_JNITest.cpp"],
 
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp
new file mode 100644
index 0000000..602b704
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_jni_lib_open_from_apk",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
deleted file mode 100644
index 6b3b55e..0000000
--- a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_jni_lib_open_from_apk
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_loc_auto/Android.bp b/core/tests/coretests/apks/install_loc_auto/Android.bp
new file mode 100644
index 0000000..6393915
--- /dev/null
+++ b/core/tests/coretests/apks/install_loc_auto/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_loc_auto",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_loc_auto/Android.mk b/core/tests/coretests/apks/install_loc_auto/Android.mk
deleted file mode 100644
index 6435f36..0000000
--- a/core/tests/coretests/apks/install_loc_auto/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_loc_auto
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_loc_internal/Android.bp b/core/tests/coretests/apks/install_loc_internal/Android.bp
new file mode 100644
index 0000000..770aaa5
--- /dev/null
+++ b/core/tests/coretests/apks/install_loc_internal/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_loc_internal",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_loc_internal/Android.mk b/core/tests/coretests/apks/install_loc_internal/Android.mk
deleted file mode 100644
index 8cc8b8e..0000000
--- a/core/tests/coretests/apks/install_loc_internal/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_loc_internal
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_loc_sdcard/Android.bp b/core/tests/coretests/apks/install_loc_sdcard/Android.bp
new file mode 100644
index 0000000..1779401
--- /dev/null
+++ b/core/tests/coretests/apks/install_loc_sdcard/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_loc_sdcard",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_loc_sdcard/Android.mk b/core/tests/coretests/apks/install_loc_sdcard/Android.mk
deleted file mode 100644
index e1411c2..0000000
--- a/core/tests/coretests/apks/install_loc_sdcard/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_loc_sdcard
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_loc_unspecified/Android.bp b/core/tests/coretests/apks/install_loc_unspecified/Android.bp
new file mode 100644
index 0000000..21c0f82
--- /dev/null
+++ b/core/tests/coretests/apks/install_loc_unspecified/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_loc_unspecified",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_loc_unspecified/Android.mk b/core/tests/coretests/apks/install_loc_unspecified/Android.mk
deleted file mode 100644
index 0741d04..0000000
--- a/core/tests/coretests/apks/install_loc_unspecified/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_loc_unspecified
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_multi_package/Android.bp b/core/tests/coretests/apks/install_multi_package/Android.bp
new file mode 100644
index 0000000..249242e
--- /dev/null
+++ b/core/tests/coretests/apks/install_multi_package/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_multi_package",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_multi_package/Android.mk b/core/tests/coretests/apks/install_multi_package/Android.mk
deleted file mode 100644
index 3f163de..0000000
--- a/core/tests/coretests/apks/install_multi_package/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_multi_package
-
-LOCAL_USE_AAPT2 := true
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-#include $(BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/apks/install_use_perm_good/Android.bp b/core/tests/coretests/apks/install_use_perm_good/Android.bp
new file mode 100644
index 0000000..bb41ebb
--- /dev/null
+++ b/core/tests/coretests/apks/install_use_perm_good/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_use_perm_good",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_use_perm_good/Android.mk b/core/tests/coretests/apks/install_use_perm_good/Android.mk
deleted file mode 100644
index e2661a1..0000000
--- a/core/tests/coretests/apks/install_use_perm_good/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_use_perm_good
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_uses_feature/Android.bp b/core/tests/coretests/apks/install_uses_feature/Android.bp
new file mode 100644
index 0000000..0ec747b
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_feature/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_uses_feature",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_uses_feature/Android.mk b/core/tests/coretests/apks/install_uses_feature/Android.mk
deleted file mode 100644
index b60d734..0000000
--- a/core/tests/coretests/apks/install_uses_feature/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_uses_feature
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_verifier_bad/Android.bp b/core/tests/coretests/apks/install_verifier_bad/Android.bp
new file mode 100644
index 0000000..1265739
--- /dev/null
+++ b/core/tests/coretests/apks/install_verifier_bad/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_verifier_bad",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_verifier_bad/Android.mk b/core/tests/coretests/apks/install_verifier_bad/Android.mk
deleted file mode 100644
index 745b4d3..0000000
--- a/core/tests/coretests/apks/install_verifier_bad/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_verifier_bad
-
-LOCAL_USE_AAPT2 := true
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_verifier_good/Android.bp b/core/tests/coretests/apks/install_verifier_good/Android.bp
new file mode 100644
index 0000000..4911ffb
--- /dev/null
+++ b/core/tests/coretests/apks/install_verifier_good/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_install_verifier_good",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_verifier_good/Android.mk b/core/tests/coretests/apks/install_verifier_good/Android.mk
deleted file mode 100644
index 150fd8d..0000000
--- a/core/tests/coretests/apks/install_verifier_good/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := install_verifier_good
-
-LOCAL_USE_AAPT2 := true
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/keyset/Android.bp b/core/tests/coretests/apks/keyset/Android.bp
new file mode 100644
index 0000000..e252b08
--- /dev/null
+++ b/core/tests/coretests/apks/keyset/Android.bp
@@ -0,0 +1,120 @@
+//apks signed by keyset_A
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_sa_unone",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksCoreTests_keyset_A_cert",
+    manifest: "uNone/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_sa_ua",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksCoreTests_keyset_A_cert",
+    manifest: "uA/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_sa_ub",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksCoreTests_keyset_A_cert",
+    manifest: "uB/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_sa_uab",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksCoreTests_keyset_A_cert",
+    manifest: "uAB/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_sa_ua_ub",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksCoreTests_keyset_A_cert",
+    manifest: "uAuB/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_permdef_sa_unone",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksCoreTests_keyset_A_cert",
+    manifest: "permDef/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_permuse_sa_ua_ub",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksCoreTests_keyset_A_cert",
+    manifest: "permUse/AndroidManifest.xml",
+}
+
+//apks signed by keyset_B
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_sb_ua",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksCoreTests_keyset_B_cert",
+    manifest: "uA/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_sb_ub",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksCoreTests_keyset_B_cert",
+    manifest: "uB/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_permuse_sb_ua_ub",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksCoreTests_keyset_B_cert",
+    manifest: "permUse/AndroidManifest.xml",
+}
+
+//apks signed by keyset_A and keyset_B
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_sab_ua",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksCoreTests_keyset_A_cert",
+    additional_certificates: [":FrameworksCoreTests_keyset_B_cert"],
+    manifest: "uA/AndroidManifest.xml",
+}
+
+//apks signed by keyset_A and unit_test
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_sau_ub",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: ":FrameworksCoreTests_keyset_A_cert",
+    additional_certificates: [":FrameworksCoreTests_keyset_B_cert"],
+    manifest: "uB/AndroidManifest.xml",
+}
+
+//apks signed by platform only
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_splat_api",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: "platform",
+    manifest: "api_test/AndroidManifest.xml",
+}
+
+//apks signed by platform and keyset_A
+android_test_helper_app {
+    name: "FrameworksCoreTests_keyset_splata_api",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    certificate: "platform",
+    additional_certificates: [":FrameworksCoreTests_keyset_A_cert"],
+    manifest: "api_test/AndroidManifest.xml",
+}
diff --git a/core/tests/coretests/apks/keyset/Android.mk b/core/tests/coretests/apks/keyset/Android.mk
deleted file mode 100644
index 306dc90..0000000
--- a/core/tests/coretests/apks/keyset/Android.mk
+++ /dev/null
@@ -1,108 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-#apks signed by keyset_A
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sa_unone
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := uNone/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sa_ua
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := uA/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sa_ub
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := uB/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sa_uab
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := uAB/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sa_ua_ub
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := uAuB/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_permdef_sa_unone
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := permDef/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_permuse_sa_ua_ub
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := permUse/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-#apks signed by keyset_B
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sb_ua
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_B
-LOCAL_MANIFEST_FILE := uA/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sb_ub
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_B
-LOCAL_MANIFEST_FILE := uB/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_permuse_sb_ua_ub
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_B
-LOCAL_MANIFEST_FILE := permUse/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-#apks signed by keyset_A and keyset_B
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sab_ua
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_B
-LOCAL_MANIFEST_FILE := uA/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-#apks signed by keyset_A and unit_test
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_sau_ub
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_B
-LOCAL_MANIFEST_FILE := uB/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-#apks signed by platform only
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_splat_api
-LOCAL_CERTIFICATE := platform
-LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-#apks signed by platform and keyset_A
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := keyset_splata_api
-LOCAL_CERTIFICATE := platform
-LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_A
-LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml
-include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/locales/Android.bp b/core/tests/coretests/apks/locales/Android.bp
new file mode 100644
index 0000000..4a730ef
--- /dev/null
+++ b/core/tests/coretests/apks/locales/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_locales",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+
+    srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/locales/Android.mk b/core/tests/coretests/apks/locales/Android.mk
deleted file mode 100644
index 9cb13dd..0000000
--- a/core/tests/coretests/apks/locales/Android.mk
+++ /dev/null
@@ -1,8 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := locales
-
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/version/Android.bp b/core/tests/coretests/apks/version/Android.bp
new file mode 100644
index 0000000..371ccfc
--- /dev/null
+++ b/core/tests/coretests/apks/version/Android.bp
@@ -0,0 +1,54 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_version_1",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    aaptflags: [
+        "--version-code 1",
+        "--version-name 1.0",
+    ],
+    certificate: ":FrameworksCoreTests_unit_test_cert",
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_version_2",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    aaptflags: [
+        "--version-code 2",
+        "--version-name 2.0",
+    ],
+    certificate: ":FrameworksCoreTests_unit_test_cert",
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_version_3",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    aaptflags: [
+        "--version-code 3",
+        "--version-name 3.0",
+    ],
+    certificate: ":FrameworksCoreTests_unit_test_cert",
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_version_1_diff",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    aaptflags: [
+        "--version-code 1",
+        "--version-name 1.0",
+    ],
+    certificate: ":FrameworksCoreTests_unit_test_cert",
+}
+
+android_test_helper_app {
+    name: "FrameworksCoreTests_version_2_diff",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    aaptflags: [
+        "--version-code 2",
+        "--version-name 2.0",
+    ],
+    certificate: ":FrameworksCoreTests_unit_test_cert",
+}
diff --git a/core/tests/coretests/apks/version/Android.mk b/core/tests/coretests/apks/version/Android.mk
deleted file mode 100644
index 3635a58..0000000
--- a/core/tests/coretests/apks/version/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := version_1
-LOCAL_AAPT_FLAGS := --version-code 1 --version-name 1.0
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := version_2
-LOCAL_AAPT_FLAGS := --version-code 2 --version-name 2.0
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := version_3
-LOCAL_AAPT_FLAGS := --version-code 3 --version-name 3.0
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := version_1_diff
-LOCAL_AAPT_FLAGS := --version-code 1 --version-name 1.0
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test_diff
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := version_2_diff
-LOCAL_AAPT_FLAGS := --version-code 2 --version-name 2.0
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test_diff
-include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/version_nosys/Android.bp b/core/tests/coretests/apks/version_nosys/Android.bp
new file mode 100644
index 0000000..5756678
--- /dev/null
+++ b/core/tests/coretests/apks/version_nosys/Android.bp
@@ -0,0 +1,10 @@
+android_test_helper_app {
+    name: "FrameworksCoreTests_version_1_nosys",
+    defaults: ["FrameworksCoreTests_apks_defaults"],
+    srcs: ["**/*.java"],
+    aaptflags: [
+        "--version-code 1",
+        "--version-name 1.0",
+    ],
+    certificate: ":FrameworksCoreTests_unit_test_cert",
+}
diff --git a/core/tests/coretests/apks/version_nosys/Android.mk b/core/tests/coretests/apks/version_nosys/Android.mk
deleted file mode 100644
index bbc8e12..0000000
--- a/core/tests/coretests/apks/version_nosys/Android.mk
+++ /dev/null
@@ -1,9 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_PACKAGE_NAME := version_1_nosys
-LOCAL_AAPT_FLAGS := --version-code 1 --version-name 1.0
-LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test
-include $(FrameworkCoreTests_BUILD_PACKAGE)
-
diff --git a/core/tests/coretests/certs/Android.bp b/core/tests/coretests/certs/Android.bp
new file mode 100644
index 0000000..bd5c829
--- /dev/null
+++ b/core/tests/coretests/certs/Android.bp
@@ -0,0 +1,14 @@
+android_app_certificate {
+    name: "FrameworksCoreTests_keyset_A_cert",
+    certificate: "keyset_A",
+}
+
+android_app_certificate {
+    name: "FrameworksCoreTests_keyset_B_cert",
+    certificate: "keyset_B",
+}
+
+android_app_certificate {
+    name: "FrameworksCoreTests_unit_test_cert",
+    certificate: "unit_test",
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 9fabe44..d73c174 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -28,6 +28,7 @@
 import android.app.IInstrumentationWatcher;
 import android.app.IUiAutomationConnection;
 import android.app.ProfilerInfo;
+import android.content.AutofillOptions;
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
 import android.content.IIntentReceiver;
@@ -407,7 +408,7 @@
                 IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1,
                 boolean b2, boolean b3, Configuration configuration,
                 CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1,
-                boolean autofillCompatEnabled, ContentCaptureOptions o) throws RemoteException {
+                AutofillOptions ao, ContentCaptureOptions co) throws RemoteException {
         }
 
         @Override
diff --git a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
index bb535b6..30cc7ff 100644
--- a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
+++ b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
@@ -46,11 +46,11 @@
                 RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 2));
         assertEqualsContract(one, two);
 
-        RulesState differentSystemRules = new RulesState(
+        RulesState differentBaseRules = new RulesState(
                 "2016b", formatVersion(1, 2), false /* operationInProgress */,
                 RulesState.STAGED_OPERATION_INSTALL, rulesVersion("2016a", 3),
                 RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 2));
-        assertFalse(one.equals(differentSystemRules));
+        assertFalse(one.equals(differentBaseRules));
 
         RulesState differentFormatVersion = new RulesState(
                 "2016a", formatVersion(1, 1), false /* operationInProgress */,
@@ -121,14 +121,14 @@
     }
 
     @Test
-    public void isSystemVersionNewerThan() {
+    public void isBaseVersionNewerThan() {
         RulesState rulesState = new RulesState(
                 "2016b", formatVersion(1, 1), false /* operationInProgress */,
                 RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
                 RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 3));
-        assertTrue(rulesState.isSystemVersionNewerThan(rulesVersion("2016a", 1)));
-        assertFalse(rulesState.isSystemVersionNewerThan(rulesVersion("2016b", 1)));
-        assertFalse(rulesState.isSystemVersionNewerThan(rulesVersion("2016c", 1)));
+        assertTrue(rulesState.isBaseVersionNewerThan(rulesVersion("2016a", 1)));
+        assertFalse(rulesState.isBaseVersionNewerThan(rulesVersion("2016b", 1)));
+        assertFalse(rulesState.isBaseVersionNewerThan(rulesVersion("2016c", 1)));
     }
 
     private static void assertEqualsContract(RulesState one, RulesState two) {
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
index 0ed76dc..125b9ff 100644
--- a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
+++ b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
@@ -37,37 +37,37 @@
     private static final String OTHER_LIBRARY = "other.library";
 
     @Test
-    public void targeted_at_O() {
+    public void targeted_at_P() {
         PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O);
+                .targetSdkVersion(Build.VERSION_CODES.P);
 
         // Should add org.apache.http.legacy.
         PackageBuilder after = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
+                .targetSdkVersion(Build.VERSION_CODES.P)
                 .requiredLibraries(ANDROID_TEST_BASE);
 
         checkBackwardsCompatibility(before, after);
     }
 
     @Test
-    public void targeted_at_O_not_empty_usesLibraries() {
+    public void targeted_at_P_not_empty_usesLibraries() {
         PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
+                .targetSdkVersion(Build.VERSION_CODES.P)
                 .requiredLibraries(OTHER_LIBRARY);
 
         // The org.apache.http.legacy jar should be added at the start of the list because it
         // is not on the bootclasspath and the package targets pre-P.
         PackageBuilder after = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
+                .targetSdkVersion(Build.VERSION_CODES.P)
                 .requiredLibraries(ANDROID_TEST_BASE, OTHER_LIBRARY);
 
         checkBackwardsCompatibility(before, after);
     }
 
     @Test
-    public void targeted_at_O_in_usesLibraries() {
+    public void targeted_at_P_in_usesLibraries() {
         PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
+                .targetSdkVersion(Build.VERSION_CODES.P)
                 .requiredLibraries(ANDROID_TEST_BASE);
 
         // No change is required because although org.apache.http.legacy has been removed from
@@ -76,9 +76,9 @@
     }
 
     @Test
-    public void targeted_at_O_in_usesOptionalLibraries() {
+    public void targeted_at_P_in_usesOptionalLibraries() {
         PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
+                .targetSdkVersion(Build.VERSION_CODES.P)
                 .optionalLibraries(ANDROID_TEST_BASE);
 
         // No change is required because although org.apache.http.legacy has been removed from
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 17e9654..5d5754b 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -18,15 +18,11 @@
 
 import static android.provider.DeviceConfig.OnPropertyChangedListener;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.fail;
+import static com.google.common.truth.Truth.assertThat;
 
 import android.app.ActivityThread;
 import android.content.ContentResolver;
 import android.os.Bundle;
-import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
@@ -37,8 +33,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /** Tests that ensure appropriate settings are backed up. */
 @Presubmit
@@ -51,8 +47,6 @@
     private static final String sValue = "value1";
     private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
 
-    private final Object mLock = new Object();
-
     @After
     public void cleanUp() {
         deleteViaContentProvider(sNamespace, sKey);
@@ -61,14 +55,14 @@
     @Test
     public void getProperty_empty() {
         String result = DeviceConfig.getProperty(sNamespace, sKey);
-        assertNull(result);
+        assertThat(result).isNull();
     }
 
     @Test
     public void setAndGetProperty_sameNamespace() {
         DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
         String result = DeviceConfig.getProperty(sNamespace, sKey);
-        assertEquals(sValue, result);
+        assertThat(result).isEqualTo(sValue);
     }
 
     @Test
@@ -76,7 +70,7 @@
         String newNamespace = "namespace2";
         DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
         String result = DeviceConfig.getProperty(newNamespace, sKey);
-        assertNull(result);
+        assertThat(result).isNull();
     }
 
     @Test
@@ -86,9 +80,9 @@
         DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
         DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
         String result = DeviceConfig.getProperty(sNamespace, sKey);
-        assertEquals(sValue, result);
+        assertThat(result).isEqualTo(sValue);
         result = DeviceConfig.getProperty(newNamespace, sKey);
-        assertEquals(newValue, result);
+        assertThat(result).isEqualTo(newValue);
 
         // clean up
         deleteViaContentProvider(newNamespace, sKey);
@@ -100,59 +94,30 @@
         DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
         DeviceConfig.setProperty(sNamespace, sKey, newValue, false);
         String result = DeviceConfig.getProperty(sNamespace, sKey);
-        assertEquals(newValue, result);
+        assertThat(result).isEqualTo(newValue);
     }
 
     @Test
-    public void testListener() {
-        setPropertyAndAssertSuccessfulChange(sNamespace, sKey, sValue);
-    }
+    public void testListener() throws InterruptedException {
+        CountDownLatch countDownLatch = new CountDownLatch(1);
 
-    private void setPropertyAndAssertSuccessfulChange(String setNamespace, String setName,
-            String setValue) {
-        final AtomicBoolean success = new AtomicBoolean();
+        OnPropertyChangedListener changeListener = (namespace, name, value) -> {
+            assertThat(namespace).isEqualTo(sNamespace);
+            assertThat(name).isEqualTo(sKey);
+            assertThat(value).isEqualTo(sValue);
+            countDownLatch.countDown();
+        };
 
-        OnPropertyChangedListener changeListener = new OnPropertyChangedListener() {
-                    @Override
-                    public void onPropertyChanged(String namespace, String name, String value) {
-                        assertEquals(setNamespace, namespace);
-                        assertEquals(setName, name);
-                        assertEquals(setValue, value);
-                        success.set(true);
-
-                        synchronized (mLock) {
-                            mLock.notifyAll();
-                        }
-                    }
-                };
-        Executor executor = ActivityThread.currentApplication().getMainExecutor();
-        DeviceConfig.addOnPropertyChangedListener(setNamespace, executor, changeListener);
         try {
-            DeviceConfig.setProperty(setNamespace, setName, setValue, false);
-
-            final long startTimeMillis = SystemClock.uptimeMillis();
-            synchronized (mLock) {
-                while (true) {
-                    if (success.get()) {
-                        return;
-                    }
-                    final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
-                    if (elapsedTimeMillis >= WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS) {
-                        fail("Could not change setting for "
-                                + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS + " ms");
-                    }
-                    final long remainingTimeMillis = WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS
-                            - elapsedTimeMillis;
-                    try {
-                        mLock.wait(remainingTimeMillis);
-                    } catch (InterruptedException ie) {
-                        /* ignore */
-                    }
-                }
-            }
+            DeviceConfig.addOnPropertyChangedListener(sNamespace,
+                    ActivityThread.currentApplication().getMainExecutor(), changeListener);
+            DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+            assertThat(countDownLatch.await(
+                    WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
         } finally {
             DeviceConfig.removeOnPropertyChangedListener(changeListener);
         }
+
     }
 
     private static boolean deleteViaContentProvider(String namespace, String key) {
@@ -160,7 +125,7 @@
         String compositeName = namespace + "/" + key;
         Bundle result = resolver.call(
                 DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, compositeName, null);
-        assertNotNull(result);
+        assertThat(result).isNotNull();
         return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
     }
 
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 46cac7a..23cd963 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -319,6 +319,7 @@
                     Settings.Global.LOW_POWER_MODE_STICKY,
                     Settings.Global.LOW_POWER_MODE_SUGGESTION_PARAMS,
                     Settings.Global.LTE_SERVICE_FORCED,
+                    Settings.Global.LID_BEHAVIOR,
                     Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
                     Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
                     Settings.Global.MDC_INITIAL_MAX_RETRY,
@@ -429,6 +430,7 @@
                     Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
                     Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
                     Settings.Global.SHOW_TEMPERATURE_WARNING,
+                    Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
                     Settings.Global.SIGNED_CONFIG_VERSION,
                     Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
                     Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
@@ -504,7 +506,6 @@
                     Settings.Global.USER_SWITCHER_ENABLED,
                     Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
                     Settings.Global.WARNING_TEMPERATURE,
-                    Settings.Global.USB_ALARM_TEMPERATURE,
                     Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
                     Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED,
                     Settings.Global.WEBVIEW_MULTIPROCESS,
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index a88968b..e3852e1 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -301,26 +301,6 @@
     }
 
     @Test
-    public void subTreeChangeEventFromUncachedNode_clearsNodeInCache() {
-        AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
-        long id = nodeInfo.getSourceNodeId();
-        mAccessibilityCache.add(nodeInfo);
-        nodeInfo.recycle();
-
-        AccessibilityEvent event = AccessibilityEvent
-                .obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-        event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
-        event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
-
-        mAccessibilityCache.onAccessibilityEvent(event);
-        AccessibilityNodeInfo shouldBeNull = mAccessibilityCache.getNode(WINDOW_ID_1, id);
-        if (shouldBeNull != null) {
-            shouldBeNull.recycle();
-        }
-        assertNull(shouldBeNull);
-    }
-
-    @Test
     public void scrollEvent_clearsNodeAndChild() {
         AccessibilityEvent event = AccessibilityEvent
                 .obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index b6717e1..013408e 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -157,7 +157,7 @@
         }
 
         @Override
-        public void internalNotifyViewHierarchyEvent(boolean started) {
+        public void internalNotifyViewTreeEvent(boolean started) {
             throw new UnsupportedOperationException("should not have been called");
         }
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/LabeledIntentTest.java b/core/tests/coretests/src/android/view/textclassifier/LabeledIntentTest.java
new file mode 100644
index 0000000..e4e9cde
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/LabeledIntentTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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 static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+import androidx.test.InstrumentationRegistry;
+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 Intent INTENT =
+            new Intent(Intent.ACTION_VIEW).setDataAndNormalize(Uri.parse("http://www.android.com"));
+    private static final int REQUEST_CODE = 42;
+    private Context mContext;
+
+    @Before
+    public void setup() {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void resolve_preferTitleWithEntity() {
+        LabeledIntent labeledIntent = new LabeledIntent(
+                TITLE_WITHOUT_ENTITY,
+                TITLE_WITH_ENTITY,
+                DESCRIPTION,
+                INTENT,
+                REQUEST_CODE
+        );
+
+        LabeledIntent.Result result =
+                labeledIntent.resolve(mContext, /*titleChooser*/ null);
+
+        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();
+    }
+
+    @Test
+    public void resolve_useAvailableTitle() {
+        LabeledIntent labeledIntent = new LabeledIntent(
+                TITLE_WITHOUT_ENTITY,
+                null,
+                DESCRIPTION,
+                INTENT,
+                REQUEST_CODE
+        );
+
+        LabeledIntent.Result result =
+                labeledIntent.resolve(mContext, /*titleChooser*/ null);
+
+        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,
+                INTENT,
+                REQUEST_CODE
+        );
+
+        LabeledIntent.Result result =
+                labeledIntent.resolve(mContext, (labeledIntent1, resolveInfo) -> "chooser");
+
+        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,
+                INTENT,
+                REQUEST_CODE
+        );
+
+        LabeledIntent.Result result =
+                labeledIntent.resolve(mContext, (labeledIntent1, resolveInfo) -> null);
+
+        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,
+                                INTENT,
+                                REQUEST_CODE
+                        ));
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/LegacyIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/LegacyIntentFactoryTest.java
index 73d3eec..743818c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/LegacyIntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/LegacyIntentFactoryTest.java
@@ -58,9 +58,12 @@
                         null,
                         null,
                         null,
+                        null,
+                        null,
+                        null,
                         null);
 
-        List<TextClassifierImpl.LabeledIntent> intents = mLegacyIntentFactory.create(
+        List<LabeledIntent> intents = mLegacyIntentFactory.create(
                 InstrumentationRegistry.getContext(),
                 TEXT,
                 /* foreignText */ false,
@@ -68,8 +71,8 @@
                 classificationResult);
 
         assertThat(intents).hasSize(1);
-        TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
-        Intent intent = labeledIntent.getIntent();
+        LabeledIntent labeledIntent = intents.get(0);
+        Intent intent = labeledIntent.intent;
         assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
         assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT);
         assertThat(
@@ -89,9 +92,12 @@
                         null,
                         null,
                         null,
+                        null,
+                        null,
+                        null,
                         null);
 
-        List<TextClassifierImpl.LabeledIntent> intents = mLegacyIntentFactory.create(
+        List<LabeledIntent> intents = mLegacyIntentFactory.create(
                 InstrumentationRegistry.getContext(),
                 TEXT,
                 /* foreignText */ true,
@@ -99,7 +105,7 @@
                 classificationResult);
 
         assertThat(intents).hasSize(2);
-        assertThat(intents.get(0).getIntent().getAction()).isEqualTo(Intent.ACTION_DEFINE);
-        assertThat(intents.get(1).getIntent().getAction()).isEqualTo(Intent.ACTION_TRANSLATE);
+        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/TemplateClassificationIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java
index d9dac31..9fd9e8e 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java
@@ -40,7 +40,7 @@
 public class TemplateClassificationIntentFactoryTest {
 
     private static final String TEXT = "text";
-    private static final String TITLE = "Map";
+    private static final String TITLE_WITHOUT_ENTITY = "Map";
     private static final String DESCRIPTION = "Opens in Maps";
     private static final String ACTION = Intent.ACTION_VIEW;
 
@@ -69,9 +69,12 @@
                         null,
                         null,
                         null,
+                        null,
+                        null,
+                        null,
                         createRemoteActionTemplates());
 
-        List<TextClassifierImpl.LabeledIntent> intents =
+        List<LabeledIntent> intents =
                 mTemplateClassificationIntentFactory.create(
                         InstrumentationRegistry.getContext(),
                         TEXT,
@@ -80,14 +83,14 @@
                         classificationResult);
 
         assertThat(intents).hasSize(2);
-        TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
-        assertThat(labeledIntent.getTitle()).isEqualTo(TITLE);
-        Intent intent = labeledIntent.getIntent();
+        LabeledIntent labeledIntent = intents.get(0);
+        assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
+        Intent intent = labeledIntent.intent;
         assertThat(intent.getAction()).isEqualTo(ACTION);
         assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue();
 
         labeledIntent = intents.get(1);
-        intent = labeledIntent.getIntent();
+        intent = labeledIntent.intent;
         assertThat(intent.getAction()).isEqualTo(Intent.ACTION_TRANSLATE);
         assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue();
     }
@@ -105,9 +108,12 @@
                         null,
                         null,
                         null,
+                        null,
+                        null,
+                        null,
                         createRemoteActionTemplates());
 
-        List<TextClassifierImpl.LabeledIntent> intents =
+        List<LabeledIntent> intents =
                 mTemplateClassificationIntentFactory.create(
                         InstrumentationRegistry.getContext(),
                         TEXT,
@@ -116,9 +122,9 @@
                         classificationResult);
 
         assertThat(intents).hasSize(1);
-        TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
-        assertThat(labeledIntent.getTitle()).isEqualTo(TITLE);
-        Intent intent = labeledIntent.getIntent();
+        LabeledIntent labeledIntent = intents.get(0);
+        assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
+        Intent intent = labeledIntent.intent;
         assertThat(intent.getAction()).isEqualTo(ACTION);
         assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue();
     }
@@ -126,7 +132,8 @@
     private static RemoteActionTemplate[] createRemoteActionTemplates() {
         return new RemoteActionTemplate[]{
                 new RemoteActionTemplate(
-                        TITLE,
+                        TITLE_WITHOUT_ENTITY,
+                        null,
                         DESCRIPTION,
                         ACTION,
                         null,
diff --git a/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java
index a1158a7..1860734 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java
@@ -38,7 +38,8 @@
 public class TemplateIntentFactoryTest {
 
     private static final String TEXT = "text";
-    private static final String TITLE = "Map";
+    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 ACTION = Intent.ACTION_VIEW;
     private static final String DATA = Uri.parse("http://www.android.com").toString();
@@ -69,7 +70,8 @@
     @Test
     public void create_full() {
         RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
-                TITLE,
+                TITLE_WITHOUT_ENTITY,
+                TITLE_WITH_ENTITY,
                 DESCRIPTION,
                 ACTION,
                 DATA,
@@ -81,15 +83,16 @@
                 REQUEST_CODE
         );
 
-        List<TextClassifierImpl.LabeledIntent> intents =
+        List<LabeledIntent> intents =
                 mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate});
 
         assertThat(intents).hasSize(1);
-        TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
-        assertThat(labeledIntent.getTitle()).isEqualTo(TITLE);
-        assertThat(labeledIntent.getDescription()).isEqualTo(DESCRIPTION);
-        assertThat(labeledIntent.getRequestCode()).isEqualTo(REQUEST_CODE);
-        Intent intent = labeledIntent.getIntent();
+        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.requestCode).isEqualTo(REQUEST_CODE);
+        Intent intent = labeledIntent.intent;
         assertThat(intent.getAction()).isEqualTo(ACTION);
         assertThat(intent.getData().toString()).isEqualTo(DATA);
         assertThat(intent.getType()).isEqualTo(TYPE);
@@ -104,7 +107,8 @@
     @Test
     public void normalizesScheme() {
         RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
-                TITLE,
+                TITLE_WITHOUT_ENTITY,
+                TITLE_WITH_ENTITY,
                 DESCRIPTION,
                 ACTION,
                 "HTTp://www.android.com",
@@ -116,17 +120,18 @@
                 REQUEST_CODE
         );
 
-        List<TextClassifierImpl.LabeledIntent> intents =
+        List<LabeledIntent> intents =
                 mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
 
-        String data = intents.get(0).getIntent().getData().toString();
+        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,
+                TITLE_WITHOUT_ENTITY,
+                null,
                 DESCRIPTION,
                 ACTION,
                 null,
@@ -138,16 +143,17 @@
                 null
         );
 
-        List<TextClassifierImpl.LabeledIntent> intents =
+        List<LabeledIntent> intents =
                 mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate});
 
         assertThat(intents).hasSize(1);
-        TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
-        assertThat(labeledIntent.getTitle()).isEqualTo(TITLE);
-        assertThat(labeledIntent.getDescription()).isEqualTo(DESCRIPTION);
-        assertThat(labeledIntent.getRequestCode()).isEqualTo(
-                TextClassifierImpl.LabeledIntent.DEFAULT_REQUEST_CODE);
-        Intent intent = labeledIntent.getIntent();
+        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();
@@ -161,7 +167,7 @@
     public void invalidTemplate_nullTemplate() {
         RemoteActionTemplate remoteActionTemplate = null;
 
-        List<TextClassifierImpl.LabeledIntent> intents =
+        List<LabeledIntent> intents =
                 mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
 
         assertThat(intents).isEmpty();
@@ -170,7 +176,8 @@
     @Test
     public void invalidTemplate_nonEmptyPackageName() {
         RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
-                TITLE,
+                TITLE_WITHOUT_ENTITY,
+                TITLE_WITH_ENTITY,
                 DESCRIPTION,
                 ACTION,
                 DATA,
@@ -182,7 +189,7 @@
                 REQUEST_CODE
         );
 
-        List<TextClassifierImpl.LabeledIntent> intents =
+        List<LabeledIntent> intents =
                 mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
 
         assertThat(intents).isEmpty();
@@ -192,6 +199,7 @@
     public void invalidTemplate_emptyTitle() {
         RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
                 null,
+                null,
                 DESCRIPTION,
                 ACTION,
                 null,
@@ -203,7 +211,7 @@
                 null
         );
 
-        List<TextClassifierImpl.LabeledIntent> intents =
+        List<LabeledIntent> intents =
                 mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
 
         assertThat(intents).isEmpty();
@@ -212,7 +220,8 @@
     @Test
     public void invalidTemplate_emptyDescription() {
         RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
-                TITLE,
+                TITLE_WITHOUT_ENTITY,
+                TITLE_WITH_ENTITY,
                 null,
                 ACTION,
                 null,
@@ -224,7 +233,7 @@
                 null
         );
 
-        List<TextClassifierImpl.LabeledIntent> intents =
+        List<LabeledIntent> intents =
                 mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
 
         assertThat(intents).isEmpty();
@@ -233,7 +242,8 @@
     @Test
     public void invalidTemplate_emptyIntentAction() {
         RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
-                TITLE,
+                TITLE_WITHOUT_ENTITY,
+                TITLE_WITH_ENTITY,
                 DESCRIPTION,
                 null,
                 null,
@@ -245,7 +255,7 @@
                 null
         );
 
-        List<TextClassifierImpl.LabeledIntent> intents =
+        List<LabeledIntent> intents =
                 mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
 
         assertThat(intents).isEmpty();
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index 99c959e..d544029 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -93,8 +93,8 @@
         final String id = "id";
         final TextClassification reference = new TextClassification.Builder()
                 .setText(text)
-                .addAction(remoteAction0)
-                .addAction(remoteAction1)
+                .addAction(remoteAction0)  // Action intent not included.
+                .addAction(remoteAction1)  // Action intent not included.
                 .setEntityType(TextClassifier.TYPE_ADDRESS, 0.3f)
                 .setEntityType(TextClassifier.TYPE_PHONE, 0.7f)
                 .setId(id)
@@ -132,6 +132,7 @@
 
         // Extras
         assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+        assertNull(ExtrasUtils.getActionsIntents(result));
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index f27f3f9..9fbc166 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -57,7 +57,7 @@
 import com.android.internal.R;
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -524,15 +524,45 @@
         waitForIdle();
         verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture());
         assertThat(logMakerCaptor.getAllValues().get(0).getCategory(),
-                is(MetricsProto.MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN));
+                is(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN));
         assertThat(logMakerCaptor
                 .getAllValues().get(0)
-                .getTaggedData(MetricsProto.MetricsEvent.FIELD_TIME_TO_APP_TARGETS),
+                .getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS),
                 is(notNullValue()));
         assertThat(logMakerCaptor
                 .getAllValues().get(0)
-                .getTaggedData(MetricsProto.MetricsEvent.FIELD_SHARESHEET_MIMETYPE),
+                .getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE),
                 is("TestType"));
+        assertThat(logMakerCaptor
+                        .getAllValues().get(0)
+                        .getSubtype(),
+                is(MetricsEvent.PARENT_PROFILE));
+    }
+
+    @Test
+    public void testOnCreateLoggingFromWorkProfile() {
+        Intent sendIntent = createSendTextIntent();
+        sendIntent.setType("TestType");
+        sOverrides.alternateProfileSetting = MetricsEvent.MANAGED_PROFILE;
+        MetricsLogger mockLogger = sOverrides.metricsLogger;
+        ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+        mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test"));
+        waitForIdle();
+        verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture());
+        assertThat(logMakerCaptor.getAllValues().get(0).getCategory(),
+                is(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN));
+        assertThat(logMakerCaptor
+                        .getAllValues().get(0)
+                        .getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS),
+                is(notNullValue()));
+        assertThat(logMakerCaptor
+                        .getAllValues().get(0)
+                        .getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE),
+                is("TestType"));
+        assertThat(logMakerCaptor
+                        .getAllValues().get(0)
+                        .getSubtype(),
+                is(MetricsEvent.MANAGED_PROFILE));
     }
 
     @Test
@@ -547,7 +577,7 @@
         verify(mockLogger, Mockito.times(1)).write(logMakerCaptor.capture());
         // First invocation is from onCreate
         assertThat(logMakerCaptor.getAllValues().get(0).getCategory(),
-                is(MetricsProto.MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN));
+                is(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN));
     }
 
     @Test
@@ -569,7 +599,7 @@
         verify(mockLogger, Mockito.times(3)).write(logMakerCaptor.capture());
         // First invocation is from onCreate
         assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
-                is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
+                is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
         assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(),
                 is(CONTENT_PREVIEW_TEXT));
     }
@@ -599,11 +629,11 @@
         verify(mockLogger, Mockito.times(3)).write(logMakerCaptor.capture());
         // First invocation is from onCreate
         assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
-                is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
+                is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
         assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(),
                 is(CONTENT_PREVIEW_IMAGE));
         assertThat(logMakerCaptor.getAllValues().get(2).getCategory(),
-                is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
+                is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
         assertThat(logMakerCaptor.getAllValues().get(2).getSubtype(),
                 is(CONTENT_PREVIEW_IMAGE));
     }
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 57c84ff..a8dd69a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -28,6 +28,7 @@
 import android.util.Size;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
 import java.util.function.Function;
 
@@ -112,6 +113,14 @@
         return super.queryResolver(resolver, uri);
     }
 
+    @Override
+    protected boolean isWorkProfile() {
+        if (sOverrides.alternateProfileSetting != 0) {
+            return sOverrides.alternateProfileSetting == MetricsEvent.MANAGED_PROFILE;
+        }
+        return super.isWorkProfile();
+    }
+
     /**
      * We cannot directly mock the activity created since instrumentation creates it.
      * <p>
@@ -128,6 +137,7 @@
         public boolean resolverForceException;
         public Bitmap previewThumbnail;
         public MetricsLogger metricsLogger;
+        public int alternateProfileSetting;
 
         public void reset() {
             onSafelyStartCallback = null;
@@ -139,6 +149,7 @@
             resolverForceException = false;
             resolverListController = mock(ResolverListController.class);
             metricsLogger = mock(MetricsLogger.class);
+            alternateProfileSetting = 0;
         }
     }
 }
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 9b13af2..abfb4fb 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -40,6 +41,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
+import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -51,6 +53,9 @@
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -70,6 +75,11 @@
                     "android",
                     IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE
             );
+    private static final ComponentName FORWARD_TO_PARENT_COMPONENT_NAME =
+            new ComponentName(
+                    "android",
+                    IntentForwarderActivity.FORWARD_INTENT_TO_PARENT
+            );
     private static final String TYPE_PLAIN_TEXT = "text/plain";
 
     private static UserInfo MANAGED_PROFILE_INFO = new UserInfo();
@@ -522,6 +532,60 @@
         verify(sInjector).showToast(anyInt(), anyInt());
     }
 
+    @Test
+    public void forwardToManagedProfile_LoggingTest() throws Exception {
+        sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
+
+        // Intent can be forwarded.
+        when(mIPm.canForwardTo(
+                any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true);
+
+        // Managed profile exists.
+        List<UserInfo> profiles = new ArrayList<>();
+        profiles.add(CURRENT_USER_INFO);
+        profiles.add(MANAGED_PROFILE_INFO);
+        when(mUserManager.getProfiles(anyInt())).thenReturn(profiles);
+
+        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class);
+        intent.setAction(Intent.ACTION_SEND);
+        intent.setType(TYPE_PLAIN_TEXT);
+        IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent);
+
+        ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+        verify(activity.getMetricsLogger()).write(logMakerCaptor.capture());
+        assertEquals(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE,
+                logMakerCaptor.getValue().getCategory());
+        assertEquals(MetricsEvent.MANAGED_PROFILE,
+                logMakerCaptor.getValue().getSubtype());
+    }
+
+    @Test
+    public void forwardToParent_LoggingTest() throws Exception {
+        sComponentName = FORWARD_TO_PARENT_COMPONENT_NAME;
+
+        // Intent can be forwarded.
+        when(mIPm.canForwardTo(
+                any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true);
+
+        // Managed profile exists.
+        List<UserInfo> profiles = new ArrayList<>();
+        profiles.add(CURRENT_USER_INFO);
+        profiles.add(MANAGED_PROFILE_INFO);
+        when(mUserManager.getProfiles(anyInt())).thenReturn(profiles);
+
+        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class);
+        intent.setAction(Intent.ACTION_SEND);
+        intent.setType(TYPE_PLAIN_TEXT);
+        IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent);
+
+        ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+        verify(activity.getMetricsLogger()).write(logMakerCaptor.capture());
+        assertEquals(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE,
+                logMakerCaptor.getValue().getCategory());
+        assertEquals(MetricsEvent.PARENT_PROFILE,
+                logMakerCaptor.getValue().getSubtype());
+    }
+
     private void setupShouldSkipDisclosureTest() throws RemoteException {
         sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
         sActivityName = "MyTestActivity";
@@ -541,6 +605,7 @@
 
         private Intent mStartActivityIntent;
         private int mUserIdActivityLaunchedIn;
+        private MetricsLogger mMetricsLogger = mock(MetricsLogger.class);
 
         @Override
         public void onCreate(@Nullable Bundle savedInstanceState) {
@@ -559,6 +624,11 @@
             mStartActivityIntent = intent;
             mUserIdActivityLaunchedIn = userId;
         }
+
+        @Override
+        protected MetricsLogger getMetricsLogger() {
+            return mMetricsLogger;
+        }
     }
 
     public class TestInjector implements IntentForwarderActivity.Injector {
diff --git a/core/tests/utillib/Android.bp b/core/tests/utillib/Android.bp
new file mode 100644
index 0000000..1f742c2
--- /dev/null
+++ b/core/tests/utillib/Android.bp
@@ -0,0 +1,22 @@
+// Copyright (C) 2010 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.
+
+java_library {
+    name: "frameworks-core-util-lib",
+
+    srcs: ["**/*.java"],
+
+    static_libs: ["junit"],
+    libs: ["android.test.base"],
+}
diff --git a/core/tests/utillib/Android.mk b/core/tests/utillib/Android.mk
deleted file mode 100644
index be1ab1f..0000000
--- a/core/tests/utillib/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (C) 2010 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_MODULE := frameworks-core-util-lib
-LOCAL_STATIC_JAVA_LIBRARIES := junit
-LOCAL_JAVA_LIBRARIES := android.test.base
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 8d3bac8..920fb4c 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -836,7 +836,7 @@
      * @return A bitmap that represents the specified subset of source
      * @throws IllegalArgumentException if the x, y, width, height values are
      *         outside of the dimensions of the source bitmap, or width is <= 0,
-     *         or height is <= 0
+     *         or height is <= 0, or if the source bitmap has already been recycled
      */
     public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height,
             @Nullable Matrix m, boolean filter) {
@@ -849,6 +849,9 @@
         if (y + height > source.getHeight()) {
             throw new IllegalArgumentException("y + height must be <= bitmap.height()");
         }
+        if (source.isRecycled()) {
+            throw new IllegalArgumentException("cannot use a recycled source in createBitmap");
+        }
 
         // check if we can just return our argument unchanged
         if (!source.isMutable() && x == 0 && y == 0 && width == source.getWidth() &&
@@ -1270,7 +1273,7 @@
             node.setLeftTopRightBottom(0, 0, width, height);
             node.setClipToBounds(false);
             node.setForceDarkAllowed(false);
-            final RecordingCanvas canvas = node.startRecording(width, height);
+            final RecordingCanvas canvas = node.beginRecording(width, height);
             if (source.getWidth() != width || source.getHeight() != height) {
                 canvas.scale(width / (float) source.getWidth(),
                         height / (float) source.getHeight());
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index b020556..e623354 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -258,7 +258,7 @@
      *                and the renderer will draw nothing.
      */
     public void setContentRoot(@Nullable RenderNode content) {
-        RecordingCanvas canvas = mRootNode.startRecording();
+        RecordingCanvas canvas = mRootNode.beginRecording();
         if (content != null) {
             canvas.drawRenderNode(content);
         }
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 6264774..15d855e 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -16,7 +16,43 @@
 
 package android.graphics;
 
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 public class ImageFormat {
+     /** @hide */
+     @Retention(RetentionPolicy.SOURCE)
+     @IntDef(value = {
+             UNKNOWN,
+             RGB_565,
+             YV12,
+             Y8,
+             Y16,
+             NV16,
+             NV21,
+             YUY2,
+             JPEG,
+             DEPTH_JPEG,
+             YUV_420_888,
+             YUV_422_888,
+             YUV_444_888,
+             FLEX_RGB_888,
+             FLEX_RGBA_8888,
+             RAW_SENSOR,
+             RAW_PRIVATE,
+             RAW10,
+             RAW12,
+             DEPTH16,
+             DEPTH_POINT_CLOUD,
+             RAW_DEPTH,
+             PRIVATE,
+             HEIC
+     })
+     public @interface Format {
+     }
+
     /*
      * these constants are chosen to be binary compatible with their previous
      * location in PixelFormat.java
@@ -731,7 +767,7 @@
      * @return the number of bits per pixel of the given format or -1 if the
      *         format doesn't exist or is not supported.
      */
-    public static int getBitsPerPixel(int format) {
+    public static int getBitsPerPixel(@Format int format) {
         switch (format) {
             case RGB_565:
                 return 16;
@@ -781,7 +817,7 @@
      *
      * @hide
      */
-    public static boolean isPublicFormat(int format) {
+    public static boolean isPublicFormat(@Format int format) {
         switch (format) {
             case RGB_565:
             case NV16:
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 515532f..30466e1 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -31,7 +31,7 @@
  * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being released while
  * the RecordingCanvas is still holding a native reference to the memory.
  *
- * This is obtained by calling {@link RenderNode#startRecording()} and is valid until the matching
+ * This is obtained by calling {@link RenderNode#beginRecording()} and is valid until the matching
  * {@link RenderNode#endRecording()} is called. It must not be retained beyond that as it is
  * internally reused.
  */
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 09b18b7..e98879d 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -16,6 +16,9 @@
 
 package android.graphics;
 
+import android.annotation.BytesLong;
+import android.annotation.ColorInt;
+import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -59,7 +62,7 @@
  * <pre class="prettyprint">
  *     RenderNode renderNode = RenderNode.create("myRenderNode");
  *     renderNode.setLeftTopRightBottom(0, 0, 50, 50); // Set the size to 50x50
- *     RecordingCanvas canvas = renderNode.startRecording();
+ *     RecordingCanvas canvas = renderNode.beginRecording();
  *     try {
  *         // Draw with the canvas
  *         canvas.drawRect(...);
@@ -71,14 +74,13 @@
  * <h3>Drawing a RenderNode in a View</h3>
  * <pre class="prettyprint">
  *     protected void onDraw(Canvas canvas) {
- *         if (canvas instanceof RecordingCanvas) {
- *             RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+ *         if (canvas.isHardwareAccelerated()) {
  *             // Check that the RenderNode has a display list, re-recording it if it does not.
  *             if (!myRenderNode.hasDisplayList()) {
  *                 updateDisplayList(myRenderNode);
  *             }
  *             // Draw the RenderNode into this canvas.
- *             recordingCanvas.drawRenderNode(myRenderNode);
+ *             canvas.drawRenderNode(myRenderNode);
  *         }
  *     }
  * </pre>
@@ -95,7 +97,7 @@
  *
  * <h3>Properties</h3>
  * <p>In addition, a RenderNode offers several properties, such as
- * {@link #setScaleX(float)} or {@link #setLeft(int)}, that can be used to affect all
+ * {@link #setScaleX(float)} or {@link #setTranslationX(float)}, that can be used to affect all
  * the drawing commands recorded within. For instance, these properties can be used
  * to move around a large number of images without re-issuing all the individual
  * <code>canvas.drawBitmap()</code> calls.</p>
@@ -104,7 +106,7 @@
  *     private void createDisplayList() {
  *         mRenderNode = RenderNode.create("MyRenderNode");
  *         mRenderNode.setLeftTopRightBottom(0, 0, width, height);
- *         RecordingCanvas canvas = mRenderNode.startRecording();
+ *         RecordingCanvas canvas = mRenderNode.beginRecording();
  *         try {
  *             for (Bitmap b : mBitmaps) {
  *                 canvas.drawBitmap(b, 0.0f, 0.0f, null);
@@ -116,9 +118,8 @@
  *     }
  *
  *     protected void onDraw(Canvas canvas) {
- *         if (canvas instanceof RecordingCanvas) {
- *             RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
- *             recordingCanvas.drawRenderNode(mRenderNode);
+ *         if (canvas.isHardwareAccelerated())
+ *             canvas.drawRenderNode(mRenderNode);
  *         }
  *     }
  *
@@ -263,19 +264,20 @@
      * stored in this display list.
      *
      * {@link #endRecording()} must be called when the recording is finished in order to apply
-     * the updated display list.
+     * the updated display list. Failing to call {@link #endRecording()} will result in an
+     * {@link IllegalStateException} if {@link #beginRecording(int, int)} is called again.
      *
      * @param width  The width of the recording viewport. This will not alter the width of the
-     *               RenderNode itself, that must be set with {@link #setLeft(int)} and
-     *               {@link #setRight(int)}
+     *               RenderNode itself, that must be set with {@link #setPosition(Rect)}.
      * @param height The height of the recording viewport. This will not alter the height of the
-     *               RenderNode itself, that must be set with {@link #setTop(int)} and
-     *               {@link #setBottom(int)}.
+     *               RenderNode itself, that must be set with {@link #setPosition(Rect)}.
      * @return A canvas to record drawing operations.
+     * @throws IllegalStateException If a recording is already in progress. That is, the previous
+     * call to {@link #beginRecording(int, int)} did not call {@link #endRecording()}.
      * @see #endRecording()
      * @see #hasDisplayList()
      */
-    public RecordingCanvas startRecording(int width, int height) {
+    public RecordingCanvas beginRecording(int width, int height) {
         if (mCurrentRecordingCanvas != null) {
             throw new IllegalStateException(
                     "Recording currently in progress - missing #endRecording() call?");
@@ -285,21 +287,18 @@
     }
 
     /**
-     * Same as {@link #startRecording(int, int)} with the width & height set
+     * Same as {@link #beginRecording(int, int)} with the width & height set
      * to the RenderNode's own width & height. The RenderNode's width & height may be set
-     * with {@link #setLeftTopRightBottom(int, int, int, int)}.
+     * with {@link #setPosition(int, int, int, int)}.
+     *
+     * @return A canvas to record drawing operations.
+     * @throws IllegalStateException If a recording is already in progress. That is, the previous
+     * call to {@link #beginRecording(int, int)} did not call {@link #endRecording()}.
+     * @see #endRecording()
+     * @see #hasDisplayList()
      */
-    public RecordingCanvas startRecording() {
-        return startRecording(nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode));
-    }
-
-    /**
-     * @hide
-     * @deprecated use {@link #startRecording(int, int)} instead
-     */
-    @Deprecated
-    public RecordingCanvas start(int width, int height) {
-        return startRecording(width, height);
+    public RecordingCanvas beginRecording() {
+        return beginRecording(nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode));
     }
 
     /**
@@ -307,13 +306,13 @@
      * Ends the recording for this display list. Calling this method marks
      * the display list valid and {@link #hasDisplayList()} will return true.
      *
-     * @see #startRecording(int, int)
+     * @see #beginRecording(int, int)
      * @see #hasDisplayList()
      */
     public void endRecording() {
         if (mCurrentRecordingCanvas == null) {
             throw new IllegalStateException(
-                    "No recording in progress, forgot to call #startRecording()?");
+                    "No recording in progress, forgot to call #beginRecording()?");
         }
         RecordingCanvas canvas = mCurrentRecordingCanvas;
         mCurrentRecordingCanvas = null;
@@ -324,13 +323,21 @@
 
     /**
      * @hide
+     * @deprecated use {@link #beginRecording(int, int)} instead
+     */
+    @Deprecated
+    public RecordingCanvas start(int width, int height) {
+        return beginRecording(width, height);
+    }
+
+    /**
+     * @hide
      * @deprecated use {@link #endRecording()} instead
      */
     @Deprecated
     public void end(RecordingCanvas canvas) {
-        if (mCurrentRecordingCanvas != canvas) {
-            throw new IllegalArgumentException(
-                    "Canvas given isn't the one that was returned from #startRecording");
+        if (canvas != mCurrentRecordingCanvas) {
+            throw new IllegalArgumentException("Wrong canvas");
         }
         endRecording();
     }
@@ -346,7 +353,7 @@
 
     /**
      * Returns whether the RenderNode has a display list. If this returns false, the RenderNode
-     * should be re-recorded with {@link #startRecording()} and {@link #endRecording()}.
+     * should be re-recorded with {@link #beginRecording()} and {@link #endRecording()}.
      *
      * A RenderNode without a display list may still be drawn, however it will have no impact
      * on the rendering content until its display list is updated.
@@ -425,18 +432,21 @@
      * for performance or required for the current combination of {@link #setAlpha(float)} and
      * {@link #setHasOverlappingRendering(boolean)}.
      *
-     * The usage of this is instead to allow for either overriding of the internal behavior
+     * <p>The usage of this is instead to allow for either overriding of the internal behavior
      * if it's measured to be necessary for the particular rendering content in question or, more
      * usefully, to add a composition effect to the RenderNode via the optional paint parameter.
      *
-     * Note: When a RenderNode is using a compositing layer it will also result in
+     * <p>Note: When a RenderNode is using a compositing layer it will also result in
      * clipToBounds=true behavior.
      *
      * @param forceToLayer if true this forces the RenderNode to use an intermediate buffer.
      *                     Default & generally recommended value is false.
      * @param paint        The blend mode, alpha, and ColorFilter to apply to the compositing layer.
-     *                     Only applies if forceToLayer is true.
-     * @return true if anything changed, false otherwise
+     *                     Only applies if forceToLayer is true. The paint's alpha is multiplied
+     *                     with {@link #getAlpha()} to resolve the final alpha of the RenderNode.
+     *                     If null then no additional composition effects are applied on top of the
+     *                     composition layer.
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setUseCompositingLayer(boolean forceToLayer, @Nullable Paint paint) {
         boolean didChange = nSetLayerType(mNativeRenderNode, forceToLayer ? 2 : 0);
@@ -457,15 +467,22 @@
     }
 
     /**
-     * Sets the clip bounds of the RenderNode. If null, the clip bounds is removed from the
-     * RenderNode. If non-null, the RenderNode will be clipped to this rect. If
+     * Sets an additional clip on the RenderNode. If null, the extra clip is removed from the
+     * RenderNode. If non-null, the RenderNode will be clipped to this rect. In addition  if
      * {@link #setClipToBounds(boolean)} is true, then the RenderNode will be clipped to the
-     * intersection of this rectangle and the bounds of the render node.
+     * intersection of this rectangle and the bounds of the render node, which is set with
+     * {@link #setPosition(Rect)}.
      *
-     * @param rect the bounds to clip to. If null, the clip bounds are reset
-     * @return True if the clip bounds changed, false otherwise
+     * <p>This is equivalent to do a {@link Canvas#clipRect(Rect)} at the start of this
+     * RenderNode's display list. However, as this is a property of the RenderNode instead
+     * of part of the display list it can be more easily animated for transient additional
+     * clipping. An example usage of this would be the {@link android.transition.ChangeBounds}
+     * transition animation with the resizeClip=true option.
+     *
+     * @param rect the bounds to clip to. If null, the additional clip is removed.
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
-    public boolean setClipBounds(@Nullable Rect rect) {
+    public boolean setClipRect(@Nullable Rect rect) {
         if (rect == null) {
             return nSetClipBoundsEmpty(mNativeRenderNode);
         } else {
@@ -477,11 +494,12 @@
      * Set whether the Render node should clip itself to its bounds. This defaults to true,
      * and is useful to the renderer in enable quick-rejection of chunks of the tree as well as
      * better partial invalidation support. Clipping can be further restricted or controlled
-     * through the combination of this property as well as {@link #setClipBounds(Rect)}, which
+     * through the combination of this property as well as {@link #setClipRect(Rect)}, which
      * allows for a different clipping rectangle to be used in addition to or instead of the
-     * {@link #setLeftTopRightBottom(int, int, int, int)} or the RenderNode.
+     * {@link #setPosition(int, int, int, int)} or the RenderNode.
      *
      * @param clipToBounds true if the display list should clip to its bounds, false otherwise.
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setClipToBounds(boolean clipToBounds) {
         return nSetClipToBounds(mNativeRenderNode, clipToBounds);
@@ -489,7 +507,7 @@
 
     /**
      * Returns whether or not the RenderNode is clipping to its bounds. See
-     * {@link #setClipToBounds(boolean)} and {@link #setLeftTopRightBottom(int, int, int, int)}
+     * {@link #setClipToBounds(boolean)} and {@link #setPosition(int, int, int, int)}
      *
      * @return true if the render node clips to its bounds, false otherwise.
      */
@@ -498,20 +516,58 @@
     }
 
     /**
-     * Sets whether the RenderNode should be drawn immediately after the
+     * <p>Sets whether the RenderNode should be drawn immediately after the
      * closest ancestor RenderNode containing a projection receiver.
      *
+     * <p>The default is false, and the rendering of this node happens in the typical draw order.
+     *
+     * <p>If true, then at rendering time this rendernode will not be drawn in order with the
+     * {@link Canvas#drawRenderNode(RenderNode)} command that drew this RenderNode, but instead
+     * it will be re-positioned in the RenderNode tree to be drawn on the closet ancestor with a
+     * child rendernode that has {@link #setProjectionReceiver(boolean)} as true.
+     *
+     * <p>The typical usage of this is to allow a child RenderNode to draw on a parent's background,
+     * such as the platform's usage with {@link android.graphics.drawable.RippleDrawable}. Consider
+     * the following structure, built out of which RenderNode called drawRenderNode on a different
+     * RenderNode:
+     *
+     * <pre>
+     *        +-------------+
+     *        |RenderNode: P|
+     *        +-+----------++
+     *          |          |
+     *          v          v
+     *  +-------+-----+  +-+--------------+
+     *  |RenderNode: C|  |RenderNode: P'BG|
+     *  +-------+-----+  +----------------+
+     *          |
+     *          |
+     * +--------+-------+
+     * |RenderNode: C'BG|
+     * +----------------+
+     * </pre>
+     *
+     * If P'BG is a projection receiver, and C'BG is set to project backwards then C'BG will
+     * behave as if it was drawn directly by P'BG instead of by C. This includes inheriting P'BG's
+     * clip instead of C's clip.
+     *
      * @param shouldProject true if the display list should be projected onto a
-     *                      containing volume.
+     *                      containing volume. Default is false.
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setProjectBackwards(boolean shouldProject) {
         return nSetProjectBackwards(mNativeRenderNode, shouldProject);
     }
 
     /**
-     * Sets whether the RenderNode is a projection receiver - that its parent
-     * RenderNode should draw any descendent RenderNodes with
-     * ProjectBackwards=true directly on top of it. Default value is false.
+     * Sets whether the RenderNode is a projection receiver. If true then this RenderNode's parent
+     * should draw any descendant RenderNodes with ProjectBackwards=true directly on top of it.
+     * Default value is false. See
+     * {@link #setProjectBackwards(boolean)} for a description of what this entails.
+     *
+     * @param shouldRecieve True if this RenderNode is a projection receiver, false otherwise.
+     *                      Default is false.
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setProjectionReceiver(boolean shouldRecieve) {
         return nSetProjectionReceiver(mNativeRenderNode, shouldRecieve);
@@ -526,6 +582,7 @@
      * outline for those changes to be applied.
      *
      * @param outline The outline to use for this RenderNode.
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setOutline(@Nullable Outline outline) {
         if (outline == null) {
@@ -572,8 +629,9 @@
      * {@link android.R.attr#spotShadowAlpha} theme attribute
      *
      * @param color The color this RenderNode will cast for its elevation spot shadow.
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
-    public boolean setSpotShadowColor(int color) {
+    public boolean setSpotShadowColor(@ColorInt int color) {
         return nSetSpotShadowColor(mNativeRenderNode, color);
     }
 
@@ -581,7 +639,7 @@
      * @return The shadow color set by {@link #setSpotShadowColor(int)}, or black if nothing
      * was set
      */
-    public int getSpotShadowColor() {
+    public @ColorInt int getSpotShadowColor() {
         return nGetSpotShadowColor(mNativeRenderNode);
     }
 
@@ -597,8 +655,9 @@
      * {@link android.R.attr#ambientShadowAlpha} theme attribute.
      *
      * @param color The color this RenderNode will cast for its elevation shadow.
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
-    public boolean setAmbientShadowColor(int color) {
+    public boolean setAmbientShadowColor(@ColorInt int color) {
         return nSetAmbientShadowColor(mNativeRenderNode, color);
     }
 
@@ -606,7 +665,7 @@
      * @return The shadow color set by {@link #setAmbientShadowColor(int)}, or black if
      * nothing was set
      */
-    public int getAmbientShadowColor() {
+    public @ColorInt int getAmbientShadowColor() {
         return nGetAmbientShadowColor(mNativeRenderNode);
     }
 
@@ -614,6 +673,8 @@
      * Enables or disables clipping to the outline.
      *
      * @param clipToOutline true if clipping to the outline.
+     * @return True if the clipToOutline value changed, false if previous value matched the new
+     *         value.
      */
     public boolean setClipToOutline(boolean clipToOutline) {
         return nSetClipToOutline(mNativeRenderNode, clipToOutline);
@@ -640,7 +701,7 @@
 
     /**
      * Set the static matrix on the display list. The specified matrix is combined with other
-     * transforms (such as {@link #setScaleX(float)}, {@link #setRotation(float)}, etc.)
+     * transforms (such as {@link #setScaleX(float)}, {@link #setRotationZ(float)}, etc.)
      *
      * @param matrix A transform matrix to apply to this display list
      * @hide TODO Do we want this?
@@ -669,6 +730,7 @@
      * @param alpha The translucency of the display list, must be a value between 0.0f and 1.0f
      * @see View#setAlpha(float)
      * @see #getAlpha()
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setAlpha(float alpha) {
         return nSetAlpha(mNativeRenderNode, alpha);
@@ -742,7 +804,7 @@
      * Sets the base elevation of this RenderNode in pixels
      *
      * @param lift the elevation in pixels
-     * @return true if the elevation changed, false if it was the same
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setElevation(float lift) {
         return nSetElevation(mNativeRenderNode, lift);
@@ -763,6 +825,7 @@
      * @param translationX The X axis translation value of the display list, in pixels
      * @see View#setTranslationX(float)
      * @see #getTranslationX()
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setTranslationX(float translationX) {
         return nSetTranslationX(mNativeRenderNode, translationX);
@@ -783,6 +846,7 @@
      * @param translationY The Y axis translation value of the display list, in pixels
      * @see View#setTranslationY(float)
      * @see #getTranslationY()
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setTranslationY(float translationY) {
         return nSetTranslationY(mNativeRenderNode, translationY);
@@ -802,6 +866,7 @@
      *
      * @see View#setTranslationZ(float)
      * @see #getTranslationZ()
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setTranslationZ(float translationZ) {
         return nSetTranslationZ(mNativeRenderNode, translationZ);
@@ -820,19 +885,20 @@
      * Sets the rotation value for the display list around the Z axis.
      *
      * @param rotation The rotation value of the display list, in degrees
-     * @see View#setRotation(float)
-     * @see #getRotation()
+     * @see View#setRotationZ(float)
+     * @see #getRotationZ()
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
-    public boolean setRotation(float rotation) {
+    public boolean setRotationZ(float rotation) {
         return nSetRotation(mNativeRenderNode, rotation);
     }
 
     /**
      * Returns the rotation value for this display list around the Z axis, in degrees.
      *
-     * @see #setRotation(float)
+     * @see #setRotationZ(float)
      */
-    public float getRotation() {
+    public float getRotationZ() {
         return nGetRotation(mNativeRenderNode);
     }
 
@@ -842,6 +908,7 @@
      * @param rotationX The rotation value of the display list, in degrees
      * @see View#setRotationX(float)
      * @see #getRotationX()
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setRotationX(float rotationX) {
         return nSetRotationX(mNativeRenderNode, rotationX);
@@ -862,6 +929,7 @@
      * @param rotationY The rotation value of the display list, in degrees
      * @see View#setRotationY(float)
      * @see #getRotationY()
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setRotationY(float rotationY) {
         return nSetRotationY(mNativeRenderNode, rotationY);
@@ -882,6 +950,7 @@
      * @param scaleX The scale value of the display list
      * @see View#setScaleX(float)
      * @see #getScaleX()
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setScaleX(float scaleX) {
         return nSetScaleX(mNativeRenderNode, scaleX);
@@ -902,6 +971,7 @@
      * @param scaleY The scale value of the display list
      * @see View#setScaleY(float)
      * @see #getScaleY()
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setScaleY(float scaleY) {
         return nSetScaleY(mNativeRenderNode, scaleY);
@@ -922,6 +992,7 @@
      * @param pivotX The pivot value of the display list on the X axis, in pixels
      * @see View#setPivotX(float)
      * @see #getPivotX()
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setPivotX(float pivotX) {
         return nSetPivotX(mNativeRenderNode, pivotX);
@@ -942,6 +1013,7 @@
      * @param pivotY The pivot value of the display list on the Y axis, in pixels
      * @see View#setPivotY(float)
      * @see #getPivotY()
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setPivotY(float pivotY) {
         return nSetPivotY(mNativeRenderNode, pivotY);
@@ -969,6 +1041,8 @@
      * Clears any pivot previously set by a call to  {@link #setPivotX(float)} or
      * {@link #setPivotY(float)}. After calling this {@link #isPivotExplicitlySet()} will be false
      * and the pivot used for rotation will return to default of being centered on the view.
+     *
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean resetPivot() {
         return nResetPivot(mNativeRenderNode);
@@ -997,8 +1071,10 @@
      * @param distance The distance in pixels, must always be positive
      * @see #setRotationX(float)
      * @see #setRotationY(float)
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
-    public boolean setCameraDistance(float distance) {
+    public boolean setCameraDistance(
+            @FloatRange(from = 0.0f, to = Float.MAX_VALUE) float distance) {
         if (!Float.isFinite(distance) || distance < 0.0f) {
             throw new IllegalArgumentException("distance must be finite & positive, given="
                     + distance);
@@ -1013,7 +1089,7 @@
      * @return the distance along the Z axis in pixels.
      * @see #setCameraDistance(float)
      */
-    public float getCameraDistance() {
+    public @FloatRange(from = 0.0f, to = Float.MAX_VALUE) float getCameraDistance() {
         return -nGetCameraDistance(mNativeRenderNode);
     }
 
@@ -1022,6 +1098,7 @@
      *
      * @param left The left position, in pixels, of the RenderNode
      * @return true if the value changed, false otherwise
+     * @hide
      */
     public boolean setLeft(int left) {
         return nSetLeft(mNativeRenderNode, left);
@@ -1032,6 +1109,7 @@
      *
      * @param top The top position, in pixels, of the RenderNode
      * @return true if the value changed, false otherwise.
+     * @hide
      */
     public boolean setTop(int top) {
         return nSetTop(mNativeRenderNode, top);
@@ -1042,6 +1120,7 @@
      *
      * @param right The right position, in pixels, of the RenderNode
      * @return true if the value changed, false otherwise.
+     * @hide
      */
     public boolean setRight(int right) {
         return nSetRight(mNativeRenderNode, right);
@@ -1052,6 +1131,7 @@
      *
      * @param bottom The bottom position, in pixels, of the RenderNode
      * @return true if the value changed, false otherwise.
+     * @hide
      */
     public boolean setBottom(int bottom) {
         return nSetBottom(mNativeRenderNode, bottom);
@@ -1060,8 +1140,6 @@
     /**
      * Gets the left position for the RenderNode.
      *
-     * See {@link #setLeft(int)}
-     *
      * @return the left position in pixels
      */
     public int getLeft() {
@@ -1071,8 +1149,6 @@
     /**
      * Gets the top position for the RenderNode.
      *
-     * See {@link #setTop(int)}
-     *
      * @return the top position in pixels
      */
     public int getTop() {
@@ -1082,8 +1158,6 @@
     /**
      * Gets the right position for the RenderNode.
      *
-     * See {@link #setRight(int)}
-     *
      * @return the right position in pixels
      */
     public int getRight() {
@@ -1093,8 +1167,6 @@
     /**
      * Gets the bottom position for the RenderNode.
      *
-     * See {@link #setBottom(int)}
-     *
      * @return the bottom position in pixels
      */
     public int getBottom() {
@@ -1127,20 +1199,41 @@
      * @param right  The right position of the RenderNode, in pixels
      * @param bottom The bottom position of the RenderNode, in pixels
      * @return true if any values changed, false otherwise.
-     * @see #setLeft(int)
-     * @see #setTop(int)
-     * @see #setRight(int)
-     * @see #setBottom(int)
+     * @hide
      */
     public boolean setLeftTopRightBottom(int left, int top, int right, int bottom) {
         return nSetLeftTopRightBottom(mNativeRenderNode, left, top, right, bottom);
     }
 
     /**
+     * Sets the position of the RenderNode.
+     *
+     * @param left   The left position of the RenderNode, in pixels
+     * @param top    The top position of the RenderNode, in pixels
+     * @param right  The right position of the RenderNode, in pixels
+     * @param bottom The bottom position of the RenderNode, in pixels
+     * @return True if the value changed, false if the new value was the same as the previous value.
+     */
+    public boolean setPosition(int left, int top, int right, int bottom) {
+        return nSetLeftTopRightBottom(mNativeRenderNode, left, top, right, bottom);
+    }
+
+    /**
+     * Sets the position of the RenderNode.
+     *
+     * @param position The position rectangle in pixels
+     * @return True if the value changed, false if the new value was the same as the previous value.
+     */
+    public boolean setPosition(Rect position) {
+        return nSetLeftTopRightBottom(mNativeRenderNode,
+                position.left, position.top, position.right, position.bottom);
+    }
+
+    /**
      * Offsets the left and right positions for the RenderNode
      *
      * @param offset The amount that the left and right positions are offset in pixels
-     * @return true if any values changed, false otherwise.
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean offsetLeftAndRight(int offset) {
         return nOffsetLeftAndRight(mNativeRenderNode, offset);
@@ -1150,7 +1243,7 @@
      * Offsets the top and bottom values for the RenderNode
      *
      * @param offset The amount that the left and right positions are offset in pixels
-     * @return true if any values changed, false otherwise.
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean offsetTopAndBottom(int offset) {
         return nOffsetTopAndBottom(mNativeRenderNode, offset);
@@ -1170,8 +1263,10 @@
      * Gets the approximate memory usage of the RenderNode for debug purposes. Does not include
      * the memory usage of any child RenderNodes nor any bitmaps, only the memory usage of
      * this RenderNode and any data it owns.
+     *
+     * @return Approximate memory usage in bytes.
      */
-    public int computeApproximateMemoryUsage() {
+    public @BytesLong long computeApproximateMemoryUsage() {
         return nGetDebugSize(mNativeRenderNode);
     }
 
@@ -1186,7 +1281,7 @@
      * it prevent any 'false' in any of its children.
      *
      * @param allow Whether or not to allow force dark.
-     * @return true If the value has changed, false otherwise.
+     * @return True if the value changed, false if the new value was the same as the previous value.
      */
     public boolean setForceDarkAllowed(boolean allow) {
         return nSetAllowForceDark(mNativeRenderNode, allow);
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 66d8542..d7b84ff 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -356,6 +356,7 @@
 
 ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
                                          bool /*stop_at_first_match*/,
+                                         bool ignore_configuration,
                                          FindEntryResult* out_entry) const {
   // Might use this if density_override != 0.
   ResTable_config density_override_config;
@@ -399,7 +400,7 @@
 
   // If desired_config is the same as the set configuration, then we can use our filtered list
   // and we don't need to match the configurations, since they already matched.
-  const bool use_fast_path = desired_config == &configuration_;
+  const bool use_fast_path = !ignore_configuration && desired_config == &configuration_;
 
   for (size_t pi = 0; pi < package_count; pi++) {
     const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
@@ -475,21 +476,23 @@
       // ResTable_config, we must copy it.
       const auto iter_end = type_spec->types + type_spec->type_count;
       for (auto iter = type_spec->types; iter != iter_end; ++iter) {
-        ResTable_config this_config;
-        this_config.copyFromDtoH((*iter)->config);
+        ResTable_config this_config{};
 
-        if (!this_config.match(*desired_config)) {
-          continue;
-        }
+        if (!ignore_configuration) {
+          this_config.copyFromDtoH((*iter)->config);
+          if (!this_config.match(*desired_config)) {
+            continue;
+          }
 
-        if (best_config == nullptr) {
-          resolution_type = Resolution::Step::Type::INITIAL;
-        } else if (this_config.isBetterThan(*best_config, desired_config)) {
-          resolution_type = Resolution::Step::Type::BETTER_MATCH;
-        } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
-          resolution_type = Resolution::Step::Type::OVERLAID;
-        } else {
-          continue;
+          if (best_config == nullptr) {
+            resolution_type = Resolution::Step::Type::INITIAL;
+          } else if (this_config.isBetterThan(*best_config, desired_config)) {
+            resolution_type = Resolution::Step::Type::BETTER_MATCH;
+          } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
+            resolution_type = Resolution::Step::Type::OVERLAID;
+          } else {
+            continue;
+          }
         }
 
         // The configuration matches and is better than the previous selection.
@@ -506,6 +509,11 @@
         best_config = &best_config_copy;
         best_offset = offset;
 
+        if (ignore_configuration) {
+          // Any configuration will suffice, so break.
+          break;
+        }
+
         if (resource_resolution_logging_enabled_) {
           resolution_steps.push_back(Resolution::Step{resolution_type,
                                                       this_config.toString(),
@@ -622,8 +630,9 @@
 
 bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
   FindEntryResult entry;
-  ApkAssetsCookie cookie =
-      FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry);
+  ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+                                     true /* stop_at_first_match */,
+                                     true /* ignore_configuration */, &entry);
   if (cookie == kInvalidCookie) {
     return false;
   }
@@ -652,13 +661,14 @@
 
 bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const {
   FindEntryResult entry;
-  ApkAssetsCookie cookie =
-      FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry);
+  ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+                                     false /* stop_at_first_match */,
+                                     true /* ignore_configuration */, &entry);
   if (cookie != kInvalidCookie) {
     *out_flags = entry.type_flags;
-    return cookie;
+    return true;
   }
-  return kInvalidCookie;
+  return false;
 }
 
 ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
@@ -666,8 +676,8 @@
                                            ResTable_config* out_selected_config,
                                            uint32_t* out_flags) const {
   FindEntryResult entry;
-  ApkAssetsCookie cookie =
-      FindEntry(resid, density_override, false /* stop_at_first_match */, &entry);
+  ApkAssetsCookie cookie = FindEntry(resid, density_override, false /* stop_at_first_match */,
+                                     false /* ignore_configuration */, &entry);
   if (cookie == kInvalidCookie) {
     return kInvalidCookie;
   }
@@ -759,8 +769,10 @@
   }
 
   FindEntryResult entry;
-  ApkAssetsCookie cookie =
-      FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry);
+  ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+                                     false /* stop_at_first_match */,
+                                     false /* ignore_configuration */,
+                                     &entry);
   if (cookie == kInvalidCookie) {
     return nullptr;
   }
@@ -1387,7 +1399,9 @@
             // Find the cookie of the attribute resource id
             FindEntryResult attribute_entry_result;
             ApkAssetsCookie attribute_cookie =
-                o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , false,
+                o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ ,
+                                            true /* stop_at_first_match */,
+                                            true /* ignore_configuration */,
                                             &attribute_entry_result);
 
             // Determine the package id of the attribute in the destination AssetManager
diff --git a/libs/androidfw/DisplayEventDispatcher.cpp b/libs/androidfw/DisplayEventDispatcher.cpp
index 3b9a348..6606148 100644
--- a/libs/androidfw/DisplayEventDispatcher.cpp
+++ b/libs/androidfw/DisplayEventDispatcher.cpp
@@ -135,6 +135,9 @@
             case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
                 dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
                 break;
+            case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+                dispatchConfigChanged(ev.header.timestamp, ev.header.displayId, ev.config.configId);
+                break;
             default:
                 ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
                 break;
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index fc5aa9c7..1e2b36cb1 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -257,11 +257,12 @@
   // Creates a new Theme from this AssetManager.
   std::unique_ptr<Theme> NewTheme();
 
-  template <typename Func>
-  void ForEachPackage(Func func) const {
+  void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func) const {
     for (const PackageGroup& package_group : package_groups_) {
-      func(package_group.packages_.front().loaded_package_->GetPackageName(),
-           package_group.dynamic_ref_table.mAssignedPackageId);
+      if (!func(package_group.packages_.front().loaded_package_->GetPackageName(),
+           package_group.dynamic_ref_table.mAssignedPackageId)) {
+        return;
+      }
     }
   }
 
@@ -282,10 +283,13 @@
   // care about the value. In this case, the value of `FindEntryResult::type_flags` is incomplete
   // and should not be used.
   //
+  // When `ignore_configuration` is true, FindEntry will return always select the first entry in
+  // for the type seen regardless of its configuration.
+  //
   // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
   // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
   ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
-                            FindEntryResult* out_entry) const;
+                            bool ignore_configuration, FindEntryResult* out_entry) const;
 
   // Assigns package IDs to all shared library ApkAssets.
   // Should be called whenever the ApkAssets are changed.
diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
index d2addba..5381c01 100644
--- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
+++ b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
@@ -40,6 +40,8 @@
     virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
     virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
                                  bool connected) = 0;
+    virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+                                       int32_t configId) = 0;
 
     virtual int handleEvent(int receiveFd, int events, void* data);
     bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index df1537e..1bd30eb 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -161,7 +161,7 @@
             SkAutoCanvasRestore acr2(canvas, shouldClip);
             canvas->setMatrix(mProjectedDisplayList->mParentMatrix);
             if (shouldClip) {
-                clipOutline(*mProjectedDisplayList->mProjectedOutline, canvas, nullptr);
+                canvas->clipPath(*mProjectedDisplayList->mProjectedOutline->getPath());
             }
             drawBackwardsProjectedNodes(canvas, *mProjectedDisplayList);
         }
diff --git a/location/java/android/location/GnssMeasurementCorrections.java b/location/java/android/location/GnssMeasurementCorrections.java
index 3ce48b4..9d3211d 100644
--- a/location/java/android/location/GnssMeasurementCorrections.java
+++ b/location/java/android/location/GnssMeasurementCorrections.java
@@ -124,7 +124,7 @@
      * Gets a set of {@link GnssSingleSatCorrection} each containing measurement corrections for a
      * satellite in view
      */
-    public @Nullable List<GnssSingleSatCorrection> getSingleSatCorrectionList() {
+    public @Nullable List<GnssSingleSatCorrection> getSingleSatelliteCorrectionList() {
         return mSingleSatCorrectionList;
     }
 
@@ -137,7 +137,7 @@
             new Creator<GnssMeasurementCorrections>() {
                 @Override
                 public GnssMeasurementCorrections createFromParcel(Parcel parcel) {
-                    GnssMeasurementCorrections.Builder gnssMeasurementCorrectons =
+                    final GnssMeasurementCorrections.Builder gnssMeasurementCorrectons =
                             new Builder()
                                     .setLatitudeDegrees(parcel.readDouble())
                                     .setLongitudeDegrees(parcel.readDouble())
@@ -147,7 +147,7 @@
                                     .setToaGpsNanosecondsOfWeek(parcel.readLong());
                     List<GnssSingleSatCorrection> singleSatCorrectionList = new ArrayList<>();
                     parcel.readTypedList(singleSatCorrectionList, GnssSingleSatCorrection.CREATOR);
-                    gnssMeasurementCorrectons.setSingleSatCorrectionList(
+                    gnssMeasurementCorrectons.setSingleSatelliteCorrectionList(
                             singleSatCorrectionList.isEmpty() ? null : singleSatCorrectionList);
                     return gnssMeasurementCorrectons.build();
                 }
@@ -188,7 +188,7 @@
     }
 
     /** Builder for {@link GnssMeasurementCorrections} */
-    public static class Builder {
+    public static final class Builder {
         /**
          * For documentation of below fields, see corresponding fields in {@link
          * GnssMeasurementCorrections}.
@@ -253,7 +253,7 @@
          * Sets a the list of {@link GnssSingleSatCorrection} containing measurement corrections for
          * a satellite in view
          */
-        public Builder setSingleSatCorrectionList(
+        public Builder setSingleSatelliteCorrectionList(
                 @Nullable List<GnssSingleSatCorrection> singleSatCorrectionList) {
             if (singleSatCorrectionList == null) {
                 mSingleSatCorrectionList = null;
diff --git a/location/java/android/location/GnssReflectingPlane.java b/location/java/android/location/GnssReflectingPlane.java
index 64b3752..9a106a7 100644
--- a/location/java/android/location/GnssReflectingPlane.java
+++ b/location/java/android/location/GnssReflectingPlane.java
@@ -116,7 +116,7 @@
     }
 
     /** Builder for {@link GnssReflectingPlane} */
-    public static class Builder {
+    public static final class Builder {
         /** For documentation, see corresponding fields in {@link GnssReflectingPlane}. */
         private double mLatitudeDegrees;
 
diff --git a/location/java/android/location/GnssSingleSatCorrection.java b/location/java/android/location/GnssSingleSatCorrection.java
index 4d5303f..f719e1a 100644
--- a/location/java/android/location/GnssSingleSatCorrection.java
+++ b/location/java/android/location/GnssSingleSatCorrection.java
@@ -60,6 +60,7 @@
     private int mSingleSatCorrectionFlags;
 
     /** Defines the constellation of the given satellite as defined in {@link GnssStatus}. */
+    @GnssStatus.ConstellationType
     private int mConstellationType;
 
     /**
@@ -115,7 +116,7 @@
     }
 
     /** Gets a bitmask of fields present in this object */
-    public int getSingleSatCorrectionFlags() {
+    public int getSingleSatelliteCorrectionFlags() {
         return mSingleSatCorrectionFlags;
     }
 
@@ -136,7 +137,7 @@
      * <p>Interpretation depends on {@link #getConstellationType()}. See {@link
      * GnssStatus#getSvid(int)}.
      */
-    public int getSatId() {
+    public int getSatelliteId() {
         return mSatId;
     }
 
@@ -162,7 +163,7 @@
      * location.
      */
     @FloatRange(from = 0f, to = 1f)
-    public float getProbSatIsLos() {
+    public float getProbabilityLineOfSight() {
         return mProbSatIsLos;
     }
 
@@ -189,8 +190,8 @@
         return mReflectingPlane;
     }
 
-    /** Returns {@code true} if {@link #getProbSatIsLos()} is valid. */
-    public boolean hasSatelliteLineOfSight() {
+    /** Returns {@code true} if {@link #getProbabilityLineOfSight()} is valid. */
+    public boolean hasValidSatelliteLineOfSight() {
         return (mSingleSatCorrectionFlags & HAS_PROB_SAT_IS_LOS_MASK) != 0;
     }
 
@@ -220,11 +221,11 @@
                 public GnssSingleSatCorrection createFromParcel(Parcel parcel) {
                     GnssSingleSatCorrection singleSatCorrection =
                             new Builder()
-                                    .setSingleSatCorrectionFlags(parcel.readInt())
+                                    .setSingleSatelliteCorrectionFlags(parcel.readInt())
                                     .setConstellationType(parcel.readInt())
-                                    .setSatId(parcel.readInt())
+                                    .setSatelliteId(parcel.readInt())
                                     .setCarrierFrequencyHz(parcel.readFloat())
-                                    .setProbSatIsLos(parcel.readFloat())
+                                    .setProbabilityLineOfSight(parcel.readFloat())
                                     .setExcessPathLengthMeters(parcel.readFloat())
                                     .setExcessPathLengthUncertaintyMeters(parcel.readFloat())
                                     .setReflectingPlane(
@@ -272,7 +273,7 @@
     }
 
     /** Builder for {@link GnssSingleSatCorrection} */
-    public static class Builder {
+    public static final class Builder {
 
         /**
          * For documentation of below fields, see corresponding fields in {@link
@@ -289,19 +290,19 @@
         private GnssReflectingPlane mReflectingPlane;
 
         /** Sets a bitmask of fields present in this object */
-        public Builder setSingleSatCorrectionFlags(int singleSatCorrectionFlags) {
+        public Builder setSingleSatelliteCorrectionFlags(int singleSatCorrectionFlags) {
             mSingleSatCorrectionFlags = singleSatCorrectionFlags;
             return this;
         }
 
         /** Sets the constellation type. */
-        public Builder setConstellationType(int constellationType) {
+        public Builder setConstellationType(@GnssStatus.ConstellationType int constellationType) {
             mConstellationType = constellationType;
             return this;
         }
 
-        /** Sets the Satellite ID. */
-        public Builder setSatId(int satId) {
+        /** Sets the Satellite ID defined in the ICD of the given constellation. */
+        public Builder setSatelliteId(int satId) {
             mSatId = satId;
             return this;
         }
@@ -316,7 +317,8 @@
          * Sets the line-of-sight probability of the satellite at the given location in the range
          * between 0 and 1.
          */
-        public Builder setProbSatIsLos(@FloatRange(from = 0f, to = 1f) float probSatIsLos) {
+        public Builder setProbabilityLineOfSight(
+                @FloatRange(from = 0f, to = 1f) float probSatIsLos) {
             Preconditions.checkArgumentInRange(
                     probSatIsLos, 0, 1, "probSatIsLos should be between 0 and 1.");
             mProbSatIsLos = probSatIsLos;
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index dbb581f..d1b39b3 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -11,7 +11,7 @@
     method public android.os.IBinder getBinder();
     method public boolean isEnabled();
     method @Deprecated protected void onDisable();
-    method protected void onDump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
+    method @Deprecated protected void onDump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
     method @Deprecated protected void onEnable();
     method @Deprecated protected int onGetStatus(android.os.Bundle);
     method @Deprecated protected long onGetStatusUpdateTime();
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 7cd7207..fa113a8 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -240,8 +240,10 @@
     protected abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
 
     /**
-     * Dump debug information.
+     * @deprecated This callback will never be invoked on Android Q and above. This method may be
+     * removed in the future. Prefer to dump provider state via the containing service instead.
      */
+    @Deprecated
     protected void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {}
 
     /**
@@ -336,10 +338,5 @@
         public void sendExtraCommand(String command, Bundle extras) {
             onSendExtraCommand(command, extras);
         }
-
-        @Override
-        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            onDump(fd, pw, args);
-        }
     }
 }
diff --git a/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java
deleted file mode 100644
index 8f46e84..0000000
--- a/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java
+++ /dev/null
@@ -1,111 +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.location;
-
-import android.os.Parcel;
-
-import junit.framework.TestCase;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Unit tests for {@link GnssMeasurementCorrections}. */
-public class GnssMeasurementCorrectionsTest extends TestCase {
-    public void testDescribeContents() {
-        GnssMeasurementCorrections measurementCorrections =
-                new GnssMeasurementCorrections.Builder().build();
-        measurementCorrections.describeContents();
-    }
-
-    public void testWriteToParcel() {
-        GnssMeasurementCorrections.Builder measurementCorrections =
-                new GnssMeasurementCorrections.Builder();
-        setTestValues(measurementCorrections);
-        Parcel parcel = Parcel.obtain();
-        measurementCorrections.build().writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        GnssMeasurementCorrections newMeasurementCorrection =
-                GnssMeasurementCorrections.CREATOR.createFromParcel(parcel);
-        verifyTestValues(newMeasurementCorrection);
-        parcel.recycle();
-    }
-
-    private static void verifyTestValues(GnssMeasurementCorrections measurementCorrections) {
-        assertEquals(37.386051, measurementCorrections.getLatitudeDegrees());
-        assertEquals(-122.083855, measurementCorrections.getLongitudeDegrees());
-        assertEquals(32.0, measurementCorrections.getAltitudeMeters());
-        assertEquals(9.25, measurementCorrections.getHorizontalPositionUncertaintyMeters());
-        assertEquals(2.3, measurementCorrections.getVerticalPositionUncertaintyMeters());
-        assertEquals(604000000000000L, measurementCorrections.getToaGpsNanosecondsOfWeek());
-
-        GnssSingleSatCorrection singleSatCorrection =
-                measurementCorrections.getSingleSatCorrectionList().get(0);
-        GnssSingleSatCorrectionsTest.verifyTestValues(singleSatCorrection);
-
-        singleSatCorrection = measurementCorrections.getSingleSatCorrectionList().get(1);
-        assertEquals(15, singleSatCorrection.getSingleSatCorrectionFlags());
-        assertEquals(GnssStatus.CONSTELLATION_GPS, singleSatCorrection.getConstellationType());
-        assertEquals(11, singleSatCorrection.getSatId());
-        assertEquals(1575430000f, singleSatCorrection.getCarrierFrequencyHz());
-        assertEquals(0.9f, singleSatCorrection.getProbSatIsLos());
-        assertEquals(50.0f, singleSatCorrection.getExcessPathLengthMeters());
-        assertEquals(55.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters());
-        GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane();
-        assertEquals(37.386054, reflectingPlane.getLatitudeDegrees());
-        assertEquals(-122.083855, reflectingPlane.getLongitudeDegrees());
-        assertEquals(120.0, reflectingPlane.getAltitudeMeters());
-        assertEquals(153.0, reflectingPlane.getAzimuthDegrees());
-    }
-
-    private static void setTestValues(GnssMeasurementCorrections.Builder measurementCorrections) {
-        measurementCorrections
-                .setLatitudeDegrees(37.386051)
-                .setLongitudeDegrees(-122.083855)
-                .setAltitudeMeters(32)
-                .setHorizontalPositionUncertaintyMeters(9.25)
-                .setVerticalPositionUncertaintyMeters(2.3)
-                .setToaGpsNanosecondsOfWeek(604000000000000L);
-        List<GnssSingleSatCorrection> singleSatCorrectionList = new ArrayList<>();
-        singleSatCorrectionList.add(GnssSingleSatCorrectionsTest.generateTestSingleSatCorrection());
-        singleSatCorrectionList.add(generateTestSingleSatCorrection());
-        measurementCorrections.setSingleSatCorrectionList(singleSatCorrectionList);
-    }
-
-    private static GnssSingleSatCorrection generateTestSingleSatCorrection() {
-        GnssSingleSatCorrection.Builder singleSatCorrection = new GnssSingleSatCorrection.Builder();
-        singleSatCorrection
-                .setSingleSatCorrectionFlags(8)
-                .setConstellationType(GnssStatus.CONSTELLATION_GPS)
-                .setSatId(11)
-                .setCarrierFrequencyHz(1575430000f)
-                .setProbSatIsLos(0.9f)
-                .setExcessPathLengthMeters(50.0f)
-                .setExcessPathLengthUncertaintyMeters(55.0f)
-                .setReflectingPlane(generateTestReflectingPlane());
-        return singleSatCorrection.build();
-    }
-
-    private static GnssReflectingPlane generateTestReflectingPlane() {
-        GnssReflectingPlane.Builder reflectingPlane =
-                new GnssReflectingPlane.Builder()
-                        .setLatitudeDegrees(37.386054)
-                        .setLongitudeDegrees(-122.083855)
-                        .setAltitudeMeters(120.0)
-                        .setAzimuthDegrees(153);
-        return reflectingPlane.build();
-    }
-}
diff --git a/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java b/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java
deleted file mode 100644
index d7a3378..0000000
--- a/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java
+++ /dev/null
@@ -1,67 +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.location;
-
-import android.os.Parcel;
-
-import junit.framework.TestCase;
-
-/** Unit tests for {@link GnssReflectingPlane}. */
-public class GnssReflectingPlaneTest extends TestCase {
-    public void testDescribeContents() {
-        GnssReflectingPlane reflectingPlane = new GnssReflectingPlane.Builder().build();
-        reflectingPlane.describeContents();
-    }
-
-    public void testWriteToParcel() {
-        GnssReflectingPlane.Builder reflectingPlane = new GnssReflectingPlane.Builder();
-        setTestValues(reflectingPlane);
-        Parcel parcel = Parcel.obtain();
-        reflectingPlane.build().writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        GnssReflectingPlane newReflectingPlane =
-                GnssReflectingPlane.CREATOR.createFromParcel(parcel);
-        verifyTestValues(newReflectingPlane);
-        parcel.recycle();
-    }
-
-    public static void verifyTestValues(GnssReflectingPlane reflectingPlane) {
-        assertEquals(37.386052, reflectingPlane.getLatitudeDegrees());
-        assertEquals(-122.083853, reflectingPlane.getLongitudeDegrees());
-        assertEquals(100.0, reflectingPlane.getAltitudeMeters());
-        assertEquals(123.0, reflectingPlane.getAzimuthDegrees());
-    }
-
-    private static void setTestValues(GnssReflectingPlane.Builder reflectingPlane) {
-        GnssReflectingPlane refPlane = generateTestReflectingPlane();
-        reflectingPlane
-                .setLatitudeDegrees(refPlane.getLatitudeDegrees())
-                .setLongitudeDegrees(refPlane.getLongitudeDegrees())
-                .setAltitudeMeters(refPlane.getAltitudeMeters())
-                .setAzimuthDegrees(refPlane.getAzimuthDegrees());
-    }
-
-    public static GnssReflectingPlane generateTestReflectingPlane() {
-        GnssReflectingPlane.Builder reflectingPlane =
-                new GnssReflectingPlane.Builder()
-                        .setLatitudeDegrees(37.386052)
-                        .setLongitudeDegrees(-122.083853)
-                        .setAltitudeMeters(100.0)
-                        .setAzimuthDegrees(123.0);
-        return reflectingPlane.build();
-    }
-}
diff --git a/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java
deleted file mode 100644
index f358806..0000000
--- a/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java
+++ /dev/null
@@ -1,81 +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.location;
-
-import android.os.Parcel;
-
-import junit.framework.TestCase;
-
-/** Unit tests for {@link GnssSingleSatCorrection}. */
-public class GnssSingleSatCorrectionsTest extends TestCase {
-    public void testDescribeContents() {
-        GnssSingleSatCorrection singleSatCorrection = new GnssSingleSatCorrection.Builder().build();
-        singleSatCorrection.describeContents();
-    }
-
-    public void testWriteToParcel() {
-        GnssSingleSatCorrection.Builder singleSatCorrection = new GnssSingleSatCorrection.Builder();
-        setTestValues(singleSatCorrection);
-        Parcel parcel = Parcel.obtain();
-        singleSatCorrection.build().writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        GnssSingleSatCorrection newSingleSatCorrection =
-                GnssSingleSatCorrection.CREATOR.createFromParcel(parcel);
-        verifyTestValues(newSingleSatCorrection);
-        parcel.recycle();
-    }
-
-    public static void verifyTestValues(GnssSingleSatCorrection singleSatCorrection) {
-        assertEquals(15, singleSatCorrection.getSingleSatCorrectionFlags());
-        assertEquals(GnssStatus.CONSTELLATION_GALILEO, singleSatCorrection.getConstellationType());
-        assertEquals(12, singleSatCorrection.getSatId());
-        assertEquals(1575420000f, singleSatCorrection.getCarrierFrequencyHz());
-        assertEquals(0.1f, singleSatCorrection.getProbSatIsLos());
-        assertEquals(10.0f, singleSatCorrection.getExcessPathLengthMeters());
-        assertEquals(5.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters());
-        GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane();
-        GnssReflectingPlaneTest.verifyTestValues(reflectingPlane);
-    }
-
-    private static void setTestValues(GnssSingleSatCorrection.Builder singleSatCorrection) {
-        GnssSingleSatCorrection singleSatCorr = generateTestSingleSatCorrection();
-        singleSatCorrection
-                .setSingleSatCorrectionFlags(singleSatCorr.getSingleSatCorrectionFlags())
-                .setConstellationType(singleSatCorr.getConstellationType())
-                .setSatId(singleSatCorr.getSatId())
-                .setCarrierFrequencyHz(singleSatCorr.getCarrierFrequencyHz())
-                .setProbSatIsLos(singleSatCorr.getProbSatIsLos())
-                .setExcessPathLengthMeters(singleSatCorr.getExcessPathLengthMeters())
-                .setExcessPathLengthUncertaintyMeters(
-                        singleSatCorr.getExcessPathLengthUncertaintyMeters())
-                .setReflectingPlane(singleSatCorr.getReflectingPlane());
-    }
-
-    public static GnssSingleSatCorrection generateTestSingleSatCorrection() {
-        GnssSingleSatCorrection.Builder singleSatCorrection =
-                new GnssSingleSatCorrection.Builder()
-                        .setSingleSatCorrectionFlags(15)
-                        .setConstellationType(GnssStatus.CONSTELLATION_GALILEO)
-                        .setSatId(12)
-                        .setCarrierFrequencyHz(1575420000f)
-                        .setProbSatIsLos(0.1f)
-                        .setExcessPathLengthMeters(10.0f)
-                        .setExcessPathLengthUncertaintyMeters(5.0f)
-                        .setReflectingPlane(GnssReflectingPlaneTest.generateTestReflectingPlane());
-        return singleSatCorrection.build();
-    }
-}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 7de7f8f..bd828ee 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -36,6 +36,7 @@
 import android.content.Intent;
 import android.media.audiopolicy.AudioPolicy;
 import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener;
+import android.media.projection.MediaProjection;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.MediaSessionLegacyHelper;
@@ -3197,8 +3198,10 @@
         }
         final IAudioService service = getService();
         try {
+            MediaProjection projection = policy.getMediaProjection();
             String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(),
-                    policy.hasFocusListener(), policy.isFocusPolicy(), policy.isVolumeController());
+                    policy.hasFocusListener(), policy.isFocusPolicy(), policy.isVolumeController(),
+                    projection == null ? null : projection.getProjection());
             if (regId == null) {
                 return ERROR;
             } else {
diff --git a/media/java/android/media/AudioPlaybackCaptureConfiguration.java b/media/java/android/media/AudioPlaybackCaptureConfiguration.java
index 22f14ae..d714dc7 100644
--- a/media/java/android/media/AudioPlaybackCaptureConfiguration.java
+++ b/media/java/android/media/AudioPlaybackCaptureConfiguration.java
@@ -19,34 +19,52 @@
 import android.annotation.NonNull;
 import android.media.audiopolicy.AudioMix;
 import android.media.audiopolicy.AudioMixingRule;
+import android.media.projection.MediaProjection;
+import android.os.RemoteException;
 
 import com.android.internal.util.Preconditions;
 
 /**
  * Configuration for capturing audio played by other apps.
  *
+ * For privacy and copyright reason, only the following audio can be captured:
+ *  - usage MUST be UNKNOWN or GAME or MEDIA. All other usages CAN NOT be capturable.
+ *  - audio attributes MUST NOT have the FLAG_NO_CAPTURE
+ *  - played by apps that MUST be in the same user profile as the capturing app
+ *    (eg work profile can not capture user profile apps and vice-versa).
+ *  - played by apps that MUST NOT have in their manifest.xml the application
+ *    attribute android:allowPlaybackCapture="false"
+ *  - played by apps that MUST have a targetSdkVersion higher or equal to 29 (Q).
+ *
  * <p>An example for creating a capture configuration for capturing all media playback:
  *
  * <pre>
+ *     MediaProjection mediaProjection;
+ *     // Retrieve a audio capable projection from the MediaProjectionManager
  *     AudioAttributes mediaAttr = new AudioAttributes.Builder()
  *         .setUsage(AudioAttributes.USAGE_MEDIA)
  *         .build();
- *     AudioPlaybackCaptureConfiguration config = new AudioPlaybackCaptureConfiguration.Builder()
+ *     AudioPlaybackCaptureConfiguration config =
+ *              new AudioPlaybackCaptureConfiguration.Builder(mediaProjection)
  *         .addMatchingUsage(mediaAttr)
  *         .build();
  *     AudioRecord record = new AudioRecord.Builder()
- *         .setPlaybackCaptureConfig(config)
+ *         .setAudioPlaybackCaptureConfig(config)
  *         .build();
  * </pre>
  *
- * @see AudioRecord.Builder#setPlaybackCaptureConfig(AudioPlaybackCaptureConfiguration)
+ * @see MediaProjectionManager#getMediaProjection(int, Intent)
+ * @see AudioRecord.Builder#setAudioPlaybackCaptureConfig(AudioPlaybackCaptureConfiguration)
  */
 public final class AudioPlaybackCaptureConfiguration {
 
     private final AudioMixingRule mAudioMixingRule;
+    private final MediaProjection mProjection;
 
-    private AudioPlaybackCaptureConfiguration(AudioMixingRule audioMixingRule) {
+    private AudioPlaybackCaptureConfiguration(AudioMixingRule audioMixingRule,
+                                              MediaProjection projection) {
         mAudioMixingRule = audioMixingRule;
+        mProjection = projection;
     }
 
     /**
@@ -60,6 +78,9 @@
                 .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK | AudioMix.ROUTE_FLAG_RENDER)
                 .build();
     }
+    MediaProjection getMediaProjection() {
+        return mProjection;
+    }
 
     /** Builder for creating {@link AudioPlaybackCaptureConfiguration} instances. */
     public static final class Builder {
@@ -70,12 +91,26 @@
 
         private static final String ERROR_MESSAGE_MISMATCHED_RULES =
                 "Inclusive and exclusive usage rules cannot be combined";
+        private static final String ERROR_MESSAGE_START_ACTIVITY_FAILED =
+                "startActivityForResult failed";
+        private static final String ERROR_MESSAGE_NON_AUDIO_PROJECTION =
+                "MediaProjection can not project audio";
 
         private final AudioMixingRule.Builder mAudioMixingRuleBuilder;
+        private final MediaProjection mProjection;
         private int mUsageMatchType = MATCH_TYPE_UNSPECIFIED;
         private int mUidMatchType = MATCH_TYPE_UNSPECIFIED;
 
-        public Builder() {
+        /** @param projection A MediaProjection that supports audio projection. */
+        public Builder(@NonNull MediaProjection projection) {
+            Preconditions.checkNotNull(projection);
+            try {
+                Preconditions.checkArgument(projection.getProjection().canProjectAudio(),
+                                            ERROR_MESSAGE_NON_AUDIO_PROJECTION);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mProjection = projection;
             mAudioMixingRuleBuilder = new AudioMixingRule.Builder();
         }
 
@@ -155,7 +190,8 @@
          * @throws UnsupportedOperationException if the parameters set are incompatible.
          */
         public AudioPlaybackCaptureConfiguration build() {
-            return new AudioPlaybackCaptureConfiguration(mAudioMixingRuleBuilder.build());
+            return new AudioPlaybackCaptureConfiguration(mAudioMixingRuleBuilder.build(),
+                                                         mProjection);
         }
     }
 }
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 10accf2..ec1a854 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -26,6 +26,7 @@
 import android.app.ActivityThread;
 import android.media.audiopolicy.AudioMix;
 import android.media.audiopolicy.AudioPolicy;
+import android.media.projection.MediaProjection;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -401,7 +402,7 @@
         int initResult = native_setup( new WeakReference<AudioRecord>(this),
                 mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,
                 mAudioFormat, mNativeBufferSizeInBytes,
-                session, ActivityThread.currentOpPackageName(), 0 /*nativeRecordInJavaObj*/);
+                session, getCurrentOpPackageName(), 0 /*nativeRecordInJavaObj*/);
         if (initResult != SUCCESS) {
             loge("Error code "+initResult+" when initializing native AudioRecord object.");
             return; // with mState == STATE_UNINITIALIZED
@@ -413,6 +414,15 @@
         mState = STATE_INITIALIZED;
     }
 
+    private String getCurrentOpPackageName() {
+        String opPackageName = ActivityThread.currentOpPackageName();
+        if (opPackageName != null) {
+            return opPackageName;
+        }
+        // Command line utility
+        return "uid:" + Binder.getCallingUid();
+    }
+
     /**
      * A constructor which explicitly connects a Native (C++) AudioRecord. For use by
      * the AudioRecordRoutingProxy subclass.
@@ -639,7 +649,9 @@
 
         private AudioRecord buildAudioPlaybackCaptureRecord() {
             AudioMix audioMix = mAudioPlaybackCaptureConfiguration.createAudioMix(mFormat);
+            MediaProjection projection = mAudioPlaybackCaptureConfiguration.getMediaProjection();
             AudioPolicy audioPolicy = new AudioPolicy.Builder(/*context=*/ null)
+                    .setMediaProjection(projection)
                     .addMix(audioMix).build();
             AudioRecord record = audioPolicy.createAudioRecordSink(audioMix);
             record.unregisterAudioPolicyOnRelease(audioPolicy);
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index f5aeca7..571e67e 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -34,6 +34,7 @@
 import android.media.VolumePolicy;
 import android.media.audiopolicy.AudioPolicyConfig;
 import android.media.audiopolicy.IAudioPolicyCallback;
+import android.media.projection.IMediaProjection;
 
 /**
  * {@hide}
@@ -176,7 +177,7 @@
 
     String registerAudioPolicy(in AudioPolicyConfig policyConfig,
             in IAudioPolicyCallback pcb, boolean hasFocusListener, boolean isFocusPolicy,
-            boolean isVolumeController);
+            boolean isVolumeController, in IMediaProjection projection);
 
     oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb);
 
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 6116429..ba87f2b 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -16,8 +16,12 @@
 
 package android.media;
 
+import android.annotation.IntRange;
+import android.annotation.NonNull;
 import android.graphics.ImageFormat;
+import android.graphics.ImageFormat.Format;
 import android.hardware.HardwareBuffer;
+import android.hardware.HardwareBuffer.Usage;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -120,7 +124,11 @@
      *            Must be greater than 0.
      * @see Image
      */
-    public static ImageReader newInstance(int width, int height, int format, int maxImages) {
+    public static @NonNull ImageReader newInstance(
+            @IntRange(from = 1) int width,
+            @IntRange(from = 1) int height,
+            @Format             int format,
+            @IntRange(from = 1) int maxImages) {
         // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not
         // work, and is inscrutable anyway
         return new ImageReader(width, height, format, maxImages,
@@ -210,8 +218,12 @@
      * @see Image
      * @see HardwareBuffer
      */
-    public static ImageReader newInstance(int width, int height, int format, int maxImages,
-            long usage) {
+    public static @NonNull ImageReader newInstance(
+            @IntRange(from = 1) int width,
+            @IntRange(from = 1) int height,
+            @Format             int format,
+            @IntRange(from = 1) int maxImages,
+            @Usage              long usage) {
         // TODO: Check this - can't do it just yet because format support is different
         // Unify formats! The only reliable way to validate usage is to just try it and see.
 
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index dd09afc..f813d1b 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -16,7 +16,10 @@
 
 package android.media;
 
+import android.annotation.IntRange;
+import android.annotation.NonNull;
 import android.graphics.ImageFormat;
+import android.graphics.ImageFormat.Format;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.hardware.camera2.utils.SurfaceUtils;
@@ -124,7 +127,8 @@
      *            {@link #dequeueInputImage()}.
      * @return a new ImageWriter instance.
      */
-    public static ImageWriter newInstance(Surface surface, int maxImages) {
+    public static @NonNull ImageWriter newInstance(@NonNull Surface surface,
+            @IntRange(from = 1) int maxImages) {
         return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN);
     }
 
@@ -169,7 +173,8 @@
      *
      * @return a new ImageWriter instance.
      */
-    public static ImageWriter newInstance(Surface surface, int maxImages, int format) {
+    public static @NonNull ImageWriter newInstance(@NonNull Surface surface,
+            @IntRange(from = 1) int maxImages, @Format int format) {
         if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
             throw new IllegalArgumentException("Invalid format is specified: " + format);
         }
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index cf5711d..a9e33fd 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -19,9 +19,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.cas.V1_0.HidlCasPluginDescriptor;
-import android.hardware.cas.V1_1.ICas;
+import android.hardware.cas.V1_0.ICas;
+import android.hardware.cas.V1_0.IMediaCasService;
 import android.hardware.cas.V1_1.ICasListener;
-import android.hardware.cas.V1_1.IMediaCasService;
 import android.media.MediaCasException.*;
 import android.os.Bundle;
 import android.os.Handler;
@@ -99,23 +99,35 @@
 public final class MediaCas implements AutoCloseable {
     private static final String TAG = "MediaCas";
     private ICas mICas;
+    private android.hardware.cas.V1_1.ICas mICasV11;
     private EventListener mListener;
     private HandlerThread mHandlerThread;
     private EventHandler mEventHandler;
 
-    private static final Singleton<IMediaCasService> gDefault =
-            new Singleton<IMediaCasService>() {
+    private static final Singleton<IMediaCasService> sService = new Singleton<IMediaCasService>() {
         @Override
         protected IMediaCasService create() {
             try {
-                return IMediaCasService.getService(true /*wait*/);
-            } catch (RemoteException e) {}
+                Log.d(TAG, "Tried to get cas@1.1 service");
+                android.hardware.cas.V1_1.IMediaCasService serviceV11 =
+                        android.hardware.cas.V1_1.IMediaCasService.getService(true /*wait*/);
+                if (serviceV11 != null) {
+                    return serviceV11;
+                }
+            } catch (Exception eV1_1) {
+                try {
+                    Log.d(TAG, "Tried to get cas@1.0 service");
+                    return IMediaCasService.getService(true /*wait*/);
+                } catch (Exception eV1_0) {
+                    Log.d(TAG, "Failed to get cas@1.0 service");
+                }
+            }
             return null;
         }
     };
 
     static IMediaCasService getService() {
-        return gDefault.get();
+        return sService.get();
     }
 
     private void validateInternalStates() {
@@ -126,11 +138,12 @@
 
     private void cleanupAndRethrowIllegalState() {
         mICas = null;
+        mICasV11 = null;
         throw new IllegalStateException();
     }
 
-    private class EventHandler extends Handler
-    {
+    private class EventHandler extends Handler {
+
         private static final int MSG_CAS_EVENT = 0;
         private static final int MSG_CAS_SESSION_EVENT = 1;
         private static final String SESSION_KEY = "sessionId";
@@ -164,7 +177,7 @@
         }
         @Override
         public void onSessionEvent(@NonNull ArrayList<Byte> sessionId,
-                        int event, int arg, @Nullable ArrayList<Byte> data)
+                int event, int arg, @Nullable ArrayList<Byte> data)
                 throws RemoteException {
             Message msg = mEventHandler.obtainMessage();
             msg.what = EventHandler.MSG_CAS_SESSION_EVENT;
@@ -177,7 +190,6 @@
             mEventHandler.sendMessage(msg);
         }
     };
-
     /**
      * Describe a CAS plugin with its CA_system_ID and string name.
      *
@@ -338,9 +350,14 @@
                 throws MediaCasException {
             validateInternalStates();
 
+            if (mICasV11 == null) {
+                Log.d(TAG, "Send Session Event isn't supported by cas@1.0 interface");
+                throw new UnsupportedCasException("Send Session Event is not supported");
+            }
+
             try {
                 MediaCasException.throwExceptionIfNeeded(
-                        mICas.sendSessionEvent(mSessionId, event, arg, toByteArray(data)));
+                        mICasV11.sendSessionEvent(mSessionId, event, arg, toByteArray(data)));
             } catch (RemoteException e) {
                 cleanupAndRethrowIllegalState();
             }
@@ -427,7 +444,16 @@
      */
     public MediaCas(int CA_system_id) throws UnsupportedCasException {
         try {
-            mICas = getService().createPluginExt(CA_system_id, mBinder);
+            IMediaCasService service = getService();
+            android.hardware.cas.V1_1.IMediaCasService serviceV11 =
+                    android.hardware.cas.V1_1.IMediaCasService.castFrom(service);
+            if (serviceV11 == null) {
+                Log.d(TAG, "Used cas@1_0 interface to create plugin");
+                mICas = service.createPlugin(CA_system_id, mBinder);
+            } else {
+                Log.d(TAG, "Used cas@1.1 interface to create plugin");
+                mICas = mICasV11 = serviceV11.createPluginExt(CA_system_id, mBinder);
+            }
         } catch(Exception e) {
             Log.e(TAG, "Failed to create plugin: " + e);
             mICas = null;
@@ -528,7 +554,7 @@
         }
     }
 
-    private class OpenSessionCallback implements ICas.openSessionCallback {
+    private class OpenSessionCallback implements android.hardware.cas.V1_1.ICas.openSessionCallback{
         public Session mSession;
         public int mStatus;
         @Override
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index e4d356b..08ce9fc 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2645,6 +2645,7 @@
      */
     private synchronized void setSubtitleAnchor() {
         if ((mSubtitleController == null) && (ActivityThread.currentApplication() != null)) {
+            getMediaTimeProvider();
             final HandlerThread thread = new HandlerThread("SetSubtitleAnchorThread");
             thread.start();
             Handler handler = new Handler(thread.getLooper());
@@ -2660,7 +2661,7 @@
 
                         @Override
                         public Looper getSubtitleLooper() {
-                            return Looper.getMainLooper();
+                            return mTimeProvider.mEventHandler.getLooper();
                         }
                     });
                     thread.getLooper().quitSafely();
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 761b625..6fd6298 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -133,7 +133,8 @@
     }
 
 
-    int getRouteFlags() {
+    /** @hide */
+    public int getRouteFlags() {
         return mRouteFlags;
     }
 
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 65f3294..5f65d58 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -18,7 +18,9 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.media.AudioAttributes;
@@ -30,6 +32,7 @@
 import android.media.AudioTrack;
 import android.media.IAudioService;
 import android.media.MediaRecorder;
+import android.media.projection.MediaProjection;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -93,6 +96,8 @@
 
     private AudioPolicyConfig mConfig;
 
+    private final MediaProjection mProjection;
+
     /** @hide */
     public AudioPolicyConfig getConfig() { return mConfig; }
     /** @hide */
@@ -101,13 +106,17 @@
     public boolean isFocusPolicy() { return mIsFocusPolicy; }
     /** @hide */
     public boolean isVolumeController() { return mVolCb != null; }
+    /** @hide */
+    public @Nullable MediaProjection getMediaProjection() {
+        return mProjection;
+    }
 
     /**
      * The parameter is guaranteed non-null through the Builder
      */
     private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper,
             AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy,
-            AudioPolicyVolumeCallback vc) {
+            AudioPolicyVolumeCallback vc, @Nullable MediaProjection projection) {
         mConfig = config;
         mStatus = POLICY_STATUS_UNREGISTERED;
         mContext = context;
@@ -124,6 +133,7 @@
         mStatusListener = sl;
         mIsFocusPolicy = isFocusPolicy;
         mVolCb = vc;
+        mProjection = projection;
     }
 
     /**
@@ -138,6 +148,7 @@
         private AudioPolicyStatusListener mStatusListener;
         private boolean mIsFocusPolicy = false;
         private AudioPolicyVolumeCallback mVolCb;
+        private MediaProjection mProjection;
 
         /**
          * Constructs a new Builder with no audio mixes.
@@ -222,6 +233,23 @@
         }
 
         /**
+         * Set a media projection obtained through createMediaProjection().
+         *
+         * A MediaProjection that can project audio allows to register an audio
+         * policy LOOPBACK|RENDER without the MODIFY_AUDIO_ROUTING permission.
+         *
+         * @hide
+         */
+        public Builder setMediaProjection(@NonNull MediaProjection projection) {
+            if (projection == null) {
+                throw new IllegalArgumentException("Invalid null volume callback");
+            }
+            mProjection = projection;
+            return this;
+
+        }
+
+        /**
          * Combines all of the attributes that have been set on this {@code Builder} and returns a
          * new {@link AudioPolicy} object.
          * @return a new {@code AudioPolicy} object.
@@ -241,7 +269,7 @@
                         + "an AudioPolicyFocusListener");
             }
             return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper,
-                    mFocusListener, mStatusListener, mIsFocusPolicy, mVolCb);
+                    mFocusListener, mStatusListener, mIsFocusPolicy, mVolCb, mProjection);
         }
     }
 
@@ -336,11 +364,10 @@
      * played.
      * @param uid UID of the application to affect.
      * @param devices list of devices to which the audio stream of the application may be routed.
-     * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
-     *          otherwise.
+     * @return true if the change was successful, false otherwise.
      */
     @SystemApi
-    public int setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) {
+    public boolean setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) {
         if (devices == null) {
             throw new IllegalArgumentException("Illegal null list of audio devices");
         }
@@ -365,10 +392,10 @@
             try {
                 final int status = service.setUidDeviceAffinity(this.cb(),
                         uid, deviceTypes, deviceAdresses);
-                return status;
+                return (status == AudioManager.SUCCESS);
             } catch (RemoteException e) {
                 Log.e(TAG, "Dead object in setUidDeviceAffinity", e);
-                return AudioManager.ERROR;
+                return false;
             }
         }
     }
@@ -378,11 +405,10 @@
      * Removes audio device affinity previously set by
      * {@link #setUidDeviceAffinity(int, java.util.List)}.
      * @param uid UID of the application affected.
-     * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
-     *          otherwise.
+     * @return true if the change was successful, false otherwise.
      */
     @SystemApi
-    public int removeUidDeviceAffinity(int uid) {
+    public boolean removeUidDeviceAffinity(int uid) {
         synchronized (mLock) {
             if (mStatus != POLICY_STATUS_REGISTERED) {
                 throw new IllegalStateException("Cannot use unregistered AudioPolicy");
@@ -390,10 +416,10 @@
             final IAudioService service = getService();
             try {
                 final int status = service.removeUidDeviceAffinity(this.cb(), uid);
-                return status;
+                return (status == AudioManager.SUCCESS);
             } catch (RemoteException e) {
                 Log.e(TAG, "Dead object in removeUidDeviceAffinity", e);
-                return AudioManager.ERROR;
+                return false;
             }
         }
     }
@@ -417,24 +443,57 @@
                 Log.e(TAG, "Cannot use unregistered AudioPolicy");
                 return false;
             }
-            if (mContext == null) {
-                Log.e(TAG, "Cannot use AudioPolicy without context");
-                return false;
-            }
             if (mRegistrationId == null) {
                 Log.e(TAG, "Cannot use unregistered AudioPolicy");
                 return false;
             }
         }
-        if (!(PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
-                        android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
+
+        // Loopback|capture only need an audio projection, everything else need MODIFY_AUDIO_ROUTING
+        boolean canModifyAudioRouting = PackageManager.PERMISSION_GRANTED
+                == checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
+
+        boolean canProjectAudio;
+        try {
+            canProjectAudio = mProjection != null && mProjection.getProjection().canProjectAudio();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to check if MediaProjection#canProjectAudio");
+            throw e.rethrowFromSystemServer();
+        }
+
+        if (!((isLoopbackRenderPolicy() && canProjectAudio) || canModifyAudioRouting)) {
             Slog.w(TAG, "Cannot use AudioPolicy for pid " + Binder.getCallingPid() + " / uid "
-                    + Binder.getCallingUid() + ", needs MODIFY_AUDIO_ROUTING");
+                    + Binder.getCallingUid() + ", needs MODIFY_AUDIO_ROUTING or "
+                    + "MediaProjection that can project audio.");
             return false;
         }
         return true;
     }
 
+    private boolean isLoopbackRenderPolicy() {
+        synchronized (mLock) {
+            return mConfig.mMixes.stream().allMatch(mix -> mix.getRouteFlags()
+                    == (mix.ROUTE_FLAG_RENDER | mix.ROUTE_FLAG_LOOP_BACK));
+        }
+    }
+
+    /**
+     * Returns {@link PackageManager#PERMISSION_GRANTED} if the caller has the given permission.
+     */
+    private @PackageManager.PermissionResult int checkCallingOrSelfPermission(String permission) {
+        if (mContext != null) {
+            return mContext.checkCallingOrSelfPermission(permission);
+        }
+        Slog.v(TAG, "Null context, checking permission via ActivityManager");
+        int pid = Binder.getCallingPid();
+        int uid = Binder.getCallingUid();
+        try {
+            return ActivityManager.getService().checkPermission(permission, pid, uid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private void checkMixReadyToUse(AudioMix mix, boolean forTrack)
             throws IllegalArgumentException{
         if (mix == null) {
@@ -621,7 +680,6 @@
      *
      */
     public static abstract class AudioPolicyVolumeCallback {
-        /** @hide */
         public AudioPolicyVolumeCallback() {}
         /**
          * Called when volume key-related changes are triggered, on the key down event.
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index f725cac..a56fd31 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -18,8 +18,6 @@
 
 import android.annotation.NonNull;
 import android.media.AudioFormat;
-import android.media.AudioManager;
-import android.media.AudioPatch;
 import android.media.audiopolicy.AudioMixingRule.AudioMixMatchCriterion;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/media/java/android/media/session/ControllerLink.java b/media/java/android/media/session/ControllerLink.java
index 40c7166..5828fbd 100644
--- a/media/java/android/media/session/ControllerLink.java
+++ b/media/java/android/media/session/ControllerLink.java
@@ -158,6 +158,18 @@
     }
 
     /**
+     * Gets the session info of the connected session.
+     */
+    @Nullable
+    Bundle getSessionInfo() {
+        try {
+            return mISessionController.getSessionInfo();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
      * Gets the {@link PendingIntent} for launching UI of the connected session.
      */
     @Nullable
@@ -666,6 +678,12 @@
             return null;
         }
 
+        /** Stub method for ISessionController.getSessionInfo */
+        @Nullable
+        public Bundle getSessionInfo() {
+            return null;
+        }
+
         /** Stub method for ISessionController.getLaunchPendingIntent */
         @Nullable
         public PendingIntent getLaunchPendingIntent() {
@@ -856,6 +874,11 @@
         }
 
         @Override
+        public Bundle getSessionInfo() {
+            return mControllerStub.getSessionInfo();
+        }
+
+        @Override
         public PendingIntent getLaunchPendingIntent() {
             return mControllerStub.getLaunchPendingIntent();
         }
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 3e7b4fb..298085f 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -44,6 +44,7 @@
     void unregisterCallback(in ControllerCallbackLink cb);
     String getPackageName();
     String getTag();
+    Bundle getSessionInfo();
     PendingIntent getLaunchPendingIntent();
     long getFlags();
     MediaController.PlaybackInfo getVolumeAttributes();
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 8143bfa..e85461f 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -35,7 +35,7 @@
  */
 interface ISessionManager {
     SessionLink createSession(String packageName, in SessionCallbackLink sessionCb, String tag,
-            int userId);
+            in Bundle sessionInfo, int userId);
     void notifySession2Created(in Session2Token sessionToken);
     List<MediaSession.Token> getSessions(in ComponentName compName, int userId);
     List<Session2Token> getSession2Tokens(int userId);
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 9e4199c..fb21f69 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -79,6 +79,7 @@
     private boolean mCbRegistered = false;
     private String mPackageName;
     private String mTag;
+    private Bundle mSessionInfo;
 
     private final TransportControls mTransportControls;
 
@@ -410,6 +411,23 @@
     }
 
     /**
+     * Gets the additional session information which was set when the session was created.
+     *
+     * @return The additional session information
+     */
+    @Nullable
+    public Bundle getSessionInfo() {
+        if (mSessionInfo == null) {
+            try {
+                mSessionInfo = mSessionBinder.getSessionInfo();
+            } catch (RuntimeException e) {
+                Log.d(TAG, "Dead object in getSessionInfo.", e);
+            }
+        }
+        return mSessionInfo;
+    }
+
+    /**
      * Get the session's tag for debugging purposes.
      *
      * @return The session's tag.
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 8ab893b..9fdefa6 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -132,6 +132,27 @@
      * @param tag A short name for debugging purposes.
      */
     public MediaSession(@NonNull Context context, @NonNull String tag) {
+        this(context, tag, null);
+    }
+
+    /**
+     * Creates a new session. The session will automatically be registered with
+     * the system but will not be published until {@link #setActive(boolean)
+     * setActive(true)} is called. You must call {@link #release()} when
+     * finished with the session.
+     * <p>
+     * The {@code sessionInfo} can include additional unchanging information about this session.
+     * For example, it can include the version of the application, or the list of the custom
+     * commands that this session supports.
+     *
+     * @param context The context to use to create the session.
+     * @param tag A short name for debugging purposes.
+     * @param sessionInfo A bundle for additional information about this session.
+     *                    Controllers can get this information by calling
+     *                    {@link MediaController#getSessionInfo()}.
+     */
+    public MediaSession(@NonNull Context context, @NonNull String tag,
+            @Nullable Bundle sessionInfo) {
         if (context == null) {
             throw new IllegalArgumentException("context cannot be null.");
         }
@@ -142,7 +163,7 @@
                 .getSystemService(Context.MEDIA_SESSION_SERVICE);
         try {
             SessionCallbackLink cbLink = new SessionCallbackLink(context);
-            SessionLink sessionLink = manager.createSession(cbLink, tag);
+            SessionLink sessionLink = manager.createSession(cbLink, tag, sessionInfo);
             mImpl = new MediaSessionEngine(context, sessionLink, cbLink);
             mMaxBitmapSize = context.getResources().getDimensionPixelSize(
                     android.R.dimen.config_mediaMetadataBitmapMaxSize);
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 46f6c71..fde4f88 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -28,6 +28,7 @@
 import android.media.IRemoteVolumeController;
 import android.media.MediaSession2;
 import android.media.Session2Token;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -101,13 +102,15 @@
      * Create a new session in the system and get the binder for it.
      *
      * @param tag A short name for debugging purposes.
+     * @param sessionInfo A bundle for additional information about this session.
      * @return The binder object from the system
      * @hide
      */
     @NonNull
-    public SessionLink createSession(@NonNull SessionCallbackLink cbStub, @NonNull String tag) {
+    public SessionLink createSession(@NonNull SessionCallbackLink cbStub, @NonNull String tag,
+            @Nullable Bundle sessionInfo) {
         try {
-            return mService.createSession(mContext.getPackageName(), cbStub, tag,
+            return mService.createSession(mContext.getPackageName(), cbStub, tag, sessionInfo,
                     UserHandle.myUserId());
         } catch (RemoteException e) {
             throw new RuntimeException(e);
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 8ed265d..65b58ab 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -20,8 +20,8 @@
         "android_media_MediaScanner.cpp",
         "android_media_MediaSync.cpp",
         "android_media_ResampleInputStream.cpp",
+        "android_media_Streams.cpp",
         "android_media_SyncParams.cpp",
-        "android_media_Utils.cpp",
         "android_mtp_MtpDatabase.cpp",
         "android_mtp_MtpDevice.cpp",
         "android_mtp_MtpServer.cpp",
@@ -34,6 +34,7 @@
         "libutils",
         "libbinder",
         "libmedia",
+        "libmedia_jni_utils",
         "libmedia_omx",
         "libmediametrics",
         "libmediadrm",
@@ -85,6 +86,36 @@
 }
 
 cc_library_shared {
+    name: "libmedia_jni_utils",
+    srcs: [
+        "android_media_Utils.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libmedia_omx",
+        "libnativewindow",
+        "libui",
+        "libutils",
+        "android.hidl.token@1.0-utils",
+    ],
+
+    include_dirs: [
+        "system/media/camera/include",
+    ],
+
+    export_include_dirs: ["."],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
+
+cc_library_shared {
     name: "libmedia2_jni",
 
     srcs: [
@@ -125,7 +156,6 @@
         "libcrypto",
         "libcutils",
         "libjsoncpp",
-        "libmedia_helper",
         "libmedia_player2_util",
         "libmediaplayer2",
         "libmediaplayer2-protos",
@@ -133,7 +163,6 @@
         "libmediautils",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
-        "libstagefright",
         "libstagefright_esds",
         "libstagefright_foundation_without_imemory",
         "libstagefright_httplive",
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index f07f1e8..150b6f9 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -23,7 +23,7 @@
 #include "android_media_MediaCrypto.h"
 #include "android_media_MediaDescrambler.h"
 #include "android_media_MediaMetricsJNI.h"
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/android_view_Surface.h"
 #include "android_util_Binder.h"
diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp
index 6b8f745..923d1d2 100644
--- a/media/jni/android_media_MediaCodecList.cpp
+++ b/media/jni/android_media_MediaCodecList.cpp
@@ -31,7 +31,7 @@
 #include "android_runtime/AndroidRuntime.h"
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 
 using namespace android;
 
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index c6b171b..f5ae9d0 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -22,7 +22,7 @@
 #include "android_media_MediaDataSource.h"
 #include "android_media_MediaExtractor.h"
 #include "android_media_MediaMetricsJNI.h"
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 #include "android_os_HwRemoteBinder.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index c1226fa..a480784 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -18,6 +18,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaMetadataRetrieverJNI"
 
+#include <cmath>
 #include <assert.h>
 #include <utils/Log.h>
 #include <utils/threads.h>
@@ -32,7 +33,7 @@
 #include <nativehelper/JNIHelp.h>
 #include "android_runtime/AndroidRuntime.h"
 #include "android_media_MediaDataSource.h"
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 #include "android_util_Binder.h"
 
 #include "android/graphics/GraphicsJNI.h"
diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp
index f11452a..f0aa4c3 100644
--- a/media/jni/android_media_MediaMuxer.cpp
+++ b/media/jni/android_media_MediaMuxer.cpp
@@ -18,7 +18,7 @@
 #define LOG_TAG "MediaMuxer-JNI"
 #include <utils/Log.h>
 
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 35b1081..d24edc7 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -44,7 +44,7 @@
 #include "android_media_PlaybackParams.h"
 #include "android_media_SyncParams.h"
 #include "android_media_VolumeShaper.h"
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 
 #include "android_os_Parcel.h"
 #include "android_util_Binder.h"
diff --git a/media/jni/android_media_Streams.cpp b/media/jni/android_media_Streams.cpp
new file mode 100644
index 0000000..b7cbd97
--- /dev/null
+++ b/media/jni/android_media_Streams.cpp
@@ -0,0 +1,559 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "AndroidMediaStreams"
+
+#include <utils/Log.h>
+#include "android_media_Streams.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <nativehelper/ScopedLocalRef.h>
+
+namespace android {
+
+AssetStream::AssetStream(SkStream* stream)
+    : mStream(stream), mPosition(0) {
+}
+
+AssetStream::~AssetStream() {
+}
+
+piex::Error AssetStream::GetData(
+        const size_t offset, const size_t length, std::uint8_t* data) {
+    // Seek first.
+    if (mPosition != offset) {
+        if (!mStream->seek(offset)) {
+            return piex::Error::kFail;
+        }
+    }
+
+    // Read bytes.
+    size_t size = mStream->read((void*)data, length);
+    mPosition = offset + size;
+
+    return size == length ? piex::Error::kOk : piex::Error::kFail;
+}
+
+BufferedStream::BufferedStream(SkStream* stream)
+    : mStream(stream) {
+}
+
+BufferedStream::~BufferedStream() {
+}
+
+piex::Error BufferedStream::GetData(
+        const size_t offset, const size_t length, std::uint8_t* data) {
+    // Seek first.
+    if (offset + length > mStreamBuffer.bytesWritten()) {
+        size_t sizeToRead = offset + length - mStreamBuffer.bytesWritten();
+        if (sizeToRead <= kMinSizeToRead) {
+            sizeToRead = kMinSizeToRead;
+        }
+
+        void* tempBuffer = malloc(sizeToRead);
+        if (tempBuffer == NULL) {
+          return piex::Error::kFail;
+        }
+
+        size_t bytesRead = mStream->read(tempBuffer, sizeToRead);
+        if (bytesRead != sizeToRead) {
+            free(tempBuffer);
+            return piex::Error::kFail;
+        }
+        mStreamBuffer.write(tempBuffer, bytesRead);
+        free(tempBuffer);
+    }
+
+    // Read bytes.
+    if (mStreamBuffer.read((void*)data, offset, length)) {
+        return piex::Error::kOk;
+    } else {
+        return piex::Error::kFail;
+    }
+}
+
+FileStream::FileStream(const int fd)
+    : mPosition(0) {
+    mFile = fdopen(fd, "r");
+    if (mFile == NULL) {
+        return;
+    }
+}
+
+FileStream::FileStream(const String8 filename)
+    : mPosition(0) {
+    mFile = fopen(filename.string(), "r");
+    if (mFile == NULL) {
+        return;
+    }
+}
+
+FileStream::~FileStream() {
+    if (mFile != NULL) {
+        fclose(mFile);
+        mFile = NULL;
+    }
+}
+
+piex::Error FileStream::GetData(
+        const size_t offset, const size_t length, std::uint8_t* data) {
+    if (mFile == NULL) {
+        return piex::Error::kFail;
+    }
+
+    // Seek first.
+    if (mPosition != offset) {
+        fseek(mFile, offset, SEEK_SET);
+    }
+
+    // Read bytes.
+    size_t size = fread((void*)data, sizeof(std::uint8_t), length, mFile);
+    mPosition += size;
+
+    // Handle errors and verify the size.
+    if (ferror(mFile) || size != length) {
+        ALOGV("GetData read failed: (offset: %zu, length: %zu)", offset, length);
+        return piex::Error::kFail;
+    }
+    return piex::Error::kOk;
+}
+
+bool FileStream::exists() const {
+    return mFile != NULL;
+}
+
+bool GetExifFromRawImage(
+        piex::StreamInterface* stream, const String8& filename,
+        piex::PreviewImageData& image_data) {
+    // Reset the PreviewImageData to its default.
+    image_data = piex::PreviewImageData();
+
+    if (!piex::IsRaw(stream)) {
+        // Format not supported.
+        ALOGV("Format not supported: %s", filename.string());
+        return false;
+    }
+
+    piex::Error err = piex::GetPreviewImageData(stream, &image_data);
+
+    if (err != piex::Error::kOk) {
+        // The input data seems to be broken.
+        ALOGV("Raw image not detected: %s (piex error code: %d)", filename.string(), (int32_t)err);
+        return false;
+    }
+
+    return true;
+}
+
+bool ConvertKeyValueArraysToKeyedVector(
+        JNIEnv *env, jobjectArray keys, jobjectArray values,
+        KeyedVector<String8, String8>* keyedVector) {
+
+    int nKeyValuePairs = 0;
+    bool failed = false;
+    if (keys != NULL && values != NULL) {
+        nKeyValuePairs = env->GetArrayLength(keys);
+        failed = (nKeyValuePairs != env->GetArrayLength(values));
+    }
+
+    if (!failed) {
+        failed = ((keys != NULL && values == NULL) ||
+                  (keys == NULL && values != NULL));
+    }
+
+    if (failed) {
+        ALOGE("keys and values arrays have different length");
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return false;
+    }
+
+    for (int i = 0; i < nKeyValuePairs; ++i) {
+        // No need to check on the ArrayIndexOutOfBoundsException, since
+        // it won't happen here.
+        jstring key = (jstring) env->GetObjectArrayElement(keys, i);
+        jstring value = (jstring) env->GetObjectArrayElement(values, i);
+
+        const char* keyStr = env->GetStringUTFChars(key, NULL);
+        if (!keyStr) {  // OutOfMemoryError
+            return false;
+        }
+
+        const char* valueStr = env->GetStringUTFChars(value, NULL);
+        if (!valueStr) {  // OutOfMemoryError
+            env->ReleaseStringUTFChars(key, keyStr);
+            return false;
+        }
+
+        keyedVector->add(String8(keyStr), String8(valueStr));
+
+        env->ReleaseStringUTFChars(key, keyStr);
+        env->ReleaseStringUTFChars(value, valueStr);
+        env->DeleteLocalRef(key);
+        env->DeleteLocalRef(value);
+    }
+    return true;
+}
+
+static jobject makeIntegerObject(JNIEnv *env, int32_t value) {
+    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Integer"));
+    CHECK(clazz.get() != NULL);
+
+    jmethodID integerConstructID =
+        env->GetMethodID(clazz.get(), "<init>", "(I)V");
+    CHECK(integerConstructID != NULL);
+
+    return env->NewObject(clazz.get(), integerConstructID, value);
+}
+
+static jobject makeLongObject(JNIEnv *env, int64_t value) {
+    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Long"));
+    CHECK(clazz.get() != NULL);
+
+    jmethodID longConstructID = env->GetMethodID(clazz.get(), "<init>", "(J)V");
+    CHECK(longConstructID != NULL);
+
+    return env->NewObject(clazz.get(), longConstructID, value);
+}
+
+static jobject makeFloatObject(JNIEnv *env, float value) {
+    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Float"));
+    CHECK(clazz.get() != NULL);
+
+    jmethodID floatConstructID =
+        env->GetMethodID(clazz.get(), "<init>", "(F)V");
+    CHECK(floatConstructID != NULL);
+
+    return env->NewObject(clazz.get(), floatConstructID, value);
+}
+
+static jobject makeByteBufferObject(
+        JNIEnv *env, const void *data, size_t size) {
+    jbyteArray byteArrayObj = env->NewByteArray(size);
+    env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data);
+
+    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/nio/ByteBuffer"));
+    CHECK(clazz.get() != NULL);
+
+    jmethodID byteBufWrapID =
+        env->GetStaticMethodID(
+                clazz.get(), "wrap", "([B)Ljava/nio/ByteBuffer;");
+    CHECK(byteBufWrapID != NULL);
+
+    jobject byteBufObj = env->CallStaticObjectMethod(
+            clazz.get(), byteBufWrapID, byteArrayObj);
+
+    env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL;
+
+    return byteBufObj;
+}
+
+static void SetMapInt32(
+        JNIEnv *env, jobject hashMapObj, jmethodID hashMapPutID,
+        const char *key, int32_t value) {
+    jstring keyObj = env->NewStringUTF(key);
+    jobject valueObj = makeIntegerObject(env, value);
+
+    env->CallObjectMethod(hashMapObj, hashMapPutID, keyObj, valueObj);
+
+    env->DeleteLocalRef(valueObj); valueObj = NULL;
+    env->DeleteLocalRef(keyObj); keyObj = NULL;
+}
+
+status_t ConvertMessageToMap(
+        JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
+    ScopedLocalRef<jclass> hashMapClazz(
+            env, env->FindClass("java/util/HashMap"));
+
+    if (hashMapClazz.get() == NULL) {
+        return -EINVAL;
+    }
+
+    jmethodID hashMapConstructID =
+        env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
+
+    if (hashMapConstructID == NULL) {
+        return -EINVAL;
+    }
+
+    jmethodID hashMapPutID =
+        env->GetMethodID(
+                hashMapClazz.get(),
+                "put",
+                "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
+    if (hashMapPutID == NULL) {
+        return -EINVAL;
+    }
+
+    jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
+
+    for (size_t i = 0; i < msg->countEntries(); ++i) {
+        AMessage::Type valueType;
+        const char *key = msg->getEntryNameAt(i, &valueType);
+
+        if (!strncmp(key, "android._", 9)) {
+            // don't expose private keys (starting with android._)
+            continue;
+        }
+
+        jobject valueObj = NULL;
+
+        switch (valueType) {
+            case AMessage::kTypeInt32:
+            {
+                int32_t val;
+                CHECK(msg->findInt32(key, &val));
+
+                valueObj = makeIntegerObject(env, val);
+                break;
+            }
+
+            case AMessage::kTypeInt64:
+            {
+                int64_t val;
+                CHECK(msg->findInt64(key, &val));
+
+                valueObj = makeLongObject(env, val);
+                break;
+            }
+
+            case AMessage::kTypeFloat:
+            {
+                float val;
+                CHECK(msg->findFloat(key, &val));
+
+                valueObj = makeFloatObject(env, val);
+                break;
+            }
+
+            case AMessage::kTypeString:
+            {
+                AString val;
+                CHECK(msg->findString(key, &val));
+
+                valueObj = env->NewStringUTF(val.c_str());
+                break;
+            }
+
+            case AMessage::kTypeBuffer:
+            {
+                sp<ABuffer> buffer;
+                CHECK(msg->findBuffer(key, &buffer));
+
+                valueObj = makeByteBufferObject(
+                        env, buffer->data(), buffer->size());
+                break;
+            }
+
+            case AMessage::kTypeRect:
+            {
+                int32_t left, top, right, bottom;
+                CHECK(msg->findRect(key, &left, &top, &right, &bottom));
+
+                SetMapInt32(
+                        env,
+                        hashMap,
+                        hashMapPutID,
+                        AStringPrintf("%s-left", key).c_str(),
+                        left);
+
+                SetMapInt32(
+                        env,
+                        hashMap,
+                        hashMapPutID,
+                        AStringPrintf("%s-top", key).c_str(),
+                        top);
+
+                SetMapInt32(
+                        env,
+                        hashMap,
+                        hashMapPutID,
+                        AStringPrintf("%s-right", key).c_str(),
+                        right);
+
+                SetMapInt32(
+                        env,
+                        hashMap,
+                        hashMapPutID,
+                        AStringPrintf("%s-bottom", key).c_str(),
+                        bottom);
+                break;
+            }
+
+            default:
+                break;
+        }
+
+        if (valueObj != NULL) {
+            jstring keyObj = env->NewStringUTF(key);
+
+            env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
+
+            env->DeleteLocalRef(keyObj); keyObj = NULL;
+            env->DeleteLocalRef(valueObj); valueObj = NULL;
+        }
+    }
+
+    *map = hashMap;
+
+    return OK;
+}
+
+status_t ConvertKeyValueArraysToMessage(
+        JNIEnv *env, jobjectArray keys, jobjectArray values,
+        sp<AMessage> *out) {
+    ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String"));
+    CHECK(stringClass.get() != NULL);
+    ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer"));
+    CHECK(integerClass.get() != NULL);
+    ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long"));
+    CHECK(longClass.get() != NULL);
+    ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float"));
+    CHECK(floatClass.get() != NULL);
+    ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
+    CHECK(byteBufClass.get() != NULL);
+
+    sp<AMessage> msg = new AMessage;
+
+    jsize numEntries = 0;
+
+    if (keys != NULL) {
+        if (values == NULL) {
+            return -EINVAL;
+        }
+
+        numEntries = env->GetArrayLength(keys);
+
+        if (numEntries != env->GetArrayLength(values)) {
+            return -EINVAL;
+        }
+    } else if (values != NULL) {
+        return -EINVAL;
+    }
+
+    for (jsize i = 0; i < numEntries; ++i) {
+        jobject keyObj = env->GetObjectArrayElement(keys, i);
+
+        if (!env->IsInstanceOf(keyObj, stringClass.get())) {
+            return -EINVAL;
+        }
+
+        const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL);
+
+        if (tmp == NULL) {
+            return -ENOMEM;
+        }
+
+        AString key = tmp;
+
+        env->ReleaseStringUTFChars((jstring)keyObj, tmp);
+        tmp = NULL;
+
+        if (key.startsWith("android._")) {
+            // don't propagate private keys (starting with android._)
+            continue;
+        }
+
+        jobject valueObj = env->GetObjectArrayElement(values, i);
+
+        if (env->IsInstanceOf(valueObj, stringClass.get())) {
+            const char *value = env->GetStringUTFChars((jstring)valueObj, NULL);
+
+            if (value == NULL) {
+                return -ENOMEM;
+            }
+
+            msg->setString(key.c_str(), value);
+
+            env->ReleaseStringUTFChars((jstring)valueObj, value);
+            value = NULL;
+        } else if (env->IsInstanceOf(valueObj, integerClass.get())) {
+            jmethodID intValueID =
+                env->GetMethodID(integerClass.get(), "intValue", "()I");
+            CHECK(intValueID != NULL);
+
+            jint value = env->CallIntMethod(valueObj, intValueID);
+
+            msg->setInt32(key.c_str(), value);
+        } else if (env->IsInstanceOf(valueObj, longClass.get())) {
+            jmethodID longValueID =
+                env->GetMethodID(longClass.get(), "longValue", "()J");
+            CHECK(longValueID != NULL);
+
+            jlong value = env->CallLongMethod(valueObj, longValueID);
+
+            msg->setInt64(key.c_str(), value);
+        } else if (env->IsInstanceOf(valueObj, floatClass.get())) {
+            jmethodID floatValueID =
+                env->GetMethodID(floatClass.get(), "floatValue", "()F");
+            CHECK(floatValueID != NULL);
+
+            jfloat value = env->CallFloatMethod(valueObj, floatValueID);
+
+            msg->setFloat(key.c_str(), value);
+        } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) {
+            jmethodID positionID =
+                env->GetMethodID(byteBufClass.get(), "position", "()I");
+            CHECK(positionID != NULL);
+
+            jmethodID limitID =
+                env->GetMethodID(byteBufClass.get(), "limit", "()I");
+            CHECK(limitID != NULL);
+
+            jint position = env->CallIntMethod(valueObj, positionID);
+            jint limit = env->CallIntMethod(valueObj, limitID);
+
+            sp<ABuffer> buffer = new ABuffer(limit - position);
+
+            void *data = env->GetDirectBufferAddress(valueObj);
+
+            if (data != NULL) {
+                memcpy(buffer->data(),
+                       (const uint8_t *)data + position,
+                       buffer->size());
+            } else {
+                jmethodID arrayID =
+                    env->GetMethodID(byteBufClass.get(), "array", "()[B");
+                CHECK(arrayID != NULL);
+
+                jbyteArray byteArray =
+                    (jbyteArray)env->CallObjectMethod(valueObj, arrayID);
+                CHECK(byteArray != NULL);
+
+                env->GetByteArrayRegion(
+                        byteArray,
+                        position,
+                        buffer->size(),
+                        (jbyte *)buffer->data());
+
+                env->DeleteLocalRef(byteArray); byteArray = NULL;
+            }
+
+            msg->setBuffer(key.c_str(), buffer);
+        }
+    }
+
+    *out = msg;
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/jni/android_media_Streams.h b/media/jni/android_media_Streams.h
new file mode 100644
index 0000000..d174f9a
--- /dev/null
+++ b/media/jni/android_media_Streams.h
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_MEDIA_STREAMS_H_
+#define _ANDROID_MEDIA_STREAMS_H_
+
+#include "src/piex_types.h"
+#include "src/piex.h"
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include <SkStream.h>
+
+
+namespace android {
+
+class AssetStream : public piex::StreamInterface {
+private:
+    SkStream *mStream;
+    size_t mPosition;
+
+public:
+    explicit AssetStream(SkStream* stream);
+    ~AssetStream();
+
+    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
+    // provided by the caller, guaranteed to be at least "length" bytes long.
+    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
+    // 'offset' bytes from the start of the stream.
+    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
+    // change the contents of 'data'.
+    piex::Error GetData(
+            const size_t offset, const size_t length, std::uint8_t* data) override;
+};
+
+class BufferedStream : public piex::StreamInterface {
+private:
+    SkStream *mStream;
+    // Growable memory stream
+    SkDynamicMemoryWStream mStreamBuffer;
+
+    // Minimum size to read on filling the buffer.
+    const size_t kMinSizeToRead = 8192;
+
+public:
+    explicit BufferedStream(SkStream* stream);
+    ~BufferedStream();
+
+    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
+    // provided by the caller, guaranteed to be at least "length" bytes long.
+    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
+    // 'offset' bytes from the start of the stream.
+    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
+    // change the contents of 'data'.
+    piex::Error GetData(
+            const size_t offset, const size_t length, std::uint8_t* data) override;
+};
+
+class FileStream : public piex::StreamInterface {
+private:
+    FILE *mFile;
+    size_t mPosition;
+
+public:
+    explicit FileStream(const int fd);
+    explicit FileStream(const String8 filename);
+    ~FileStream();
+
+    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
+    // provided by the caller, guaranteed to be at least "length" bytes long.
+    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
+    // 'offset' bytes from the start of the stream.
+    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
+    // change the contents of 'data'.
+    piex::Error GetData(
+            const size_t offset, const size_t length, std::uint8_t* data) override;
+    bool exists() const;
+};
+
+// Reads EXIF metadata from a given raw image via piex.
+// And returns true if the operation is successful; otherwise, false.
+bool GetExifFromRawImage(
+        piex::StreamInterface* stream, const String8& filename, piex::PreviewImageData& image_data);
+
+// Returns true if the conversion is successful; otherwise, false.
+bool ConvertKeyValueArraysToKeyedVector(
+        JNIEnv *env, jobjectArray keys, jobjectArray values,
+        KeyedVector<String8, String8>* vector);
+
+struct AMessage;
+status_t ConvertMessageToMap(
+        JNIEnv *env, const sp<AMessage> &msg, jobject *map);
+
+status_t ConvertKeyValueArraysToMessage(
+        JNIEnv *env, jobjectArray keys, jobjectArray values,
+        sp<AMessage> *msg);
+
+};  // namespace android
+
+#endif //  _ANDROID_MEDIA_STREAMS_H_
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index 01baadb..2ef7b9e 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -21,12 +21,6 @@
 #include <utils/Log.h>
 #include "android_media_Utils.h"
 
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/AMessage.h>
-
-#include <nativehelper/ScopedLocalRef.h>
-
 #define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
 
 // Must be in sync with the value in HeicCompositeStream.cpp
@@ -34,533 +28,6 @@
 
 namespace android {
 
-AssetStream::AssetStream(SkStream* stream)
-    : mStream(stream), mPosition(0) {
-}
-
-AssetStream::~AssetStream() {
-}
-
-piex::Error AssetStream::GetData(
-        const size_t offset, const size_t length, std::uint8_t* data) {
-    // Seek first.
-    if (mPosition != offset) {
-        if (!mStream->seek(offset)) {
-            return piex::Error::kFail;
-        }
-    }
-
-    // Read bytes.
-    size_t size = mStream->read((void*)data, length);
-    mPosition = offset + size;
-
-    return size == length ? piex::Error::kOk : piex::Error::kFail;
-}
-
-BufferedStream::BufferedStream(SkStream* stream)
-    : mStream(stream) {
-}
-
-BufferedStream::~BufferedStream() {
-}
-
-piex::Error BufferedStream::GetData(
-        const size_t offset, const size_t length, std::uint8_t* data) {
-    // Seek first.
-    if (offset + length > mStreamBuffer.bytesWritten()) {
-        size_t sizeToRead = offset + length - mStreamBuffer.bytesWritten();
-        if (sizeToRead <= kMinSizeToRead) {
-            sizeToRead = kMinSizeToRead;
-        }
-
-        void* tempBuffer = malloc(sizeToRead);
-        if (tempBuffer == NULL) {
-          return piex::Error::kFail;
-        }
-
-        size_t bytesRead = mStream->read(tempBuffer, sizeToRead);
-        if (bytesRead != sizeToRead) {
-            free(tempBuffer);
-            return piex::Error::kFail;
-        }
-        mStreamBuffer.write(tempBuffer, bytesRead);
-        free(tempBuffer);
-    }
-
-    // Read bytes.
-    if (mStreamBuffer.read((void*)data, offset, length)) {
-        return piex::Error::kOk;
-    } else {
-        return piex::Error::kFail;
-    }
-}
-
-FileStream::FileStream(const int fd)
-    : mPosition(0) {
-    mFile = fdopen(fd, "r");
-    if (mFile == NULL) {
-        return;
-    }
-}
-
-FileStream::FileStream(const String8 filename)
-    : mPosition(0) {
-    mFile = fopen(filename.string(), "r");
-    if (mFile == NULL) {
-        return;
-    }
-}
-
-FileStream::~FileStream() {
-    if (mFile != NULL) {
-        fclose(mFile);
-        mFile = NULL;
-    }
-}
-
-piex::Error FileStream::GetData(
-        const size_t offset, const size_t length, std::uint8_t* data) {
-    if (mFile == NULL) {
-        return piex::Error::kFail;
-    }
-
-    // Seek first.
-    if (mPosition != offset) {
-        fseek(mFile, offset, SEEK_SET);
-    }
-
-    // Read bytes.
-    size_t size = fread((void*)data, sizeof(std::uint8_t), length, mFile);
-    mPosition += size;
-
-    // Handle errors and verify the size.
-    if (ferror(mFile) || size != length) {
-        ALOGV("GetData read failed: (offset: %zu, length: %zu)", offset, length);
-        return piex::Error::kFail;
-    }
-    return piex::Error::kOk;
-}
-
-bool FileStream::exists() const {
-    return mFile != NULL;
-}
-
-bool GetExifFromRawImage(
-        piex::StreamInterface* stream, const String8& filename,
-        piex::PreviewImageData& image_data) {
-    // Reset the PreviewImageData to its default.
-    image_data = piex::PreviewImageData();
-
-    if (!piex::IsRaw(stream)) {
-        // Format not supported.
-        ALOGV("Format not supported: %s", filename.string());
-        return false;
-    }
-
-    piex::Error err = piex::GetPreviewImageData(stream, &image_data);
-
-    if (err != piex::Error::kOk) {
-        // The input data seems to be broken.
-        ALOGV("Raw image not detected: %s (piex error code: %d)", filename.string(), (int32_t)err);
-        return false;
-    }
-
-    return true;
-}
-
-bool ConvertKeyValueArraysToKeyedVector(
-        JNIEnv *env, jobjectArray keys, jobjectArray values,
-        KeyedVector<String8, String8>* keyedVector) {
-
-    int nKeyValuePairs = 0;
-    bool failed = false;
-    if (keys != NULL && values != NULL) {
-        nKeyValuePairs = env->GetArrayLength(keys);
-        failed = (nKeyValuePairs != env->GetArrayLength(values));
-    }
-
-    if (!failed) {
-        failed = ((keys != NULL && values == NULL) ||
-                  (keys == NULL && values != NULL));
-    }
-
-    if (failed) {
-        ALOGE("keys and values arrays have different length");
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        return false;
-    }
-
-    for (int i = 0; i < nKeyValuePairs; ++i) {
-        // No need to check on the ArrayIndexOutOfBoundsException, since
-        // it won't happen here.
-        jstring key = (jstring) env->GetObjectArrayElement(keys, i);
-        jstring value = (jstring) env->GetObjectArrayElement(values, i);
-
-        const char* keyStr = env->GetStringUTFChars(key, NULL);
-        if (!keyStr) {  // OutOfMemoryError
-            return false;
-        }
-
-        const char* valueStr = env->GetStringUTFChars(value, NULL);
-        if (!valueStr) {  // OutOfMemoryError
-            env->ReleaseStringUTFChars(key, keyStr);
-            return false;
-        }
-
-        keyedVector->add(String8(keyStr), String8(valueStr));
-
-        env->ReleaseStringUTFChars(key, keyStr);
-        env->ReleaseStringUTFChars(value, valueStr);
-        env->DeleteLocalRef(key);
-        env->DeleteLocalRef(value);
-    }
-    return true;
-}
-
-static jobject makeIntegerObject(JNIEnv *env, int32_t value) {
-    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Integer"));
-    CHECK(clazz.get() != NULL);
-
-    jmethodID integerConstructID =
-        env->GetMethodID(clazz.get(), "<init>", "(I)V");
-    CHECK(integerConstructID != NULL);
-
-    return env->NewObject(clazz.get(), integerConstructID, value);
-}
-
-static jobject makeLongObject(JNIEnv *env, int64_t value) {
-    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Long"));
-    CHECK(clazz.get() != NULL);
-
-    jmethodID longConstructID = env->GetMethodID(clazz.get(), "<init>", "(J)V");
-    CHECK(longConstructID != NULL);
-
-    return env->NewObject(clazz.get(), longConstructID, value);
-}
-
-static jobject makeFloatObject(JNIEnv *env, float value) {
-    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Float"));
-    CHECK(clazz.get() != NULL);
-
-    jmethodID floatConstructID =
-        env->GetMethodID(clazz.get(), "<init>", "(F)V");
-    CHECK(floatConstructID != NULL);
-
-    return env->NewObject(clazz.get(), floatConstructID, value);
-}
-
-static jobject makeByteBufferObject(
-        JNIEnv *env, const void *data, size_t size) {
-    jbyteArray byteArrayObj = env->NewByteArray(size);
-    env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data);
-
-    ScopedLocalRef<jclass> clazz(env, env->FindClass("java/nio/ByteBuffer"));
-    CHECK(clazz.get() != NULL);
-
-    jmethodID byteBufWrapID =
-        env->GetStaticMethodID(
-                clazz.get(), "wrap", "([B)Ljava/nio/ByteBuffer;");
-    CHECK(byteBufWrapID != NULL);
-
-    jobject byteBufObj = env->CallStaticObjectMethod(
-            clazz.get(), byteBufWrapID, byteArrayObj);
-
-    env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL;
-
-    return byteBufObj;
-}
-
-static void SetMapInt32(
-        JNIEnv *env, jobject hashMapObj, jmethodID hashMapPutID,
-        const char *key, int32_t value) {
-    jstring keyObj = env->NewStringUTF(key);
-    jobject valueObj = makeIntegerObject(env, value);
-
-    env->CallObjectMethod(hashMapObj, hashMapPutID, keyObj, valueObj);
-
-    env->DeleteLocalRef(valueObj); valueObj = NULL;
-    env->DeleteLocalRef(keyObj); keyObj = NULL;
-}
-
-status_t ConvertMessageToMap(
-        JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
-    ScopedLocalRef<jclass> hashMapClazz(
-            env, env->FindClass("java/util/HashMap"));
-
-    if (hashMapClazz.get() == NULL) {
-        return -EINVAL;
-    }
-
-    jmethodID hashMapConstructID =
-        env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
-
-    if (hashMapConstructID == NULL) {
-        return -EINVAL;
-    }
-
-    jmethodID hashMapPutID =
-        env->GetMethodID(
-                hashMapClazz.get(),
-                "put",
-                "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-
-    if (hashMapPutID == NULL) {
-        return -EINVAL;
-    }
-
-    jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
-
-    for (size_t i = 0; i < msg->countEntries(); ++i) {
-        AMessage::Type valueType;
-        const char *key = msg->getEntryNameAt(i, &valueType);
-
-        if (!strncmp(key, "android._", 9)) {
-            // don't expose private keys (starting with android._)
-            continue;
-        }
-
-        jobject valueObj = NULL;
-
-        switch (valueType) {
-            case AMessage::kTypeInt32:
-            {
-                int32_t val;
-                CHECK(msg->findInt32(key, &val));
-
-                valueObj = makeIntegerObject(env, val);
-                break;
-            }
-
-            case AMessage::kTypeInt64:
-            {
-                int64_t val;
-                CHECK(msg->findInt64(key, &val));
-
-                valueObj = makeLongObject(env, val);
-                break;
-            }
-
-            case AMessage::kTypeFloat:
-            {
-                float val;
-                CHECK(msg->findFloat(key, &val));
-
-                valueObj = makeFloatObject(env, val);
-                break;
-            }
-
-            case AMessage::kTypeString:
-            {
-                AString val;
-                CHECK(msg->findString(key, &val));
-
-                valueObj = env->NewStringUTF(val.c_str());
-                break;
-            }
-
-            case AMessage::kTypeBuffer:
-            {
-                sp<ABuffer> buffer;
-                CHECK(msg->findBuffer(key, &buffer));
-
-                valueObj = makeByteBufferObject(
-                        env, buffer->data(), buffer->size());
-                break;
-            }
-
-            case AMessage::kTypeRect:
-            {
-                int32_t left, top, right, bottom;
-                CHECK(msg->findRect(key, &left, &top, &right, &bottom));
-
-                SetMapInt32(
-                        env,
-                        hashMap,
-                        hashMapPutID,
-                        AStringPrintf("%s-left", key).c_str(),
-                        left);
-
-                SetMapInt32(
-                        env,
-                        hashMap,
-                        hashMapPutID,
-                        AStringPrintf("%s-top", key).c_str(),
-                        top);
-
-                SetMapInt32(
-                        env,
-                        hashMap,
-                        hashMapPutID,
-                        AStringPrintf("%s-right", key).c_str(),
-                        right);
-
-                SetMapInt32(
-                        env,
-                        hashMap,
-                        hashMapPutID,
-                        AStringPrintf("%s-bottom", key).c_str(),
-                        bottom);
-                break;
-            }
-
-            default:
-                break;
-        }
-
-        if (valueObj != NULL) {
-            jstring keyObj = env->NewStringUTF(key);
-
-            env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
-
-            env->DeleteLocalRef(keyObj); keyObj = NULL;
-            env->DeleteLocalRef(valueObj); valueObj = NULL;
-        }
-    }
-
-    *map = hashMap;
-
-    return OK;
-}
-
-status_t ConvertKeyValueArraysToMessage(
-        JNIEnv *env, jobjectArray keys, jobjectArray values,
-        sp<AMessage> *out) {
-    ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String"));
-    CHECK(stringClass.get() != NULL);
-    ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer"));
-    CHECK(integerClass.get() != NULL);
-    ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long"));
-    CHECK(longClass.get() != NULL);
-    ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float"));
-    CHECK(floatClass.get() != NULL);
-    ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
-    CHECK(byteBufClass.get() != NULL);
-
-    sp<AMessage> msg = new AMessage;
-
-    jsize numEntries = 0;
-
-    if (keys != NULL) {
-        if (values == NULL) {
-            return -EINVAL;
-        }
-
-        numEntries = env->GetArrayLength(keys);
-
-        if (numEntries != env->GetArrayLength(values)) {
-            return -EINVAL;
-        }
-    } else if (values != NULL) {
-        return -EINVAL;
-    }
-
-    for (jsize i = 0; i < numEntries; ++i) {
-        jobject keyObj = env->GetObjectArrayElement(keys, i);
-
-        if (!env->IsInstanceOf(keyObj, stringClass.get())) {
-            return -EINVAL;
-        }
-
-        const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL);
-
-        if (tmp == NULL) {
-            return -ENOMEM;
-        }
-
-        AString key = tmp;
-
-        env->ReleaseStringUTFChars((jstring)keyObj, tmp);
-        tmp = NULL;
-
-        if (key.startsWith("android._")) {
-            // don't propagate private keys (starting with android._)
-            continue;
-        }
-
-        jobject valueObj = env->GetObjectArrayElement(values, i);
-
-        if (env->IsInstanceOf(valueObj, stringClass.get())) {
-            const char *value = env->GetStringUTFChars((jstring)valueObj, NULL);
-
-            if (value == NULL) {
-                return -ENOMEM;
-            }
-
-            msg->setString(key.c_str(), value);
-
-            env->ReleaseStringUTFChars((jstring)valueObj, value);
-            value = NULL;
-        } else if (env->IsInstanceOf(valueObj, integerClass.get())) {
-            jmethodID intValueID =
-                env->GetMethodID(integerClass.get(), "intValue", "()I");
-            CHECK(intValueID != NULL);
-
-            jint value = env->CallIntMethod(valueObj, intValueID);
-
-            msg->setInt32(key.c_str(), value);
-        } else if (env->IsInstanceOf(valueObj, longClass.get())) {
-            jmethodID longValueID =
-                env->GetMethodID(longClass.get(), "longValue", "()J");
-            CHECK(longValueID != NULL);
-
-            jlong value = env->CallLongMethod(valueObj, longValueID);
-
-            msg->setInt64(key.c_str(), value);
-        } else if (env->IsInstanceOf(valueObj, floatClass.get())) {
-            jmethodID floatValueID =
-                env->GetMethodID(floatClass.get(), "floatValue", "()F");
-            CHECK(floatValueID != NULL);
-
-            jfloat value = env->CallFloatMethod(valueObj, floatValueID);
-
-            msg->setFloat(key.c_str(), value);
-        } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) {
-            jmethodID positionID =
-                env->GetMethodID(byteBufClass.get(), "position", "()I");
-            CHECK(positionID != NULL);
-
-            jmethodID limitID =
-                env->GetMethodID(byteBufClass.get(), "limit", "()I");
-            CHECK(limitID != NULL);
-
-            jint position = env->CallIntMethod(valueObj, positionID);
-            jint limit = env->CallIntMethod(valueObj, limitID);
-
-            sp<ABuffer> buffer = new ABuffer(limit - position);
-
-            void *data = env->GetDirectBufferAddress(valueObj);
-
-            if (data != NULL) {
-                memcpy(buffer->data(),
-                       (const uint8_t *)data + position,
-                       buffer->size());
-            } else {
-                jmethodID arrayID =
-                    env->GetMethodID(byteBufClass.get(), "array", "()[B");
-                CHECK(arrayID != NULL);
-
-                jbyteArray byteArray =
-                    (jbyteArray)env->CallObjectMethod(valueObj, arrayID);
-                CHECK(byteArray != NULL);
-
-                env->GetByteArrayRegion(
-                        byteArray,
-                        position,
-                        buffer->size(),
-                        (jbyte *)buffer->data());
-
-                env->DeleteLocalRef(byteArray); byteArray = NULL;
-            }
-
-            msg->setBuffer(key.c_str(), buffer);
-        }
-    }
-
-    *out = msg;
-
-    return OK;
-}
-
 // -----------Utility functions used by ImageReader/Writer JNI-----------------
 
 enum {
diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h
index 19c1b88..12841c0 100644
--- a/media/jni/android_media_Utils.h
+++ b/media/jni/android_media_Utils.h
@@ -17,100 +17,10 @@
 #ifndef _ANDROID_MEDIA_UTILS_H_
 #define _ANDROID_MEDIA_UTILS_H_
 
-#include "src/piex_types.h"
-#include "src/piex.h"
-
-#include <android_runtime/AndroidRuntime.h>
 #include <gui/CpuConsumer.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <SkStream.h>
 
 namespace android {
 
-class AssetStream : public piex::StreamInterface {
-private:
-    SkStream *mStream;
-    size_t mPosition;
-
-public:
-    explicit AssetStream(SkStream* stream);
-    ~AssetStream();
-
-    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
-    // provided by the caller, guaranteed to be at least "length" bytes long.
-    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
-    // 'offset' bytes from the start of the stream.
-    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
-    // change the contents of 'data'.
-    piex::Error GetData(
-            const size_t offset, const size_t length, std::uint8_t* data) override;
-};
-
-class BufferedStream : public piex::StreamInterface {
-private:
-    SkStream *mStream;
-    // Growable memory stream
-    SkDynamicMemoryWStream mStreamBuffer;
-
-    // Minimum size to read on filling the buffer.
-    const size_t kMinSizeToRead = 8192;
-
-public:
-    explicit BufferedStream(SkStream* stream);
-    ~BufferedStream();
-
-    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
-    // provided by the caller, guaranteed to be at least "length" bytes long.
-    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
-    // 'offset' bytes from the start of the stream.
-    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
-    // change the contents of 'data'.
-    piex::Error GetData(
-            const size_t offset, const size_t length, std::uint8_t* data) override;
-};
-
-class FileStream : public piex::StreamInterface {
-private:
-    FILE *mFile;
-    size_t mPosition;
-
-public:
-    explicit FileStream(const int fd);
-    explicit FileStream(const String8 filename);
-    ~FileStream();
-
-    // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
-    // provided by the caller, guaranteed to be at least "length" bytes long.
-    // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
-    // 'offset' bytes from the start of the stream.
-    // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
-    // change the contents of 'data'.
-    piex::Error GetData(
-            const size_t offset, const size_t length, std::uint8_t* data) override;
-    bool exists() const;
-};
-
-// Reads EXIF metadata from a given raw image via piex.
-// And returns true if the operation is successful; otherwise, false.
-bool GetExifFromRawImage(
-        piex::StreamInterface* stream, const String8& filename, piex::PreviewImageData& image_data);
-
-// Returns true if the conversion is successful; otherwise, false.
-bool ConvertKeyValueArraysToKeyedVector(
-        JNIEnv *env, jobjectArray keys, jobjectArray values,
-        KeyedVector<String8, String8>* vector);
-
-struct AMessage;
-status_t ConvertMessageToMap(
-        JNIEnv *env, const sp<AMessage> &msg, jobject *map);
-
-status_t ConvertKeyValueArraysToMessage(
-        JNIEnv *env, jobjectArray keys, jobjectArray values,
-        sp<AMessage> *msg);
-
 // -----------Utility functions used by ImageReader/Writer JNI-----------------
 
 typedef CpuConsumer::LockedBuffer LockedImage;
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 06a7182..1f89d86 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -18,7 +18,7 @@
 #include "utils/Log.h"
 #include "utils/String8.h"
 
-#include "android_media_Utils.h"
+#include "android_media_Streams.h"
 #include "mtp.h"
 #include "IMtpDatabase.h"
 #include "MtpDataPacket.h"
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
index 2db575b..3fecd53 100644
--- a/native/android/choreographer.cpp
+++ b/native/android/choreographer.cpp
@@ -70,6 +70,8 @@
 
     void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
     void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+    void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+                               int32_t configId) override;
 
     void scheduleCallbacks();
 
@@ -164,6 +166,13 @@
             this, displayId, toString(connected));
 }
 
+void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId,
+                                          int32_t configId) {
+    ALOGV("choreographer %p ~ received config changed event (displayId=%"
+            ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%s), ignoring.",
+            this, displayId, toString(configId));
+}
+
 void Choreographer::handleMessage(const Message& message) {
     switch (message.what) {
     case MSG_SCHEDULE_CALLBACKS:
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index e591ea9..93d2b67 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -81,9 +81,9 @@
         <com.android.systemui.statusbar.car.CarFacetButton
             android:id="@+id/phone_nav"
             style="@style/NavigationBarButton"
-            systemui:componentNames="com.android.car.dialer/.TelecomActivity"
             systemui:icon="@drawable/car_ic_phone"
-            systemui:intent="intent:#Intent;component=com.android.car.dialer/.TelecomActivity;launchFlags=0x14000000;end"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end"
+            systemui:packages="com.android.car.dialer"
             systemui:selectedIcon="@drawable/car_ic_phone_selected"
             systemui:useMoreIcon="false"
         />
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index b700bf3..5f1f26d 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -28,6 +28,7 @@
     static_libs: [
         "netd_aidl_interface-java",
         "networkstack-aidl-interfaces-java",
+        "datastallprotosnano",
     ]
 }
 
@@ -43,4 +44,4 @@
     jarjar_rules: "jarjar-rules-shared.txt",
     manifest: "AndroidManifest.xml",
     required: ["NetworkStackPermissionStub"],
-}
\ No newline at end of file
+}
diff --git a/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java b/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java
new file mode 100644
index 0000000..225dc0f
--- /dev/null
+++ b/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java
@@ -0,0 +1,228 @@
+/*
+ * 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.net.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.util.NetworkStackUtils;
+import android.net.wifi.WifiInfo;
+
+import com.android.internal.util.HexDump;
+import com.android.server.connectivity.nano.CellularData;
+import com.android.server.connectivity.nano.DataStallEventProto;
+import com.android.server.connectivity.nano.DnsEvent;
+import com.android.server.connectivity.nano.WifiData;
+
+import com.google.protobuf.nano.MessageNano;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Class to record the stats of detection level information for data stall.
+ *
+ * @hide
+ */
+public final class DataStallDetectionStats {
+    private static final int UNKNOWN_SIGNAL_STRENGTH = -1;
+    @NonNull
+    final byte[] mCellularInfo;
+    @NonNull
+    final byte[] mWifiInfo;
+    @NonNull
+    final byte[] mDns;
+    final int mEvaluationType;
+    final int mNetworkType;
+
+    public DataStallDetectionStats(@Nullable byte[] cell, @Nullable byte[] wifi,
+                @NonNull int[] returnCode, @NonNull long[] dnsTime, int evalType, int netType) {
+        mCellularInfo = emptyCellDataIfNull(cell);
+        mWifiInfo = emptyWifiInfoIfNull(wifi);
+
+        DnsEvent dns = new DnsEvent();
+        dns.dnsReturnCode = returnCode;
+        dns.dnsTime = dnsTime;
+        mDns = MessageNano.toByteArray(dns);
+        mEvaluationType = evalType;
+        mNetworkType = netType;
+    }
+
+    private byte[] emptyCellDataIfNull(@Nullable byte[] cell) {
+        if (cell != null) return cell;
+
+        CellularData data  = new CellularData();
+        data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_UNKNOWN;
+        data.networkMccmnc = "";
+        data.simMccmnc = "";
+        data.signalStrength = UNKNOWN_SIGNAL_STRENGTH;
+        return MessageNano.toByteArray(data);
+    }
+
+    private byte[] emptyWifiInfoIfNull(@Nullable byte[] wifi) {
+        if (wifi != null) return wifi;
+
+        WifiData data = new WifiData();
+        data.wifiBand = DataStallEventProto.AP_BAND_UNKNOWN;
+        data.signalStrength = UNKNOWN_SIGNAL_STRENGTH;
+        return MessageNano.toByteArray(data);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("type: ").append(mNetworkType)
+          .append(", evaluation type: ")
+          .append(mEvaluationType)
+          .append(", wifi info: ")
+          .append(HexDump.toHexString(mWifiInfo))
+          .append(", cell info: ")
+          .append(HexDump.toHexString(mCellularInfo))
+          .append(", dns: ")
+          .append(HexDump.toHexString(mDns));
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(@Nullable final Object o) {
+        if (!(o instanceof DataStallDetectionStats)) return false;
+        final DataStallDetectionStats other = (DataStallDetectionStats) o;
+        return (mNetworkType == other.mNetworkType)
+            && (mEvaluationType == other.mEvaluationType)
+            && Arrays.equals(mWifiInfo, other.mWifiInfo)
+            && Arrays.equals(mCellularInfo, other.mCellularInfo)
+            && Arrays.equals(mDns, other.mDns);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mNetworkType, mEvaluationType, mWifiInfo, mCellularInfo, mDns);
+    }
+
+    /**
+     * Utility to create an instance of {@Link DataStallDetectionStats}
+     *
+     * @hide
+     */
+    public static class Builder {
+        @Nullable
+        private byte[] mCellularInfo;
+        @Nullable
+        private byte[] mWifiInfo;
+        @NonNull
+        private final List<Integer> mDnsReturnCode = new ArrayList<Integer>();
+        @NonNull
+        private final List<Long> mDnsTimeStamp = new ArrayList<Long>();
+        private int mEvaluationType;
+        private int mNetworkType;
+
+        /**
+         * Add a dns event into Builder.
+         *
+         * @param code the return code of the dns event.
+         * @param timeMs the elapsedRealtime in ms that the the dns event was received from netd.
+         * @return {@code this} {@link Builder} instance.
+         */
+        public Builder addDnsEvent(int code, long timeMs) {
+            mDnsReturnCode.add(code);
+            mDnsTimeStamp.add(timeMs);
+            return this;
+        }
+
+        /**
+         * Set the dns evaluation type into Builder.
+         *
+         * @param type the return code of the dns event.
+         * @return {@code this} {@link Builder} instance.
+         */
+        public Builder setEvaluationType(int type) {
+            mEvaluationType = type;
+            return this;
+        }
+
+        /**
+         * Set the network type into Builder.
+         *
+         * @param type the network type of the logged network.
+         * @return {@code this} {@link Builder} instance.
+         */
+        public Builder setNetworkType(int type) {
+            mNetworkType = type;
+            return this;
+        }
+
+        /**
+         * Set the wifi data into Builder.
+         *
+         * @param info a {@link WifiInfo} of the connected wifi network.
+         * @return {@code this} {@link Builder} instance.
+         */
+        public Builder setWiFiData(@Nullable final WifiInfo info) {
+            WifiData data = new WifiData();
+            data.wifiBand = getWifiBand(info);
+            data.signalStrength = (info != null) ? info.getRssi() : UNKNOWN_SIGNAL_STRENGTH;
+            mWifiInfo = MessageNano.toByteArray(data);
+            return this;
+        }
+
+        private static int getWifiBand(@Nullable final WifiInfo info) {
+            if (info == null) return DataStallEventProto.AP_BAND_UNKNOWN;
+
+            int freq = info.getFrequency();
+            // Refer to ScanResult.is5GHz() and ScanResult.is24GHz().
+            if (freq > 4900 && freq < 5900) {
+                return DataStallEventProto.AP_BAND_5GHZ;
+            } else if (freq > 2400 && freq < 2500) {
+                return DataStallEventProto.AP_BAND_2GHZ;
+            } else {
+                return DataStallEventProto.AP_BAND_UNKNOWN;
+            }
+        }
+
+        /**
+         * Set the cellular data into Builder.
+         *
+         * @param radioType the radio technology of the logged cellular network.
+         * @param roaming a boolean indicates if logged cellular network is roaming or not.
+         * @param networkMccmnc the mccmnc of the camped network.
+         * @param simMccmnc the mccmnc of the sim.
+         * @return {@code this} {@link Builder} instance.
+         */
+        public Builder setCellData(int radioType, boolean roaming,
+                @NonNull String networkMccmnc, @NonNull String simMccmnc, int ss) {
+            CellularData data  = new CellularData();
+            data.ratType = radioType;
+            data.isRoaming = roaming;
+            data.networkMccmnc = networkMccmnc;
+            data.simMccmnc = simMccmnc;
+            data.signalStrength = ss;
+            mCellularInfo = MessageNano.toByteArray(data);
+            return this;
+        }
+
+        /**
+         * Create a new {@Link DataStallDetectionStats}.
+         */
+        public DataStallDetectionStats build() {
+            return new DataStallDetectionStats(mCellularInfo, mWifiInfo,
+                    NetworkStackUtils.convertToIntArray(mDnsReturnCode),
+                    NetworkStackUtils.convertToLongArray(mDnsTimeStamp),
+                    mEvaluationType, mNetworkType);
+        }
+    }
+}
diff --git a/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java b/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java
new file mode 100644
index 0000000..17a36ad
--- /dev/null
+++ b/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java
@@ -0,0 +1,66 @@
+/*
+ * 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.net.metrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.captiveportal.CaptivePortalProbeResult;
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+import com.android.server.connectivity.nano.DataStallEventProto;
+
+/**
+ * Collection of utilities for data stall metrics.
+ *
+ * To see if the logs are properly sent to statsd, execute following command.
+ *
+ * $ adb shell cmd stats print-logs
+ * $ adb logcat | grep statsd  OR $ adb logcat -b stats
+ *
+ * @hide
+ */
+public class DataStallStatsUtils {
+    private static final String TAG = DataStallStatsUtils.class.getSimpleName();
+    private static final boolean DBG = false;
+
+    private static int probeResultToEnum(@Nullable final CaptivePortalProbeResult result) {
+        if (result == null) return DataStallEventProto.INVALID;
+
+        // TODO: Add partial connectivity support.
+        if (result.isSuccessful()) {
+            return DataStallEventProto.VALID;
+        } else if (result.isPortal()) {
+            return DataStallEventProto.PORTAL;
+        } else {
+            return DataStallEventProto.INVALID;
+        }
+    }
+
+    /**
+     * Write the metric to {@link StatsLog}.
+     */
+    public static void write(@NonNull final DataStallDetectionStats stats,
+            @NonNull final CaptivePortalProbeResult result) {
+        int validationResult = probeResultToEnum(result);
+        if (DBG) {
+            Log.d(TAG, "write: " + stats + " with result: " + validationResult
+                    + ", dns: " + HexDump.toHexString(stats.mDns));
+        }
+        // TODO(b/124613085): Send to Statsd once the public StatsLog API is ready.
+    }
+}
diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
index 98123a5..481dbda 100644
--- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
+++ b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
@@ -16,8 +16,11 @@
 
 package android.net.util;
 
+import android.annotation.NonNull;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.util.List;
 
 /**
  * Collection of utilities for the network stack.
@@ -40,4 +43,26 @@
         } catch (IOException ignored) {
         }
     }
+
+    /**
+     * Returns an int array from the given Integer list.
+     */
+    public static int[] convertToIntArray(@NonNull List<Integer> list) {
+        int[] array = new int[list.size()];
+        for (int i = 0; i < list.size(); i++) {
+            array[i] = list.get(i);
+        }
+        return array;
+    }
+
+    /**
+     * Returns a long array from the given long list.
+     */
+    public static long[] convertToLongArray(@NonNull List<Long> list) {
+        long[] array = new long[list.size()];
+        for (int i = 0; i < list.size(); i++) {
+            array[i] = list.get(i);
+        }
+        return array;
+    }
 }
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 4b846b0..e82a5d7 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -33,6 +33,7 @@
 import static android.net.metrics.ValidationProbeEvent.PROBE_PRIVDNS;
 import static android.net.util.NetworkStackUtils.isEmpty;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -50,6 +51,8 @@
 import android.net.Uri;
 import android.net.captiveportal.CaptivePortalProbeResult;
 import android.net.captiveportal.CaptivePortalProbeSpec;
+import android.net.metrics.DataStallDetectionStats;
+import android.net.metrics.DataStallStatsUtils;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
 import android.net.metrics.ValidationProbeEvent;
@@ -66,8 +69,10 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.AccessNetworkConstants;
+import android.telephony.CellSignalStrength;
 import android.telephony.NetworkRegistrationState;
 import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -126,6 +131,9 @@
     private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
     private static final int DEFAULT_DATA_STALL_EVALUATION_TYPES =
             (1 << DATA_STALL_EVALUATION_TYPE_DNS);
+    // Reevaluate it as intending to increase the number. Larger log size may cause statsd
+    // log buffer bust and have stats log lost.
+    private static final int DEFAULT_DNS_LOG_SIZE = 20;
 
     enum EvaluationResult {
         VALIDATED(true),
@@ -244,6 +252,7 @@
     private final ConnectivityManager mCm;
     private final IpConnectivityLog mMetricsLog;
     private final Dependencies mDependencies;
+    private final DataStallStatsUtils mDetectionStatsUtils;
 
     // Configuration values for captive portal detection probes.
     private final String mCaptivePortalUserAgent;
@@ -302,17 +311,19 @@
     private final int mDataStallEvaluationType;
     private final DnsStallDetector mDnsStallDetector;
     private long mLastProbeTime;
+    // Set to true if data stall is suspected and reset to false after metrics are sent to statsd.
+    private boolean mCollectDataStallMetrics = false;
 
     public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
             SharedLog validationLog) {
         this(context, cb, network, new IpConnectivityLog(), validationLog,
-                Dependencies.DEFAULT);
+                Dependencies.DEFAULT, new DataStallStatsUtils());
     }
 
     @VisibleForTesting
     protected NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
             IpConnectivityLog logger, SharedLog validationLogs,
-            Dependencies deps) {
+            Dependencies deps, DataStallStatsUtils detectionStatsUtils) {
         // Add suffix indicating which NetworkMonitor we're talking about.
         super(TAG + "/" + network.toString());
 
@@ -325,6 +336,7 @@
         mValidationLogs = validationLogs;
         mCallback = cb;
         mDependencies = deps;
+        mDetectionStatsUtils = detectionStatsUtils;
         mNonPrivateDnsBypassNetwork = network;
         mNetwork = deps.getPrivateDnsBypassNetwork(network);
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -656,6 +668,7 @@
                 case EVENT_DNS_NOTIFICATION:
                     mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
                     if (isDataStall()) {
+                        mCollectDataStallMetrics = true;
                         validationLog("Suspecting data stall, reevaluate");
                         transitionTo(mEvaluatingState);
                     }
@@ -667,6 +680,66 @@
         }
     }
 
+    private void writeDataStallStats(@NonNull final CaptivePortalProbeResult result) {
+        /*
+         * Collect data stall detection level information for each transport type. Collect type
+         * specific information for cellular and wifi only currently. Generate
+         * DataStallDetectionStats for each transport type. E.g., if a network supports both
+         * TRANSPORT_WIFI and TRANSPORT_VPN, two DataStallDetectionStats will be generated.
+         */
+        final int[] transports = mNetworkCapabilities.getTransportTypes();
+
+        for (int i = 0; i < transports.length; i++) {
+            DataStallStatsUtils.write(buildDataStallDetectionStats(transports[i]), result);
+        }
+        mCollectDataStallMetrics = false;
+    }
+
+    @VisibleForTesting
+    protected DataStallDetectionStats buildDataStallDetectionStats(int transport) {
+        final DataStallDetectionStats.Builder stats = new DataStallDetectionStats.Builder();
+        if (VDBG_STALL) log("collectDataStallMetrics: type=" + transport);
+        stats.setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS);
+        stats.setNetworkType(transport);
+        switch (transport) {
+            case NetworkCapabilities.TRANSPORT_WIFI:
+                // TODO: Update it if status query in dual wifi is supported.
+                final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+                stats.setWiFiData(wifiInfo);
+                break;
+            case NetworkCapabilities.TRANSPORT_CELLULAR:
+                final boolean isRoaming = !mNetworkCapabilities.hasCapability(
+                        NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
+                final SignalStrength ss = mTelephonyManager.getSignalStrength();
+                // TODO(b/120452078): Support multi-sim.
+                stats.setCellData(
+                        mTelephonyManager.getDataNetworkType(),
+                        isRoaming,
+                        mTelephonyManager.getNetworkOperator(),
+                        mTelephonyManager.getSimOperator(),
+                        (ss != null)
+                        ? ss.getLevel() : CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
+                break;
+            default:
+                // No transport type specific information for the other types.
+                break;
+        }
+        addDnsEvents(stats);
+
+        return stats.build();
+    }
+
+    @VisibleForTesting
+    protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) {
+        final int size = mDnsStallDetector.mResultIndices.size();
+        for (int i = 1; i <= DEFAULT_DNS_LOG_SIZE && i <= size; i++) {
+            final int index = mDnsStallDetector.mResultIndices.indexOf(size - i);
+            stats.addDnsEvent(mDnsStallDetector.mDnsEvents[index].mReturnCode,
+                    mDnsStallDetector.mDnsEvents[index].mTimeStamp);
+        }
+    }
+
+
     // Being in the MaybeNotifyState State indicates the user may have been notified that sign-in
     // is required.  This State takes care to clear the notification upon exit from the State.
     private class MaybeNotifyState extends State {
@@ -972,6 +1045,11 @@
                     final CaptivePortalProbeResult probeResult =
                             (CaptivePortalProbeResult) message.obj;
                     mLastProbeTime = SystemClock.elapsedRealtime();
+
+                    if (mCollectDataStallMetrics) {
+                        writeDataStallStats(probeResult);
+                    }
+
                     if (probeResult.isSuccessful()) {
                         // Transit EvaluatingPrivateDnsState to get to Validated
                         // state (even if no Private DNS validation required).
@@ -1617,7 +1695,6 @@
      */
     @VisibleForTesting
     protected class DnsStallDetector {
-        private static final int DEFAULT_DNS_LOG_SIZE = 50;
         private int mConsecutiveTimeoutCount = 0;
         private int mSize;
         final DnsResult[] mDnsEvents;
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index 9a16bb7..ddb7030 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -39,6 +39,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.INetworkMonitorCallbacks;
@@ -48,8 +49,11 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.captiveportal.CaptivePortalProbeResult;
+import android.net.metrics.DataStallDetectionStats;
+import android.net.metrics.DataStallStatsUtils;
 import android.net.metrics.IpConnectivityLog;
 import android.net.util.SharedLog;
+import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
 import android.os.ConditionVariable;
@@ -58,6 +62,7 @@
 import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.telephony.CellSignalStrength;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 
@@ -98,6 +103,8 @@
     private @Mock NetworkMonitor.Dependencies mDependencies;
     private @Mock INetworkMonitorCallbacks mCallbacks;
     private @Spy Network mNetwork = new Network(TEST_NETID);
+    private @Mock DataStallStatsUtils mDataStallStatsUtils;
+    private @Mock WifiInfo mWifiInfo;
 
     private static final int TEST_NETID = 4242;
 
@@ -105,10 +112,12 @@
     private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204";
     private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204";
     private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204";
+    private static final String TEST_MCCMNC = "123456";
 
     private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
     private static final int RETURN_CODE_DNS_SUCCESS = 0;
     private static final int RETURN_CODE_DNS_TIMEOUT = 255;
+    private static final int DEFAULT_DNS_TIMEOUT_THRESHOLD = 5;
 
     private static final int HANDLER_TIMEOUT_MS = 1000;
 
@@ -186,9 +195,9 @@
         private long mProbeTime = 0;
 
         WrappedNetworkMonitor(Context context, Network network, IpConnectivityLog logger,
-                Dependencies deps) {
+                Dependencies deps, DataStallStatsUtils statsUtils) {
                 super(context, mCallbacks, network, logger,
-                        new SharedLog("test_nm"), deps);
+                        new SharedLog("test_nm"), deps, statsUtils);
         }
 
         @Override
@@ -199,11 +208,16 @@
         protected void setLastProbeTime(long time) {
             mProbeTime = time;
         }
+
+        @Override
+        protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) {
+            generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+        }
     }
 
     private WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() {
         final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(
-                mContext, mNetwork, mLogger, mDependencies);
+                mContext, mNetwork, mLogger, mDependencies, mDataStallStatsUtils);
         when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES);
         nm.start();
         waitForIdle(nm.getHandler());
@@ -212,7 +226,7 @@
 
     private WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() {
         final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(
-                mContext, mNetwork, mLogger, mDependencies);
+                mContext, mNetwork, mLogger, mDependencies, mDataStallStatsUtils);
         when(mCm.getNetworkCapabilities(any())).thenReturn(NOT_METERED_CAPABILITIES);
         nm.start();
         waitForIdle(nm.getHandler());
@@ -222,7 +236,7 @@
     private NetworkMonitor makeMonitor() {
         final NetworkMonitor nm = new NetworkMonitor(
                 mContext, mCallbacks, mNetwork, mLogger, mValidationLogger,
-                mDependencies);
+                mDependencies, mDataStallStatsUtils);
         nm.start();
         waitForIdle(nm.getHandler());
         return nm;
@@ -384,7 +398,7 @@
     public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() {
         WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
         wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
-        makeDnsTimeoutEvent(wrappedMonitor, 5);
+        makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
         assertTrue(wrappedMonitor.isDataStall());
     }
 
@@ -395,7 +409,7 @@
         assertFalse(wrappedMonitor.isDataStall());
 
         wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
-        makeDnsTimeoutEvent(wrappedMonitor, 5);
+        makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
         assertTrue(wrappedMonitor.isDataStall());
     }
 
@@ -429,7 +443,7 @@
         // Test dns events happened in valid dns time threshold.
         WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
         wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
-        makeDnsTimeoutEvent(wrappedMonitor, 5);
+        makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
         assertFalse(wrappedMonitor.isDataStall());
         wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
         assertTrue(wrappedMonitor.isDataStall());
@@ -438,7 +452,7 @@
         setValidDataStallDnsTimeThreshold(0);
         wrappedMonitor = makeMeteredWrappedNetworkMonitor();
         wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
-        makeDnsTimeoutEvent(wrappedMonitor, 5);
+        makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
         assertFalse(wrappedMonitor.isDataStall());
         wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
         assertFalse(wrappedMonitor.isDataStall());
@@ -505,6 +519,59 @@
                 .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
     }
 
+    @Test
+    public void testDataStall_StallSuspectedAndSendMetrics() throws IOException {
+        WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+        makeDnsTimeoutEvent(wrappedMonitor, 5);
+        assertTrue(wrappedMonitor.isDataStall());
+        verify(mDataStallStatsUtils, times(1)).write(any(), any());
+    }
+
+    @Test
+    public void testDataStall_NoStallSuspectedAndSendMetrics() throws IOException {
+        WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+        makeDnsTimeoutEvent(wrappedMonitor, 3);
+        assertFalse(wrappedMonitor.isDataStall());
+        verify(mDataStallStatsUtils, never()).write(any(), any());
+    }
+
+    @Test
+    public void testCollectDataStallMetrics() {
+        WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+
+        when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
+        when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC);
+        when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC);
+
+        DataStallDetectionStats.Builder stats =
+                new DataStallDetectionStats.Builder()
+                .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS)
+                .setNetworkType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                .setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */,
+                        true /* roaming */,
+                        TEST_MCCMNC /* networkMccmnc */,
+                        TEST_MCCMNC /* simMccmnc */,
+                        CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN /* signalStrength */);
+        generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+
+        assertEquals(wrappedMonitor.buildDataStallDetectionStats(
+                 NetworkCapabilities.TRANSPORT_CELLULAR), stats.build());
+
+        when(mWifi.getConnectionInfo()).thenReturn(mWifiInfo);
+
+        stats = new DataStallDetectionStats.Builder()
+                .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS)
+                .setNetworkType(NetworkCapabilities.TRANSPORT_WIFI)
+                .setWiFiData(mWifiInfo);
+        generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
+
+        assertEquals(
+                wrappedMonitor.buildDataStallDetectionStats(NetworkCapabilities.TRANSPORT_WIFI),
+                stats.build());
+    }
+
     private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
         for (int i = 0; i < count; i++) {
             wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
@@ -594,5 +661,11 @@
     private void setStatus(HttpURLConnection connection, int status) throws IOException {
         doReturn(status).when(connection).getResponseCode();
     }
+
+    private void generateTimeoutDnsEvent(DataStallDetectionStats.Builder stats, int num) {
+        for (int i = 0; i < num; i++) {
+            stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, 123456789 /* timeMs */);
+        }
+    }
 }
 
diff --git a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
index 28b05396..82e33cc 100644
--- a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
+++ b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
@@ -46,8 +46,6 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 
-
-
 /**
  * Online Sign Up Login Web View launched during Provision Process of Hotspot 2.0 rel2.
  */
@@ -64,6 +62,7 @@
     private WebView mWebView;
     private SwipeRefreshLayout mSwipeRefreshLayout;
     private ProgressBar mProgressBar;
+    private boolean mForceDisconnect = true;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -141,6 +140,7 @@
                 if (DBG) {
                     Log.d(TAG, "Lost for the current Network, close the browser");
                 }
+                mForceDisconnect = false; // It is already disconnected.
                 if (mNetwork.equals(network)) {
                     finishAndRemoveTask();
                 }
@@ -193,12 +193,6 @@
                 mWebView.goBack();
                 return true;
             }
-
-            // In case of back key, it needs to disconnect current connection with OSU AP to
-            // abort current Provisioning flow.
-            if (mWifiManager != null) {
-                mWifiManager.disconnect();
-            }
         }
         return super.onKeyDown(keyCode, event);
     }
@@ -207,6 +201,11 @@
     protected void onDestroy() {
         if (mNetworkCallback != null) {
             mCm.unregisterNetworkCallback(mNetworkCallback);
+            mNetworkCallback = null;
+        }
+        if (mWifiManager != null && mForceDisconnect) {
+            mWifiManager.disconnect();
+            mWifiManager = null;
         }
         super.onDestroy();
     }
@@ -232,6 +231,7 @@
 
     private class OsuWebViewClient extends WebViewClient {
         boolean mPageError = false;
+        boolean mRedirectResponseReceived = false;
 
         @Override
         public void onPageStarted(WebView view, String urlString, Bitmap favicon) {
@@ -247,6 +247,10 @@
 
             // Do not show the page error on UI.
             if (mPageError) {
+                if (mRedirectResponseReceived) {
+                    // Do not disconnect current connection while provisioning is in progress.
+                    mForceDisconnect = false;
+                }
                 finishAndRemoveTask();
             }
         }
@@ -255,6 +259,7 @@
         public void onReceivedError(WebView view, WebResourceRequest request,
                 WebResourceError error) {
             if (request.getUrl().toString().startsWith("http://127.0.0.1")) {
+                mRedirectResponseReceived = true;
                 view.stopLoading();
             }
 
@@ -266,5 +271,4 @@
             }
          }
     }
-
 }
diff --git a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
index 330049f..6e95a0e 100644
--- a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
+++ b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
@@ -90,6 +90,7 @@
     private int mHeaderTitleRes;
     private int mHeaderDetailsRes;
     private int mHeaderEmptyRes;
+    private CharSequence mHeaderDetails;
     private View.OnClickListener mDetailsOnClickListener;
 
     /**
@@ -148,6 +149,14 @@
     }
 
     /**
+     * Set the text for app entities header details.
+     */
+    public AppEntitiesHeaderController setHeaderDetails(CharSequence detailsText) {
+        mHeaderDetails = detailsText;
+        return this;
+    }
+
+    /**
      * Register a callback to be invoked when header details view is clicked.
      */
     public AppEntitiesHeaderController setHeaderDetailsClickListener(
@@ -232,11 +241,13 @@
     }
 
     private void bindHeaderDetailsView() {
-        CharSequence detailsText = "";
-        try {
-            detailsText = mContext.getText(mHeaderDetailsRes);
-        } catch (Resources.NotFoundException e) {
-            Log.e(TAG, "Resource of header details can't not be found!", e);
+        CharSequence detailsText = mHeaderDetails;
+        if (TextUtils.isEmpty(detailsText)) {
+            try {
+                detailsText = mContext.getText(mHeaderDetailsRes);
+            } catch (Resources.NotFoundException e) {
+                Log.e(TAG, "Resource of header details can't not be found!", e);
+            }
         }
         mHeaderDetailsView.setText(detailsText);
         mHeaderDetailsView.setVisibility(
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index f865563..68f1e6f 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -136,8 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"আঁতৰোৱা এপ্‌সমূহ"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"আঁতৰোৱা এপ্‌ আৰু ব্যৱহাৰকাৰীসমূহ"</string>
-    <!-- no translation found for data_usage_ota (5377889154805560860) -->
-    <skip />
+    <string name="data_usage_ota" msgid="5377889154805560860">"ছিষ্টেম আপডে’ট"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"ইউএছবি টেডাৰিং"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"প\'ৰ্টেবল হটস্পট"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ব্লুটুথ টেডাৰিং"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index ef8dd3d..4f29131 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -136,8 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"সরানো অ্যাপ্লিকেশানগুলি"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"সরানো অ্যাপ্লিকেশানগুলি এবং ব্যবহারকারীগণ"</string>
-    <!-- no translation found for data_usage_ota (5377889154805560860) -->
-    <skip />
+    <string name="data_usage_ota" msgid="5377889154805560860">"সিস্টেম আপডেট"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB টিথারিং"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"পোর্টেবল হটস্পট"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ব্লুটুথ টিথারিং"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 97b9036..36e04bb 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -136,8 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"દૂર કરેલી ઍપ્લિકેશનો"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"દૂર કરેલી ઍપ્લિકેશનો અને વપરાશકર્તાઓ"</string>
-    <!-- no translation found for data_usage_ota (5377889154805560860) -->
-    <skip />
+    <string name="data_usage_ota" msgid="5377889154805560860">"સિસ્ટમ અપડેટ"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ટિથરિંગ"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"પોર્ટેબલ હૉટસ્પૉટ"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"બ્લૂટૂથ ટિથરિંગ"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 6e37c9a..89d6361 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -136,8 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"אפליקציות שהוסרו"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"אפליקציות ומשתמשים שהוסרו"</string>
-    <!-- no translation found for data_usage_ota (5377889154805560860) -->
-    <skip />
+    <string name="data_usage_ota" msgid="5377889154805560860">"עדכוני מערכת"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"‏שיתוף אינטרנט דרך USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"נקודה לשיתוף אינטרנט"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"‏שיתוף אינטרנט דרך Bluetooth"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 41b0fbd..1e9a0d5 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -136,8 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ತೆಗೆದುಹಾಕಲಾದ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ಬಳಕೆದಾರರನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
-    <!-- no translation found for data_usage_ota (5377889154805560860) -->
-    <skip />
+    <string name="data_usage_ota" msgid="5377889154805560860">"ಸಿಸ್ಟಂ ಅಪ್‌ಡೇಟ್‌ಗಳು"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ಟೆಥರಿಂಗ್"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ಪೋರ್ಟಬಲ್ ಹಾಟ್‌ಸ್ಪಾಟ್"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ಬ್ಲೂಟೂತ್‌‌ ಟೆಥರಿಂಗ್‌"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 0292cab..ae97fb2 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -136,8 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"നീക്കംചെയ്‌ത അപ്ലിക്കേഷനുകൾ"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"നീക്കംചെയ്‌ത അപ്ലിക്കേഷനുകളും ഉപയോക്താക്കളും"</string>
-    <!-- no translation found for data_usage_ota (5377889154805560860) -->
-    <skip />
+    <string name="data_usage_ota" msgid="5377889154805560860">"സിസ്‌റ്റം അപ്‌ഡേറ്റുകൾ"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ടെതറിംഗ്"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"പോർട്ടബിൾ ഹോട്ട്സ്‌പോട്ട്"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ബ്ലൂടൂത്ത് ടെതറിംഗ്"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 0c2f0ee..88de8f4 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -136,8 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"काढलेले अॅप्स"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"काढलेले अॅप्स आणि वापरकर्ते"</string>
-    <!-- no translation found for data_usage_ota (5377889154805560860) -->
-    <skip />
+    <string name="data_usage_ota" msgid="5377889154805560860">"सिस्टम अपडेट"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB टेदरिंग"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"पोर्टेबल हॉटस्पॉट"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ब्लूटूथ टेदरिंग"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 7daf956..ac15f1b 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -136,8 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"एन्ड्रोइड OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"हटाइएका अनुप्रयोगहरू"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"अनुप्रयोगहरू र प्रयोगकर्ताहरू हटाइयो।"</string>
-    <!-- no translation found for data_usage_ota (5377889154805560860) -->
-    <skip />
+    <string name="data_usage_ota" msgid="5377889154805560860">"प्रणालीसम्बन्धी अद्यावधिकहरू"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB टेथर गर्दै"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"पोर्टेबल हटस्पट"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ब्लुटुथ टेथर गर्दै"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 54ddf84..847586e 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -136,8 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"କଢ଼ାଯାଇଥିବା ଆପ୍‌ଗୁଡ଼ିକ"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ଆପ୍‌ ଏବଂ ଉପଯୋଗକର୍ତ୍ତା ବାହାର କରାଗଲା"</string>
-    <!-- no translation found for data_usage_ota (5377889154805560860) -->
-    <skip />
+    <string name="data_usage_ota" msgid="5377889154805560860">"ସିଷ୍ଟମ୍ ଅପ୍‌ଡେଟ୍"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ଟିଥରିଙ୍ଗ"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ପୋର୍ଟବଲ୍‌ ହଟସ୍ପଟ୍‌"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ବ୍ଲୁଟୂଥ ଟିଥରିଙ୍ଗ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index dcf3e3c..0675b6b 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -136,8 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ਹਟਾਏ ਗਏ ਐਪਸ"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ਹਟਾਏ ਗਏ ਐਪਸ ਅਤੇ ਉਪਭੋਗਤਾ"</string>
-    <!-- no translation found for data_usage_ota (5377889154805560860) -->
-    <skip />
+    <string name="data_usage_ota" msgid="5377889154805560860">"ਸਿਸਟਮ ਅੱਪਡੇਟ"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ਟੈਦਰਿੰਗ"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ਪੋਰਟੇਬਲ ਹੌਟਸਪੌਟ"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ਬਲੂਟੁੱਥ ਟੈਦਰਿੰਗ"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 71716ce..3cf43c8 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -136,8 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"அகற்றப்பட்ட பயன்பாடுகள்"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"அகற்றப்பட்ட பயன்பாடுகள் மற்றும் பயனர்கள்"</string>
-    <!-- no translation found for data_usage_ota (5377889154805560860) -->
-    <skip />
+    <string name="data_usage_ota" msgid="5377889154805560860">"சிஸ்டம் புதுப்பிப்புகள்"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB டெதெரிங்"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"போர்ட்டபிள் ஹாட்ஸ்பாட்"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"புளூடூத் டெதெரிங்"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 65ed110..c223629 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -136,8 +136,7 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ہٹائی گئی ایپس"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ہٹائی گئی ایپس اور صارفین"</string>
-    <!-- no translation found for data_usage_ota (5377889154805560860) -->
-    <skip />
+    <string name="data_usage_ota" msgid="5377889154805560860">"سسٹم اپ ڈیٹس"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"‏USB ٹیدرنگ"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"پورٹیبل ہاٹ اسپاٹ"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"بلوٹوتھ ٹیدرنگ"</string>
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
index 63a958e..9a07ca8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
@@ -85,6 +85,23 @@
     }
 
     @Test
+    public void setHeaderDetails_onlyDetailsTextSet_shouldSetToDetailsView() {
+        mController.setHeaderDetails(TITLE).apply();
+        final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details);
+
+        assertThat(view.getText()).isEqualTo(TITLE);
+    }
+
+    @Test
+    public void setHeaderDetails_detailsTextAndResBothSet_shouldSetTextToDetailsView() {
+        mController.setHeaderDetailsRes(R.string.expand_button_title);
+        mController.setHeaderDetails(TITLE).apply();
+        final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details);
+
+        assertThat(view.getText()).isEqualTo(TITLE);
+    }
+
+    @Test
     public void setHeaderDetailsClickListener_setClickListener_detailsViewAttachClickListener() {
         mController.setHeaderDetailsClickListener(v -> {
         }).apply();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index ef90dc9..0ee16a9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -23,6 +23,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
@@ -2440,6 +2441,7 @@
 
     private void loadGlobalSettings(SQLiteDatabase db) {
         SQLiteStatement stmt = null;
+        final Resources res = mContext.getResources();
         try {
             stmt = db.compileStatement("INSERT OR IGNORE INTO global(name,value)"
                     + " VALUES(?,?);");
@@ -2468,7 +2470,7 @@
 
             loadSetting(stmt, Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
                     ("1".equals(SystemProperties.get("ro.kernel.qemu")) ||
-                        mContext.getResources().getBoolean(R.bool.def_stay_on_while_plugged_in))
+                        res.getBoolean(R.bool.def_stay_on_while_plugged_in))
                      ? 1 : 0);
 
             loadIntegerSetting(stmt, Settings.Global.WIFI_SLEEP_POLICY,
@@ -2505,14 +2507,14 @@
             loadBooleanSetting(stmt, Settings.Global.DEVICE_PROVISIONED,
                     R.bool.def_device_provisioned);
 
-            final int maxBytes = mContext.getResources().getInteger(
+            final int maxBytes = res.getInteger(
                     R.integer.def_download_manager_max_bytes_over_mobile);
             if (maxBytes > 0) {
                 loadSetting(stmt, Settings.Global.DOWNLOAD_MAX_BYTES_OVER_MOBILE,
                         Integer.toString(maxBytes));
             }
 
-            final int recommendedMaxBytes = mContext.getResources().getInteger(
+            final int recommendedMaxBytes = res.getInteger(
                     R.integer.def_download_manager_recommended_max_bytes_over_mobile);
             if (recommendedMaxBytes > 0) {
                 loadSetting(stmt, Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE,
@@ -2609,6 +2611,20 @@
 
             loadSetting(stmt, Settings.Global.DEVICE_NAME, getDefaultDeviceName());
 
+            // Set default lid/cover behaviour according to legacy device config
+            final int defaultLidBehavior;
+            if (res.getBoolean(com.android.internal.R.bool.config_lidControlsSleep)) {
+                // WindowManagerFuncs.LID_BEHAVIOR_SLEEP
+                defaultLidBehavior = 1;
+            } else if (res.getBoolean(com.android.internal.R.bool.config_lidControlsScreenLock)) {
+                // WindowManagerFuncs.LID_BEHAVIOR_LOCK
+                defaultLidBehavior = 2;
+            } else {
+                // WindowManagerFuncs.LID_BEHAVIOR_NONE
+                defaultLidBehavior = 0;
+            }
+            loadSetting(stmt, Settings.Global.LID_BEHAVIOR, defaultLidBehavior);
+
             /*
              * IMPORTANT: Do not add any more upgrade steps here as the global,
              * secure, and system settings are no longer stored in a database
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 81b304d..0f8fd92 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1353,11 +1353,11 @@
                 Settings.Global.SHOW_TEMPERATURE_WARNING,
                 GlobalSettingsProto.TemperatureWarning.SHOW_TEMPERATURE_WARNING);
         dumpSetting(s, p,
+                Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
+                GlobalSettingsProto.TemperatureWarning.SHOW_USB_TEMPERATURE_ALARM);
+        dumpSetting(s, p,
                 Settings.Global.WARNING_TEMPERATURE,
                 GlobalSettingsProto.TemperatureWarning.WARNING_TEMPERATURE_LEVEL);
-        dumpSetting(s, p,
-                Settings.Global.USB_ALARM_TEMPERATURE,
-                GlobalSettingsProto.TemperatureWarning.USB_ALARM_TEMPERATURE_LEVEL);
         p.end(tempWarningToken);
 
         final long tetherToken = p.start(GlobalSettingsProto.TETHER);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index a7ad223..ceafbfa 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1379,8 +1379,8 @@
             }
         }
         if (enableOverride) {
-            if (Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
-                final Setting overridden = getLocationProvidersAllowedSetting(owningUserId);
+            if (Secure.LOCATION_MODE.equals(name)) {
+                final Setting overridden = getLocationModeSetting(owningUserId);
                 if (overridden != null) {
                     return overridden;
                 }
@@ -1475,7 +1475,7 @@
         return null;
     }
 
-    private Setting getLocationProvidersAllowedSetting(int owningUserId) {
+    private Setting getLocationModeSetting(int owningUserId) {
         synchronized (mLock) {
             final Setting setting = getGlobalSetting(
                     Global.LOCATION_GLOBAL_KILL_SWITCH);
@@ -1486,7 +1486,7 @@
             final SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
                     SETTINGS_TYPE_SECURE, owningUserId);
             return settingsState.new Setting(
-                    Secure.LOCATION_PROVIDERS_ALLOWED,
+                    Secure.LOCATION_MODE,
                     "", // value
                     "", // tag
                     "", // default value
@@ -1497,7 +1497,7 @@
                 @Override
                 public boolean update(String value, boolean setDefault, String packageName,
                         String tag, boolean forceNonSystemPackage) {
-                    Slog.wtf(LOG_TAG, "update shoudln't be called on this instance.");
+                    Slog.wtf(LOG_TAG, "update shouldn't be called on this instance.");
                     return false;
                 }
             };
@@ -3115,7 +3115,7 @@
                 final int key = makeKey(SETTINGS_TYPE_SECURE, userId);
                 mGenerationRegistry.incrementGeneration(key);
 
-                final Uri uri = getNotificationUriFor(key, Secure.LOCATION_PROVIDERS_ALLOWED);
+                final Uri uri = getNotificationUriFor(key, Secure.LOCATION_MODE);
                 mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED,
                         userId, 0, uri).sendToTarget();
             }
diff --git a/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml b/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml
deleted file mode 100644
index a76b7b1..0000000
--- a/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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
-  -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <shape android:shape="rectangle">
-            <solid android:color="?android:attr/colorBackgroundFloating"/>
-            <corners
-                    android:topLeftRadius="@dimen/corner_size"
-                    android:topRightRadius="@dimen/corner_size"/>
-        </shape>
-    </item>
-</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_tune_black_16dp.xml b/packages/SystemUI/res/drawable/ic_tune_black_16dp.xml
new file mode 100644
index 0000000..339cb70
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_tune_black_16dp.xml
@@ -0,0 +1,25 @@
+<!--
+    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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="16dp"
+        android:height="16dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,17v2h6v-2L3,17zM3,5v2h10L13,5L3,5zM13,21v-2h8v-2h-8v-2h-2v6h2zM7,9v2L3,11v2h4v2h2L9,9L7,9zM21,13v-2L11,11v2h10zM15,9h2L17,7h4L21,5h-4L17,3h-2v6z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_customize_tile_decoration.xml b/packages/SystemUI/res/drawable/qs_customize_tile_decoration.xml
new file mode 100644
index 0000000..f086cec
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_customize_tile_decoration.xml
@@ -0,0 +1,17 @@
+<!--
+     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.
+-->
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+       android:color="@color/qs_customize_decoration"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
index abe1429..215dee4 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
@@ -15,7 +15,7 @@
 -->
 <inset xmlns:android="http://schemas.android.com/apk/res/android">
     <shape>
-        <solid android:color="?android:attr/colorPrimary"/>
+        <solid android:color="@color/qs_customize_background"/>
         <corners android:radius="?android:attr/dialogCornerRadius" />
     </shape>
 </inset>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
index 557cae1..648d45b 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
@@ -15,7 +15,7 @@
 -->
 <inset xmlns:android="http://schemas.android.com/apk/res/android">
     <shape>
-        <solid android:color="?android:attr/colorSecondary"/>
+        <solid android:color="@color/qs_customize_background"/>
         <corners
             android:topLeftRadius="?android:attr/dialogCornerRadius"
             android:topRightRadius="?android:attr/dialogCornerRadius" />
diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_layout_rot90.xml b/packages/SystemUI/res/layout-sw600dp/navigation_layout_rot90.xml
deleted file mode 100644
index 78304fd..0000000
--- a/packages/SystemUI/res/layout-sw600dp/navigation_layout_rot90.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <FrameLayout
-        android:id="@+id/nav_buttons"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-
-        <LinearLayout
-            android:id="@+id/ends_group"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:orientation="horizontal"
-            android:clipChildren="false" />
-
-        <LinearLayout
-            android:id="@+id/center_group"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:gravity="center"
-            android:orientation="horizontal"
-            android:clipChildren="false" />
-
-    </FrameLayout>
-
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml
index a8b1b2e..56a3cd5 100644
--- a/packages/SystemUI/res/layout/bubble_expanded_view.xml
+++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml
@@ -29,10 +29,9 @@
 
     <FrameLayout
         android:id="@+id/header_permission_wrapper"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:animateLayoutChanges="true"
-        android:background="@drawable/bubble_expanded_header_bg">
+        android:animateLayoutChanges="true">
 
         <LinearLayout
             android:id="@+id/header_layout"
diff --git a/packages/SystemUI/res/layout/bubble_permission_view.xml b/packages/SystemUI/res/layout/bubble_permission_view.xml
index 7fbb78a..c9d8a91 100644
--- a/packages/SystemUI/res/layout/bubble_permission_view.xml
+++ b/packages/SystemUI/res/layout/bubble_permission_view.xml
@@ -17,7 +17,7 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="@dimen/bubble_permission_height"
     android:animateLayoutChanges="true"
     android:orientation="vertical"
     android:paddingStart="@dimen/bubble_expanded_header_horizontal_padding"
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index 5b9816d..66c0c5c 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -71,7 +71,7 @@
         android:gravity="center_vertical"
         android:ellipsize="marquee"
         android:textDirection="locale"
-        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
         android:textColor="?attr/wallpaperTextColorSecondary"
         android:singleLine="true"
         systemui:showMissingSim="true"
diff --git a/packages/SystemUI/res/layout/nav_bar_tuner_inflater.xml b/packages/SystemUI/res/layout/nav_bar_tuner_inflater.xml
index b237633..133b215 100644
--- a/packages/SystemUI/res/layout/nav_bar_tuner_inflater.xml
+++ b/packages/SystemUI/res/layout/nav_bar_tuner_inflater.xml
@@ -20,8 +20,8 @@
     android:layout_width="match_parent"
     android:layout_height="@dimen/navigation_bar_size">
 
-    <include android:id="@+id/rot0" layout="@layout/navigation_layout" />
+    <include android:id="@+id/horizontal" layout="@layout/navigation_layout" />
 
-    <include android:id="@+id/rot90" layout="@layout/navigation_layout_rot90" />
+    <include android:id="@+id/vertical" layout="@layout/navigation_layout_vertical" />
 
 </com.android.systemui.tuner.PreviewNavInflater>
diff --git a/packages/SystemUI/res/layout/navigation_layout.xml b/packages/SystemUI/res/layout/navigation_layout.xml
index d72021e..db1c79d 100644
--- a/packages/SystemUI/res/layout/navigation_layout.xml
+++ b/packages/SystemUI/res/layout/navigation_layout.xml
@@ -22,7 +22,8 @@
     android:layout_marginStart="@dimen/rounded_corner_content_padding"
     android:layout_marginEnd="@dimen/rounded_corner_content_padding"
     android:paddingStart="@dimen/nav_content_padding"
-    android:paddingEnd="@dimen/nav_content_padding">
+    android:paddingEnd="@dimen/nav_content_padding"
+    android:id="@+id/horizontal">
 
     <com.android.systemui.statusbar.phone.NearestTouchFrame
         android:id="@+id/nav_buttons"
diff --git a/packages/SystemUI/res/layout/navigation_layout_rot90.xml b/packages/SystemUI/res/layout/navigation_layout_vertical.xml
similarity index 95%
rename from packages/SystemUI/res/layout/navigation_layout_rot90.xml
rename to packages/SystemUI/res/layout/navigation_layout_vertical.xml
index 24a0c71..285c5c4 100644
--- a/packages/SystemUI/res/layout/navigation_layout_rot90.xml
+++ b/packages/SystemUI/res/layout/navigation_layout_vertical.xml
@@ -22,7 +22,8 @@
     android:layout_marginTop="@dimen/rounded_corner_content_padding"
     android:layout_marginBottom="@dimen/rounded_corner_content_padding"
     android:paddingTop="@dimen/nav_content_padding"
-    android:paddingBottom="@dimen/nav_content_padding">
+    android:paddingBottom="@dimen/nav_content_padding"
+    android:id="@+id/vertical">
 
     <com.android.systemui.statusbar.phone.NearestTouchFrame
         android:id="@+id/nav_buttons"
diff --git a/packages/SystemUI/res/layout/qs_customize_divider.xml b/packages/SystemUI/res/layout/qs_customize_divider.xml
index 6fcfa8d..d6664fe 100644
--- a/packages/SystemUI/res/layout/qs_customize_divider.xml
+++ b/packages/SystemUI/res/layout/qs_customize_divider.xml
@@ -24,5 +24,4 @@
     android:paddingTop="20dp"
     android:paddingBottom="13dp"
     android:textAppearance="@style/TextAppearance.QSEdit.Headers"
-    android:textColor="?android:attr/colorAccent"
     android:text="@string/drag_to_add_tiles" />
diff --git a/packages/SystemUI/res/layout/qs_customize_header.xml b/packages/SystemUI/res/layout/qs_customize_header.xml
index d54dfee..58066a3 100644
--- a/packages/SystemUI/res/layout/qs_customize_header.xml
+++ b/packages/SystemUI/res/layout/qs_customize_header.xml
@@ -24,5 +24,4 @@
     android:gravity="center"
     android:minHeight="@dimen/qs_customize_header_min_height"
     android:textAppearance="@style/TextAppearance.QSEdit.Headers"
-    android:textColor="?android:attr/colorAccent"
     android:text="@string/drag_to_rearrange_tiles" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 87b4551..09f512f 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -37,7 +37,8 @@
             android:layout_height="wrap_content"
             android:background="@drawable/qs_customizer_toolbar"
             android:navigationContentDescription="@*android:string/action_bar_up_description"
-            style="?android:attr/toolbarStyle" />
+            style="@style/QSCustomizeToolbar"
+            />
 
         <androidx.recyclerview.widget.RecyclerView
             android:id="@android:id/list"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index e67bb60..ca34c23 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -87,7 +87,7 @@
                 android:background="@drawable/rounded_bg_bottom_background">
                 <com.android.keyguard.AlphaOptimizedImageButton
                     android:id="@+id/settings"
-                    android:src="@drawable/ic_settings_16dp"
+                    android:src="@drawable/ic_tune_black_16dp"
                     android:layout_width="@dimen/volume_dialog_tap_target_size"
                     android:layout_height="@dimen/volume_dialog_tap_target_size"
                     android:layout_gravity="center"
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 2c5120d..5803bf1 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -41,4 +41,10 @@
 
     <!-- The color of the text inside a notification -->
     <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
+
+    <!-- The color of the background in the top part of QSCustomizer -->
+    <color name="qs_customize_background">@color/GM2_grey_900</color>
+
+    <!-- The color of the background in the bottom part of QSCustomizer -->
+    <color name="qs_customize_decoration">@color/GM2_grey_800</color>
 </resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d5f29ba..0e4ffee 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -35,6 +35,8 @@
     <color name="status_bar_clock_color">#FFFFFFFF</color>
     <color name="qs_user_detail_icon_muted">#FFFFFFFF</color> <!-- not so muted after all -->
     <color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black -->
+    <color name="qs_customize_background">@color/GM2_grey_50</color>
+    <color name="qs_customize_decoration">@color/GM2_grey_100</color>
 
     <!-- Tint color for the content on the notification overflow card. -->
     <color name="keyguard_overflow_content_color">#ff686868</color>
@@ -72,7 +74,7 @@
     <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_light</color>
 
     <!-- The "inside" of a notification, reached via longpress -->
-    <color name="notification_guts_bg_color">#f8f9fa</color>
+    <color name="notification_guts_bg_color">@color/GM2_grey_50</color>
 
     <color name="assist_orb_color">#ffffff</color>
 
@@ -123,7 +125,7 @@
 
     <color name="zen_introduction">#ffffffff</color>
 
-    <color name="smart_reply_button_text">#5F6368</color>
+    <color name="smart_reply_button_text">@color/GM2_grey_700</color>
     <color name="smart_reply_button_text_dark_bg">@*android:color/notification_primary_text_color_dark</color>
     <color name="smart_reply_button_background">#ffffffff</color>
     <color name="smart_reply_button_stroke">#ffdadce0</color>
@@ -134,4 +136,17 @@
 
     <!-- Logout button -->
     <color name="logout_button_bg_color">#ccffffff</color>
+
+    <!-- GM2 colors -->
+    <color name="GM2_grey_50">#F8F9FA</color>
+    <color name="GM2_grey_100">#F1F3F4</color>
+    <color name="GM2_grey_200">#E8EAED</color>
+    <color name="GM2_grey_300">#DADCE0</color>
+    <color name="GM2_grey_400">#BDC1C6</color>
+    <color name="GM2_grey_500">#9AA0A6</color>
+    <color name="GM2_grey_600">#80868B</color>
+    <color name="GM2_grey_650">#70757A</color>
+    <color name="GM2_grey_700">#5F6368</color>
+    <color name="GM2_grey_800">#3C4043</color>
+    <color name="GM2_grey_900">#202124</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 4396a42..b4dc0eff 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -329,16 +329,11 @@
 
     <bool name="quick_settings_show_full_alarm">false</bool>
 
-    <!-- Whether to show a warning notification when the device reaches a certain temperature. -->
+    <!-- Whether to show a warning notification when device's skin temperature is high. -->
     <integer name="config_showTemperatureWarning">0</integer>
 
-    <!-- Temp at which to show a warning notification if config_showTemperatureWarning is true.
-         If < 0, uses the skin temperature sensor shutdown value from
-         HardwarePropertiesManager#getDeviceTemperatures - config_warningTemperatureTolerance. -->
-    <integer name="config_warningTemperature">-1</integer>
-
-    <!-- Fudge factor for how much below the shutdown temp to show the warning. -->
-    <integer name="config_warningTemperatureTolerance">2</integer>
+    <!-- Whether to show a alarm dialog when device's usb port is overheating. -->
+    <integer name="config_showUsbPortAlarm">0</integer>
 
     <!-- Accessibility actions -->
     <item type="id" name="action_split_task_to_left" />
@@ -485,4 +480,5 @@
     </string-array>
 
     <integer name="ongoing_appops_dialog_max_apps">5</integer>
+
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1f6e3c2..536bc4e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1045,4 +1045,8 @@
     <dimen name="bubble_header_icon_size">48dp</dimen>
     <!-- Size of the app icon shown in the bubble permission view -->
     <dimen name="bubble_permission_icon_size">24dp</dimen>
+    <!-- Space between the pointer triangle and the bubble expanded view -->
+    <dimen name="bubble_pointer_margin">8dp</dimen>
+    <!-- Height of the permission prompt shown with bubbles -->
+    <dimen name="bubble_permission_height">120dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 0fde2de..4905e57 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2118,6 +2118,14 @@
     <string name="high_temp_notif_message">Some features limited while phone cools down</string>
     <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=300] -->
     <string name="high_temp_dialog_message">Your phone will automatically try to cool down. You can still use your phone, but it may run slower.\n\nOnce your phone has cooled down, it will run normally.</string>
+    <!-- Title for alarm dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=30] -->
+    <string name="high_temp_alarm_title">Unplug charger</string>
+    <!-- Text body for dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=300] -->
+    <string name="high_temp_alarm_notify_message">There\u2019s an issue charging this device. Unplug the power adapter, and take care as the cable may be warm.</string>
+    <!-- Text for See care steps button [CHAR LIMIT=300] -->
+    <string name="high_temp_alarm_help_care_steps">See care steps</string>
+    <!-- Text link directs to usb overheat help page. -->
+    <string name="high_temp_alarm_help_url" translatable="false">help_uri_usb_warm</string>
 
     <!-- SysUI Tuner: Button to select lock screen shortcut [CHAR LIMIT=60] -->
     <string name="lockscreen_shortcut_left">Left shortcut</string>
@@ -2378,5 +2386,4 @@
     <string name="no_bubbles">Block</string>
     <!-- Text used for button allowing user to approve / enable bubbles [CHAR LIMIT=20] -->
     <string name="yes_bubbles">Allow</string>
-
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 96cdbd38..1e411cf 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -458,8 +458,15 @@
     </style>
 
     <style name="TextAppearance.QSEdit.Headers"
-        parent="@*android:style/TextAppearance.Material.Body2">
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        parent="@*android:style/TextAppearance.DeviceDefault.Body2">
+        <item name="android:textSize">11sp</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:textAllCaps">true</item>
+    </style>
+
+    <style name="QSCustomizeToolbar" parent="@*android:style/Widget.DeviceDefault.Toolbar">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:elevation">10dp</item>
     </style>
 
     <style name="edit_theme" parent="qs_theme">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java
index 5bc1f25..d17725f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java
@@ -32,4 +32,17 @@
         StatsLog.write(19, action, srcState, dstState, extension,
                 swipeUpEnabled);
     }
+
+    /**
+     *  StatsLog.write(StatsLog.STYLE_EVENT, action, colorPackageHash,
+     *           fontPackageHash, shapePackageHash, clockPackageHash,
+     *           launcherGrid, wallpaperCategoryHash, wallpaperIdHash);
+     */
+    public static void write(int action, int colorPackageHash,
+            int fontPackageHash, int shapePackageHash, int clockPackageHash,
+            int launcherGrid, int wallpaperCategoryHash, int wallpaperIdHash) {
+        StatsLog.write(179, action, colorPackageHash,
+                fontPackageHash, shapePackageHash, clockPackageHash,
+                launcherGrid, wallpaperCategoryHash, wallpaperIdHash);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 583dac7..9dd9717 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -17,7 +17,6 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import android.annotation.Nullable;
 import android.app.Presentation;
 import android.content.Context;
 import android.graphics.Point;
@@ -32,15 +31,11 @@
 import android.view.View;
 import android.view.WindowManager;
 
-import java.util.function.BooleanSupplier;
-
 // TODO(multi-display): Support multiple external displays
 public class KeyguardDisplayManager {
     protected static final String TAG = "KeyguardDisplayManager";
     private static boolean DEBUG = KeyguardConstants.DEBUG;
 
-    private final ViewMediatorCallback mCallback;
-
     private final MediaRouter mMediaRouter;
     private final DisplayManager mDisplayService;
     private final Context mContext;
@@ -57,7 +52,7 @@
         public void onDisplayAdded(int displayId) {
             final Display display = mDisplayService.getDisplay(displayId);
             if (mShowing) {
-                notifyIfChanged(() -> showPresentation(display));
+                showPresentation(display);
             }
         }
 
@@ -76,13 +71,12 @@
 
         @Override
         public void onDisplayRemoved(int displayId) {
-            notifyIfChanged(() -> hidePresentation(displayId));
+            hidePresentation(displayId);
         }
     };
 
-    public KeyguardDisplayManager(Context context, ViewMediatorCallback callback) {
+    public KeyguardDisplayManager(Context context) {
         mContext = context;
-        mCallback = callback;
         mMediaRouter = mContext.getSystemService(MediaRouter.class);
         mDisplayService = mContext.getSystemService(DisplayManager.class);
         mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
@@ -138,42 +132,13 @@
 
     /**
      * @param displayId The id of the display to hide the presentation off.
-     * @return {@code true} if the a presentation was removed.
-     *         {@code false} if the presentation was not added before.
      */
-    private boolean hidePresentation(int displayId) {
+    private void hidePresentation(int displayId) {
         final Presentation presentation = mPresentations.get(displayId);
         if (presentation != null) {
             presentation.dismiss();
             mPresentations.remove(displayId);
-            return true;
         }
-        return false;
-    }
-
-    private void notifyIfChanged(BooleanSupplier updateMethod) {
-        if (updateMethod.getAsBoolean()) {
-            final int[] displayList = getPresentationDisplayIds();
-            mCallback.onSecondaryDisplayShowingChanged(displayList);
-        }
-    }
-
-    /**
-     * @return An array of displayId's on which a {@link KeyguardPresentation} is showing on.
-     */
-    @Nullable
-    private int[] getPresentationDisplayIds() {
-        final int size = mPresentations.size();
-        if (size == 0) return null;
-
-        final int[] displayIds = new int[size];
-        for (int i = mPresentations.size() - 1; i >= 0; i--) {
-            final Presentation presentation = mPresentations.valueAt(i);
-            if (presentation != null) {
-                displayIds[i] = presentation.getDisplay().getDisplayId();
-            }
-        }
-        return displayIds;
     }
 
     public void show() {
@@ -181,7 +146,7 @@
             if (DEBUG) Log.v(TAG, "show");
             mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
                     mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
-            notifyIfChanged(() -> updateDisplays(true /* showing */));
+            updateDisplays(true /* showing */);
         }
         mShowing = true;
     }
@@ -190,7 +155,7 @@
         if (mShowing) {
             if (DEBUG) Log.v(TAG, "hide");
             mMediaRouter.removeCallback(mMediaRouterCallback);
-            notifyIfChanged(() -> updateDisplays(false /* showing */));
+            updateDisplays(false /* showing */);
         }
         mShowing = false;
     }
@@ -200,19 +165,19 @@
         @Override
         public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
             if (DEBUG) Log.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
-            notifyIfChanged(() -> updateDisplays(mShowing));
+            updateDisplays(mShowing);
         }
 
         @Override
         public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
             if (DEBUG) Log.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
-            notifyIfChanged(() -> updateDisplays(mShowing));
+            updateDisplays(mShowing);
         }
 
         @Override
         public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
             if (DEBUG) Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
-            notifyIfChanged(() -> updateDisplays(mShowing));
+            updateDisplays(mShowing);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
index a07c5cb..6e06130 100644
--- a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
@@ -96,11 +96,6 @@
     int getBouncerPromptReason();
 
     /**
-     * Invoked when the secondary displays showing a keyguard window changes.
-     */
-    void onSecondaryDisplayShowingChanged(int[] displayId);
-
-    /**
      * Consumes a message that was enqueued to be displayed on the next time the bouncer shows up.
      * @return Message that should be displayed above the challenge.
      */
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index c5dc324..3827e44 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -34,6 +34,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -241,6 +242,7 @@
         // Draw clock view hierarchy to canvas.
         Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888);
         Canvas canvas = new Canvas(bitmap);
+        dispatchVisibilityAggregated(clockView, true);
         clockView.measure(MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY),
                 MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY));
         clockView.layout(0, 0, mWidth, mHeight);
@@ -250,6 +252,24 @@
         return bitmap;
     }
 
+    private void dispatchVisibilityAggregated(View view, boolean isVisible) {
+        // Similar to View.dispatchVisibilityAggregated implementation.
+        final boolean thisVisible = view.getVisibility() == View.VISIBLE;
+        if (thisVisible || !isVisible) {
+            view.onVisibilityAggregated(isVisible);
+        }
+
+        if (view instanceof ViewGroup) {
+            isVisible = thisVisible && isVisible;
+            ViewGroup vg = (ViewGroup) view;
+            int count = vg.getChildCount();
+
+            for (int i = 0; i < count; i++) {
+                dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
+            }
+        }
+    }
+
     private void notifyClockChanged(ClockPlugin plugin) {
         for (int i = 0; i < mListeners.size(); i++) {
             // It probably doesn't make sense to supply the same plugin instances to multiple
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 5672073..87f004f 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -61,7 +61,6 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -266,7 +265,6 @@
     @Inject Lazy<NotificationListener> mNotificationListener;
     @Inject Lazy<NotificationLogger> mNotificationLogger;
     @Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager;
-    @Inject Lazy<NotificationRowBinder> mNotificationRowBinder;
     @Inject Lazy<NotificationFilter> mNotificationFilter;
     @Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider;
     @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
@@ -440,7 +438,6 @@
         mProviders.put(NotificationLogger.class, mNotificationLogger::get);
         mProviders.put(NotificationViewHierarchyManager.class,
                 mNotificationViewHierarchyManager::get);
-        mProviders.put(NotificationRowBinder.class, mNotificationRowBinder::get);
         mProviders.put(NotificationFilter.class, mNotificationFilter::get);
         mProviders.put(NotificationInterruptionStateProvider.class,
                 mNotificationInterruptionStateProvider::get);
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 9efa656..60e6083 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -474,8 +474,6 @@
         if (anim == null) {
             return;
         }
-        int duration = SNAP_ANIM_LEN;
-        anim.setDuration(duration);
         anim.addListener(new AnimatorListenerAdapter() {
             boolean wasCancelled = false;
 
@@ -495,6 +493,9 @@
         });
         prepareSnapBackAnimation(animView, anim);
         mSnappingChild = true;
+        float maxDistance = Math.abs(targetLeft - getTranslation(animView));
+        mFlingAnimationUtils.apply(anim, getTranslation(animView), targetLeft, velocity,
+                maxDistance);
         anim.start();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 4eea9f8..471619e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -80,16 +80,21 @@
     // Enables some subset of notifs to automatically become bubbles
     private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
 
-    // Secure settings flags
-    // Feature level flag
+    /** Flag to enable or disable the entire feature */
     private static final String ENABLE_BUBBLES = "experiment_enable_bubbles";
-    // Auto bubble flags set whether different notification types should be presented as a bubble
+    /** Auto bubble flags set whether different notif types should be presented as a bubble */
     private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging";
     private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing";
     private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all";
-    // Use an activity view for an auto-bubbled notification if it has an appropriate content intent
+
+    /** Use an activityView for an auto-bubbled notifs if it has an appropriate content intent */
     private static final String ENABLE_BUBBLE_CONTENT_INTENT = "experiment_bubble_content_intent";
 
+    /** Whether the row of bubble circles are anchored to the top or bottom of the screen. */
+    private static final String ENABLE_BUBBLES_AT_TOP = "experiment_enable_top_bubbles";
+    /** Flag to position the header below the activity view */
+    private static final String ENABLE_BUBBLE_FOOTER = "experiment_enable_bubble_footer";
+
     private final Context mContext;
     private final NotificationEntryManager mNotificationEntryManager;
     private final IActivityTaskManager mActivityTaskManager;
@@ -548,6 +553,22 @@
                 ENABLE_BUBBLES, 1) != 0;
     }
 
+    /**
+     * Whether bubbles should be positioned at the top of the screen or not.
+     */
+    public static boolean showBubblesAtTop(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ENABLE_BUBBLES_AT_TOP, 0) != 0;
+    }
+
+    /**
+     * Whether the bubble chrome should display as a footer or not (in which case it's a header).
+     */
+    public static boolean useFooter(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ENABLE_BUBBLE_FOOTER, 0) != 0;
+    }
+
     /** PinnedStackListener that dispatches IME visibility updates to the stack. */
     private class BubblesImeListener extends IPinnedStackListener.Stub {
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 7884800..603b3b9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -39,6 +39,7 @@
 import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -48,7 +49,6 @@
 import android.util.Log;
 import android.util.StatsLog;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
@@ -70,8 +70,13 @@
 public class BubbleExpandedView extends LinearLayout implements View.OnClickListener {
     private static final String TAG = "BubbleExpandedView";
 
+    // Configurable via bubble settings; just for testing
+    private boolean mUseFooter;
+    private boolean mShowOnTop;
+
     // The triangle pointing to the expanded view
     private View mPointerView;
+    private int mPointerMargin;
 
     // Header
     private View mHeaderView;
@@ -90,6 +95,8 @@
 
     private int mMinHeight;
     private int mHeaderHeight;
+    private int mBubbleHeight;
+    private int mPermissionHeight;
 
     private NotificationEntry mEntry;
     private PackageManager mPm;
@@ -150,6 +157,7 @@
         mPm = context.getPackageManager();
         mMinHeight = getResources().getDimensionPixelSize(
                 R.dimen.bubble_expanded_default_height);
+        mPointerMargin = getResources().getDimensionPixelSize(R.dimen.bubble_pointer_margin);
         try {
             mNotificationManagerService = INotificationManager.Stub.asInterface(
                     ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE));
@@ -172,12 +180,17 @@
         int bgColor = ta.getColor(0, Color.WHITE);
         ta.recycle();
 
+        mShowOnTop = BubbleController.showBubblesAtTop(getContext());
+        mUseFooter = BubbleController.useFooter(getContext());
+
         ShapeDrawable triangleDrawable = new ShapeDrawable(
-                TriangleShape.create(width, height, true /* pointUp */));
+                TriangleShape.create(width, height, mShowOnTop /* pointUp */));
         triangleDrawable.setTint(bgColor);
         mPointerView.setBackground(triangleDrawable);
 
         FrameLayout viewWrapper = findViewById(R.id.header_permission_wrapper);
+        viewWrapper.setBackground(createHeaderPermissionBackground(bgColor));
+
         LayoutTransition transition = new LayoutTransition();
         transition.setDuration(200);
 
@@ -193,8 +206,11 @@
         viewWrapper.setLayoutTransition(transition);
         viewWrapper.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
 
+
         mHeaderHeight = getContext().getResources().getDimensionPixelSize(
                 R.dimen.bubble_expanded_header_height);
+        mPermissionHeight = getContext().getResources().getDimensionPixelSize(
+                R.dimen.bubble_permission_height);
         mHeaderView = findViewById(R.id.header_layout);
         mDeepLinkIcon = findViewById(R.id.deep_link_button);
         mSettingsIcon = findViewById(R.id.settings_button);
@@ -226,6 +242,37 @@
             activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
             return view.onApplyWindowInsets(insets);
         });
+
+        if (!mShowOnTop) {
+            removeView(mPointerView);
+            if (mUseFooter) {
+                View divider = findViewById(R.id.divider);
+                viewWrapper.removeView(divider);
+                removeView(viewWrapper);
+                addView(divider);
+                addView(viewWrapper);
+            }
+            addView(mPointerView);
+        }
+    }
+
+    /**
+     * Creates a background with corners rounded based on how the view is configured to display
+     */
+    private Drawable createHeaderPermissionBackground(int bgColor) {
+        TypedArray ta2 = getContext().obtainStyledAttributes(
+                new int[] {android.R.attr.dialogCornerRadius});
+        final float cr = ta2.getDimension(0, 0f);
+        ta2.recycle();
+
+        float[] radii = mUseFooter
+                ? new float[] {0, 0, 0, 0, cr, cr, cr, cr}
+                : new float[] {cr, cr, cr, cr, 0, 0, 0, 0};
+        GradientDrawable chromeBackground = new GradientDrawable();
+        chromeBackground.setShape(GradientDrawable.RECTANGLE);
+        chromeBackground.setCornerRadii(radii);
+        chromeBackground.setColor(bgColor);
+        return chromeBackground;
     }
 
     /**
@@ -332,7 +379,11 @@
 
             // Use notification view
             mNotifRow = mEntry.getRow();
-            addView(mNotifRow);
+            if (mShowOnTop) {
+                addView(mNotifRow);
+            } else {
+                addView(mNotifRow, mUseFooter ? 0 : 1);
+            }
         }
         updateView();
     }
@@ -345,6 +396,17 @@
         return true;
     }
 
+    /**
+     * @return total height that the expanded view occupies.
+     */
+    int getExpandedSize() {
+        int chromeHeight = mPermissionView.getVisibility() != View.VISIBLE
+                ? mHeaderHeight
+                : mPermissionHeight;
+        return mBubbleHeight + mPointerView.getHeight() + mPointerMargin
+                + chromeHeight;
+    }
+
     void updateHeight() {
         if (usingActivityView()) {
             Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
@@ -358,12 +420,19 @@
                         ? data.getDesiredHeight()
                         : mMinHeight;
             }
-            int max = mStackView.getMaxExpandedHeight() - mHeaderHeight;
+            int chromeHeight = mPermissionView.getVisibility() != View.VISIBLE
+                    ? mHeaderHeight
+                    : mPermissionHeight;
+            int max = mStackView.getMaxExpandedHeight() - chromeHeight - mPointerView.getHeight()
+                    - mPointerMargin;
             int height = Math.min(desiredHeight, max);
             height = Math.max(height, mMinHeight);
             LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
             lp.height = height;
+            mBubbleHeight = height;
             mActivityView.setLayoutParams(lp);
+        } else {
+            mBubbleHeight = mNotifRow != null ? mNotifRow.getIntrinsicHeight() : mMinHeight;
         }
     }
 
@@ -412,6 +481,7 @@
             } else if (mOnBubbleBlockedListener != null) {
                 mOnBubbleBlockedListener.onBubbleBlocked(mEntry);
             }
+            mStackView.onExpandedHeightChanged();
             logBubbleClickEvent(mEntry.notification,
                     allowed ? StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_IN :
                             StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_OUT);
@@ -446,23 +516,14 @@
     /**
      * Removes and releases an ActivityView if one was previously created for this bubble.
      */
-    public void destroyActivityView(ViewGroup tmpParent) {
+    public void destroyActivityView() {
         if (mActivityView == null) {
             return;
         }
-        if (!mActivityViewReady) {
-            // release not needed, never initialized?
-            mActivityView = null;
-            return;
+        if (mActivityViewReady) {
+            mActivityView.release();
         }
-        // HACK: release() will crash if the view is not attached.
-        if (!isAttachedToWindow()) {
-            mActivityView.setVisibility(View.GONE);
-            tmpParent.addView(this);
-        }
-
-        mActivityView.release();
-        ((ViewGroup) getParent()).removeView(this);
+        removeView(mActivityView);
         mActivityView = null;
         mActivityViewReady = false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index e20be8e..8235d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -173,7 +173,7 @@
         int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
 
         mStackAnimationController = new StackAnimationController();
-        mExpandedAnimationController = new ExpandedAnimationController();
+        mExpandedAnimationController = new ExpandedAnimationController(mDisplaySize);
 
         mBubbleContainer = new PhysicsAnimationLayout(context);
         mBubbleContainer.setMaxRenderedChildren(
@@ -348,7 +348,7 @@
 
         // Remove it from the views
         int removedIndex = mBubbleContainer.indexOfChild(b.iconView);
-        b.expandedView.destroyActivityView(this /* tmpParent */);
+        b.expandedView.destroyActivityView();
         mBubbleContainer.removeView(b.iconView);
 
         int bubbleCount = mBubbleContainer.getChildCount();
@@ -377,7 +377,7 @@
     public void stackDismissed() {
         for (Bubble bubble : mBubbleData.getBubbles()) {
             bubble.entry.setBubbleDismissed(true);
-            bubble.expandedView.destroyActivityView(this /* tmpParent */);
+            bubble.expandedView.destroyActivityView();
         }
         mBubbleData.clear();
         collapseStack();
@@ -513,8 +513,7 @@
             final float yStart = Math.min(
                     mStackAnimationController.getStackPosition().y,
                     mExpandedAnimateYDistance);
-            final float yDest = getStatusBarHeight()
-                    + mExpandedBubble.iconView.getHeight() + mBubblePadding;
+            final float yDest = getYPositionForExpandedView();
 
             if (shouldExpand) {
                 mExpandedViewContainer.setTranslationX(xStart);
@@ -668,13 +667,39 @@
      * y position when the bubbles are expanded as well as the bounds of the dismiss target.
      */
     int getMaxExpandedHeight() {
+        boolean showOnTop = BubbleController.showBubblesAtTop(getContext());
         int expandedY = (int) mExpandedAnimationController.getExpandedY();
-        int bubbleContainerHeight = mBubbleContainer.getChildAt(0) != null
-                ? mBubbleContainer.getChildAt(0).getHeight()
-                : 0;
-        // PIP dismiss view uses FLAG_LAYOUT_IN_SCREEN so we need to subtract the bottom inset
-        int pipDismissHeight = mPipDismissHeight - getBottomInset();
-        return mDisplaySize.y - expandedY - mBubbleSize - pipDismissHeight;
+        if (showOnTop) {
+            // PIP dismiss view uses FLAG_LAYOUT_IN_SCREEN so we need to subtract the bottom inset
+            int pipDismissHeight = mPipDismissHeight - getBottomInset();
+            return mDisplaySize.y - expandedY - mBubbleSize - pipDismissHeight;
+        } else {
+            return expandedY - getStatusBarHeight();
+        }
+    }
+
+    /**
+     * Calculates the y position of the expanded view when it is expanded.
+     */
+    float getYPositionForExpandedView() {
+        boolean showOnTop = BubbleController.showBubblesAtTop(getContext());
+        if (showOnTop) {
+            return getStatusBarHeight() + mBubbleSize + mBubblePadding;
+        } else {
+            return mExpandedAnimationController.getExpandedY()
+                    - mExpandedBubble.expandedView.getExpandedSize() - mBubblePadding;
+        }
+    }
+
+    /**
+     * Called when the height of the currently expanded view has changed (not via an
+     * update to the bubble's desired height but for some other reason, e.g. permission view
+     * goes away).
+     */
+    void onExpandedHeightChanged() {
+        if (mIsExpanded) {
+            requestUpdate();
+        }
     }
 
     /**
@@ -751,6 +776,8 @@
 
         mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
         if (mIsExpanded) {
+            final float y = getYPositionForExpandedView();
+            mExpandedViewContainer.setTranslationY(y);
             mExpandedBubble.expandedView.updateView();
         }
 
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 f0d9be1..f7896b0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -17,6 +17,7 @@
 package com.android.systemui.bubbles.animation;
 
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.view.View;
 import android.view.WindowInsets;
@@ -25,6 +26,7 @@
 import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubbleController;
 
 import com.google.android.collect.Sets;
 
@@ -61,6 +63,14 @@
     private float mBubbleSizePx;
     /** Height of the status bar. */
     private float mStatusBarHeight;
+    /** Size of display. */
+    private Point mDisplaySize;
+    /** Size of dismiss target at bottom of screen. */
+    private float mPipDismissHeight;
+
+    public ExpandedAnimationController(Point displaySize) {
+        mDisplaySize = displaySize;
+    }
 
     /**
      * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause
@@ -88,6 +98,7 @@
         mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
         mStatusBarHeight =
                 res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+        mPipDismissHeight = res.getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height);
     }
 
     /**
@@ -204,16 +215,19 @@
 
     /** The Y value of the row of expanded bubbles. */
     public float getExpandedY() {
+        boolean showOnTop = mLayout != null
+                && BubbleController.showBubblesAtTop(mLayout.getContext());
         final WindowInsets insets = mLayout != null ? mLayout.getRootWindowInsets() : null;
-        if (insets != null) {
+        if (showOnTop && insets != null) {
             return mBubblePaddingPx + Math.max(
                     mStatusBarHeight,
                     insets.getDisplayCutout() != null
                             ? insets.getDisplayCutout().getSafeInsetTop()
                             : 0);
+        } else {
+            int bottomInset = insets != null ? insets.getSystemWindowInsetBottom() : 0;
+            return mDisplaySize.y - mBubbleSizePx - (mPipDismissHeight - bottomInset);
         }
-
-        return mBubblePaddingPx;
     }
 
     /** Runs the given Runnable after all translation-related animations have ended. */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 2d1dba6..d1e127d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -38,6 +38,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
@@ -60,7 +62,6 @@
     private final Context mContext;
     private final AlarmManager mAlarmManager;
     private final SensorManager mSensorManager;
-    private final TriggerSensor[] mSensors;
     private final ContentResolver mResolver;
     private final TriggerSensor mPickupSensor;
     private final DozeParameters mDozeParameters;
@@ -68,10 +69,12 @@
     private final WakeLock mWakeLock;
     private final Consumer<Boolean> mProxCallback;
     private final Callback mCallback;
+    @VisibleForTesting
+    protected final TriggerSensor[] mSensors;
 
     private final Handler mHandler = new Handler();
     private final ProxSensor mProxSensor;
-
+    private long mDebounceFrom;
 
     public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
             DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
@@ -134,13 +137,21 @@
                         mConfig.wakeScreenGestureAvailable() && alwaysOn,
                         DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
                         false /* reports touch coordinates */,
-                        false /* touchscreen */),
+                        false /* touchscreen */, mConfig.getWakeLockScreenDebounce()),
         };
 
         mProxSensor = new ProxSensor(policy);
         mCallback = callback;
     }
 
+    /**
+     * Temporarily disable some sensors to avoid turning on the device while the user is
+     * turning it off.
+     */
+    public void requestTemporaryDisable() {
+        mDebounceFrom = SystemClock.uptimeMillis();
+    }
+
     private Sensor findSensorWithType(String type) {
         return findSensorWithType(mSensorManager, type);
     }
@@ -320,7 +331,8 @@
         }
     }
 
-    private class TriggerSensor extends TriggerEventListener {
+    @VisibleForTesting
+    class TriggerSensor extends TriggerEventListener {
         final Sensor mSensor;
         final boolean mConfigured;
         final int mPulseReason;
@@ -467,23 +479,25 @@
     /**
      * A Sensor that is injected via plugin.
      */
-    private class PluginSensor extends TriggerSensor {
+    @VisibleForTesting
+    class PluginSensor extends TriggerSensor implements SensorManagerPlugin.SensorEventListener {
 
-        private final SensorManagerPlugin.Sensor mPluginSensor;
-        private final SensorManagerPlugin.SensorEventListener mTriggerEventListener = (event) -> {
-            DozeLog.traceSensor(mContext, mPulseReason);
-            mHandler.post(mWakeLock.wrap(() -> {
-                if (DEBUG) Log.d(TAG, "onSensorEvent: " + triggerEventToString(event));
-                mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
-                        event.getValues());
-            }));
-        };
+        final SensorManagerPlugin.Sensor mPluginSensor;
+        private long mDebounce;
 
         PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
                 int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
+            this(sensor, setting, configured, pulseReason, reportsTouchCoordinates,
+                    requiresTouchscreen, 0L /* debounce */);
+        }
+
+        PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
+                int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen,
+                long debounce) {
             super(null, setting, configured, pulseReason, reportsTouchCoordinates,
                     requiresTouchscreen);
             mPluginSensor = sensor;
+            mDebounce = debounce;
         }
 
         @Override
@@ -492,11 +506,11 @@
             AsyncSensorManager asyncSensorManager = (AsyncSensorManager) mSensorManager;
             if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting)
                     && !mRegistered) {
-                asyncSensorManager.registerPluginListener(mPluginSensor, mTriggerEventListener);
+                asyncSensorManager.registerPluginListener(mPluginSensor, this);
                 mRegistered = true;
                 if (DEBUG) Log.d(TAG, "registerPluginListener");
             } else if (mRegistered) {
-                asyncSensorManager.unregisterPluginListener(mPluginSensor, mTriggerEventListener);
+                asyncSensorManager.unregisterPluginListener(mPluginSensor, this);
                 mRegistered = false;
                 if (DEBUG) Log.d(TAG, "unregisterPluginListener");
             }
@@ -524,6 +538,21 @@
             }
             return sb.append(']').toString();
         }
+
+        @Override
+        public void onSensorChanged(SensorManagerPlugin.SensorEvent event) {
+            DozeLog.traceSensor(mContext, mPulseReason);
+            mHandler.post(mWakeLock.wrap(() -> {
+                final long now = SystemClock.uptimeMillis();
+                if (now < mDebounceFrom + mDebounce) {
+                    if (DEBUG) Log.d(TAG, "onSensorEvent dropped: " + triggerEventToString(event));
+                    return;
+                }
+                if (DEBUG) Log.d(TAG, "onSensorEvent: " + triggerEventToString(event));
+                mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
+                        event.getValues());
+            }));
+        }
     }
 
     public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 70bf903..b6e830c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -143,8 +143,12 @@
 
         if (isWakeDisplay) {
             onWakeScreen(wakeEvent, mMachine.getState());
-        } else if (isLongPress || isWakeLockScreen) {
+        } else if (isLongPress) {
             requestPulse(pulseReason, sensorPerformedProxCheck);
+        } else if (isWakeLockScreen) {
+            if (wakeEvent) {
+                requestPulse(pulseReason, sensorPerformedProxCheck);
+            }
         } else {
             proximityCheckThenCall((result) -> {
                 if (result == ProximityCheck.RESULT_NEAR) {
@@ -228,6 +232,7 @@
                 if (mDockManager != null) {
                     mDockManager.addListener(mDockEventListener);
                 }
+                mDozeSensors.requestTemporaryDisable();
                 checkTriggersAtInit();
                 break;
             case DOZE:
@@ -250,6 +255,9 @@
                 mDozeSensors.setTouchscreenSensorsListening(false);
                 mDozeSensors.setProxListening(true);
                 break;
+            case DOZE_PULSE_DONE:
+                mDozeSensors.requestTemporaryDisable();
+                break;
             case FINISH:
                 mBroadcastReceiver.unregister(mContext);
                 mDozeHost.removeCallback(mHostCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 66cfadf..172746e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -21,7 +21,6 @@
 import static com.android.internal.telephony.IccCardConstants.State.ABSENT;
 import static com.android.internal.telephony.IccCardConstants.State.PIN_REQUIRED;
 import static com.android.internal.telephony.IccCardConstants.State.PUK_REQUIRED;
-import static com.android.internal.telephony.IccCardConstants.State.READY;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -95,7 +94,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 
 /**
  * Mediates requests related to the keyguard.  This includes queries about the
@@ -247,9 +245,6 @@
     // AOD is enabled and status bar is in AOD state.
     private boolean mAodShowing;
 
-    // display ids of the external display on which we have put a keyguard window
-    private int[] mSecondaryDisplaysShowing;
-
     /** Cached value of #isInputRestricted */
     private boolean mInputRestricted;
 
@@ -687,13 +682,6 @@
             mCustomMessage = null;
             return message;
         }
-
-        @Override
-        public void onSecondaryDisplayShowingChanged(int[] displayIds) {
-            synchronized (KeyguardViewMediator.this) {
-                setShowingLocked(mShowing, mAodShowing, displayIds, false);
-            }
-        }
     };
 
     public void userActivity() {
@@ -722,7 +710,7 @@
         mContext.registerReceiver(mDelayedLockBroadcastReceiver, delayedActionFilter,
                 SYSTEMUI_PERMISSION, null /* scheduler */);
 
-        mKeyguardDisplayManager = new KeyguardDisplayManager(mContext, mViewMediatorCallback);
+        mKeyguardDisplayManager = new KeyguardDisplayManager(mContext);
 
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
 
@@ -738,10 +726,10 @@
             setShowingLocked(!shouldWaitForProvisioning()
                     && !mLockPatternUtils.isLockScreenDisabled(
                             KeyguardUpdateMonitor.getCurrentUser()),
-                    mAodShowing, mSecondaryDisplaysShowing, true /* forceCallbacks */);
+                    mAodShowing, true /* forceCallbacks */);
         } else {
             // The system's keyguard is disabled or missing.
-            setShowingLocked(false, mAodShowing, mSecondaryDisplaysShowing, true);
+            setShowingLocked(false, mAodShowing, true);
         }
 
         mStatusBarKeyguardViewManager =
@@ -1764,12 +1752,10 @@
         playSound(mTrustedSoundId);
     }
 
-    private void updateActivityLockScreenState(boolean showing, boolean aodShowing,
-            int[] secondaryDisplaysShowing) {
+    private void updateActivityLockScreenState(boolean showing, boolean aodShowing) {
         mUiOffloadThread.submit(() -> {
             try {
-                ActivityTaskManager.getService().setLockScreenShown(showing, aodShowing,
-                        secondaryDisplaysShowing);
+                ActivityTaskManager.getService().setLockScreenShown(showing, aodShowing);
             } catch (RemoteException e) {
             }
         });
@@ -1892,8 +1878,7 @@
 
             if (!mHiding) {
                 // Tell ActivityManager that we canceled the keyguardExitAnimation.
-                setShowingLocked(mShowing, mAodShowing, mSecondaryDisplaysShowing,
-                        true /* force */);
+                setShowingLocked(mShowing, mAodShowing, true /* force */);
                 return;
             }
             mHiding = false;
@@ -2163,23 +2148,19 @@
     }
 
     private void setShowingLocked(boolean showing, boolean aodShowing) {
-        setShowingLocked(showing, aodShowing, mSecondaryDisplaysShowing,
-                false /* forceCallbacks */);
+        setShowingLocked(showing, aodShowing, false /* forceCallbacks */);
     }
 
-    private void setShowingLocked(boolean showing, boolean aodShowing,
-            int[] secondaryDisplaysShowing, boolean forceCallbacks) {
+    private void setShowingLocked(boolean showing, boolean aodShowing, boolean forceCallbacks) {
         final boolean notifyDefaultDisplayCallbacks = showing != mShowing
                 || aodShowing != mAodShowing || forceCallbacks;
-        if (notifyDefaultDisplayCallbacks
-                || !Arrays.equals(secondaryDisplaysShowing, mSecondaryDisplaysShowing)) {
+        if (notifyDefaultDisplayCallbacks) {
             mShowing = showing;
             mAodShowing = aodShowing;
-            mSecondaryDisplaysShowing = secondaryDisplaysShowing;
             if (notifyDefaultDisplayCallbacks) {
                 notifyDefaultDisplayCallbacks(showing);
             }
-            updateActivityLockScreenState(showing, aodShowing, secondaryDisplaysShowing);
+            updateActivityLockScreenState(showing, aodShowing);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 50dda1c..fdb0b36 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.power;
 
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -41,6 +42,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.view.View;
+import android.view.WindowManager;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -48,10 +50,13 @@
 import com.android.settingslib.Utils;
 import com.android.settingslib.fuelgauge.BatterySaverUtils;
 import com.android.settingslib.utils.PowerUtil;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.util.NotificationChannels;
+import com.android.systemui.volume.Events;
 
 import java.io.PrintWriter;
 import java.text.NumberFormat;
@@ -118,6 +123,7 @@
     private final Context mContext;
     private final NotificationManager mNoMan;
     private final PowerManager mPowerMan;
+    private final KeyguardManager mKeyguard;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final Receiver mReceiver = new Receiver();
     private final Intent mOpenBatterySettings = settings(Intent.ACTION_POWER_USAGE_SUMMARY);
@@ -141,6 +147,7 @@
     private boolean mHighTempWarning;
     private SystemUIDialog mHighTempDialog;
     private SystemUIDialog mThermalShutdownDialog;
+    @VisibleForTesting SystemUIDialog mUsbHighTempDialog;
 
     /**
      */
@@ -149,6 +156,7 @@
         mContext = context;
         mNoMan = mContext.getSystemService(NotificationManager.class);
         mPowerMan = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mKeyguard = mContext.getSystemService(KeyguardManager.class);
         mReceiver.init();
     }
 
@@ -165,6 +173,8 @@
         pw.print("mHighTempDialog="); pw.println(mHighTempDialog != null ? "not null" : null);
         pw.print("mThermalShutdownDialog=");
         pw.println(mThermalShutdownDialog != null ? "not null" : null);
+        pw.print("mUsbHighTempDialog=");
+        pw.println(mUsbHighTempDialog != null ? "not null" : null);
     }
 
     private int getLowBatteryAutoTriggerDefaultLevel() {
@@ -434,6 +444,53 @@
     }
 
     @Override
+    public void showUsbHighTemperatureAlarm() {
+        mHandler.post(() -> showUsbHighTemperatureAlarmInternal());
+    }
+
+    private void showUsbHighTemperatureAlarmInternal() {
+        if (mUsbHighTempDialog != null) {
+            return;
+        }
+
+        final SystemUIDialog d = new SystemUIDialog(mContext, R.style.Theme_SystemUI_Dialog_Alert);
+        d.setCancelable(false);
+        d.setIconAttribute(android.R.attr.alertDialogIcon);
+        d.setTitle(R.string.high_temp_alarm_title);
+        d.setShowForAllUsers(true);
+        d.setMessage(mContext.getString(R.string.high_temp_alarm_notify_message, ""));
+        d.setPositiveButton((com.android.internal.R.string.ok),
+                (dialogInterface, which) -> mUsbHighTempDialog = null);
+        d.setNegativeButton((R.string.high_temp_alarm_help_care_steps),
+                (dialogInterface, which) -> {
+                    final String contextString = mContext.getString(
+                            R.string.high_temp_alarm_help_url);
+                    final Intent helpIntent = new Intent();
+                    helpIntent.setClassName("com.android.settings",
+                            "com.android.settings.HelpTrampoline");
+                    helpIntent.putExtra(Intent.EXTRA_TEXT, contextString);
+                    Dependency.get(ActivityStarter.class).startActivity(helpIntent,
+                            true /* dismissShade */, resultCode -> {
+                                mUsbHighTempDialog = null;
+                            });
+                });
+        d.setOnDismissListener(dialogInterface -> {
+            mUsbHighTempDialog = null;
+            Events.writeEvent(mContext, Events.EVENT_DISMISS_USB_OVERHEAT_ALARM,
+                    Events.DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED,
+                    mKeyguard.isKeyguardLocked());
+        });
+        d.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        d.show();
+        mUsbHighTempDialog = d;
+
+        Events.writeEvent(mContext, Events.EVENT_SHOW_USB_OVERHEAT_ALARM,
+                Events.SHOW_REASON_USB_OVERHEAD_ALARM_CHANGED,
+                mKeyguard.isKeyguardLocked());
+    }
+
+    @Override
     public void updateLowBatteryWarning() {
         updateNotification();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index c43f572..e27c25e 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -27,7 +27,6 @@
 import android.database.ContentObserver;
 import android.os.BatteryManager;
 import android.os.Handler;
-import android.os.HardwarePropertiesManager;
 import android.os.IBinder;
 import android.os.IThermalEventListener;
 import android.os.IThermalService;
@@ -43,7 +42,6 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
@@ -71,7 +69,6 @@
     final Receiver mReceiver = new Receiver();
 
     private PowerManager mPowerManager;
-    private HardwarePropertiesManager mHardwarePropertiesManager;
     private WarningsUI mWarnings;
     private final Configuration mLastConfiguration = new Configuration();
     private long mTimeRemaining = Long.MAX_VALUE;
@@ -82,30 +79,21 @@
     private boolean mLowWarningShownThisChargeCycle;
     private boolean mSevereWarningShownThisChargeCycle;
     private Future mLastShowWarningTask;
+    private boolean mEnableSkinTemperatureWarning;
+    private boolean mEnableUsbTemperatureAlarm;
 
     private int mLowBatteryAlertCloseLevel;
     private final int[] mLowBatteryReminderLevels = new int[2];
 
     private long mScreenOffTime = -1;
 
-    private float mThresholdTemp;
-    private float[] mRecentTemps = new float[MAX_RECENT_TEMPS];
-    private int mNumTemps;
-    private long mNextLogTime;
     @VisibleForTesting IThermalService mThermalService;
 
     @VisibleForTesting int mBatteryLevel = 100;
     @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
 
-    // by using the same instance (method references are not guaranteed to be the same object
-    // We create a method reference here so that we are guaranteed that we can remove a callback
-    // each time they are created).
-    private final Runnable mUpdateTempCallback = this::updateTemperatureWarning;
-
     public void start() {
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        mHardwarePropertiesManager = (HardwarePropertiesManager)
-                mContext.getSystemService(Context.HARDWARE_PROPERTIES_SERVICE);
         mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
         mWarnings = Dependency.get(WarningsUI.class);
         mEnhancedEstimates = Dependency.get(EnhancedEstimates.class);
@@ -128,7 +116,7 @@
         // to the temperature being too high.
         showThermalShutdownDialog();
 
-        initTemperatureWarning();
+        initTemperature();
     }
 
     @Override
@@ -137,7 +125,7 @@
 
         // Safe to modify mLastConfiguration here as it's only updated by the main thread (here).
         if ((mLastConfiguration.updateFrom(newConfig) & mask) != 0) {
-            mHandler.post(this::initTemperatureWarning);
+            mHandler.post(this::initTemperature);
         }
     }
 
@@ -383,30 +371,16 @@
         return canShowWarning || canShowSevereWarning;
     }
 
-    private void initTemperatureWarning() {
+    private void initTemperature() {
         ContentResolver resolver = mContext.getContentResolver();
         Resources resources = mContext.getResources();
-        if (Settings.Global.getInt(resolver, Settings.Global.SHOW_TEMPERATURE_WARNING,
-                resources.getInteger(R.integer.config_showTemperatureWarning)) == 0) {
-            return;
-        }
 
-        mThresholdTemp = Settings.Global.getFloat(resolver, Settings.Global.WARNING_TEMPERATURE,
-                resources.getInteger(R.integer.config_warningTemperature));
-
-        if (mThresholdTemp < 0f) {
-            // Get the shutdown temperature, adjust for warning tolerance.
-            float[] throttlingTemps = mHardwarePropertiesManager.getDeviceTemperatures(
-                    HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
-                    HardwarePropertiesManager.TEMPERATURE_SHUTDOWN);
-            if (throttlingTemps == null
-                    || throttlingTemps.length == 0
-                    || throttlingTemps[0] == HardwarePropertiesManager.UNDEFINED_TEMPERATURE) {
-                return;
-            }
-            mThresholdTemp = throttlingTemps[0] -
-                    resources.getInteger(R.integer.config_warningTemperatureTolerance);
-        }
+        mEnableSkinTemperatureWarning = Settings.Global.getInt(resolver,
+                Settings.Global.SHOW_TEMPERATURE_WARNING,
+                resources.getInteger(R.integer.config_showTemperatureWarning)) != 0;
+        mEnableUsbTemperatureAlarm = Settings.Global.getInt(resolver,
+                Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
+                resources.getInteger(R.integer.config_showUsbPortAlarm)) != 0;
 
         if (mThermalService == null) {
             // Enable push notifications of throttling from vendor thermal
@@ -416,21 +390,27 @@
 
             if (b != null) {
                 mThermalService = IThermalService.Stub.asInterface(b);
-                try {
-                    mThermalService.registerThermalEventListenerWithType(
-                            new ThermalEventListener(), Temperature.TYPE_SKIN);
-                } catch (RemoteException e) {
-                    // Should never happen.
-                }
+                registerThermalEventListener();
             } else {
                 Slog.w(TAG, "cannot find thermalservice, no throttling push notifications");
             }
         }
+    }
 
-        setNextLogTime();
-
-        // We have passed all of the checks, start checking the temp
-        mHandler.post(mUpdateTempCallback);
+    @VisibleForTesting
+    void registerThermalEventListener() {
+        try {
+            if (mEnableSkinTemperatureWarning) {
+                mThermalService.registerThermalEventListenerWithType(
+                        new ThermalEventSkinListener(), Temperature.TYPE_SKIN);
+            }
+            if (mEnableUsbTemperatureAlarm) {
+                mThermalService.registerThermalEventListenerWithType(
+                        new ThermalEventUsbListener(), Temperature.TYPE_USB_PORT);
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to register thermal callback.", e);
+        }
     }
 
     private void showThermalShutdownDialog() {
@@ -440,81 +420,6 @@
         }
     }
 
-    @VisibleForTesting
-    protected void updateTemperatureWarning() {
-        float[] temps = mHardwarePropertiesManager.getDeviceTemperatures(
-                HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
-                HardwarePropertiesManager.TEMPERATURE_CURRENT);
-        if (temps.length != 0) {
-            float temp = temps[0];
-            mRecentTemps[mNumTemps++] = temp;
-
-            StatusBar statusBar = getComponent(StatusBar.class);
-            if (statusBar != null && !statusBar.isDeviceInVrMode()
-                    && temp >= mThresholdTemp) {
-                logAtTemperatureThreshold(temp);
-                mWarnings.showHighTemperatureWarning();
-            } else {
-                mWarnings.dismissHighTemperatureWarning();
-            }
-        }
-
-        logTemperatureStats();
-
-        // Remove any pending callbacks as we only want to enable one
-        mHandler.removeCallbacks(mUpdateTempCallback);
-        mHandler.postDelayed(mUpdateTempCallback, TEMPERATURE_INTERVAL);
-    }
-
-    private void logAtTemperatureThreshold(float temp) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("currentTemp=").append(temp)
-                .append(",thresholdTemp=").append(mThresholdTemp)
-                .append(",batteryStatus=").append(mBatteryStatus)
-                .append(",recentTemps=");
-        for (int i = 0; i < mNumTemps; i++) {
-            sb.append(mRecentTemps[i]).append(',');
-        }
-        Slog.i(TAG, sb.toString());
-    }
-
-    /**
-     * Calculates and logs min, max, and average
-     * {@link HardwarePropertiesManager#DEVICE_TEMPERATURE_SKIN} over the past
-     * {@link #TEMPERATURE_LOGGING_INTERVAL}.
-     */
-    private void logTemperatureStats() {
-        if (mNextLogTime > System.currentTimeMillis() && mNumTemps != MAX_RECENT_TEMPS) {
-            return;
-        }
-
-        if (mNumTemps > 0) {
-            float sum = mRecentTemps[0], min = mRecentTemps[0], max = mRecentTemps[0];
-            for (int i = 1; i < mNumTemps; i++) {
-                float temp = mRecentTemps[i];
-                sum += temp;
-                if (temp > max) {
-                    max = temp;
-                }
-                if (temp < min) {
-                    min = temp;
-                }
-            }
-
-            float avg = sum / mNumTemps;
-            Slog.i(TAG, "avg=" + avg + ",min=" + min + ",max=" + max);
-            MetricsLogger.histogram(mContext, "device_skin_temp_avg", (int) avg);
-            MetricsLogger.histogram(mContext, "device_skin_temp_min", (int) min);
-            MetricsLogger.histogram(mContext, "device_skin_temp_max", (int) max);
-        }
-        setNextLogTime();
-        mNumTemps = 0;
-    }
-
-    private void setNextLogTime() {
-        mNextLogTime = System.currentTimeMillis() + TEMPERATURE_LOGGING_INTERVAL;
-    }
-
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.print("mLowBatteryAlertCloseLevel=");
         pw.println(mLowBatteryAlertCloseLevel);
@@ -541,34 +446,80 @@
                 Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
         pw.print("bucket: ");
         pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
-        pw.print("mThresholdTemp=");
-        pw.println(Float.toString(mThresholdTemp));
-        pw.print("mNextLogTime=");
-        pw.println(Long.toString(mNextLogTime));
+        pw.print("mEnableSkinTemperatureWarning=");
+        pw.println(mEnableSkinTemperatureWarning);
+        pw.print("mEnableUsbTemperatureAlarm=");
+        pw.println(mEnableUsbTemperatureAlarm);
         mWarnings.dump(pw);
     }
 
     public interface WarningsUI {
         void update(int batteryLevel, int bucket, long screenOffTime);
+
         void updateEstimate(Estimate estimate);
+
         void updateThresholds(long lowThreshold, long severeThreshold);
+
         void dismissLowBatteryWarning();
+
         void showLowBatteryWarning(boolean playSound);
+
         void dismissInvalidChargerWarning();
+
         void showInvalidChargerWarning();
+
         void updateLowBatteryWarning();
+
         boolean isInvalidChargerWarningShowing();
+
         void dismissHighTemperatureWarning();
+
         void showHighTemperatureWarning();
+
+        /**
+         * Display USB overheat alarm
+         */
+        void showUsbHighTemperatureAlarm();
+
         void showThermalShutdownWarning();
+
         void dump(PrintWriter pw);
+
         void userSwitched();
     }
 
-    // Thermal event received from vendor thermal management subsystem
-    private final class ThermalEventListener extends IThermalEventListener.Stub {
+    // Thermal event received from thermal service manager subsystem
+    @VisibleForTesting
+    final class ThermalEventSkinListener extends IThermalEventListener.Stub {
         @Override public void notifyThrottling(Temperature temp) {
-            mHandler.post(mUpdateTempCallback);
+            int status = temp.getStatus();
+
+            if (status >= Temperature.THROTTLING_EMERGENCY) {
+                StatusBar statusBar = getComponent(StatusBar.class);
+                if (statusBar != null && !statusBar.isDeviceInVrMode()) {
+                    mWarnings.showHighTemperatureWarning();
+                    Slog.d(TAG, "ThermalEventSkinListener: notifyThrottling was called "
+                            + ", current skin status = " + status
+                            + ", temperature = " + temp.getValue());
+                }
+            } else {
+                mWarnings.dismissHighTemperatureWarning();
+            }
+        }
+    }
+
+    // Thermal event received from thermal service manager subsystem
+    @VisibleForTesting
+    final class ThermalEventUsbListener extends IThermalEventListener.Stub {
+        @Override public void notifyThrottling(Temperature temp) {
+            int status = temp.getStatus();
+
+            if (status >= Temperature.THROTTLING_EMERGENCY) {
+                mWarnings.showUsbHighTemperatureAlarm();
+                Slog.d(TAG, "ThermalEventUsbListener: notifyThrottling was called "
+                        + ", current usb port status = " + status
+                        + ", temperature = " + temp.getValue());
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 64ad95c..c209b31 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -38,7 +38,6 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QS;
@@ -103,10 +102,6 @@
         mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
                 mContext.getString(com.android.internal.R.string.reset));
         mToolbar.setTitle(R.string.qs_edit);
-        int accentColor = Utils.getColorAttrDefaultColor(context, android.R.attr.colorAccent);
-        mToolbar.setTitleTextColor(accentColor);
-        mToolbar.getNavigationIcon().setTint(accentColor);
-        mToolbar.getOverflowIcon().setTint(accentColor);
         mRecyclerView = findViewById(android.R.id.list);
         mTransparentView = findViewById(R.id.customizer_transparent_view);
         mTileAdapter = new TileAdapter(getContext());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index a29e93a..608f646 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -19,9 +19,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.res.TypedArray;
 import android.graphics.Canvas;
-import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -505,13 +504,10 @@
     };
 
     private class TileItemDecoration extends ItemDecoration {
-        private final ColorDrawable mDrawable;
+        private final Drawable mDrawable;
 
         private TileItemDecoration(Context context) {
-            TypedArray ta =
-                    context.obtainStyledAttributes(new int[]{android.R.attr.colorSecondary});
-            mDrawable = new ColorDrawable(ta.getColor(0, 0));
-            ta.recycle();
+            mDrawable = context.getDrawable(R.drawable.qs_customize_tile_decoration);
         }
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 52a8814..3e40cfc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -22,6 +22,7 @@
 import android.content.res.Resources;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -208,6 +209,9 @@
             if (wifiConnected) {
                 minimalContentDescription.append(cb.wifiSignalContentDescription).append(",");
                 minimalContentDescription.append(removeDoubleQuotes(cb.ssid));
+                if (!TextUtils.isEmpty(state.secondaryLabel)) {
+                    minimalContentDescription.append(",").append(state.secondaryLabel);
+                }
             }
         }
         state.contentDescription = minimalContentDescription.toString();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
index 22d1d5b..d427260 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -32,6 +32,8 @@
  */
 public class FlingAnimationUtils {
 
+    private static final String TAG = "FlingAnimationUtils";
+
     private static final float LINEAR_OUT_SLOW_IN_X2 = 0.35f;
     private static final float LINEAR_OUT_SLOW_IN_X2_MAX = 0.68f;
     private static final float LINEAR_OUT_FASTER_IN_X2 = 0.5f;
@@ -195,6 +197,10 @@
     }
 
     private Interpolator getInterpolator(float startGradient, float velocityFactor) {
+        if (Float.isNaN(velocityFactor)) {
+            Log.e(TAG, "Invalid velocity factor", new Throwable());
+            return Interpolators.LINEAR_OUT_SLOW_IN;
+        }
         if (startGradient != mCachedStartGradient
                 || velocityFactor != mCachedVelocityFactor) {
             float speedup = mSpeedUpFactor * (1.0f - velocityFactor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index c945afd..f34b912 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -15,7 +15,6 @@
  */
 package com.android.systemui.statusbar;
 
-import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
@@ -27,8 +26,7 @@
  * want to perform some action before doing so).
  */
 public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener,
-        ActivatableNotificationView.OnActivatedListener,
-        NotificationRowBinder.BindRowCallback {
+        ActivatableNotificationView.OnActivatedListener {
     /**
      * Returns true if the presenter is not visible. For example, it may not be necessary to do
      * animations if this returns true.
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 3fbc641..4ed9ae4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -35,6 +35,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
@@ -124,16 +125,7 @@
         return mRemoteInputManager;
     }
 
-    private NotificationRowBinder getRowBinder() {
-        if (mNotificationRowBinder == null) {
-            mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
-        }
-        return mNotificationRowBinder;
-    }
-
-    // TODO: Remove this once we can always use a mocked row binder in our tests
-    @VisibleForTesting
-    void setRowBinder(NotificationRowBinder notificationRowBinder) {
+    public void setRowBinder(NotificationRowBinder notificationRowBinder) {
         mNotificationRowBinder = notificationRowBinder;
     }
 
@@ -345,7 +337,7 @@
 
         Dependency.get(LeakDetector.class).trackInstance(entry);
         // Construct the expanded view.
-        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification));
+        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification));
 
         abortExistingInflation(key);
 
@@ -386,7 +378,7 @@
             listener.onPreEntryUpdated(entry);
         }
 
-        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification));
+        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification));
         updateNotifications();
 
         if (DEBUG) {
@@ -440,7 +432,7 @@
 
         // By comparing the old and new UI adjustments, reinflate the view accordingly.
         for (NotificationEntry entry : entries) {
-            getRowBinder().onNotificationRankingUpdated(
+            requireBinder().onNotificationRankingUpdated(
                     entry,
                     oldImportances.get(entry.key),
                     oldAdjustments.get(entry.key),
@@ -486,4 +478,12 @@
             activeExtender.setShouldManageLifetime(entry, false);
         }
     }
+
+    private NotificationRowBinder requireBinder() {
+        if (mNotificationRowBinder == null) {
+            throw new RuntimeException("You must initialize NotificationEntryManager by calling"
+                    + "setRowBinder() before using.");
+        }
+        return mNotificationRowBinder;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinder.java
new file mode 100644
index 0000000..7504e86
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinder.java
@@ -0,0 +1,52 @@
+/*
+ * 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.Nullable;
+
+import com.android.systemui.statusbar.NotificationUiAdjustment;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+
+/**
+ * Used by the {@link NotificationEntryManager}. When notifications are added or updated, the binder
+ * is asked to (re)inflate and prepare their views. This inflation must occur off the main thread.
+ */
+public interface NotificationRowBinder {
+    /**
+     * Called when a notification has been added or updated. The binder must asynchronously inflate
+     * and bind the views associated with the notification.
+     *
+     * TODO: The caller is notified when the inflation completes, but this is currently a very
+     * roundabout business. Add an explicit completion/failure callback to this method.
+     */
+    void inflateViews(
+            NotificationEntry entry,
+            Runnable onDismissRunnable)
+            throws InflationException;
+
+    /**
+     * Called when the ranking has been updated (but not add or remove has been done). The binder
+     * should inspect the old and new adjustments and re-inflate the entry's views if necessary
+     * (e.g. if something important changed).
+     */
+    void onNotificationRankingUpdated(
+            NotificationEntry entry,
+            @Nullable Integer oldImportance,
+            NotificationUiAdjustment oldAdjustment,
+            NotificationUiAdjustment newAdjustment);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
index 6f5baf9..b91cdaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.collection;
 
 import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
 import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
 import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
@@ -39,7 +38,9 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationUiAdjustment;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.NotificationClicker;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
@@ -50,13 +51,8 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-
 /** Handles inflating and updating views for notifications. */
-@Singleton
-public class NotificationRowBinder {
+public class NotificationRowBinderImpl implements NotificationRowBinder {
 
     private static final String TAG = "NotificationViewManager";
 
@@ -84,9 +80,7 @@
     private NotificationClicker mNotificationClicker;
     private final NotificationLogger mNotificationLogger = Dependency.get(NotificationLogger.class);
 
-    @Inject
-    public NotificationRowBinder(Context context,
-            @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress) {
+    public NotificationRowBinderImpl(Context context, boolean allowLongPress) {
         mContext = context;
         mMessagingUtil = new NotificationMessagingUtil(context);
         mAllowLongPress = allowLongPress;
@@ -122,6 +116,7 @@
     /**
      * Inflates the views for the given entry (possibly asynchronously).
      */
+    @Override
     public void inflateViews(
             NotificationEntry entry,
             Runnable onDismissRunnable)
@@ -192,6 +187,7 @@
      * Updates the views bound to an entry when the entry's ranking changes, either in-place or by
      * reinflating them.
      */
+    @Override
     public void onNotificationRankingUpdated(
             NotificationEntry entry,
             @Nullable Integer oldImportance,
@@ -264,7 +260,7 @@
     }
 
     private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
-         mNotificationLogger.onExpansionChanged(key, userAction, expanded);
+        mNotificationLogger.onExpansionChanged(key, userAction, expanded);
     }
 
     /** Callback for when a row is bound to an entry. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
index cf3f89e..409d60f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
@@ -135,7 +135,7 @@
         final Bitmap hardBitmap = SurfaceControl
                 .screenshot(new Rect(), mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
                         mNavigationBarView.getContext().getDisplay().getRotation());
-        if (cropRect.bottom <= hardBitmap.getHeight()
+        if (hardBitmap != null && cropRect.bottom <= hardBitmap.getHeight()
                 && cropRect.left + cropRect.width() <= hardBitmap.getWidth()) {
             final Bitmap cropBitmap = Bitmap.createBitmap(hardBitmap, cropRect.left, cropRect.top,
                     cropRect.width(), cropRect.height());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index c9fa89e..faa2ab1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -23,18 +23,15 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
-import android.view.Display;
-import android.view.Display.Mode;
 import android.view.Gravity;
 import android.view.LayoutInflater;
-import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.WindowManager;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.Space;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.PluginListener;
@@ -84,21 +81,21 @@
     private static final String WEIGHT_CENTERED_SUFFIX = "WC";
 
     private final List<NavBarButtonProvider> mPlugins = new ArrayList<>();
-    private final Display mDisplay;
 
     protected LayoutInflater mLayoutInflater;
     protected LayoutInflater mLandscapeInflater;
 
-    protected FrameLayout mRot0;
-    protected FrameLayout mRot90;
-    private boolean isRot0Landscape;
+    protected FrameLayout mHorizontal;
+    protected FrameLayout mVertical;
 
-    private SparseArray<ButtonDispatcher> mButtonDispatchers;
+    @VisibleForTesting
+    SparseArray<ButtonDispatcher> mButtonDispatchers;
     private String mCurrentLayout;
 
     private View mLastPortrait;
     private View mLastLandscape;
 
+    private boolean mIsVertical;
     private boolean mAlternativeOrder;
     private boolean mUsingCustomLayout;
 
@@ -107,14 +104,11 @@
     public NavigationBarInflaterView(Context context, AttributeSet attrs) {
         super(context, attrs);
         createInflaters();
-        mDisplay = ((WindowManager)
-                context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
-        Mode displayMode = mDisplay.getMode();
-        isRot0Landscape = displayMode.getPhysicalWidth() > displayMode.getPhysicalHeight();
         mOverviewProxyService = Dependency.get(OverviewProxyService.class);
     }
 
-    private void createInflaters() {
+    @VisibleForTesting
+    void createInflaters() {
         mLayoutInflater = LayoutInflater.from(mContext);
         Configuration landscape = new Configuration();
         landscape.setTo(mContext.getResources().getConfiguration());
@@ -132,13 +126,12 @@
 
     private void inflateChildren() {
         removeAllViews();
-        mRot0 = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout, this, false);
-        mRot0.setId(R.id.rot0);
-        addView(mRot0);
-        mRot90 = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout_rot90, this,
-                false);
-        mRot90.setId(R.id.rot90);
-        addView(mRot90);
+        mHorizontal = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout,
+                this /* root */, false /* attachToRoot */);
+        addView(mHorizontal);
+        mVertical = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout_vertical,
+                this /* root */, false /* attachToRoot */);
+        addView(mVertical);
         updateAlternativeOrder();
     }
 
@@ -198,12 +191,9 @@
         }
     }
 
-    public void updateButtonDispatchersCurrentView() {
+    void updateButtonDispatchersCurrentView() {
         if (mButtonDispatchers != null) {
-            final int rotation = mDisplay.getRotation();
-            final boolean portrait = rotation == Surface.ROTATION_0
-                    || rotation == Surface.ROTATION_180;
-            final View view = portrait ? mRot0 : mRot90;
+            View view = mIsVertical ? mVertical : mHorizontal;
             for (int i = 0; i < mButtonDispatchers.size(); i++) {
                 final ButtonDispatcher dispatcher = mButtonDispatchers.valueAt(i);
                 dispatcher.setCurrentView(view);
@@ -211,7 +201,13 @@
         }
     }
 
-    public void setAlternativeOrder(boolean alternativeOrder) {
+    void setVertical(boolean vertical) {
+        if (vertical != mIsVertical) {
+            mIsVertical = vertical;
+        }
+    }
+
+    void setAlternativeOrder(boolean alternativeOrder) {
         if (alternativeOrder != mAlternativeOrder) {
             mAlternativeOrder = alternativeOrder;
             updateAlternativeOrder();
@@ -219,10 +215,10 @@
     }
 
     private void updateAlternativeOrder() {
-        updateAlternativeOrder(mRot0.findViewById(R.id.ends_group));
-        updateAlternativeOrder(mRot0.findViewById(R.id.center_group));
-        updateAlternativeOrder(mRot90.findViewById(R.id.ends_group));
-        updateAlternativeOrder(mRot90.findViewById(R.id.center_group));
+        updateAlternativeOrder(mHorizontal.findViewById(R.id.ends_group));
+        updateAlternativeOrder(mHorizontal.findViewById(R.id.center_group));
+        updateAlternativeOrder(mVertical.findViewById(R.id.ends_group));
+        updateAlternativeOrder(mVertical.findViewById(R.id.center_group));
     }
 
     private void updateAlternativeOrder(View v) {
@@ -232,10 +228,10 @@
     }
 
     private void initiallyFill(ButtonDispatcher buttonDispatcher) {
-        addAll(buttonDispatcher, (ViewGroup) mRot0.findViewById(R.id.ends_group));
-        addAll(buttonDispatcher, (ViewGroup) mRot0.findViewById(R.id.center_group));
-        addAll(buttonDispatcher, (ViewGroup) mRot90.findViewById(R.id.ends_group));
-        addAll(buttonDispatcher, (ViewGroup) mRot90.findViewById(R.id.center_group));
+        addAll(buttonDispatcher, mHorizontal.findViewById(R.id.ends_group));
+        addAll(buttonDispatcher, mHorizontal.findViewById(R.id.center_group));
+        addAll(buttonDispatcher, mVertical.findViewById(R.id.ends_group));
+        addAll(buttonDispatcher, mVertical.findViewById(R.id.center_group));
     }
 
     private void addAll(ButtonDispatcher buttonDispatcher, ViewGroup parent) {
@@ -267,17 +263,23 @@
         String[] center = sets[1].split(BUTTON_SEPARATOR);
         String[] end = sets[2].split(BUTTON_SEPARATOR);
         // Inflate these in start to end order or accessibility traversal will be messed up.
-        inflateButtons(start, mRot0.findViewById(R.id.ends_group), isRot0Landscape, true);
-        inflateButtons(start, mRot90.findViewById(R.id.ends_group), !isRot0Landscape, true);
+        inflateButtons(start, mHorizontal.findViewById(R.id.ends_group),
+                false /* landscape */, true /* start */);
+        inflateButtons(start, mVertical.findViewById(R.id.ends_group),
+                true /* landscape */, true /* start */);
 
-        inflateButtons(center, mRot0.findViewById(R.id.center_group), isRot0Landscape, false);
-        inflateButtons(center, mRot90.findViewById(R.id.center_group), !isRot0Landscape, false);
+        inflateButtons(center, mHorizontal.findViewById(R.id.center_group),
+                false /* landscape */, false /* start */);
+        inflateButtons(center, mVertical.findViewById(R.id.center_group),
+                true /* landscape */, false /* start */);
 
-        addGravitySpacer(mRot0.findViewById(R.id.ends_group));
-        addGravitySpacer(mRot90.findViewById(R.id.ends_group));
+        addGravitySpacer(mHorizontal.findViewById(R.id.ends_group));
+        addGravitySpacer(mVertical.findViewById(R.id.ends_group));
 
-        inflateButtons(end, mRot0.findViewById(R.id.ends_group), isRot0Landscape, false);
-        inflateButtons(end, mRot90.findViewById(R.id.ends_group), !isRot0Landscape, false);
+        inflateButtons(end, mHorizontal.findViewById(R.id.ends_group),
+                false /* landscape */, false /* start */);
+        inflateButtons(end, mVertical.findViewById(R.id.ends_group),
+                true /* landscape */, false /* start */);
 
         updateButtonDispatchersCurrentView();
     }
@@ -472,8 +474,8 @@
                 mButtonDispatchers.valueAt(i).clear();
             }
         }
-        clearAllChildren(mRot0.findViewById(R.id.nav_buttons));
-        clearAllChildren(mRot90.findViewById(R.id.nav_buttons));
+        clearAllChildren(mHorizontal.findViewById(R.id.nav_buttons));
+        clearAllChildren(mVertical.findViewById(R.id.nav_buttons));
     }
 
     private void clearAllChildren(ViewGroup group) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index d6d3d08..f82b05e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -116,9 +116,11 @@
     final static boolean ALTERNATE_CAR_MODE_UI = false;
 
     View mCurrentView = null;
-    View[] mRotatedViews = new View[4];
+    private View mVertical;
+    private View mHorizontal;
 
-    boolean mVertical;
+    /** Indicates that navigation bar is vertical. */
+    private boolean mIsVertical;
     private int mCurrentRotation = -1;
 
     boolean mLongClickableAccessibilityButton;
@@ -350,7 +352,7 @@
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        mVertical = false;
+        mIsVertical = false;
         mLongClickableAccessibilityButton = false;
 
         // Set up the context group of buttons
@@ -471,7 +473,7 @@
 
     public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
         mOnVerticalChangedListener = onVerticalChangedListener;
-        notifyVerticalChangedListener(mVertical);
+        notifyVerticalChangedListener(mIsVertical);
     }
 
     @Override
@@ -547,10 +549,6 @@
         return mCurrentView;
     }
 
-    public View[] getAllViews() {
-        return mRotatedViews;
-    }
-
     public ButtonDispatcher getRecentsButton() {
         return mButtonDispatchers.get(R.id.recent_apps);
     }
@@ -657,7 +655,7 @@
 
         // Animate the back button's rotation to the new degrees and only in portrait move up the
         // back button to line up with the other buttons
-        float targetY = !mOverviewProxyService.shouldShowSwipeUpUI() && !mVertical && useAltBack
+        float targetY = !mOverviewProxyService.shouldShowSwipeUpUI() && !mIsVertical && useAltBack
                 ? - getResources().getDimension(R.dimen.navbar_back_button_ime_offset)
                 : 0;
         ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable,
@@ -669,7 +667,7 @@
     }
 
     private void orientHomeButton(KeyButtonDrawable drawable) {
-        drawable.setRotation(mVertical ? 90 : 0);
+        drawable.setRotation(mIsVertical ? 90 : 0);
     }
 
     private KeyButtonDrawable chooseNavigationIconDrawable(@DrawableRes int icon,
@@ -964,7 +962,7 @@
         getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
 
         DockedStackExistsListener.register(mDockedListener);
-        updateRotatedViews();
+        updateOrientationViews();
         reloadNavIcons();
     }
 
@@ -1028,34 +1026,35 @@
         view.setTranslationY(posY);
     }
 
-    private void updateRotatedViews() {
-        mRotatedViews[Surface.ROTATION_0] =
-                mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
-        mRotatedViews[Surface.ROTATION_270] =
-                mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
+    private void updateOrientationViews() {
+        mHorizontal = findViewById(R.id.horizontal);
+        mVertical = findViewById(R.id.vertical);
 
         updateCurrentView();
     }
 
-    public boolean needsReorient(int rotation) {
+    boolean needsReorient(int rotation) {
         return mCurrentRotation != rotation;
     }
 
     private void updateCurrentView() {
-        final int rot = getContextDisplay().getRotation();
-        for (int i=0; i<4; i++) {
-            mRotatedViews[i].setVisibility(View.GONE);
-        }
-        mCurrentView = mRotatedViews[rot];
+        resetViews();
+        mCurrentView = mIsVertical ? mVertical : mHorizontal;
         mCurrentView.setVisibility(View.VISIBLE);
-        mNavigationInflaterView.setAlternativeOrder(rot == Surface.ROTATION_90);
+        mNavigationInflaterView.setVertical(mIsVertical);
+        mCurrentRotation = getContextDisplay().getRotation();
+        mNavigationInflaterView.setAlternativeOrder(mCurrentRotation == Surface.ROTATION_90);
         mNavigationInflaterView.updateButtonDispatchersCurrentView();
         updateLayoutTransitionsEnabled();
-        mCurrentRotation = rot;
+    }
+
+    private void resetViews() {
+        mHorizontal.setVisibility(View.GONE);
+        mVertical.setVisibility(View.GONE);
     }
 
     private void updateRecentsIcon() {
-        mDockedIcon.setRotation(mDockedStackExists && mVertical ? 90 : 0);
+        mDockedIcon.setRotation(mDockedStackExists && mIsVertical ? 90 : 0);
         getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon);
         mBarTransitions.reapplyDarkIntensity();
     }
@@ -1077,7 +1076,7 @@
     }
 
     public boolean isVertical() {
-        return mVertical;
+        return mIsVertical;
     }
 
     public void reorient() {
@@ -1101,7 +1100,7 @@
         updateTaskSwitchHelper();
         updateNavButtonIcons();
 
-        getHomeButton().setVertical(mVertical);
+        getHomeButton().setVertical(mIsVertical);
     }
 
     private void updateTaskSwitchHelper() {
@@ -1134,9 +1133,12 @@
                     "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh));
 
         final boolean newVertical = w > 0 && h > w;
-        if (newVertical != mVertical) {
-            mVertical = newVertical;
-            //Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n"));
+        if (newVertical != mIsVertical) {
+            mIsVertical = newVertical;
+            if (DEBUG) {
+                Log.d(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w,
+                        mIsVertical ? "y" : "n"));
+            }
             reorient();
             notifyVerticalChangedListener(newVertical);
         }
@@ -1347,7 +1349,7 @@
 
         pw.println(String.format("      disabled=0x%08x vertical=%s menu=%s darkIntensity=%.2f",
                         mDisabledFlags,
-                        mVertical ? "true" : "false",
+                        mIsVertical ? "true" : "false",
                         getMenuButton().isVisible() ? "true" : "false",
                         getLightTransitionsController().getCurrentDarkIntensity()));
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 13d4489..41580f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -195,9 +195,9 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationListController;
-import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -390,7 +390,6 @@
     protected NotificationEntryManager mEntryManager;
     private NotificationListController mNotificationListController;
     private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
-    private NotificationRowBinder mNotificationRowBinder;
     protected NotificationViewHierarchyManager mViewHierarchyManager;
     protected ForegroundServiceController mForegroundServiceController;
     protected AppOpsController mAppOpsController;
@@ -620,7 +619,6 @@
         mEntryManager = Dependency.get(NotificationEntryManager.class);
         mNotificationInterruptionStateProvider =
                 Dependency.get(NotificationInterruptionStateProvider.class);
-        mNotificationRowBinder = Dependency.get(NotificationRowBinder.class);
         mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
         mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
         mAppOpsController = Dependency.get(AppOpsController.class);
@@ -1032,10 +1030,15 @@
                 mStatusBarWindow, this, mNotificationPanel,
                 (NotificationListContainer) mStackScroller);
 
+        final NotificationRowBinderImpl rowBinder =
+                new NotificationRowBinderImpl(
+                        mContext,
+                        SystemUIFactory.getInstance().provideAllowNotificationLongPress());
+
         mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
                 mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
                 mScrimController, mActivityLaunchAnimator, mStatusBarKeyguardViewManager,
-                mNotificationAlertingManager);
+                mNotificationAlertingManager, rowBinder);
 
         mNotificationListController =
                 new NotificationListController(
@@ -1051,7 +1054,9 @@
         mNotificationActivityStarter = new StatusBarNotificationActivityStarter(
                 mContext, mNotificationPanel, mPresenter, mHeadsUpManager, mActivityLaunchAnimator);
         mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
-        mNotificationRowBinder.setNotificationClicker(new NotificationClicker(
+
+        mEntryManager.setRowBinder(rowBinder);
+        rowBinder.setNotificationClicker(new NotificationClicker(
                 this, Dependency.get(BubbleController.class), mNotificationActivityStarter));
 
         mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 3ce66c5..6fe8964 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -64,9 +64,9 @@
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.notification.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -79,7 +79,8 @@
 import java.util.ArrayList;
 
 public class StatusBarNotificationPresenter implements NotificationPresenter,
-        ConfigurationController.ConfigurationListener {
+        ConfigurationController.ConfigurationListener,
+        NotificationRowBinderImpl.BindRowCallback {
 
     private final LockscreenGestureLogger mLockscreenGestureLogger =
             Dependency.get(LockscreenGestureLogger.class);
@@ -97,8 +98,6 @@
             (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
     private final NotificationEntryManager mEntryManager =
             Dependency.get(NotificationEntryManager.class);
-    private final NotificationRowBinder mNotificationRowBinder =
-            Dependency.get(NotificationRowBinder.class);
     private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
             Dependency.get(NotificationInterruptionStateProvider.class);
     private final NotificationMediaManager mMediaManager =
@@ -140,7 +139,8 @@
             ScrimController scrimController,
             ActivityLaunchAnimator activityLaunchAnimator,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            NotificationAlertingManager notificationAlertingManager) {
+            NotificationAlertingManager notificationAlertingManager,
+            NotificationRowBinderImpl notificationRowBinder) {
         mContext = context;
         mNotificationPanel = panel;
         mHeadsUpManager = headsUp;
@@ -217,7 +217,7 @@
             mEntryManager.addNotificationLifetimeExtender(mGutsManager);
             mEntryManager.addNotificationLifetimeExtenders(
                     remoteInputManager.getLifetimeExtenders());
-            mNotificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
+            notificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
                     mEntryManager, this);
             mNotificationInterruptionStateProvider.setUpWithPresenter(
                     this, mHeadsUpManager, this::canHeadsUp);
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 ad4ba75..d3ff63a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -34,9 +34,12 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.AttributeSet;
 import android.view.ActionMode;
 import android.view.DisplayCutout;
+import android.view.GestureDetector;
 import android.view.InputDevice;
 import android.view.InputQueue;
 import android.view.KeyEvent;
@@ -53,6 +56,7 @@
 import android.widget.FrameLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.internal.view.FloatingActionMode;
 import com.android.internal.widget.FloatingToolbar;
 import com.android.systemui.Dependency;
@@ -63,16 +67,23 @@
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.tuner.TunerService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+/**
+ * Combined status bar and notification panel view. Also holding backdrop and scrims.
+ */
 public class StatusBarWindowView extends FrameLayout {
     public static final String TAG = "StatusBarWindowView";
     public static final boolean DEBUG = StatusBar.DEBUG;
 
+    private final GestureDetector mGestureDetector;
+    private final StatusBarStateController mStatusBarStateController;
+    private boolean mDoubleTapEnabled;
+    private boolean mSingleTapEnabled;
     private DragDownHelper mDragDownHelper;
-    private DoubleTapHelper mDoubleTapHelper;
     private NotificationStackScrollLayout mStackScrollLayout;
     private NotificationPanelView mNotificationPanel;
     private View mBrightnessMirror;
@@ -95,8 +106,37 @@
     private boolean mTouchActive;
     private boolean mExpandAnimationRunning;
     private boolean mExpandAnimationPending;
-    private final StatusBarStateController
-            mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+
+    private final GestureDetector.SimpleOnGestureListener mGestureListener =
+            new GestureDetector.SimpleOnGestureListener() {
+        @Override
+        public boolean onSingleTapConfirmed(MotionEvent e) {
+            if (mSingleTapEnabled) {
+                mService.wakeUpIfDozing(SystemClock.uptimeMillis(), StatusBarWindowView.this,
+                        "SINGLE_TAP");
+            }
+            return mSingleTapEnabled;
+        }
+
+        @Override
+        public boolean onDoubleTap(MotionEvent e) {
+            if (mDoubleTapEnabled) {
+                mService.wakeUpIfDozing(SystemClock.uptimeMillis(), StatusBarWindowView.this,
+                        "DOUBLE_TAP");
+            }
+            return mDoubleTapEnabled;
+        }
+    };
+    private final TunerService.Tunable mTunable = (key, newValue) -> {
+        AmbientDisplayConfiguration configuration = new AmbientDisplayConfiguration(mContext);
+        switch (key) {
+            case Settings.Secure.DOZE_DOUBLE_TAP_GESTURE:
+                mDoubleTapEnabled = configuration.doubleTapGestureEnabled(UserHandle.USER_CURRENT);
+                break;
+            case Settings.Secure.DOZE_TAP_SCREEN_GESTURE:
+                mSingleTapEnabled = configuration.tapGestureEnabled(UserHandle.USER_CURRENT);
+        }
+    };
 
     /**
      * If set to true, the current gesture started below the notch and we need to dispatch touch
@@ -110,10 +150,11 @@
         mTransparentSrcPaint.setColor(0);
         mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
         mFalsingManager = FalsingManager.getInstance(context);
-        mDoubleTapHelper = new DoubleTapHelper(this, active -> {}, () -> {
-            mService.wakeUpIfDozing(SystemClock.uptimeMillis(), this, "DOUBLE_TAP");
-            return true;
-        }, null, null);
+        mGestureDetector = new GestureDetector(context, mGestureListener);
+        mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+        Dependency.get(TunerService.class).addTunable(mTunable,
+                Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
+                Settings.Secure.DOZE_TAP_SCREEN_GESTURE);
     }
 
     @Override
@@ -306,6 +347,7 @@
             return false;
         }
         mFalsingManager.onTouchEvent(ev, getWidth(), getHeight());
+        mGestureDetector.onTouchEvent(ev);
         if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) {
             // Disallow new pointers while the brightness mirror is visible. This is so that you
             // can't touch anything other than the brightness slider while the mirror is showing
@@ -366,7 +408,6 @@
     public boolean onTouchEvent(MotionEvent ev) {
         boolean handled = false;
         if (mService.isDozing()) {
-            mDoubleTapHelper.onTouchEvent(ev);
             handled = !mService.isPulsing();
         }
         if ((mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !handled)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index ca55e1f..1596ddb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -53,6 +53,8 @@
     public static final int EVENT_TOUCH_LEVEL_DONE = 16;  // (stream|int) (level|bool)
     public static final int EVENT_ZEN_CONFIG_CHANGED = 17; // (allow/disallow|string)
     public static final int EVENT_RINGER_TOGGLE = 18; // (ringer_mode)
+    public static final int EVENT_SHOW_USB_OVERHEAT_ALARM = 19; // (reason|int) (keyguard|bool)
+    public static final int EVENT_DISMISS_USB_OVERHEAT_ALARM = 20; // (reason|int) (keyguard|bool)
 
     private static final String[] EVENT_TAGS = {
             "show_dialog",
@@ -73,7 +75,9 @@
             "mute_changed",
             "touch_level_done",
             "zen_mode_config_changed",
-            "ringer_toggle"
+            "ringer_toggle",
+            "show_usb_overheat_alarm",
+            "dismiss_usb_overheat_alarm"
     };
 
     public static final int DISMISS_REASON_UNKNOWN = 0;
@@ -85,6 +89,7 @@
     public static final int DISMISS_REASON_DONE_CLICKED = 6;
     public static final int DISMISS_STREAM_GONE = 7;
     public static final int DISMISS_REASON_OUTPUT_CHOOSER = 8;
+    public static final int DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED = 9;
     public static final String[] DISMISS_REASONS = {
             "unknown",
             "touch_outside",
@@ -94,16 +99,19 @@
             "settings_clicked",
             "done_clicked",
             "a11y_stream_changed",
-            "output_chooser"
+            "output_chooser",
+            "usb_temperature_below_threshold"
     };
 
     public static final int SHOW_REASON_UNKNOWN = 0;
     public static final int SHOW_REASON_VOLUME_CHANGED = 1;
     public static final int SHOW_REASON_REMOTE_VOLUME_CHANGED = 2;
+    public static final int SHOW_REASON_USB_OVERHEAD_ALARM_CHANGED = 3;
     public static final String[] SHOW_REASONS = {
         "unknown",
         "volume_changed",
-        "remote_volume_changed"
+        "remote_volume_changed",
+        "usb_temperature_above_threshold"
     };
 
     public static final int ICON_STATE_UNKNOWN = 0;
@@ -181,6 +189,19 @@
                 case EVENT_SUPPRESSOR_CHANGED:
                     sb.append(list[0]).append(' ').append(list[1]);
                     break;
+                case EVENT_SHOW_USB_OVERHEAT_ALARM:
+                    MetricsLogger.visible(context, MetricsEvent.POWER_OVERHEAT_ALARM);
+                    MetricsLogger.histogram(context, "show_usb_overheat_alarm",
+                            (Boolean) list[1] ? 1 : 0);
+                    sb.append(SHOW_REASONS[(Integer) list[0]]).append(" keyguard=").append(list[1]);
+                    break;
+                case EVENT_DISMISS_USB_OVERHEAT_ALARM:
+                    MetricsLogger.hidden(context, MetricsEvent.POWER_OVERHEAT_ALARM);
+                    MetricsLogger.histogram(context, "dismiss_usb_overheat_alarm",
+                            (Boolean) list[1] ? 1 : 0);
+                    sb.append(DISMISS_REASONS[(Integer) list[0]])
+                        .append(" keyguard=").append(list[1]);
+                    break;
                 default:
                     sb.append(Arrays.asList(list));
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 617b191..ffd8206 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -25,6 +25,7 @@
 import static android.media.AudioManager.STREAM_MUSIC;
 import static android.media.AudioManager.STREAM_RING;
 import static android.media.AudioManager.STREAM_VOICE_CALL;
+import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 
@@ -431,8 +432,7 @@
         if (mSettingsIcon != null) {
             mSettingsIcon.setOnClickListener(v -> {
                 Events.writeEvent(mContext, Events.EVENT_SETTINGS_CLICK);
-                Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                Intent intent = new Intent(Settings.Panel.ACTION_VOLUME);
                 dismissH(DISMISS_REASON_SETTINGS_CLICKED);
                 Dependency.get(ActivityStarter.class).startActivity(intent,
                         true /* dismissShade */);
@@ -442,6 +442,7 @@
 
     public void initRingerH() {
         if (mRingerIcon != null) {
+            mRingerIcon.setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE);
             mRingerIcon.setOnClickListener(v -> {
                 Prefs.putBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true);
                 final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index e91a7e9..0114075 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -82,6 +83,9 @@
         mTestableLooper = TestableLooper.get(this);
         mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
                 new Handler(mTestableLooper.getLooper()));
+        mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class);
+        mTunerService = mDependency.injectMockDependency(TunerService.class);
+        mFragmentService = mDependency.injectMockDependency(FragmentService.class);
 
         mStatusBar = mock(StatusBar.class);
         mWindowManager = mock(WindowManager.class);
@@ -93,12 +97,9 @@
         when(mWindowManager.getDefaultDisplay()).thenReturn(display);
         mContext.addMockSystemService(WindowManager.class, mWindowManager);
 
-        mFragmentService = mDependency.injectMockDependency(FragmentService.class);
         mFragmentHostManager = mock(FragmentHostManager.class);
         when(mFragmentService.getFragmentHostManager(any())).thenReturn(mFragmentHostManager);
 
-        mTunerService = mDependency.injectMockDependency(TunerService.class);
-
 
         mScreenDecorations = new ScreenDecorations() {
             @Override
@@ -126,8 +127,7 @@
         };
         mScreenDecorations.mContext = mContext;
         mScreenDecorations.mComponents = mContext.getComponents();
-
-        mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class);
+        reset(mTunerService);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
index 3bd582f..b4059c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -40,7 +41,8 @@
 public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestCase {
 
     @Spy
-    private ExpandedAnimationController mExpandedController = new ExpandedAnimationController();
+    private ExpandedAnimationController mExpandedController =
+            new ExpandedAnimationController(new Point(500, 1000) /* displaySize */);
 
     private int mStackOffset;
     private float mBubblePadding;
@@ -167,7 +169,7 @@
             assertEquals(mBubblePadding + (i * (mBubbleSize + mBubblePadding)),
                     mLayout.getChildAt(i).getTranslationX(),
                     2f);
-            assertEquals(mBubblePadding + mCutoutInsetSize,
+            assertEquals(mExpandedController.getExpandedY(),
                     mLayout.getChildAt(i).getTranslationY(), 2f);
 
             if (i < mMaxRenderedBubbles) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
new file mode 100644
index 0000000..066dff2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.doze;
+
+import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AlarmManager;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.SensorManagerPlugin;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.wakelock.WakeLock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Consumer;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class DozeSensorsTest extends SysuiTestCase {
+
+    @Mock
+    private AlarmManager mAlarmManager;
+    @Mock
+    private AsyncSensorManager mSensorManager;
+    @Mock
+    private DozeParameters mDozeParameters;
+    @Mock
+    private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+    @Mock
+    private WakeLock mWakeLock;
+    @Mock
+    private DozeSensors.Callback mCallback;
+    @Mock
+    private Consumer<Boolean> mProxCallback;
+    @Mock
+    private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
+    private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
+    private TestableLooper mTestableLooper;
+    private DozeSensors mDozeSensors;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mTestableLooper = TestableLooper.get(this);
+        when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L);
+        when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+        doAnswer(invocation -> {
+            ((Runnable) invocation.getArgument(0)).run();
+            return null;
+        }).when(mWakeLock).wrap(any(Runnable.class));
+        mDozeSensors = new TestableDozeSensors();
+    }
+
+    @Test
+    public void testSensorDebounce() {
+        mDozeSensors.setListening(true);
+
+        mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class));
+        mTestableLooper.processAllMessages();
+        verify(mCallback).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
+                anyBoolean(), anyFloat(), anyFloat(), eq(null));
+
+        mDozeSensors.requestTemporaryDisable();
+        reset(mCallback);
+        mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class));
+        mTestableLooper.processAllMessages();
+        verify(mCallback, never()).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
+                anyBoolean(), anyFloat(), anyFloat(), eq(null));
+    }
+
+    private class TestableDozeSensors extends DozeSensors {
+
+        TestableDozeSensors() {
+            super(getContext(), mAlarmManager, mSensorManager, mDozeParameters,
+                    mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback,
+                    mAlwaysOnDisplayPolicy);
+            for (TriggerSensor sensor : mSensors) {
+                if (sensor instanceof PluginSensor
+                        && ((PluginSensor) sensor).mPluginSensor.getType()
+                        == TYPE_WAKE_LOCK_SCREEN) {
+                    mWakeLockScreenListener = (PluginSensor) sensor;
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index bf6cc53..cd500b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -16,9 +16,8 @@
 
 package com.android.systemui.power;
 
-import static android.test.MoreAsserts.assertNotEqual;
+import static com.google.common.truth.Truth.assertThat;
 
-import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
@@ -38,7 +37,6 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.NotificationChannels;
 
-import java.util.concurrent.TimeUnit;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -151,4 +149,13 @@
         verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(),
                 eq(SystemMessage.NOTE_THERMAL_SHUTDOWN), any());
     }
+
+    @Test
+    public void testShowUsbHighTemperatureAlarm() {
+        mPowerNotificationWarnings.showUsbHighTemperatureAlarm();
+        waitForIdleSync(mContext.getMainThreadHandler());
+        assertThat(mPowerNotificationWarnings.mUsbHighTempDialog).isNotNull();
+
+        mPowerNotificationWarnings.mUsbHighTempDialog.dismiss();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index c28e74e..0aed63d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -14,14 +14,14 @@
 
 package com.android.systemui.power;
 
-import static android.os.HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN;
-import static android.os.HardwarePropertiesManager.TEMPERATURE_CURRENT;
-import static android.os.HardwarePropertiesManager.TEMPERATURE_SHUTDOWN;
 import static android.provider.Settings.Global.SHOW_TEMPERATURE_WARNING;
+import static android.provider.Settings.Global.SHOW_USB_TEMPERATURE_ALARM;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.anyObject;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -31,9 +31,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.BatteryManager;
-import android.os.HardwarePropertiesManager;
+import android.os.IThermalEventListener;
 import android.os.IThermalService;
 import android.os.PowerManager;
+import android.os.Temperature;
 import android.provider.Settings;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -74,97 +75,104 @@
     private static final int OLD_BATTERY_LEVEL_NINE = 9;
     private static final int OLD_BATTERY_LEVEL_10 = 10;
     private static final long VERY_BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(15);
-    private HardwarePropertiesManager mHardProps;
     private WarningsUI mMockWarnings;
     private PowerUI mPowerUI;
     private EnhancedEstimates mEnhancedEstimates;
     @Mock private PowerManager mPowerManager;
     @Mock private IThermalService mThermalServiceMock;
+    private IThermalEventListener mThermalEventUsbListener;
+    private IThermalEventListener mThermalEventSkinListener;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mMockWarnings = mDependency.injectMockDependency(WarningsUI.class);
         mEnhancedEstimates = mDependency.injectMockDependency(EnhancedEstimates.class);
-        mHardProps = mock(HardwarePropertiesManager.class);
 
         mContext.putComponent(StatusBar.class, mock(StatusBar.class));
-        mContext.addMockSystemService(Context.HARDWARE_PROPERTIES_SERVICE, mHardProps);
         mContext.addMockSystemService(Context.POWER_SERVICE, mPowerManager);
 
         createPowerUi();
+        mThermalEventSkinListener = mPowerUI.new ThermalEventSkinListener();
+        mThermalEventUsbListener = mPowerUI.new ThermalEventUsbListener();
     }
 
     @Test
-    public void testNoConfig_NoWarnings() {
-        setOverThreshold();
-        Settings.Global.putString(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, null);
-        TestableResources resources = mContext.getOrCreateTestableResources();
-        resources.addOverride(R.integer.config_showTemperatureWarning, 0);
-        resources.addOverride(R.integer.config_warningTemperature, 55);
-
+    public void testSkinWarning_throttlingCritical() throws Exception {
         mPowerUI.start();
-        verify(mMockWarnings, never()).showHighTemperatureWarning();
-    }
 
-    @Test
-    public void testConfig_NoWarnings() {
-        setUnderThreshold();
-        Settings.Global.putString(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, null);
-        TestableResources resources = mContext.getOrCreateTestableResources();
-        resources.addOverride(R.integer.config_showTemperatureWarning, 1);
-        resources.addOverride(R.integer.config_warningTemperature, 55);
+        final Temperature temp = getCriticalStatusTemp(Temperature.TYPE_SKIN, "skin1");
+        mThermalEventSkinListener.notifyThrottling(temp);
 
-        mPowerUI.start();
-        verify(mMockWarnings, never()).showHighTemperatureWarning();
-    }
-
-    @Test
-    public void testConfig_Warnings() {
-        setOverThreshold();
-        Settings.Global.putString(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, null);
-        TestableResources resources = mContext.getOrCreateTestableResources();
-        resources.addOverride(R.integer.config_showTemperatureWarning, 1);
-        resources.addOverride(R.integer.config_warningTemperature, 55);
-
-        mPowerUI.start();
-        // Guarantees mHandler has processed all messages.
+        // dismiss skin high temperature warning when throttling status is critical
         TestableLooper.get(this).processAllMessages();
-        verify(mMockWarnings).showHighTemperatureWarning();
+        verify(mMockWarnings, never()).showHighTemperatureWarning();
+        verify(mMockWarnings, times(1)).dismissHighTemperatureWarning();
     }
 
     @Test
-    public void testSettingOverrideConfig() {
-        setOverThreshold();
+    public void testSkinWarning_throttlingEmergency() throws Exception {
+        mPowerUI.start();
+
+        final Temperature temp = getEmergencyStatusTemp(Temperature.TYPE_SKIN, "skin2");
+        mThermalEventSkinListener.notifyThrottling(temp);
+
+        // show skin high temperature warning when throttling status is emergency
+        TestableLooper.get(this).processAllMessages();
+        verify(mMockWarnings, times(1)).showHighTemperatureWarning();
+        verify(mMockWarnings, never()).dismissHighTemperatureWarning();
+    }
+
+    @Test
+    public void testUsbAlarm_throttlingCritical() throws Exception {
+        mPowerUI.start();
+
+        final Temperature temp = getCriticalStatusTemp(Temperature.TYPE_USB_PORT, "usb1");
+        mThermalEventUsbListener.notifyThrottling(temp);
+
+        // not show usb high temperature alarm when throttling status is critical
+        TestableLooper.get(this).processAllMessages();
+        verify(mMockWarnings, never()).showUsbHighTemperatureAlarm();
+    }
+
+    @Test
+    public void testUsbAlarm_throttlingEmergency() throws Exception {
+        mPowerUI.start();
+
+        final Temperature temp = getEmergencyStatusTemp(Temperature.TYPE_USB_PORT, "usb2");
+        mThermalEventUsbListener.notifyThrottling(temp);
+
+        // show usb high temperature alarm when throttling status is emergency
+        TestableLooper.get(this).processAllMessages();
+        verify(mMockWarnings, times(1)).showUsbHighTemperatureAlarm();
+    }
+
+    @Test
+    public void testSettingOverrideConfig_enableSkinTemperatureWarning() throws Exception {
         Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 1);
         TestableResources resources = mContext.getOrCreateTestableResources();
         resources.addOverride(R.integer.config_showTemperatureWarning, 0);
-        resources.addOverride(R.integer.config_warningTemperature, 55);
 
         mPowerUI.start();
-        // Guarantees mHandler has processed all messages.
+        mPowerUI.registerThermalEventListener();
+
         TestableLooper.get(this).processAllMessages();
-        verify(mMockWarnings).showHighTemperatureWarning();
+        verify(mThermalServiceMock, times(1))
+                .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN));
     }
 
     @Test
-    public void testShutdownBasedThreshold() {
-        int tolerance = 2;
-        Settings.Global.putString(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, null);
+    public void testSettingOverrideConfig_enableUsbTemperatureAlarm() throws Exception {
+        Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 1);
         TestableResources resources = mContext.getOrCreateTestableResources();
-        resources.addOverride(R.integer.config_showTemperatureWarning, 1);
-        resources.addOverride(R.integer.config_warningTemperature, -1);
-        resources.addOverride(R.integer.config_warningTemperatureTolerance, tolerance);
-        when(mHardProps.getDeviceTemperatures(DEVICE_TEMPERATURE_SKIN, TEMPERATURE_SHUTDOWN))
-                .thenReturn(new float[] { 55 + tolerance });
+        resources.addOverride(R.integer.config_showUsbPortAlarm, 0);
 
-        setCurrentTemp(54); // Below threshold.
         mPowerUI.start();
-        verify(mMockWarnings, never()).showHighTemperatureWarning();
+        mPowerUI.registerThermalEventListener();
 
-        setCurrentTemp(56); // Above threshold.
-        mPowerUI.updateTemperatureWarning();
-        verify(mMockWarnings).showHighTemperatureWarning();
+        TestableLooper.get(this).processAllMessages();
+        verify(mThermalServiceMock, times(1))
+                .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT));
     }
 
     @Test
@@ -532,17 +540,14 @@
         verify(mEnhancedEstimates, times(2)).getEstimate();
     }
 
-    private void setCurrentTemp(float temp) {
-        when(mHardProps.getDeviceTemperatures(DEVICE_TEMPERATURE_SKIN, TEMPERATURE_CURRENT))
-                .thenReturn(new float[] { temp });
+    private Temperature getEmergencyStatusTemp(int type, String name) {
+        final float value = 65;
+        return new Temperature(value, type, name, Temperature.THROTTLING_EMERGENCY);
     }
 
-    private void setOverThreshold() {
-        setCurrentTemp(50000);
-    }
-
-    private void setUnderThreshold() {
-        setCurrentTemp(5);
+    private Temperature getCriticalStatusTemp(int type, String name) {
+        final float value = 60;
+        return new Temperature(value, type, name, Temperature.THROTTLING_CRITICAL);
     }
 
     private void createPowerUi() {
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 04e7cab..0d4328f 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
@@ -67,6 +67,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationData;
 import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -102,10 +104,8 @@
     @Mock private KeyguardEnvironment mEnvironment;
     @Mock private ExpandableNotificationRow mRow;
     @Mock private NotificationListContainer mListContainer;
-    @Mock
-    private NotificationEntryListener mEntryListener;
-    @Mock
-    private NotificationRowBinder.BindRowCallback mBindCallback;
+    @Mock private NotificationEntryListener mEntryListener;
+    @Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback;
     @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private NotificationListenerService.RankingMap mRankingMap;
     @Mock private RemoteInputController mRemoteInputController;
@@ -235,10 +235,12 @@
         mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
         mEntryManager.addNotificationEntryListener(mEntryListener);
 
-        NotificationRowBinder notificationRowBinder = Dependency.get(NotificationRowBinder.class);
+        NotificationRowBinderImpl notificationRowBinder =
+                new NotificationRowBinderImpl(mContext, true /* allowLongPress */);
         notificationRowBinder.setUpWithPresenter(
                 mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback);
         notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
+        mEntryManager.setRowBinder(notificationRowBinder);
 
         setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java
new file mode 100644
index 0000000..093749a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.util.SparseArray;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.CommandQueue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** atest NavigationBarInflaterViewTest */
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class NavigationBarInflaterViewTest extends SysuiTestCase {
+
+    private NavigationBarInflaterView mNavBarInflaterView;
+
+    private static final int BUTTON_ID = 0;
+
+    @Before
+    public void setUp() {
+        mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+
+        mNavBarInflaterView = spy(new NavigationBarInflaterView(mContext, null));
+        doNothing().when(mNavBarInflaterView).createInflaters();
+
+        mNavBarInflaterView.mButtonDispatchers = new SparseArray<>(1);
+        mNavBarInflaterView.mButtonDispatchers.put(BUTTON_ID, new ButtonDispatcher(BUTTON_ID));
+
+        initializeViews();
+    }
+
+    private void initializeViews() {
+        mNavBarInflaterView.mVertical = mock(FrameLayout.class);
+        mNavBarInflaterView.mHorizontal = mock(FrameLayout.class);
+        initializeLayout(mNavBarInflaterView.mVertical);
+        initializeLayout(mNavBarInflaterView.mHorizontal);
+    }
+
+    private void initializeLayout(FrameLayout layout) {
+        View verticalChildView = mock(View.class);
+        verticalChildView.setId(BUTTON_ID);
+        doReturn(layout).when(verticalChildView).getParent();
+        doReturn(verticalChildView).when(layout).findViewById(BUTTON_ID);
+    }
+
+    @After
+    public void tearDown() {
+        mNavBarInflaterView = null;
+    }
+
+    @Test
+    public void testUpdateButtonDispatchersCurrentView_isVerticalTrue() {
+        mNavBarInflaterView.setVertical(true);
+
+        mNavBarInflaterView.updateButtonDispatchersCurrentView();
+
+        ButtonDispatcher button = mNavBarInflaterView.mButtonDispatchers.get(BUTTON_ID);
+        assertEquals("Buttons need to be set to vertical layout",
+                mNavBarInflaterView.mVertical.getId(),
+                ((View) button.getCurrentView().getParent()).getId());
+    }
+
+    @Test
+    public void testUpdateButtonDispatchersCurrentView_isVerticalFalse() {
+        mNavBarInflaterView.setVertical(false);
+
+        mNavBarInflaterView.updateButtonDispatchersCurrentView();
+
+        ButtonDispatcher button = mNavBarInflaterView.mButtonDispatchers.get(BUTTON_ID);
+        assertEquals("Buttons need to be set to horizon layout",
+                mNavBarInflaterView.mHorizontal.getId(),
+                ((View) button.getCurrentView().getParent()).getId());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index e4da859..f72d411 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -38,6 +38,7 @@
 import android.graphics.Color;
 import android.os.Handler;
 import android.os.Looper;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -251,6 +252,7 @@
         assertScrimTint(mScrimBehind, false /* tinted */);
     }
 
+    @FlakyTest(bugId = 124858892)
     @Test
     public void transitionToUnlocked() {
         mScrimController.setPanelExpansion(0f);
@@ -295,6 +297,7 @@
         Assert.assertEquals(mScrimState, ScrimState.BOUNCER_SCRIMMED);
     }
 
+    @FlakyTest(bugId = 124858892)
     @Test
     public void panelExpansion() {
         mScrimController.setPanelExpansion(0f);
@@ -317,6 +320,7 @@
                 mScrimBehindAlpha, mScrimBehind.getViewAlpha(), 0.01f);
     }
 
+    @FlakyTest(bugId = 124858892)
     @Test
     public void panelExpansionAffectsAlpha() {
         mScrimController.setPanelExpansion(0f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 12cb9957..d2b0f7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -41,6 +41,7 @@
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationAlertingManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 
@@ -74,7 +75,8 @@
                 statusBarWindowView, mock(NotificationListContainerViewGroup.class),
                 mock(DozeScrimController.class), mock(ScrimController.class),
                 mock(ActivityLaunchAnimator.class), mock(StatusBarKeyguardViewManager.class),
-                mock(NotificationAlertingManager.class));
+                mock(NotificationAlertingManager.class),
+                mock(NotificationRowBinderImpl.class));
     }
 
     @Test
diff --git a/proto/Android.bp b/proto/Android.bp
index 817a54d..7b119a7 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -6,6 +6,7 @@
     },
     srcs: ["src/**/*.proto"],
     no_framework_libs: true,
+    sdk_version: "9",
     // Pin java_version until jarjar is certified to support later versions. http://b/72703434
     java_version: "1.8",
     target: {
@@ -27,14 +28,4 @@
     srcs: ["src/metrics_constants/metrics_constants.proto"],
     no_framework_libs: true,
     sdk_version: "system_current",
-    // Pin java_version until jarjar is certified to support later versions. http://b/72703434
-    java_version: "1.8",
-    target: {
-        android: {
-            jarjar_rules: "jarjar-rules.txt",
-        },
-        host: {
-            static_libs: ["libprotobuf-java-nano"],
-        },
-    },
 }
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 530d115..8929e82 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -247,6 +247,12 @@
     LOCATION_GONE = 6; // the view isn't laid out at all
   }
 
+  // Subtypes for profile logging
+  enum ActiveUserProfile {
+    PARENT_PROFILE = 1;
+    MANAGED_PROFILE = 2;
+  }
+
   // Known visual elements: views or controls.
   enum View {
     // Unknown view
@@ -3589,7 +3595,7 @@
     // OPEN: Settings > Apps > Default Apps > Default sms
     DEFAULT_SMS_PICKER = 789;
 
-    // OPEN: Settings > Apps > Default Apps > Default notification assistant
+    // OPEN: Settings > Apps > Notification > Notification Assistant
     DEFAULT_NOTIFICATION_ASSISTANT = 790;
 
     // OPEN: Settings > Apps > Default Apps > Warning dialog to confirm selection
@@ -6162,6 +6168,11 @@
     // OS: P
     FIELD_AUTOFILL_SESSION_ID = 1456;
 
+    // FIELD: Device USB overheat alarm trigger.
+    // CATEGORY: GLOBAL_SYSTEM_UI
+    // OS: P
+    POWER_OVERHEAT_ALARM = 1457;
+
     // ---- End P Constants, all P constants go above this line ----
 
     // Time since this notification last interrupted (visibly or audible) the user
@@ -7002,6 +7013,58 @@
     // Different display can have different orientations, so need to log display id
     FIELD_DISPLAY_ID = 1660;
 
+    // ACTION: Changing from work to parent profile or vice versa
+    // OS: Q
+    ACTION_SWITCH_SHARE_PROFILE = 1661;
+
+    // ACTION: Show Contextual homepage, log latency in loading cards
+    ACTION_CONTEXTUAL_HOME_SHOW = 1662;
+
+    // ACTION: Contextual card displays
+    ACTION_CONTEXTUAL_CARD_SHOW = 1663;
+
+    // ACTION: Contextual cards are eligible to be shown, but don't rank high
+    ACTION_CONTEXTUAL_CARD_NOT_SHOW = 1664;
+
+    // ACTION: Settings > long press a card, and click dismiss
+    // Contextual card is dismissed
+    ACTION_CONTEXTUAL_CARD_DISMISS = 1665;
+
+    // ACTION: Settings > click a card
+    // Contextual card is clicked
+    ACTION_CONTEXTUAL_CARD_CLICK = 1666;
+
+    // Mapping: go/at-mapping
+    PAGE_ATSSI = 1667;
+
+    PAGE_ATSII = 1668;
+
+    PAGE_ATUS = 1669;
+
+    PAGE_ATSSP = 1670;
+
+    PAGE_ATSAP = 1671;
+
+    PAGE_ATSCP = 1672;
+
+    PAGE_ATHNP = 1673;
+
+    ACTION_ATSG = 1674;
+
+    ACTION_ATPG = 1675;
+
+    ACTION_ATCLPB = 1676;
+
+    ACTION_ATCGIB = 1677;
+
+    ACTION_ATCPAB = 1678;
+
+    ACTION_ATCSAUC = 1679;
+
+    ACTION_ATCSCUC = 1680;
+
+    ACTION_ATCHNUC = 1681;
+
     // ---- End Q Constants, all Q constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index ee3777d..670e794 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -521,6 +521,9 @@
 
   // Total number of saved networks with mac randomization enabled.
   optional int32 num_saved_networks_with_mac_randomization = 138;
+
+  // Link Probe metrics
+  optional LinkProbeStats link_probe_stats = 139;
 }
 
 // Information that gets logged for every WiFi connection.
@@ -1959,7 +1962,6 @@
   // The total duration elapsed while in this mobility state, in ms
   optional int64 total_duration_ms = 3;
 
-
   // the total duration elapsed while in this mobility state with PNO scans running, in ms
   optional int64 pno_duration_ms = 4;
 }
@@ -2098,6 +2100,8 @@
 
 // Easy Connect (DPP)
 message WifiDppLog {
+  reserved 6;
+
   // Number of Configurator-Initiator requests
   optional int32 num_dpp_configurator_initiator_requests = 1;
 
@@ -2114,7 +2118,7 @@
   repeated DppFailureStatusHistogramBucket dpp_failure_code = 5;
 
   // Easy Connect (DPP) operation time bucket
-  repeated HistogramBucket dpp_operation_time = 6;
+  repeated HistogramBucketInt32 dpp_operation_time = 7;
 
   // Histogram bucket for Wi-Fi DPP configurator success
   message DppConfiguratorSuccessStatusHistogramBucket {
@@ -2134,18 +2138,6 @@
     optional int32 count = 2;
   }
 
-  // Histogram bucket for Wi-Fi DPP logs. Range is [start, end)
-  message HistogramBucket {
-    // lower range of the bucket (inclusive)
-    optional int32 start = 1;
-
-    // upper range of the bucket (exclusive)
-    optional int32 end = 2;
-
-    // number of samples in the bucket
-    optional int32 count = 3;
-  }
-
   enum DppConfiguratorSuccessCode {
     // Unknown success code
     EASY_CONNECT_EVENT_SUCCESS_UNKNOWN = 0;
@@ -2211,3 +2203,76 @@
     optional int32 count = 3;
   }
 }
+
+// Histogram bucket counting with int32. Range is [start, end)
+message HistogramBucketInt32 {
+  // lower range of the bucket (inclusive)
+  optional int32 start = 1;
+
+  // upper range of the bucket (exclusive)
+  optional int32 end = 2;
+
+  // number of samples in the bucket
+  optional int32 count = 3;
+}
+
+// Single entry in a map from int32 => int32
+message MapEntryInt32Int32 {
+  // the key
+  optional int32 key = 1;
+
+  // the value
+  optional int32 value = 2;
+}
+
+message LinkProbeStats {
+  enum LinkProbeFailureReason {
+    // unknown reason
+    LINK_PROBE_FAILURE_REASON_UNKNOWN = 0;
+
+    // Specified MCS rate when it is unsupported by the driver.
+    LINK_PROBE_FAILURE_REASON_MCS_UNSUPPORTED = 1;
+
+    // Driver reported that no ACK was received for the transmitted probe.
+    LINK_PROBE_FAILURE_REASON_NO_ACK = 2;
+
+    // Driver failed to report on the status of the transmitted probe within the timeout.
+    LINK_PROBE_FAILURE_REASON_TIMEOUT = 3;
+
+    // An existing link probe is in progress.
+    LINK_PROBE_FAILURE_REASON_ALREADY_STARTED = 4;
+  }
+
+  // Counts the number of failures for each failure reason.
+  message LinkProbeFailureReasonCount {
+    // The failure reason.
+    optional LinkProbeFailureReason failure_reason = 1;
+
+    // The number of occurrences for this failure reason.
+    optional int32 count = 2;
+  }
+
+  // Counts the occurrences of RSSI values when a link probe succeeds.
+  repeated MapEntryInt32Int32 success_rssi_counts = 1;
+
+  // Counts the occurrences of RSSI values when a link probe fails.
+  repeated MapEntryInt32Int32 failure_rssi_counts = 2;
+
+  // Counts the occurrences of Link Speed values when a link probe succeeds.
+  repeated MapEntryInt32Int32 success_link_speed_counts = 3;
+
+  // Counts the occurrences of Link Speed values when a link probe fails.
+  repeated MapEntryInt32Int32 failure_link_speed_counts = 4;
+
+  // Histogram for the number of seconds since the last TX success when a link probe succeeds.
+  repeated HistogramBucketInt32 success_seconds_since_last_tx_success_histogram = 5;
+
+  // Histogram for the number of seconds since the last TX success when a link probe fails.
+  repeated HistogramBucketInt32 failure_seconds_since_last_tx_success_histogram = 6;
+
+  // Histogram for the elapsed time of successful link probes, in ms.
+  repeated HistogramBucketInt32 success_elapsed_time_ms_histogram = 7;
+
+  // Counts the occurrences of error codes for failed link probes.
+  repeated LinkProbeFailureReasonCount failure_reason_counts = 8;
+}
diff --git a/services/art-profile b/services/art-profile
index e750380..7892fcb 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -2942,7 +2942,7 @@
 HSPLcom/android/server/policy/WindowManagerPolicy;->onKeyguardOccludedChangedLw(Z)V
 HSPLcom/android/server/policy/WindowManagerPolicy;->onLockTaskStateChangedLw(I)V
 HSPLcom/android/server/policy/WindowManagerPolicy;->onSystemUiStarted()V
-HSPLcom/android/server/policy/WindowManagerPolicy;->performHapticFeedbackLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;IZ)Z
+HSPLcom/android/server/policy/WindowManagerPolicy;->performHapticFeedback(ILjava/lang/String;IZ)Z
 HSPLcom/android/server/policy/WindowManagerPolicy;->prepareAddWindowLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;Landroid/view/WindowManager$LayoutParams;)I
 HSPLcom/android/server/policy/WindowManagerPolicy;->registerShortcutKey(JLcom/android/internal/policy/IShortcutService;)V
 HSPLcom/android/server/policy/WindowManagerPolicy;->removeWindowLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;)V
@@ -15736,7 +15736,7 @@
 PLcom/android/server/policy/PhoneWindowManager;->onKeyguardOccludedChangedLw(Z)V
 PLcom/android/server/policy/PhoneWindowManager;->onOverlayChangedLw()V
 PLcom/android/server/policy/PhoneWindowManager;->onSystemUiStarted()V
-PLcom/android/server/policy/PhoneWindowManager;->performHapticFeedbackLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;IZ)Z
+PLcom/android/server/policy/PhoneWindowManager;->performHapticFeedback(ILjava/lang/String;IZ)Z
 PLcom/android/server/policy/PhoneWindowManager;->powerPress(JZI)V
 PLcom/android/server/policy/PhoneWindowManager;->prepareAddWindowLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;Landroid/view/WindowManager$LayoutParams;)I
 PLcom/android/server/policy/PhoneWindowManager;->readCameraLensCoverState()V
@@ -18331,7 +18331,7 @@
 PLcom/android/server/wm/Session;->killSessionLocked()V
 PLcom/android/server/wm/Session;->onRectangleOnScreenRequested(Landroid/os/IBinder;Landroid/graphics/Rect;)V
 PLcom/android/server/wm/Session;->onWindowSurfaceVisibilityChanged(Lcom/android/server/wm/WindowSurfaceController;ZI)V
-PLcom/android/server/wm/Session;->performHapticFeedback(Landroid/view/IWindow;IZ)Z
+PLcom/android/server/wm/Session;->performHapticFeedback(IZ)Z
 PLcom/android/server/wm/Session;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
 PLcom/android/server/wm/Session;->remove(Landroid/view/IWindow;)V
 PLcom/android/server/wm/Session;->sendWallpaperCommand(Landroid/os/IBinder;Ljava/lang/String;IIILandroid/os/Bundle;Z)Landroid/os/Bundle;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index e0fb337..245e2c9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -30,6 +30,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityThread;
+import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -69,6 +70,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AbstractRemoteService;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
@@ -108,6 +110,8 @@
     private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '[';
     private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']';
 
+    private static final int DEFAULT_AUGMENTED_AUTOFILL_REQUEST_TIMEOUT_MILLIS = 5_000;
+
     /**
      * Maximum number of partitions that can be allowed in a session.
      *
@@ -161,6 +165,11 @@
     @GuardedBy("mLock")
     private int mSupportedSmartSuggestionModes;
 
+    @GuardedBy("mLock")
+    int mAugmentedServiceIdleUnbindTimeoutMs;
+    @GuardedBy("mLock")
+    int mAugmentedServiceRequestTimeoutMs;
+
     public AutofillManagerService(Context context) {
         super(context,
                 new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE),
@@ -170,12 +179,12 @@
 
         DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_AUTOFILL,
                 ActivityThread.currentApplication().getMainExecutor(),
-                (namespace, name, value) -> setSmartSuggestionModesFromDeviceConfig(value));
+                (namespace, key, value) -> onDeviceConfigChange(key, value));
 
         setLogLevelFromSettings();
         setMaxPartitionsFromSettings();
         setMaxVisibleDatasetsFromSettings();
-        setSmartSuggestionModesFromDeviceConfig();
+        setDeviceConfigProperties();
 
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -226,6 +235,18 @@
         }
     }
 
+    private void onDeviceConfigChange(@NonNull String key, @Nullable String value) {
+        switch (key) {
+            case AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES:
+            case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT:
+            case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT:
+                setDeviceConfigProperties();
+                break;
+            default:
+                Slog.i(mTag, "Ignoring change on " + key);
+        }
+    }
+
     @Override // from AbstractMasterSystemService
     protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
             boolean disabled) {
@@ -456,27 +477,24 @@
         }
     }
 
-    private void setSmartSuggestionModesFromDeviceConfig() {
-        final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_AUTOFILL,
-                AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES);
-        setSmartSuggestionModesFromDeviceConfig(value);
-    }
-
-    private void setSmartSuggestionModesFromDeviceConfig(@Nullable String value) {
-        if (sDebug) Slog.d(TAG, "setSmartSuggestionEmulationFromDeviceConfig(): value=" + value);
-        final int flags;
-        if (value == null) {
-            flags = AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
-        } else {
-            try {
-                flags = Integer.parseInt(value);
-            } catch (Exception e) {
-                Slog.w(TAG, "setSmartSuggestionEmulationFromDeviceConfig(): NAN:" + value);
-                return;
-            }
-        }
+    private void setDeviceConfigProperties() {
         synchronized (mLock) {
-            mSupportedSmartSuggestionModes = flags;
+            mAugmentedServiceIdleUnbindTimeoutMs = Helper.getIntDeviceConfigProperty(
+                    AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT,
+                    (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS);
+            mAugmentedServiceRequestTimeoutMs = Helper.getIntDeviceConfigProperty(
+                    AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT,
+                    DEFAULT_AUGMENTED_AUTOFILL_REQUEST_TIMEOUT_MILLIS);
+            mSupportedSmartSuggestionModes = Helper.getIntDeviceConfigProperty(
+                    AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES,
+                    AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM);
+            if (verbose) {
+                Slog.v(mTag, "setDeviceConfigProperties(): "
+                        + "augmentedIdleTimeout=" + mAugmentedServiceIdleUnbindTimeoutMs
+                        + ", augmentedRequestTimeout=" + mAugmentedServiceRequestTimeoutMs
+                        + ", smartSuggestionMode="
+                        + getSmartSuggestionModeToString(mSupportedSmartSuggestionModes));
+            }
         }
     }
 
@@ -699,12 +717,31 @@
         }
 
         @Override
-        public boolean isCompatibilityModeRequested(@NonNull String packageName,
+        public AutofillOptions getAutofillOptions(@NonNull String packageName,
                 long versionCode, @UserIdInt int userId) {
-            return mAutofillCompatState.isCompatibilityModeRequested(
+            final int loggingLevel;
+            if (verbose) {
+                loggingLevel = AutofillManager.FLAG_ADD_CLIENT_VERBOSE
+                        | AutofillManager.FLAG_ADD_CLIENT_DEBUG;
+            } else if (debug) {
+                loggingLevel = AutofillManager.FLAG_ADD_CLIENT_DEBUG;
+            } else {
+                loggingLevel = AutofillManager.NO_LOGGING;
+            }
+            final boolean compatModeEnabled = mAutofillCompatState.isCompatibilityModeRequested(
                     packageName, versionCode, userId);
-        }
+            final AutofillOptions options = new AutofillOptions(loggingLevel, compatModeEnabled);
 
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service =
+                        getServiceForUserLocked(UserHandle.getCallingUserId());
+                if (service != null) {
+                    service.setAugmentedAutofillWhitelistLocked(options, packageName);
+                }
+            }
+
+            return options;
+        }
     }
 
     /**
@@ -1260,6 +1297,10 @@
                         pw.print("Smart Suggestion modes: ");
                         pw.println(getSmartSuggestionModeToString(mSupportedSmartSuggestionModes));
                     }
+                    pw.print("Augmented Service Idle Unbind Timeout: ");
+                    pw.println(mAugmentedServiceIdleUnbindTimeoutMs);
+                    pw.print("Augmented Service Request Timeout: ");
+                    pw.println(mAugmentedServiceRequestTimeoutMs);
                     if (showHistory) {
                         pw.println(); pw.println("Requests history:"); pw.println();
                         mRequestsHistory.reverseDump(fd, pw, args);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 15dce4a..3f33813 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -28,6 +28,7 @@
 import android.app.ActivityManagerInternal;
 import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
+import android.content.AutofillOptions;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -97,7 +98,7 @@
     private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
 
     /** Minimum interval to prune abandoned sessions */
-    private static final int MAX_ABANDONED_SESSION_MILLIS = 30000;
+    private static final int MAX_ABANDONED_SESSION_MILLIS = 30_000;
 
     private final AutoFillUI mUi;
     private final MetricsLogger mMetricsLogger = new MetricsLogger();
@@ -1084,8 +1085,11 @@
                             if (remoteService != null) {
                                 remoteService.destroy();
                             }
+                            mRemoteAugmentedAutofillService = null;
                         }
-                    }, mMaster.isInstantServiceAllowed(), mMaster.verbose);
+                    }, mMaster.isInstantServiceAllowed(), mMaster.verbose,
+                    mMaster.mAugmentedServiceIdleUnbindTimeoutMs,
+                    mMaster.mAugmentedServiceRequestTimeoutMs);
         }
 
         return mRemoteAugmentedAutofillService;
@@ -1095,14 +1099,17 @@
      * Called when the {@link #mAugmentedAutofillResolver} changed (among other places).
      */
     private void updateRemoteAugmentedAutofillService(@Nullable String serviceName) {
-        if (serviceName == null) {
-            if (sVerbose) Slog.v(TAG, "updateRemoteAugmentedAutofillService(): time's up!");
-            synchronized (mLock) {
-                if (mRemoteAugmentedAutofillService != null) {
-                    mRemoteAugmentedAutofillService.destroy();
-                    mRemoteAugmentedAutofillService = null;
+        synchronized (mLock) {
+            if (mRemoteAugmentedAutofillService != null) {
+                if (sVerbose) {
+                    Slog.v(TAG, "updateRemoteAugmentedAutofillService(): "
+                            + "destroying old remote service");
                 }
+                mRemoteAugmentedAutofillService.destroy();
+                mRemoteAugmentedAutofillService = null;
             }
+
+            mRemoteAugmentedAutofillService = getRemoteAugmentedAutofillServiceLocked();
         }
     }
 
@@ -1169,6 +1176,13 @@
         return mWhitelistedAugmentAutofillPackages.contains(packageName);
     }
 
+    @GuardedBy("mLock")
+    void setAugmentedAutofillWhitelistLocked(@NonNull AutofillOptions options,
+            @NonNull String packageName) {
+        // TODO(b/122595322): need to setwhitelisted activities as well.
+        options.augmentedEnabled = mWhitelistedAugmentAutofillPackages.contains(packageName);
+    }
+
     private void whitelistForAugmentedAutofillPackages(@NonNull List<String> packages) {
         // TODO(b/123100824): add CTS test for when it's null
         synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 3c0da7d..d300bf2 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -22,9 +22,11 @@
 import android.app.assist.AssistStructure.ViewNode;
 import android.content.ComponentName;
 import android.metrics.LogMaker;
+import android.provider.DeviceConfig;
 import android.service.autofill.Dataset;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Slog;
 import android.view.WindowManager;
 import android.view.autofill.AutofillId;
@@ -205,6 +207,21 @@
         }
     }
 
+    /**
+     * Gets the value of a device config property from the Autofill namespace.
+     */
+    static int getIntDeviceConfigProperty(@NonNull String key, int defaultValue) {
+        final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_AUTOFILL, key);
+        if (value == null) return defaultValue;
+
+        try {
+            return Integer.parseInt(value);
+        } catch (Exception e) {
+            Log.w(TAG, "error parsing value (" + value + ") of property " + key + ": " + e);
+            return defaultValue;
+        }
+    }
+
     private interface ViewNodeFilter {
         boolean matches(ViewNode node);
     }
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 88228fb..a38c3cf 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -31,7 +31,6 @@
 import android.service.autofill.augmented.AugmentedAutofillService;
 import android.service.autofill.augmented.IAugmentedAutofillService;
 import android.service.autofill.augmented.IFillCallback;
-import android.text.format.DateUtils;
 import android.util.Pair;
 import android.util.Slog;
 import android.view.autofill.AutofillId;
@@ -48,13 +47,17 @@
 
     private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName();
 
-    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
+    private final int mIdleUnbindTimeoutMs;
+    private final int mRequestTimeoutMs;
 
     RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
             int userId, RemoteAugmentedAutofillServiceCallbacks callbacks,
-            boolean bindInstantServiceAllowed, boolean verbose) {
+            boolean bindInstantServiceAllowed, boolean verbose, int idleUnbindTimeoutMs,
+            int requestTimeoutMs) {
         super(context, AugmentedAutofillService.SERVICE_INTERFACE, serviceName, userId, callbacks,
                 bindInstantServiceAllowed, verbose);
+        mIdleUnbindTimeoutMs = idleUnbindTimeoutMs;
+        mRequestTimeoutMs = requestTimeoutMs;
 
         // Bind right away.
         scheduleBind();
@@ -108,12 +111,12 @@
 
     @Override // from AbstractRemoteService
     protected long getTimeoutIdleBindMillis() {
-        return PERMANENT_BOUND_TIMEOUT_MS;
+        return mIdleUnbindTimeoutMs;
     }
 
     @Override // from AbstractRemoteService
     protected long getRemoteRequestMillis() {
-        return TIMEOUT_REMOTE_REQUEST_MILLIS;
+        return mRequestTimeoutMs;
     }
 
     /**
@@ -209,7 +212,7 @@
         protected void onTimeout(RemoteAugmentedAutofillService remoteService) {
             // TODO(b/122858578): must update the logged AUTOFILL_AUGMENTED_REQUEST with the
             // timeout
-            Slog.w(TAG, "PendingAutofillRequest timed out (" + TIMEOUT_REMOTE_REQUEST_MILLIS
+            Slog.w(TAG, "PendingAutofillRequest timed out (" + remoteService.mRequestTimeoutMs
                     + "ms) for " + remoteService);
             // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks
             finish();
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 848f249..2b94d10d6 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -17,6 +17,7 @@
 package com.android.server.autofill;
 
 import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
+import static android.service.autofill.FillRequest.FLAG_AUGMENTED_AUTOFILL_REQUEST;
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
 import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
@@ -527,6 +528,16 @@
      */
     @GuardedBy("mLock")
     private void requestNewFillResponseLocked(int flags) {
+
+        if ((flags & FLAG_AUGMENTED_AUTOFILL_REQUEST) != 0) {
+            // TODO(b/122858578): log metrics
+            if (sVerbose) {
+                Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead");
+            }
+            triggerAugmentedAutofillLocked();
+            return;
+        }
+
         int requestId;
 
         do {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index d5713a1..9f7a940 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -52,6 +52,7 @@
 import android.view.contentcapture.UserDataRemovalRequest;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AbstractRemoteService;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
@@ -103,11 +104,12 @@
     private boolean mDisabledByDeviceConfig;
 
     // Device-config settings that are cached and passed back to apps
-    public int mDevCfgLoggingLevel;
-    public int mDevCfgMaxBufferSize;
-    public int mDevCfgIdleFlushingFrequencyMs;
-    public int mDevCfgTextChangeFlushingFrequencyMs;
-    public int mDevCfgLogHistorySize;
+    @GuardedBy("mLock") int mDevCfgLoggingLevel;
+    @GuardedBy("mLock") int mDevCfgMaxBufferSize;
+    @GuardedBy("mLock") int mDevCfgIdleFlushingFrequencyMs;
+    @GuardedBy("mLock") int mDevCfgTextChangeFlushingFrequencyMs;
+    @GuardedBy("mLock") int mDevCfgLogHistorySize;
+    @GuardedBy("mLock") int mDevCfgIdleUnbindTimeoutMs;
 
     public ContentCaptureManagerService(@NonNull Context context) {
         super(context, new FrameworkResourcesServiceNameResolver(context,
@@ -238,6 +240,7 @@
             case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY:
             case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE:
             case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY:
+            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT:
                 setFineTuneParamsFromDeviceConfig();
                 return;
             default:
@@ -246,22 +249,29 @@
     }
 
     private void setFineTuneParamsFromDeviceConfig() {
-        mDevCfgMaxBufferSize = ContentCaptureHelper.getIntDeviceConfigProperty(
-                ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE,
-                ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE);
-        mDevCfgIdleFlushingFrequencyMs = ContentCaptureHelper.getIntDeviceConfigProperty(
-                ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY,
-                ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS);
-        mDevCfgTextChangeFlushingFrequencyMs = ContentCaptureHelper.getIntDeviceConfigProperty(
-                ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY,
-                ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS);
-        mDevCfgLogHistorySize = ContentCaptureHelper.getIntDeviceConfigProperty(
-                ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20);
-        if (verbose) {
-            Slog.v(mTag, "setFineTuneParamsFromDeviceConfig(): bufferSize=" + mDevCfgMaxBufferSize
-                    + ", idleFlush=" + mDevCfgIdleFlushingFrequencyMs
-                    + ", textFluxh=" + mDevCfgTextChangeFlushingFrequencyMs
-                    + ", logHistory=" + mDevCfgLogHistorySize);
+        synchronized (mLock) {
+            mDevCfgMaxBufferSize = ContentCaptureHelper.getIntDeviceConfigProperty(
+                    ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE,
+                    ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE);
+            mDevCfgIdleFlushingFrequencyMs = ContentCaptureHelper.getIntDeviceConfigProperty(
+                    ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY,
+                    ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS);
+            mDevCfgTextChangeFlushingFrequencyMs = ContentCaptureHelper.getIntDeviceConfigProperty(
+                    ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY,
+                    ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS);
+            mDevCfgLogHistorySize = ContentCaptureHelper.getIntDeviceConfigProperty(
+                    ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20);
+            mDevCfgIdleUnbindTimeoutMs = ContentCaptureHelper.getIntDeviceConfigProperty(
+                    ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT,
+                    (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS);
+            if (verbose) {
+                Slog.v(mTag, "setFineTuneParamsFromDeviceConfig(): "
+                        + "bufferSize=" + mDevCfgMaxBufferSize
+                        + ", idleFlush=" + mDevCfgIdleFlushingFrequencyMs
+                        + ", textFluxh=" + mDevCfgTextChangeFlushingFrequencyMs
+                        + ", logHistory=" + mDevCfgLogHistorySize
+                        + ", idleUnbindTimeoutMs=" + mDevCfgIdleUnbindTimeoutMs);
+            }
         }
     }
 
@@ -472,6 +482,8 @@
         pw.print(prefix2); pw.print("textChangeFlushingFrequencyMs: ");
         pw.println(mDevCfgTextChangeFlushingFrequencyMs);
         pw.print(prefix2); pw.print("logHistorySize: "); pw.println(mDevCfgLogHistorySize);
+        pw.print(prefix2); pw.print("idleUnbindTimeoutMs: ");
+        pw.println(mDevCfgIdleUnbindTimeoutMs);
     }
 
     final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 5b48046..b33259d 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -126,7 +126,7 @@
             mRemoteService = new RemoteContentCaptureService(mMaster.getContext(),
                     ContentCaptureService.SERVICE_INTERFACE, serviceComponentName,
                     mRemoteServiceCallback, mUserId, this, mMaster.isBindInstantServiceAllowed(),
-                    mMaster.verbose);
+                    mMaster.verbose, mMaster.mDevCfgIdleUnbindTimeoutMs);
         }
     }
 
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index de9896e..dc07c0a 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -26,7 +26,6 @@
 import android.service.contentcapture.IContentCaptureService;
 import android.service.contentcapture.IContentCaptureServiceCallback;
 import android.service.contentcapture.SnapshotData;
-import android.text.format.DateUtils;
 import android.util.Slog;
 import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.UserDataRemovalRequest;
@@ -38,17 +37,17 @@
         extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService,
         IContentCaptureService> {
 
-    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
-
     private final IBinder mServerCallback;
+    private final int mIdleUnbindTimeoutMs;
 
     RemoteContentCaptureService(Context context, String serviceInterface,
             ComponentName serviceComponentName, IContentCaptureServiceCallback callback, int userId,
             ContentCaptureServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
-            boolean verbose) {
+            boolean verbose, int idleUnbindTimeoutMs) {
         super(context, serviceInterface, serviceComponentName, userId, callbacks,
                 bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2);
         mServerCallback = callback.asBinder();
+        mIdleUnbindTimeoutMs = idleUnbindTimeoutMs;
 
         // Bind right away, which will trigger a onConnected() on service's
         scheduleBind();
@@ -61,14 +60,7 @@
 
     @Override // from AbstractRemoteService
     protected long getTimeoutIdleBindMillis() {
-        // TODO(b/111276913): read from Settings so it can be changed in the field
-        return PERMANENT_BOUND_TIMEOUT_MS;
-    }
-
-    @Override // from AbstractRemoteService
-    protected long getRemoteRequestMillis() {
-        // TODO(b/111276913): read from Settings so it can be changed in the field
-        return TIMEOUT_REMOTE_REQUEST_MILLIS;
+        return mIdleUnbindTimeoutMs;
     }
 
     @Override // from RemoteService
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 915c131..c37a805 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1060,7 +1060,8 @@
             handleRegisterNetworkRequest(new NetworkRequestInfo(
                     null, networkRequest, new Binder()));
         } else {
-            handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID);
+            handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID,
+                    /* callOnUnavailable */ false);
         }
     }
 
@@ -2374,6 +2375,11 @@
 
             pw.decreaseIndent();
         }
+
+        pw.println();
+        pw.println("NetworkStackClient logs:");
+        pw.increaseIndent();
+        NetworkStackClient.getInstance().dump(pw);
     }
 
     private void dumpNetworks(IndentingPrintWriter pw) {
@@ -2641,11 +2647,25 @@
             return true;
         }
 
+        private boolean maybeHandleNetworkFactoryMessage(Message msg) {
+            switch (msg.what) {
+                default:
+                    return false;
+                case NetworkFactory.EVENT_UNFULFILLABLE_REQUEST: {
+                    handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.sendingUid,
+                            /* callOnUnavailable */ true);
+                    break;
+                }
+            }
+            return true;
+        }
+
         @Override
         public void handleMessage(Message msg) {
-            if (!maybeHandleAsyncChannelMessage(msg) &&
-                    !maybeHandleNetworkMonitorMessage(msg) &&
-                    !maybeHandleNetworkAgentInfoMessage(msg)) {
+            if (!maybeHandleAsyncChannelMessage(msg)
+                    && !maybeHandleNetworkMonitorMessage(msg)
+                    && !maybeHandleNetworkAgentInfoMessage(msg)
+                    && !maybeHandleNetworkFactoryMessage(msg)) {
                 maybeHandleNetworkAgentMessage(msg);
             }
         }
@@ -2787,6 +2807,9 @@
         if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {
             if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
                 if (VDBG) log("NetworkFactory connected");
+                // Finish setting up the full connection
+                mNetworkFactoryInfos.get(msg.replyTo).asyncChannel.sendMessage(
+                        AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
                 // A network factory has connected.  Send it all current NetworkRequests.
                 for (NetworkRequestInfo nri : mNetworkRequests.values()) {
                     if (nri.request.isListen()) continue;
@@ -2957,7 +2980,8 @@
         if (existingRequest != null) { // remove the existing request.
             if (DBG) log("Replacing " + existingRequest.request + " with "
                     + nri.request + " because their intents matched.");
-            handleReleaseNetworkRequest(existingRequest.request, getCallingUid());
+            handleReleaseNetworkRequest(existingRequest.request, getCallingUid(),
+                    /* callOnUnavailable */ false);
         }
         handleRegisterNetworkRequest(nri);
     }
@@ -2983,7 +3007,7 @@
             int callingUid) {
         NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
         if (nri != null) {
-            handleReleaseNetworkRequest(nri.request, callingUid);
+            handleReleaseNetworkRequest(nri.request, callingUid, /* callOnUnavailable */ false);
         }
     }
 
@@ -3066,7 +3090,8 @@
         callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
     }
 
-    private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) {
+    private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid,
+            boolean callOnUnavailable) {
         final NetworkRequestInfo nri =
                 getNriForAppRequest(request, callingUid, "release NetworkRequest");
         if (nri == null) {
@@ -3076,6 +3101,9 @@
             log("releasing " + nri.request + " (release request)");
         }
         handleRemoveNetworkRequest(nri);
+        if (callOnUnavailable) {
+            callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
+        }
     }
 
     private void handleRemoveNetworkRequest(final NetworkRequestInfo nri) {
@@ -3507,7 +3535,8 @@
                     break;
                 }
                 case EVENT_RELEASE_NETWORK_REQUEST: {
-                    handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.arg1);
+                    handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.arg1,
+                            /* callOnUnavailable */ false);
                     break;
                 }
                 case EVENT_SET_ACCEPT_UNVALIDATED: {
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 0ed5beb..4d39f9a 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -145,7 +145,7 @@
 # ---------------------------
 # SystemServer.run() starts:
 3010 boot_progress_system_run (time|2|3)
-
+3011 system_server_start (start_count|1),(uptime|2|3),(elapse_time|2|3)
 
 # ---------------------------
 # PackageManagerService.java
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 5989a46..d8b96e4 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1972,7 +1972,7 @@
                     continue;
                 }
 
-                // requests that ignore location settings will never provider notifications
+                // requests that ignore location settings will never provide notifications
                 if (isSettingsExemptLocked(record)) {
                     continue;
                 }
@@ -2010,19 +2010,22 @@
         WorkSource worksource = new WorkSource();
         ProviderRequest providerRequest = new ProviderRequest();
 
-        long backgroundThrottleInterval;
-
-        long identity = Binder.clearCallingIdentity();
-        try {
-            backgroundThrottleInterval = Settings.Global.getLong(
-                    mContext.getContentResolver(),
-                    Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
-                    DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-
         if (records != null && !records.isEmpty()) {
+            long backgroundThrottleInterval;
+
+            long identity = Binder.clearCallingIdentity();
+            try {
+                backgroundThrottleInterval = Settings.Global.getLong(
+                        mContext.getContentResolver(),
+                        Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
+                        DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+
+            final boolean isForegroundOnlyMode =
+                    mPowerManager.getLocationPowerSaveMode()
+                            == PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
             // initialize the low power mode to true and set to false if any of the records requires
             providerRequest.lowPowerMode = true;
             for (UpdateRecord record : records) {
@@ -2037,7 +2040,9 @@
                         record.mReceiver.mAllowedResolutionLevel)) {
                     continue;
                 }
-                if (!provider.isUseableLocked()) {
+                final boolean isBatterySaverDisablingLocation =
+                        isForegroundOnlyMode && !record.mIsForegroundUid;
+                if (!provider.isUseableLocked() || isBatterySaverDisablingLocation) {
                     if (isSettingsExemptLocked(record)) {
                         providerRequest.locationSettingsIgnored = true;
                         providerRequest.lowPowerMode = false;
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 526aebe..097a7d6 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -42,6 +42,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.MediaStore;
@@ -235,9 +236,17 @@
      * Handler for on start pinning message
      */
     private void handlePinOnStart() {
-         // Files to pin come from the overlay and can be specified per-device config
-        String[] filesToPin = mContext.getResources().getStringArray(
-                com.android.internal.R.array.config_defaultPinnerServiceFiles);
+        final String bootImage = SystemProperties.get("dalvik.vm.boot-image", "");
+        String[] filesToPin = null;
+        if (bootImage.endsWith("apex.art")) {
+            // Use the files listed for that specific boot image
+            filesToPin = mContext.getResources().getStringArray(
+                  com.android.internal.R.array.config_apexBootImagePinnerServiceFiles);
+        } else {
+            // Files to pin come from the overlay and can be specified per-device config
+            filesToPin = mContext.getResources().getStringArray(
+                  com.android.internal.R.array.config_defaultPinnerServiceFiles);
+        }
         // Continue trying to pin each file even if we fail to pin some of them
         for (String fileToPin : filesToPin) {
             PinnedFile pf = pinFile(fileToPin,
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index cd9d84c..ada3947 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -347,12 +347,6 @@
     @GuardedBy("mPackagesLock")
     private final SparseArray<ArraySet<String>> mPackages = new SparseArray<>();
 
-    /**
-     * List of volumes visible to any user.
-     * TODO: may be have a map of userId -> volumes?
-     */
-    private final CopyOnWriteArrayList<VolumeInfo> mVisibleVols = new CopyOnWriteArrayList<>();
-
     private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
 
     /** Holding lock for AppFuse business */
@@ -956,8 +950,6 @@
                 addInternalVolumeLocked();
             }
 
-            mVisibleVols.clear();
-
             try {
                 mVold.reset();
 
@@ -1895,9 +1887,6 @@
     private void mount(VolumeInfo vol) {
         try {
             mVold.mount(vol.id, vol.mountFlags, vol.mountUserId);
-            if ((vol.mountFlags & VolumeInfo.MOUNT_FLAG_VISIBLE) != 0) {
-                mVisibleVols.add(vol);
-            }
         } catch (Exception e) {
             Slog.wtf(TAG, e);
         }
@@ -1914,9 +1903,6 @@
     private void unmount(VolumeInfo vol) {
         try {
             mVold.unmount(vol.id);
-            if ((vol.mountFlags & VolumeInfo.MOUNT_FLAG_VISIBLE) != 0) {
-                mVisibleVols.remove(vol);
-            }
         } catch (Exception e) {
             Slog.wtf(TAG, e);
         }
@@ -2811,6 +2797,7 @@
 
     @Override
     public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
+        Slog.d(TAG, "unlockUserKey: " + userId);
         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
 
         if (StorageManager.isFileEncryptedNativeOrEmulated()) {
@@ -3850,14 +3837,6 @@
             pw.decreaseIndent();
 
             pw.println();
-            pw.println("mVisibleVols:");
-            pw.increaseIndent();
-            for (int i = 0; i < mVisibleVols.size(); i++) {
-                mVisibleVols.get(i).dump(pw);
-            }
-            pw.decreaseIndent();
-
-            pw.println();
             pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
 
             pw.println();
@@ -4055,33 +4034,9 @@
         }
 
         @Override
-        public String[] getVisibleVolumesForUser(int userId) {
-            final ArrayList<String> visibleVolsForUser = new ArrayList<>();
-            for (int i = mVisibleVols.size() - 1; i >= 0; --i) {
-                final VolumeInfo vol = mVisibleVols.get(i);
-                if (vol.isVisibleForUser(userId)) {
-                    visibleVolsForUser.add(getVolumeLabel(vol));
-                }
-            }
-            return visibleVolsForUser.toArray(new String[visibleVolsForUser.size()]);
-        }
-
-        @Override
         public String getSandboxId(String packageName) {
             return StorageManagerService.this.getSandboxId(packageName,
                     mPmInternal.getSharedUserIdForPackage(packageName));
         }
-
-        private String getVolumeLabel(VolumeInfo vol) {
-            // STOPSHIP: Label needs to part of VolumeInfo and need to be passed on from vold
-            switch (vol.getType()) {
-                case VolumeInfo.TYPE_EMULATED:
-                    return "emulated";
-                case VolumeInfo.TYPE_PUBLIC:
-                    return vol.fsUuid == null ? vol.id : vol.fsUuid;
-                default:
-                    return null;
-            }
-        }
     }
 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index fd946cd..5633082c 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -177,8 +177,6 @@
 
     private ServiceState[] mServiceState;
 
-    private int[] mNetworkType;
-
     private int[] mVoiceActivationState;
 
     private int[] mDataActivationState;
@@ -213,6 +211,9 @@
     private CallAttributes mCallAttributes = new CallAttributes(new PreciseCallState(),
             TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality());
 
+    // network type of the call associated with the mCallAttributes and mCallQuality
+    private int mCallNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
     private int[] mSrvccState;
 
     private int mDefaultSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -375,7 +376,6 @@
         mDataConnectionNetworkType = new int[numPhones];
         mCallIncomingNumber = new String[numPhones];
         mServiceState = new ServiceState[numPhones];
-        mNetworkType = new int[numPhones];
         mVoiceActivationState = new int[numPhones];
         mDataActivationState = new int[numPhones];
         mUserMobileDataState = new boolean[numPhones];
@@ -396,7 +396,6 @@
             mDataActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
             mCallIncomingNumber[i] =  "";
             mServiceState[i] =  new ServiceState();
-            mNetworkType[i] = mServiceState[i].getVoiceNetworkType();
             mSignalStrength[i] =  new SignalStrength();
             mUserMobileDataState[i] = false;
             mMessageWaiting[i] =  false;
@@ -998,21 +997,6 @@
             if (validatePhoneId(phoneId)) {
                 mServiceState[phoneId] = state;
 
-                boolean notifyCallAttributes = true;
-                if (mNetworkType[phoneId] != mServiceState[phoneId].getVoiceNetworkType()) {
-                    mNetworkType[phoneId] = state.getVoiceNetworkType();
-                    mCallAttributes = new CallAttributes(mPreciseCallState, mNetworkType[phoneId],
-                            mCallQuality);
-                } else {
-                    // No change to network type, so no need to notify call attributes
-                    notifyCallAttributes = false;
-                }
-
-                if (mCallQuality == null) {
-                    // No call quality reported yet, so no need to notify call attributes
-                    notifyCallAttributes = false;
-                }
-
                 for (Record r : mRecords) {
                     if (VDBG) {
                         log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
@@ -1040,14 +1024,6 @@
                             mRemoveList.add(r.binder);
                         }
                     }
-                    if (notifyCallAttributes && r.matchPhoneStateListenerEvent(
-                                    PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED)) {
-                        try {
-                            r.callback.onCallAttributesChanged(mCallAttributes);
-                        } catch (RemoteException ex) {
-                            mRemoveList.add(r.binder);
-                        }
-                    }
                 }
             } else {
                 log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId);
@@ -1574,7 +1550,7 @@
                 log("notifyPreciseCallState: mCallQuality is null, skipping call attributes");
                 notifyCallAttributes = false;
             } else {
-                mCallAttributes = new CallAttributes(mPreciseCallState, mNetworkType[phoneId],
+                mCallAttributes = new CallAttributes(mPreciseCallState, mCallNetworkType,
                         mCallQuality);
             }
 
@@ -1840,16 +1816,16 @@
     }
 
     @Override
-    public void notifyCallQualityChanged(CallQuality callQuality, int phoneId) {
+    public void notifyCallQualityChanged(CallQuality callQuality, int phoneId,
+            int callNetworkType) {
         if (!checkNotifyPermission("notifyCallQualityChanged()")) {
             return;
         }
 
         // merge CallQuality with PreciseCallState and network type
         mCallQuality = callQuality;
-        mCallAttributes = new CallAttributes(mPreciseCallState,
-                mNetworkType[phoneId],
-                callQuality);
+        mCallNetworkType = callNetworkType;
+        mCallAttributes = new CallAttributes(mPreciseCallState, callNetworkType, callQuality);
 
         synchronized (mRecords) {
             TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
@@ -1886,7 +1862,6 @@
                 pw.println("mCallState=" + mCallState[i]);
                 pw.println("mCallIncomingNumber=" + mCallIncomingNumber[i]);
                 pw.println("mServiceState=" + mServiceState[i]);
-                pw.println("mNetworkType=" + mNetworkType[i]);
                 pw.println("mVoiceActivationState= " + mVoiceActivationState[i]);
                 pw.println("mDataActivationState= " + mDataActivationState[i]);
                 pw.println("mUserMobileDataState= " + mUserMobileDataState[i]);
@@ -1900,6 +1875,7 @@
                 pw.println("mImsCallDisconnectCause=" + mImsReasonInfo.get(i).toString());
                 pw.decreaseIndent();
             }
+            pw.println("mCallNetworkType=" + mCallNetworkType);
             pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState);
             pw.println("mPreciseCallState=" + mPreciseCallState);
             pw.println("mCallDisconnectCause=" + mCallDisconnectCause);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7c46f1d..f2902b1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -187,6 +187,7 @@
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
+import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
@@ -1486,6 +1487,8 @@
 
     private static String sTheRealBuildSerial = Build.UNKNOWN;
 
+    private ParcelFileDescriptor[] mLifeMonitorFds;
+
     final class UiHandler extends Handler {
         public UiHandler() {
             super(com.android.server.UiThread.get().getLooper(), null, true);
@@ -1858,7 +1861,7 @@
                     ProcessRecord proc;
                     int procState;
                     int statType;
-                    int pid;
+                    int pid = -1;
                     long lastPssTime;
                     synchronized (ActivityManagerService.this) {
                         if (mPendingPssProcesses.size() <= 0) {
@@ -4741,12 +4744,12 @@
 
 
             // Figure out whether the app needs to run in autofill compat mode.
-            boolean isAutofillCompatEnabled = false;
+            AutofillOptions autofillOptions = null;
             if (UserHandle.getAppId(app.info.uid) >= Process.FIRST_APPLICATION_UID) {
                 final AutofillManagerInternal afm = LocalServices.getService(
                         AutofillManagerInternal.class);
                 if (afm != null) {
-                    isAutofillCompatEnabled = afm.isCompatibilityModeRequested(
+                    autofillOptions = afm.getAutofillOptions(
                             app.info.packageName, app.info.versionCode, app.userId);
                 }
             }
@@ -4779,7 +4782,7 @@
                         new Configuration(app.getWindowProcessController().getConfiguration()),
                         app.compat, getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
-                        buildSerial, isAutofillCompatEnabled, contentCaptureOptions);
+                        buildSerial, autofillOptions, contentCaptureOptions);
             } else {
                 thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                         null, null, null, testMode,
@@ -4788,7 +4791,7 @@
                         new Configuration(app.getWindowProcessController().getConfiguration()),
                         app.compat, getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
-                        buildSerial, isAutofillCompatEnabled, contentCaptureOptions);
+                        buildSerial, autofillOptions, contentCaptureOptions);
             }
             if (profilerInfo != null) {
                 profilerInfo.closeFd();
@@ -15036,7 +15039,7 @@
                         oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
                                 oldRecord.intent,
                                 Activity.RESULT_CANCELED, null, null,
-                                false, false, oldRecord.userId, oldRecord);
+                                false, false, oldRecord.userId);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Failure ["
                                 + queue.mQueueName + "] sending broadcast result of "
@@ -17107,6 +17110,13 @@
     }
 
     @Override
+    public boolean startUserInForegroundWithListener(final int userId,
+            @Nullable IProgressListener unlockListener) {
+        // Permission check done inside UserController.
+        return mUserController.startUser(userId, /* foreground */ true, unlockListener);
+    }
+
+    @Override
     public boolean unlockUser(int userId, byte[] token, byte[] secret, IProgressListener listener) {
         return mUserController.unlockUser(userId, token, secret, listener);
     }
@@ -18500,4 +18510,24 @@
     private boolean isOnOffloadQueue(int flags) {
         return (mEnableOffloadQueue && ((flags & Intent.FLAG_RECEIVER_OFFLOAD) != 0));
     }
+
+    @Override
+    public ParcelFileDescriptor getLifeMonitor() {
+        if (!isCallerShell()) {
+            throw new SecurityException("Only shell can call it");
+        }
+        synchronized (this) {
+            try {
+                if (mLifeMonitorFds == null) {
+                    mLifeMonitorFds = ParcelFileDescriptor.createPipe();
+                }
+                // The returned FD will be closed, but we want to keep our reader open,
+                // so return a dup instead.
+                return mLifeMonitorFds[0].dup();
+            } catch (IOException e) {
+                Slog.w(TAG, "Unable to create pipe", e);
+                return null;
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 820caf1..be17b1b 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -38,6 +38,8 @@
     static final String KEY_DEFERRAL = "bcast_deferral";
     static final String KEY_DEFERRAL_DECAY_FACTOR = "bcast_deferral_decay_factor";
     static final String KEY_DEFERRAL_FLOOR = "bcast_deferral_floor";
+    static final String KEY_ALLOW_BG_ACTIVITY_START_TIMEOUT =
+            "bcast_allow_bg_activity_start_timeout";
 
     // All time intervals are in milliseconds
     private static final long DEFAULT_TIMEOUT = 10_000;
@@ -45,6 +47,7 @@
     private static final long DEFAULT_DEFERRAL = 5_000;
     private static final float DEFAULT_DEFERRAL_DECAY_FACTOR = 0.75f;
     private static final long DEFAULT_DEFERRAL_FLOOR = 0;
+    private static final long DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT = 10_000;
 
     // All time constants are in milliseconds
 
@@ -59,6 +62,8 @@
     public float DEFERRAL_DECAY_FACTOR = DEFAULT_DEFERRAL_DECAY_FACTOR;
     // Minimum that the deferral time can decay to until the backlog fully clears
     public long DEFERRAL_FLOOR = DEFAULT_DEFERRAL_FLOOR;
+    // For how long after a whitelisted receiver's start its process can start a background activity
+    public long ALLOW_BG_ACTIVITY_START_TIMEOUT = DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT;
 
     // Settings override tracking for this instance
     private String mSettingsKey;
@@ -113,6 +118,8 @@
             DEFERRAL_DECAY_FACTOR = mParser.getFloat(KEY_DEFERRAL_DECAY_FACTOR,
                     DEFERRAL_DECAY_FACTOR);
             DEFERRAL_FLOOR = mParser.getLong(KEY_DEFERRAL_FLOOR, DEFERRAL_FLOOR);
+            ALLOW_BG_ACTIVITY_START_TIMEOUT = mParser.getLong(KEY_ALLOW_BG_ACTIVITY_START_TIMEOUT,
+                    ALLOW_BG_ACTIVITY_START_TIMEOUT);
         }
     }
 
@@ -145,6 +152,9 @@
 
             pw.print("    "); pw.print(KEY_DEFERRAL_FLOOR); pw.print(" = ");
             TimeUtils.formatDuration(DEFERRAL_FLOOR, pw);
+
+            pw.print("    "); pw.print(KEY_ALLOW_BG_ACTIVITY_START_TIMEOUT); pw.print(" = ");
+            TimeUtils.formatDuration(ALLOW_BG_ACTIVITY_START_TIMEOUT, pw);
             pw.println();
         }
     }
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index d9ea1da..efb1c44 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -74,9 +74,6 @@
     static final int MAX_BROADCAST_SUMMARY_HISTORY
             = ActivityManager.isLowRamDeviceStatic() ? 25 : 300;
 
-    // For how long after a whitelisted receiver's start its process can start a background activity
-    private static final int RECEIVER_BG_ACTIVITY_START_TIMEOUT_MS = 10_000;
-
     final ActivityManagerService mService;
 
     /**
@@ -310,9 +307,6 @@
         r.curApp = app;
         app.curReceivers.add(r);
         app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
-        if (r.allowBackgroundActivityStarts) {
-            app.addAllowBackgroundActivityStartsToken(r);
-        }
         mService.mProcessList.updateLruProcessLocked(app, false, null);
         if (!skipOomAdj) {
             mService.updateOomAdjLocked();
@@ -454,8 +448,25 @@
             Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
         }
         if (r.allowBackgroundActivityStarts && r.curApp != null) {
-            r.curApp.removeAllowBackgroundActivityStartsToken(r);
-         }
+            if (elapsed > mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT) {
+                // if the receiver has run for more than allowed bg activity start timeout,
+                // just remove the token for this process now and we're done
+                r.curApp.removeAllowBackgroundActivityStartsToken(r);
+            } else {
+                // the receiver had run for less than allowed bg activity start timeout,
+                // so allow the process to still start activities from bg for some more time
+                String msgToken = (r.curApp.toShortString() + r.toString()).intern();
+                // first, if there exists a past scheduled request to remove this token, drop
+                // that request - we don't want the token to be swept from under our feet...
+                mHandler.removeCallbacksAndMessages(msgToken);
+                // ...then schedule the removal of the token after the extended timeout
+                mHandler.postAtTime(() -> {
+                    if (r.curApp != null) {
+                        r.curApp.removeAllowBackgroundActivityStartsToken(r);
+                    }
+                }, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
+            }
+        }
         // If we're abandoning this broadcast before any receivers were actually spun up,
         // nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply.
         if (r.nextReceiver > 0) {
@@ -554,7 +565,7 @@
 
     void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
             Intent intent, int resultCode, String data, Bundle extras,
-            boolean ordered, boolean sticky, int sendingUser, BroadcastRecord br)
+            boolean ordered, boolean sticky, int sendingUser)
             throws RemoteException {
         // Send the intent to the receiver asynchronously using one-way binder calls.
         if (app != null) {
@@ -562,15 +573,6 @@
                 // If we have an app thread, do the call through that so it is
                 // correctly ordered with other one-way calls.
                 try {
-                    if (br.allowBackgroundActivityStarts) {
-                        app.addAllowBackgroundActivityStartsToken(br);
-                        // schedule removal of the whitelisting token after the timeout
-                        mHandler.postDelayed(() -> {
-                            if (app != null) {
-                                app.removeAllowBackgroundActivityStartsToken(br);
-                            }
-                        }, RECEIVER_BG_ACTIVITY_START_TIMEOUT_MS);
-                    }
                     app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                             data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
                 // TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
@@ -794,9 +796,13 @@
                     skipReceiverLocked(r);
                 }
             } else {
+                if (r.receiverTime == 0) {
+                    r.receiverTime = SystemClock.uptimeMillis();
+                }
+                maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
                 performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                         new Intent(r.intent), r.resultCode, r.resultData,
-                        r.resultExtras, r.ordered, r.initialSticky, r.userId, r);
+                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
             }
             if (ordered) {
                 r.state = BroadcastRecord.CALL_DONE_RECEIVE;
@@ -1100,7 +1106,7 @@
                             }
                             performReceiveLocked(r.callerApp, r.resultTo,
                                     new Intent(r.intent), r.resultCode,
-                                    r.resultData, r.resultExtras, false, false, r.userId, r);
+                                    r.resultData, r.resultExtras, false, false, r.userId);
                             // Set this to null so that the reference
                             // (local and remote) isn't kept in the mBroadcastHistory.
                             r.resultTo = null;
@@ -1255,6 +1261,9 @@
                 r.state = BroadcastRecord.IDLE;
                 scheduleBroadcastsLocked();
             } else {
+                if (filter.receiverList != null) {
+                    maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
+                }
                 if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
                     scheduleTempWhitelistLocked(filter.owningUid,
                             brOptions.getTemporaryAppWhitelistDuration(), r);
@@ -1561,6 +1570,7 @@
             try {
                 app.addPackage(info.activityInfo.packageName,
                         info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
+                maybeAddAllowBackgroundActivityStartsToken(app, r);
                 processCurBroadcastLocked(r, app, skipOomAdj);
                 return;
             } catch (RemoteException e) {
@@ -1611,10 +1621,23 @@
             return;
         }
 
+        maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
         mPendingBroadcast = r;
         mPendingBroadcastRecvIndex = recIdx;
     }
 
+    private void maybeAddAllowBackgroundActivityStartsToken(ProcessRecord proc, BroadcastRecord r) {
+        if (r == null || proc == null || !r.allowBackgroundActivityStarts) {
+            return;
+        }
+        String msgToken = (proc.toShortString() + r.toString()).intern();
+        // first, if there exists a past scheduled request to remove this token, drop
+        // that request - we don't want the token to be swept from under our feet...
+        mHandler.removeCallbacksAndMessages(msgToken);
+        // ...then add the token
+        proc.addAllowBackgroundActivityStartsToken(r);
+    }
+
     final void setBroadcastTimeoutLocked(long timeoutTime) {
         if (! mPendingBroadcastTimeoutMessage) {
             Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3a61dd9..30798a8 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1764,8 +1764,6 @@
                     .getPackagesForUid(uid);
             final StorageManagerInternal storageManagerInternal =
                     LocalServices.getService(StorageManagerInternal.class);
-            final String[] visibleVolIds = storageManagerInternal
-                    .getVisibleVolumesForUser(UserHandle.getUserId(uid));
             final String sandboxId = storageManagerInternal.getSandboxId(app.info.packageName);
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                     app.processName);
@@ -1776,7 +1774,7 @@
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, null, app.info.packageName,
-                        packageNames, visibleVolIds, sandboxId,
+                        packageNames, sandboxId,
                         new String[] {PROC_START_SEQ_IDENT + app.startSeq});
             } else if (hostingType.equals("app_zygote")) {
                 final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
@@ -1785,14 +1783,14 @@
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, null, app.info.packageName,
-                        packageNames, visibleVolIds, sandboxId, /*useBlastulaPool=*/ false,
+                        packageNames, sandboxId, /*useBlastulaPool=*/ false,
                         new String[] {PROC_START_SEQ_IDENT + app.startSeq});
             } else {
                 startResult = Process.start(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, invokeWith, app.info.packageName,
-                        packageNames, visibleVolIds, sandboxId,
+                        packageNames, sandboxId,
                         new String[] {PROC_START_SEQ_IDENT + app.startSeq});
             }
             checkSlow(startTime, "startProcess: returned from zygote!");
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index cbbbe47..894a704 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -38,8 +38,9 @@
 /**
  * Maps system settings to system properties.
  * <p>The properties are dynamically updated when settings change.
+ * @hide
  */
-class SettingsToPropertiesMapper {
+public class SettingsToPropertiesMapper {
 
     private static final String TAG = "SettingsToPropertiesMapper";
 
@@ -156,8 +157,8 @@
      * during current device booting.
      * @return
      */
-    public boolean isNativeFlagsResetPerformed() {
-        String value = systemPropertiesGet(RESET_PERFORMED_PROPERTY);
+    public static boolean isNativeFlagsResetPerformed() {
+        String value = SystemProperties.get(RESET_PERFORMED_PROPERTY);
         return "true".equals(value);
     }
 
@@ -166,7 +167,7 @@
      * booting.
      * @return
      */
-    public String[] getResetNativeCategories() {
+    public static String[] getResetNativeCategories() {
         if (!isNativeFlagsResetPerformed()) {
             return new String[0];
         }
@@ -214,7 +215,7 @@
         if (value == null) {
             // It's impossible to remove system property, therefore we check previous value to
             // avoid setting an empty string if the property wasn't set.
-            if (TextUtils.isEmpty(systemPropertiesGet(key))) {
+            if (TextUtils.isEmpty(SystemProperties.get(key))) {
                 return;
             }
             value = "";
@@ -224,7 +225,7 @@
         }
 
         try {
-            systemPropertiesSet(key, value);
+            SystemProperties.set(key, value);
         } catch (Exception e) {
             // Failure to set a property can be caused by SELinux denial. This usually indicates
             // that the property wasn't whitelisted in sepolicy.
@@ -250,17 +251,7 @@
     }
 
     @VisibleForTesting
-    protected String systemPropertiesGet(String key) {
-        return SystemProperties.get(key);
-    }
-
-    @VisibleForTesting
-    protected void systemPropertiesSet(String key, String value) {
-        SystemProperties.set(key, value);
-    }
-
-    @VisibleForTesting
-    protected String getResetFlagsFileContent() {
+    static String getResetFlagsFileContent() {
         String content = null;
         try {
             File reset_flag_file = new File(RESET_RECORD_FILE_PATH);
@@ -279,4 +270,4 @@
         String settingValue = Settings.Global.getString(mContentResolver, settingName);
         setProperty(propName, settingValue);
     }
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 708de73..4485a54 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -144,7 +144,7 @@
      * Whether history is enabled.
      */
     @GuardedBy("mInMemoryLock")
-    private int mMode = AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE;
+    private int mMode = AppOpsManager.HISTORICAL_MODE_DISABLED;
 
     /**
      * This granularity has been chosen to allow clean delineation for intervals
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d902201..afdfbe3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -89,6 +89,8 @@
 import android.media.audiopolicy.AudioPolicy;
 import android.media.audiopolicy.AudioPolicyConfig;
 import android.media.audiopolicy.IAudioPolicyCallback;
+import android.media.projection.IMediaProjection;
+import android.media.projection.IMediaProjectionManager;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -99,6 +101,7 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -453,6 +456,8 @@
     // Broadcast receiver for device connections intent broadcasts
     private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
 
+    private IMediaProjectionManager mProjectionService; // to validate projection token
+
     /** Interface for UserManagerService. */
     private final UserManagerInternal mUserManagerInternal;
     private final ActivityManagerInternal mActivityManagerInternal;
@@ -6186,22 +6191,21 @@
     // Audio policy management
     //==========================================================================================
     public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb,
-            boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) {
+            boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController,
+            IMediaProjection projection) {
         AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback);
 
-        String regId = null;
-        // error handling
-        boolean hasPermissionForPolicy =
-                (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(
-                        android.Manifest.permission.MODIFY_AUDIO_ROUTING));
-        if (!hasPermissionForPolicy) {
-            Slog.w(TAG, "Can't register audio policy for pid " + Binder.getCallingPid() + " / uid "
-                    + Binder.getCallingUid() + ", need MODIFY_AUDIO_ROUTING");
+        if (!isPolicyRegisterAllowed(policyConfig, projection)) {
+            Slog.w(TAG, "Permission denied to register audio policy for pid "
+                    + Binder.getCallingPid() + " / uid " + Binder.getCallingUid()
+                    + ", need MODIFY_AUDIO_ROUTING or MediaProjection that can project audio");
             return null;
         }
 
         mDynPolicyLogger.log((new AudioEventLogger.StringEvent("registerAudioPolicy for "
                 + pcb.asBinder() + " with config:" + policyConfig)).printLog(TAG));
+
+        String regId = null;
         synchronized (mAudioPolicies) {
             try {
                 if (mAudioPolicies.containsKey(pcb.asBinder())) {
@@ -6223,6 +6227,76 @@
         return regId;
     }
 
+    /**
+     * Apps with MODIFY_AUDIO_ROUTING can register any policy.
+     * Apps with an audio capable MediaProjection are allowed to register a RENDER|LOOPBACK policy
+     * as those policy do not modify the audio routing.
+     */
+    private boolean isPolicyRegisterAllowed(AudioPolicyConfig policyConfig,
+             IMediaProjection projection) {
+
+        boolean isLoopbackRenderPolicy = policyConfig.getMixes().stream().allMatch(
+                mix -> mix.getRouteFlags() == (mix.ROUTE_FLAG_RENDER | mix.ROUTE_FLAG_LOOP_BACK));
+
+        // Policy that do not modify the audio routing only need an audio projection
+        if (isLoopbackRenderPolicy && canProjectAudio(projection)) {
+            return true;
+        }
+
+        boolean hasPermissionModifyAudioRouting =
+                (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(
+                        android.Manifest.permission.MODIFY_AUDIO_ROUTING));
+        if (hasPermissionModifyAudioRouting) {
+            return true;
+        }
+        return false;
+    }
+
+    /** @return true if projection is a valid MediaProjection that can project audio. */
+    private boolean canProjectAudio(IMediaProjection projection) {
+        if (projection == null) {
+            return false;
+        }
+
+        IMediaProjectionManager projectionService = getProjectionService();
+        if (projectionService == null) {
+            Log.e(TAG, "Can't get service IMediaProjectionManager");
+            return false;
+        }
+
+        try {
+            if (!projectionService.isValidMediaProjection(projection)) {
+                Log.w(TAG, "App passed invalid MediaProjection token");
+                return false;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Can't call .isValidMediaProjection() on IMediaProjectionManager"
+                    + projectionService.asBinder(), e);
+            return false;
+        }
+
+        try {
+            if (!projection.canProjectAudio()) {
+                Log.w(TAG, "App passed MediaProjection that can not project audio");
+                return false;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Can't call .canProjectAudio() on valid IMediaProjection"
+                    + projection.asBinder(), e);
+            return false;
+        }
+
+        return true;
+    }
+
+    private IMediaProjectionManager getProjectionService() {
+        if (mProjectionService == null) {
+            IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
+            mProjectionService = IMediaProjectionManager.Stub.asInterface(b);
+        }
+        return mProjectionService;
+    }
+
     public void unregisterAudioPolicyAsync(IAudioPolicyCallback pcb) {
         mDynPolicyLogger.log((new AudioEventLogger.StringEvent("unregisterAudioPolicyAsync for "
                 + pcb.asBinder()).printLog(TAG)));
diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java
index e80b39b..89fa2de 100644
--- a/services/core/java/com/android/server/biometrics/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java
@@ -209,11 +209,7 @@
     public void binderDied() {
         // If the current client dies we should cancel the current operation.
         Slog.e(getLogTag(), "Binder died, cancelling client");
-        try {
-            getDaemonWrapper().cancel();
-        } catch (RemoteException e) {
-            Slog.e(getLogTag(), "Remote exception", e);
-        }
+        stop(false /* initiatedByClient */);
         mToken = null;
         mListener = null;
     }
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 420b23e..d84a4d2 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -19,10 +19,11 @@
 import static android.Manifest.permission.CHANGE_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.INTERNET;
 import static android.Manifest.permission.NETWORK_STACK;
-import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
-import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+import static android.Manifest.permission.UPDATE_DEVICE_STATS;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 
@@ -32,23 +33,31 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.UserInfo;
-import android.net.Uri;
+import android.net.INetd;
+import android.net.util.NetdService;
 import android.os.Build;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
+import android.util.Slog;
+import android.util.SparseIntArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map.Entry;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 /**
@@ -75,6 +84,59 @@
     // Keys are App IDs. Values are true for SYSTEM permission and false for NETWORK permission.
     private final Map<Integer, Boolean> mApps = new HashMap<>();
 
+    // Keys are App packageNames, Values are app uids. . We need to keep track of this information
+    // because PackageListObserver#onPackageRemoved does not pass the UID.
+    @GuardedBy("mPackageNameUidMap")
+    private final Map<String, Integer> mPackageNameUidMap = new HashMap<>();
+
+    private class PackageListObserver implements PackageManagerInternal.PackageListObserver {
+        @Override
+        public void onPackageAdded(String packageName) {
+            final PackageInfo app = getPackageInfo(packageName);
+            if (app == null) {
+                Slog.wtf(TAG, "Failed to get information of installed package: " + packageName);
+                return;
+            }
+            int uid = (app.applicationInfo != null) ? app.applicationInfo.uid : INVALID_UID;
+            if (uid == INVALID_UID) {
+                Slog.wtf(TAG, "Failed to get the uid of installed package: " + packageName
+                        + "uid: " + uid);
+                return;
+            }
+            if (app.requestedPermissions == null) {
+                return;
+            }
+            sendPackagePermissionsForUid(uid,
+                    filterPermission(Arrays.asList(app.requestedPermissions)));
+            synchronized (mPackageNameUidMap) {
+                mPackageNameUidMap.put(packageName, uid);
+            }
+        }
+
+        @Override
+        public void onPackageRemoved(String packageName) {
+            int uid;
+            synchronized (mPackageNameUidMap) {
+                if (!mPackageNameUidMap.containsKey(packageName)) {
+                    return;
+                }
+                uid = mPackageNameUidMap.get(packageName);
+                mPackageNameUidMap.remove(packageName);
+            }
+            int permission = 0;
+            String[] packages = mPackageManager.getPackagesForUid(uid);
+            if (packages != null && packages.length > 0) {
+                for (String name : packages) {
+                    final PackageInfo app = getPackageInfo(name);
+                    if (app != null && app.requestedPermissions != null) {
+                        permission |= filterPermission(Arrays.asList(app.requestedPermissions));
+                    }
+                }
+            }
+            sendPackagePermissionsForUid(uid, permission);
+        }
+    }
+
     public PermissionMonitor(Context context, INetworkManagementService netd) {
         mContext = context;
         mPackageManager = context.getPackageManager();
@@ -87,12 +149,21 @@
     public synchronized void startMonitoring() {
         log("Monitoring");
 
-        List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS);
+        PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+        if (pmi != null) {
+            pmi.getPackageList(new PackageListObserver());
+        } else {
+            loge("failed to get the PackageManagerInternal service");
+        }
+        List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
+                | MATCH_ANY_USER);
         if (apps == null) {
             loge("No apps");
             return;
         }
 
+        SparseIntArray netdPermsUids = new SparseIntArray();
+
         for (PackageInfo app : apps) {
             int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
             if (uid < 0) {
@@ -110,6 +181,17 @@
                     mApps.put(uid, hasRestrictedPermission);
                 }
             }
+
+            //TODO: unify the management of the permissions into one codepath.
+            if (app.requestedPermissions != null) {
+                int otherNetdPerms = filterPermission(Arrays.asList(app.requestedPermissions));
+                if (otherNetdPerms != 0) {
+                    netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms);
+                    synchronized (mPackageNameUidMap) {
+                        mPackageNameUidMap.put(app.applicationInfo.packageName, uid);
+                    }
+                }
+            }
         }
 
         List<UserInfo> users = mUserManager.getUsers(true);  // exclude dying users
@@ -121,6 +203,7 @@
 
         log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
         update(mUsers, mApps, true);
+        sendPackagePermissionsToNetd(netdPermsUids);
     }
 
     @VisibleForTesting
@@ -339,6 +422,107 @@
         }
     }
 
+    private static int filterPermission(List<String> requestedPermissions) {
+        int permissions = 0;
+        if (requestedPermissions.contains(INTERNET)) {
+            permissions |= INetd.PERMISSION_INTERNET;
+        }
+        if (requestedPermissions.contains(UPDATE_DEVICE_STATS)) {
+            permissions |= INetd.PERMISSION_UPDATE_DEVICE_STATS;
+        }
+        return permissions;
+    }
+
+    private PackageInfo getPackageInfo(String packageName) {
+        try {
+            PackageInfo app = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS
+                    | MATCH_ANY_USER);
+            return app;
+        } catch (NameNotFoundException e) {
+            // App not found.
+            loge("NameNotFoundException " + packageName);
+            return null;
+        }
+    }
+
+    /**
+     * Called by PackageListObserver when a package is installed/uninstalled. Send the updated
+     * permission information to netd.
+     *
+     * @param uid the app uid of the package installed
+     * @param permissions the permissions the app requested and netd cares about.
+     *
+     * @hide
+     */
+    private void sendPackagePermissionsForUid(int uid, int permissions) {
+        SparseIntArray netdPermissionsAppIds = new SparseIntArray();
+        netdPermissionsAppIds.put(uid, permissions);
+        sendPackagePermissionsToNetd(netdPermissionsAppIds);
+    }
+
+    /**
+     * Called by packageManagerService to send IPC to netd. Grant or revoke the INTERNET
+     * and/or UPDATE_DEVICE_STATS permission of the uids in array.
+     *
+     * @param netdPermissionsAppIds integer pairs of uids and the permission granted to it. If the
+     * permission is 0, revoke all permissions of that uid.
+     *
+     * @hide
+     */
+    private void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) {
+        INetd netdService = NetdService.getInstance();
+        if (netdService == null) {
+            Log.e(TAG, "Failed to get the netd service");
+            return;
+        }
+        ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
+        ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
+        ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
+        ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
+        for (int i = 0; i < netdPermissionsAppIds.size(); i++) {
+            int permissions = netdPermissionsAppIds.valueAt(i);
+            switch(permissions) {
+                case (INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS):
+                    allPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+                    break;
+                case INetd.PERMISSION_INTERNET:
+                    internetPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+                    break;
+                case INetd.PERMISSION_UPDATE_DEVICE_STATS:
+                    updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+                    break;
+                case INetd.NO_PERMISSIONS:
+                    uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
+                    break;
+                default:
+                    Log.e(TAG, "unknown permission type: " + permissions + "for uid: "
+                            + netdPermissionsAppIds.keyAt(i));
+            }
+        }
+        try {
+            // TODO: add a lock inside netd to protect IPC trafficSetNetPermForUids()
+            if (allPermissionAppIds.size() != 0) {
+                netdService.trafficSetNetPermForUids(
+                        INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
+                        ArrayUtils.convertToIntArray(allPermissionAppIds));
+            }
+            if (internetPermissionAppIds.size() != 0) {
+                netdService.trafficSetNetPermForUids(INetd.PERMISSION_INTERNET,
+                        ArrayUtils.convertToIntArray(internetPermissionAppIds));
+            }
+            if (updateStatsPermissionAppIds.size() != 0) {
+                netdService.trafficSetNetPermForUids(INetd.PERMISSION_UPDATE_DEVICE_STATS,
+                        ArrayUtils.convertToIntArray(updateStatsPermissionAppIds));
+            }
+            if (uninstalledAppIds.size() != 0) {
+                netdService.trafficSetNetPermForUids(INetd.NO_PERMISSIONS,
+                        ArrayUtils.convertToIntArray(uninstalledAppIds));
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Pass appId list of special permission failed." + e);
+        }
+    }
+
     private static void log(String s) {
         if (DBG) {
             Log.d(TAG, s);
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index bfa7f9d..4e4b15f 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -19,6 +19,7 @@
 import android.Manifest;
 import android.accounts.Account;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
@@ -1174,6 +1175,7 @@
     }
 
     @Override
+    @RequiresPermission(android.Manifest.permission.CACHE_CONTENT)
     public void putCache(String packageName, Uri key, Bundle value, int userId) {
         Bundle.setDefusable(value, true);
         enforceCrossUserPermission(userId, TAG);
@@ -1196,6 +1198,7 @@
     }
 
     @Override
+    @RequiresPermission(android.Manifest.permission.CACHE_CONTENT)
     public Bundle getCache(String packageName, Uri key, int userId) {
         enforceCrossUserPermission(userId, TAG);
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b020997..01d19c0 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -37,7 +37,9 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.hardware.SensorManager;
 import android.hardware.display.AmbientBrightnessDayStats;
 import android.hardware.display.BrightnessChangeEvent;
@@ -1277,7 +1279,14 @@
         if (token == null) {
             return false;
         }
-        SurfaceControl.screenshot(token, outSurface);
+        final GraphicBuffer gb = SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(
+                token, new Rect(), 0 /* width */, 0 /* height */, false /* useIdentityTransform */,
+                0 /* rotation */);
+        try {
+            outSurface.attachAndQueueBuffer(gb);
+        } catch (RuntimeException e) {
+            Slog.w(TAG, "Failed to take screenshot - " + e.getMessage());
+        }
         return true;
     }
 
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 4891947..77df10bf 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -65,7 +65,8 @@
             new LongSparseArray<LocalDisplayDevice>();
 
     @SuppressWarnings("unused")  // Becomes active at instantiation time.
-    private HotplugDisplayEventReceiver mHotplugReceiver;
+    private PhysicalDisplayEventReceiver mPhysicalDisplayEventReceiver;
+
 
     // Called with SyncRoot lock held.
     public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
@@ -77,7 +78,7 @@
     public void registerLocked() {
         super.registerLocked();
 
-        mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
+        mPhysicalDisplayEventReceiver = new PhysicalDisplayEventReceiver(getHandler().getLooper());
 
         for (long physicalDisplayId : SurfaceControl.getPhysicalDisplayIds()) {
             tryConnectDisplayLocked(physicalDisplayId);
@@ -727,8 +728,8 @@
         }
     }
 
-    private final class HotplugDisplayEventReceiver extends DisplayEventReceiver {
-        public HotplugDisplayEventReceiver(Looper looper) {
+    private final class PhysicalDisplayEventReceiver extends DisplayEventReceiver {
+        PhysicalDisplayEventReceiver(Looper looper) {
             super(looper, VSYNC_SOURCE_APP);
         }
 
@@ -742,5 +743,15 @@
                 }
             }
         }
+
+        @Override
+        public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+            if (DEBUG) {
+                Slog.d(TAG, "onConfigChanged("
+                        + "timestampNanos=" + timestampNanos
+                        + ", builtInDisplayId=" + physicalDisplayId
+                        + ", configId=" + configId + ")");
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java
index 6899c3f..647727f 100644
--- a/services/core/java/com/android/server/gpu/GpuService.java
+++ b/services/core/java/com/android/server/gpu/GpuService.java
@@ -62,6 +62,7 @@
 
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
     private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt";
+    private static final String GAME_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt";
     private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP;
 
     private final Context mContext;
@@ -162,6 +163,25 @@
         }
     }
 
+    private static void assetToSettingsGlobal(Context context, Context driverContext,
+            String fileName, String settingsGlobal, CharSequence delimiter) {
+        try {
+            final BufferedReader reader = new BufferedReader(
+                    new InputStreamReader(driverContext.getAssets().open(fileName)));
+            final ArrayList<String> assetStrings = new ArrayList<>();
+            for (String assetString; (assetString = reader.readLine()) != null; ) {
+                assetStrings.add(assetString);
+            }
+            Settings.Global.putString(context.getContentResolver(),
+                                      settingsGlobal,
+                                      String.join(delimiter, assetStrings));
+        } catch (IOException e) {
+            if (DEBUG) {
+                Slog.w(TAG, "Failed to load " + fileName + ", abort.");
+            }
+        }
+    }
+
     private void fetchGameDriverPackageProperties() {
         final ApplicationInfo driverInfo;
         try {
@@ -186,29 +206,25 @@
         // Reset the whitelist.
         Settings.Global.putString(mContentResolver,
                                   Settings.Global.GAME_DRIVER_WHITELIST, "");
+        // Reset the sphal libraries
+        Settings.Global.putString(mContentResolver,
+                                  Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, "");
         mGameDriverVersionCode = driverInfo.longVersionCode;
 
         try {
             final Context driverContext = mContext.createPackageContext(mDriverPackageName,
                                                                         Context.CONTEXT_RESTRICTED);
-            final BufferedReader reader = new BufferedReader(
-                    new InputStreamReader(driverContext.getAssets()
-                                              .open(GAME_DRIVER_WHITELIST_FILENAME)));
-            final ArrayList<String> whitelistedPackageNames = new ArrayList<>();
-            for (String packageName; (packageName = reader.readLine()) != null; ) {
-                whitelistedPackageNames.add(packageName);
-            }
-            Settings.Global.putString(mContentResolver,
-                                      Settings.Global.GAME_DRIVER_WHITELIST,
-                                      String.join(",", whitelistedPackageNames));
+
+            assetToSettingsGlobal(mContext, driverContext, GAME_DRIVER_WHITELIST_FILENAME,
+                    Settings.Global.GAME_DRIVER_WHITELIST, ",");
+
+            assetToSettingsGlobal(mContext, driverContext, GAME_DRIVER_SPHAL_LIBRARIES_FILENAME,
+                    Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, ":");
+
         } catch (PackageManager.NameNotFoundException e) {
             if (DEBUG) {
                 Slog.w(TAG, "driver package '" + mDriverPackageName + "' not installed");
             }
-        } catch (IOException e) {
-            if (DEBUG) {
-                Slog.w(TAG, "Failed to load whitelist driver package, abort.");
-            }
         }
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 6710986..2026957 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -31,6 +31,8 @@
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.media.tv.TvContract;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager.TvInputCallback;
 import android.os.SystemProperties;
 import android.provider.Settings.Global;
 import android.util.Slog;
@@ -84,10 +86,13 @@
             .get(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT, "0").contains("tvinput");
 
     // Keeps the mapping (HDMI port ID to TV input URI) to keep track of the TV inputs ready to
-    // accept input switching request from HDMI devices. Requests for which the corresponding
-    // input ID is not yet registered by TV input framework need to be buffered for delayed
-    // processing.
-    private final HashMap<Integer, String> mTvInputs = new HashMap<>();
+    // accept input switching request from HDMI devices.
+    @GuardedBy("mLock")
+    private final HashMap<Integer, String> mPortIdToTvInputs = new HashMap<>();
+
+    // A map from TV input id to HDMI device info.
+    @GuardedBy("mLock")
+    private final HashMap<String, HdmiDeviceInfo> mTvInputsToDeviceInfo = new HashMap<>();
 
     // Copy of mDeviceInfos to guarantee thread-safety.
     @GuardedBy("mLock")
@@ -103,14 +108,57 @@
             mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, false);
         mSystemAudioControlFeatureEnabled =
             mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
-        // TODO(amyjojo): Maintain a portId to TvinputId map.
-        mTvInputs.put(2, "com.droidlogic.tvinput/.services.Hdmi1InputService/HW5");
-        mTvInputs.put(4, "com.droidlogic.tvinput/.services.Hdmi2InputService/HW6");
-        mTvInputs.put(1, "com.droidlogic.tvinput/.services.Hdmi3InputService/HW7");
     }
 
     private static final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml";
 
+    private final TvInputCallback mTvInputCallback = new TvInputCallback() {
+        @Override
+        public void onInputAdded(String inputId) {
+            addOrUpdateTvInput(inputId);
+        }
+
+        @Override
+        public void onInputRemoved(String inputId) {
+            removeTvInput(inputId);
+        }
+
+        @Override
+        public void onInputUpdated(String inputId) {
+            addOrUpdateTvInput(inputId);
+        }
+    };
+
+    @ServiceThreadOnly
+    private void addOrUpdateTvInput(String inputId) {
+        assertRunOnServiceThread();
+        synchronized (mLock) {
+            TvInputInfo tvInfo = mService.getTvInputManager().getTvInputInfo(inputId);
+            if (tvInfo == null) {
+                return;
+            }
+            HdmiDeviceInfo info = tvInfo.getHdmiDeviceInfo();
+            if (info == null) {
+                return;
+            }
+            mPortIdToTvInputs.put(info.getPortId(), inputId);
+            mTvInputsToDeviceInfo.put(inputId, info);
+        }
+    }
+
+    @ServiceThreadOnly
+    private void removeTvInput(String inputId) {
+        assertRunOnServiceThread();
+        synchronized (mLock) {
+            if (mTvInputsToDeviceInfo.get(inputId) == null) {
+                return;
+            }
+            int portId = mTvInputsToDeviceInfo.get(inputId).getPortId();
+            mPortIdToTvInputs.remove(portId);
+            mTvInputsToDeviceInfo.remove(inputId);
+        }
+    }
+
     /**
      * Called when a device is newly added or a new device is detected or
      * an existing device is updated.
@@ -248,17 +296,29 @@
         }
         if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
             mCecMessageCache.flushAll();
-        } else {
-            if (connected) {
-                launchDeviceDiscovery();
-            } else {
-                // TODO(amyjojo): remove device from mDeviceInfo
+        } else if (!connected && mPortIdToTvInputs.get(portId) != null) {
+            String tvInputId = mPortIdToTvInputs.get(portId);
+            HdmiDeviceInfo info = mTvInputsToDeviceInfo.get(tvInputId);
+            if (info == null) {
+                return;
             }
+            // Update with TIF on the device removal. TIF callback will update
+            // mPortIdToTvInputs and mPortIdToTvInputs.
+            removeCecDevice(info.getLogicalAddress());
         }
     }
 
     @Override
     @ServiceThreadOnly
+    protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
+        super.disableDevice(initiatedByCec, callback);
+        assertRunOnServiceThread();
+        mService.unregisterTvInputCallback(mTvInputCallback);
+        // TODO(amyjojo): check disableDevice and onStandby behaviors per spec
+    }
+
+    @Override
+    @ServiceThreadOnly
     protected void onStandby(boolean initiatedByCec, int standbyAction) {
         assertRunOnServiceThread();
         mTvSystemAudioModeSupport = false;
@@ -284,6 +344,7 @@
                         mAddress, mService.getPhysicalAddress(), mDeviceType));
         mService.sendCecCommand(
                 HdmiCecMessageBuilder.buildDeviceVendorIdCommand(mAddress, mService.getVendorId()));
+        mService.registerTvInputCallback(mTvInputCallback);
         int systemAudioControlOnPowerOnProp =
                 SystemProperties.getInt(
                         PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON,
@@ -996,6 +1057,17 @@
         }
         // Wake up device
         mService.wakeUp();
+        // If Audio device is the active source or is on the active path,
+        // enable system audio mode without querying TV's support on sam.
+        // This is per HDMI spec 1.4b CEC 13.15.4.2.
+        if (mService.pathToPortId(getActiveSource().physicalAddress)
+                != Constants.INVALID_PORT_ID) {
+            setSystemAudioMode(true);
+            mService.sendCecCommand(
+                HdmiCecMessageBuilder.buildSetSystemAudioMode(
+                    mAddress, Constants.ADDR_BROADCAST, true));
+            return;
+        }
         // Check if TV supports System Audio Control.
         // Handle broadcasting setSystemAudioMode on or aborting message on callback.
         queryTvSystemAudioModeSupport(new TvSystemAudioModeSupportedCallback() {
@@ -1064,9 +1136,9 @@
             setLocalActivePort(portId);
             return;
         } else {
-            String uri = mTvInputs.get(portId);
+            String uri = mPortIdToTvInputs.get(portId);
             if (uri != null) {
-                switchToTvInput(mTvInputs.get(portId));
+                switchToTvInput(uri);
             } else {
                 HdmiLogger.debug("Port number does not match any Tv Input.");
                 return;
@@ -1215,7 +1287,8 @@
         pw.println("mArcIntentUsed: " + mArcIntentUsed);
         pw.println("mRoutingPort: " + getRoutingPort());
         pw.println("mLocalActivePort: " + getLocalActivePort());
-        HdmiUtils.dumpMap(pw, "mTvInputs:", mTvInputs);
+        HdmiUtils.dumpMap(pw, "mPortIdToTvInputs:", mPortIdToTvInputs);
+        HdmiUtils.dumpMap(pw, "mTvInputsToDeviceInfo:", mTvInputsToDeviceInfo);
         HdmiUtils.dumpSparseArray(pw, "mDeviceInfos:", mDeviceInfos);
         pw.decreaseIndent();
         super.dump(pw);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 072238e..f5adb01 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -576,7 +576,8 @@
                 Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
                 Global.MHL_INPUT_SWITCHING_ENABLED,
                 Global.MHL_POWER_CHARGE_ENABLED,
-                Global.HDMI_CEC_SWITCH_ENABLED
+                Global.HDMI_CEC_SWITCH_ENABLED,
+                Global.DEVICE_NAME
         };
         for (String s : settings) {
             resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
@@ -642,6 +643,10 @@
                 case Global.MHL_POWER_CHARGE_ENABLED:
                     mMhlController.setOption(OPTION_MHL_POWER_CHARGE, toInt(enabled));
                     break;
+                case Global.DEVICE_NAME:
+                    String deviceName = readStringSetting(option, Build.MODEL);
+                    setDisplayName(deviceName);
+                    break;
             }
         }
     }
@@ -670,6 +675,15 @@
         return SystemProperties.getBoolean(key, defVal);
     }
 
+    String readStringSetting(String key, String defVal) {
+        ContentResolver cr = getContext().getContentResolver();
+        String content = Global.getString(cr, key);
+        if (TextUtils.isEmpty(content)) {
+            return defVal;
+        }
+        return content;
+    }
+
     private void initializeCec(int initiatedBy) {
         mAddressAllocated = false;
         mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
@@ -1178,13 +1192,29 @@
     }
 
     private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) {
-        // TODO: find better name instead of model name.
-        String displayName = Build.MODEL;
+        String displayName = readStringSetting(Global.DEVICE_NAME, Build.MODEL);
         return new HdmiDeviceInfo(logicalAddress,
                 getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
                 getVendorId(), displayName, powerStatus);
     }
 
+    // Set the display name in HdmiDeviceInfo of the current devices to content provided by
+    // Global.DEVICE_NAME. Only set and broadcast if the new name is different.
+    private void setDisplayName(String newDisplayName) {
+        for (HdmiCecLocalDevice device : getAllLocalDevices()) {
+            HdmiDeviceInfo deviceInfo = device.getDeviceInfo();
+            if (deviceInfo.getDisplayName().equals(newDisplayName)) {
+                continue;
+            }
+            device.setDeviceInfo(new HdmiDeviceInfo(
+                    deviceInfo.getLogicalAddress(), deviceInfo.getPhysicalAddress(),
+                    deviceInfo.getPortId(), deviceInfo.getDeviceType(), deviceInfo.getVendorId(),
+                    newDisplayName, deviceInfo.getDevicePowerStatus()));
+            sendCecCommand(HdmiCecMessageBuilder.buildSetOsdNameCommand(
+                    device.mAddress, Constants.ADDR_TV, newDisplayName));
+        }
+    }
+
     @ServiceThreadOnly
     void handleMhlHotplugEvent(int portId, boolean connected) {
         assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 2bfb31f..d2ccfcc 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -133,7 +133,8 @@
      * {@link com.android.internal.infra.AbstractRemoteService} instances, or
      * {@code null} when the service doesn't bind to remote services.
      * @param disallowProperty when not {@code null}, defines a {@link UserManager} restriction that
-     *        disables the service.
+     *        disables the service. <b>NOTE: </b> you'll also need to add it to
+     *        {@code UserRestrictionsUtils.USER_RESTRICTIONS}.
      */
     protected AbstractMasterSystemService(@NonNull Context context,
             @Nullable ServiceNameResolver serviceNameResolver,
diff --git a/services/core/java/com/android/server/job/controllers/StorageController.java b/services/core/java/com/android/server/job/controllers/StorageController.java
index c2ae53f..51187df 100644
--- a/services/core/java/com/android/server/job/controllers/StorageController.java
+++ b/services/core/java/com/android/server/job/controllers/StorageController.java
@@ -81,20 +81,16 @@
         synchronized (mLock) {
             for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
                 final JobStatus ts = mTrackedTasks.valueAt(i);
-                boolean previous = ts.setStorageNotLowConstraintSatisfied(storageNotLow);
-                if (previous != storageNotLow) {
-                    reportChange = true;
-                }
+                reportChange |= ts.setStorageNotLowConstraintSatisfied(storageNotLow);
             }
         }
-        // Let the scheduler know that state has changed. This may or may not result in an
-        // execution.
-        if (reportChange) {
-            mStateChangedListener.onControllerStateChanged();
-        }
-        // Also tell the scheduler that any ready jobs should be flushed.
         if (storageNotLow) {
+            // Tell the scheduler that any ready jobs should be flushed.
             mStateChangedListener.onRunJobNow(null);
+        } else if (reportChange) {
+            // Let the scheduler know that state has changed. This may or may not result in an
+            // execution.
+            mStateChangedListener.onControllerStateChanged();
         }
     }
 
@@ -143,9 +139,10 @@
                             + sElapsedRealtimeClock.millis());
                 }
                 mStorageLow = true;
+                maybeReportNewStorageState();
             } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
                 if (DEBUG) {
-                    Slog.d(TAG, "Available stoage high enough to do work. @ "
+                    Slog.d(TAG, "Available storage high enough to do work. @ "
                             + sElapsedRealtimeClock.millis());
                 }
                 mStorageLow = false;
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 243b6fe..5156300 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -140,6 +140,9 @@
     private static final int LOCATION_HAS_SPEED_ACCURACY = 64;
     private static final int LOCATION_HAS_BEARING_ACCURACY = 128;
 
+    // these need to match ElapsedRealtimeFlags enum in types.hal
+    private static final int ELAPSED_REALTIME_HAS_TIMESTAMP_NS = 1;
+
     // IMPORTANT - the GPS_DELETE_* symbols here must match GnssAidingData enum in IGnss.hal
     private static final int GPS_DELETE_EPHEMERIS = 0x0001;
     private static final int GPS_DELETE_ALMANAC = 0x0002;
@@ -527,6 +530,7 @@
                 mPowerManager.getPowerSaveState(ServiceType.LOCATION);
         switch (result.locationMode) {
             case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
+            case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
                 // If we are in battery saver mode and the screen is off, disable GPS.
                 disableGps |= result.batterySaverEnabled && !mPowerManager.isInteractive();
                 break;
@@ -756,10 +760,16 @@
         float speedAccuracyMetersPerSecond = location.getSpeedAccuracyMetersPerSecond();
         float bearingAccuracyDegrees = location.getBearingAccuracyDegrees();
         long timestamp = location.getTime();
-        native_inject_best_location(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
-                altitudeMeters, speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters,
-                verticalAccuracyMeters, speedAccuracyMetersPerSecond, bearingAccuracyDegrees,
-                timestamp);
+
+        int elapsedRealtimeFlags = ELAPSED_REALTIME_HAS_TIMESTAMP_NS;
+        long elapsedRealtimeNanos = location.getElapsedRealtimeNanos();
+
+        native_inject_best_location(
+                gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+                altitudeMeters, speedMetersPerSec, bearingDegrees,
+                horizontalAccuracyMeters, verticalAccuracyMeters,
+                speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
+                elapsedRealtimeFlags, elapsedRealtimeNanos);
     }
 
     /** Returns true if the location request is too frequent. */
@@ -1259,9 +1269,6 @@
 
         if (VERBOSE) Log.v(TAG, "reportLocation " + location.toString());
 
-        // It would be nice to push the elapsed real-time timestamp
-        // further down the stack, but this is still useful
-        location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
         location.setExtras(mLocationExtras.getBundle());
 
         reportLocation(location);
@@ -2150,17 +2157,11 @@
     private native int native_read_nmea(byte[] buffer, int bufferSize);
 
     private native void native_inject_best_location(
-            int gnssLocationFlags,
-            double latitudeDegrees,
-            double longitudeDegrees,
-            double altitudeMeters,
-            float speedMetersPerSec,
-            float bearingDegrees,
-            float horizontalAccuracyMeters,
-            float verticalAccuracyMeters,
-            float speedAccuracyMetersPerSecond,
-            float bearingAccuracyDegrees,
-            long timestamp);
+            int gnssLocationFlags, double latitudeDegrees, double longitudeDegrees,
+            double altitudeMeters, float speedMetersPerSec, float bearingDegrees,
+            float horizontalAccuracyMeters, float verticalAccuracyMeters,
+            float speedAccuracyMetersPerSecond, float bearingAccuracyDegrees,
+            long timestamp, int elapsedRealtimeFlags, long elapsedRealtimeNanos);
 
     private native void native_inject_location(double latitude, double longitude, float accuracy);
 
@@ -2187,4 +2188,4 @@
             int lac, int cid);
 
     private native void native_agps_set_id(int type, String setid);
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index afe3473..00e1ffa 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -34,13 +34,11 @@
 import com.android.internal.location.ILocationProviderManager;
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
-import com.android.internal.os.TransferPipe;
 import com.android.server.FgThread;
 import com.android.server.LocationManagerService;
 import com.android.server.ServiceWatcher;
 
 import java.io.FileDescriptor;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -189,14 +187,6 @@
                 pw.println("  additional packages=" + mProviderPackages);
             }
         }
-        mServiceWatcher.runOnBinderBlocking(binder -> {
-            try {
-                TransferPipe.dumpAsync(binder, fd, args);
-            } catch (IOException | RemoteException e) {
-                pw.println("  <failed to dump location provider: " + e + ">");
-            }
-            return null;
-        }, null);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index e1aa92c..ee968c8 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -412,6 +412,10 @@
             return new SyntheticPasswordManager(getContext(), storage, getUserManager());
         }
 
+        public boolean hasBiometrics() {
+            return BiometricManager.hasBiometrics(mContext);
+        }
+
         public int binderGetCallingUid() {
             return Binder.getCallingUid();
         }
@@ -1969,7 +1973,8 @@
         } catch (RemoteException ex) {
             Slog.w(TAG, "unable to clear GK secure user id");
         }
-        if (unknownUser || mUserManager.getUserInfo(userId).isManagedProfile()) {
+        UserInfo userInfo = mUserManager.getUserInfo(userId);
+        if (unknownUser || userInfo == null || userInfo.isManagedProfile()) {
             removeKeystoreProfileKey(userId);
         }
     }
@@ -2441,7 +2446,7 @@
             notifyActivePasswordMetricsAvailable(userCredential, userId);
             unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId);
             // Reset lockout
-            if (BiometricManager.hasBiometrics(mContext)) {
+            if (mInjector.hasBiometrics()) {
                 BiometricManager bm = mContext.getSystemService(BiometricManager.class);
                 Slog.i(TAG, "Resetting lockout, length: "
                         + authResult.gkResponse.getPayload().length);
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index b221241..152cf7f 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -77,6 +77,7 @@
     private final int mUserId;
     private final String mPackageName;
     private final String mTag;
+    private final Bundle mSessionInfo;
     private final ControllerLink mController;
     private final MediaSession.Token mSessionToken;
     private final SessionLink mSession;
@@ -121,13 +122,14 @@
     private String mMetadataDescription;
 
     public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
-            SessionCallbackLink cb, String tag, MediaSessionService.ServiceImpl service,
-            Looper handlerLooper) {
+            SessionCallbackLink cb, String tag, Bundle sessionInfo,
+            MediaSessionService.ServiceImpl service, Looper handlerLooper) {
         mOwnerPid = ownerPid;
         mOwnerUid = ownerUid;
         mUserId = userId;
         mPackageName = ownerPackageName;
         mTag = tag;
+        mSessionInfo = sessionInfo;
         mController = new ControllerLink(new ControllerStub());
         mSessionToken = new MediaSession.Token(mController);
         mSession = new SessionLink(new SessionStub());
@@ -1309,6 +1311,11 @@
         }
 
         @Override
+        public Bundle getSessionInfo() {
+            return mSessionInfo;
+        }
+
+        @Override
         public PendingIntent getLaunchPendingIntent() {
             return mLaunchIntent;
         }
diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
index 62d9b20..b86328b 100644
--- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
@@ -549,9 +549,11 @@
     }
 
     private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
-            String callerPackageName, SessionCallbackLink cb, String tag) throws RemoteException {
+            String callerPackageName, SessionCallbackLink cb, String tag, Bundle sessionInfo)
+            throws RemoteException {
         synchronized (mLock) {
-            return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
+            return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb,
+                    tag, sessionInfo);
         }
     }
 
@@ -563,7 +565,7 @@
      * 4. It needs to be added to the relevant user record.
      */
     private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
-            String callerPackageName, SessionCallbackLink cb, String tag) {
+            String callerPackageName, SessionCallbackLink cb, String tag, Bundle sessionInfo) {
         FullUserRecord user = getFullUserRecordLocked(userId);
         if (user == null) {
             Log.w(TAG, "Request from invalid user: " +  userId + ", pkg=" + callerPackageName);
@@ -571,7 +573,7 @@
         }
 
         final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
-                callerPackageName, cb, tag, this, mHandler.getLooper());
+                callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper());
         try {
             cb.getBinder().linkToDeath(session, 0);
         } catch (RemoteException e) {
@@ -991,7 +993,7 @@
 
         @Override
         public SessionLink createSession(String packageName, SessionCallbackLink cb, String tag,
-                int userId) throws RemoteException {
+                Bundle sessionInfo, int userId) throws RemoteException {
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
@@ -1002,8 +1004,8 @@
                 if (cb == null) {
                     throw new IllegalArgumentException("Controller callback cannot be null");
                 }
-                return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
-                        .getSessionBinder();
+                return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag,
+                        sessionInfo).getSessionBinder();
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 3557fcf..e8c402c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -594,8 +594,6 @@
             mConditionProviders.migrateToXml();
             handleSavePolicyFile();
         }
-
-        mAssistants.ensureAssistant();
     }
 
     private void loadPolicyFile() {
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index 2aaa1ed..26cc0c1 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import android.app.ActivityManager;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -49,12 +50,12 @@
     private static final String USAGE =
               "usage: cmd notification SUBCMD [args]\n\n"
             + "SUBCMDs:\n"
-            + "  allow_listener COMPONENT [user_id]\n"
-            + "  disallow_listener COMPONENT [user_id]\n"
-            + "  allow_assistant COMPONENT\n"
-            + "  remove_assistant COMPONENT\n"
-            + "  allow_dnd PACKAGE\n"
-            + "  disallow_dnd PACKAGE\n"
+            + "  allow_listener COMPONENT [user_id (current user if not specified)]\n"
+            + "  disallow_listener COMPONENT [user_id (current user if not specified)]\n"
+            + "  allow_assistant COMPONENT [user_id (current user if not specified)]\n"
+            + "  remove_assistant COMPONENT [user_id (current user if not specified)]\n"
+            + "  allow_dnd PACKAGE [user_id (current user if not specified)]\n"
+            + "  disallow_dnd PACKAGE [user_id (current user if not specified)]\n"
             + "  suspend_package PACKAGE\n"
             + "  unsuspend_package PACKAGE\n"
             + "  post [--help | flags] TAG TEXT";
@@ -109,14 +110,24 @@
         try {
             switch (cmd.replace('-', '_')) {
                 case "allow_dnd": {
-                    mBinderService.setNotificationPolicyAccessGranted(
-                            getNextArgRequired(), true);
+                    String packageName = getNextArgRequired();
+                    int userId = ActivityManager.getCurrentUser();
+                    if (peekNextArg() != null) {
+                        userId = Integer.parseInt(getNextArgRequired());
+                    }
+                    mBinderService.setNotificationPolicyAccessGrantedForUser(
+                            packageName, userId, true);
                 }
                 break;
 
                 case "disallow_dnd": {
-                    mBinderService.setNotificationPolicyAccessGranted(
-                            getNextArgRequired(), false);
+                    String packageName = getNextArgRequired();
+                    int userId = ActivityManager.getCurrentUser();
+                    if (peekNextArg() != null) {
+                        userId = Integer.parseInt(getNextArgRequired());
+                    }
+                    mBinderService.setNotificationPolicyAccessGrantedForUser(
+                            packageName, userId, false);
                 }
                 break;
                 case "allow_listener": {
@@ -125,13 +136,11 @@
                         pw.println("Invalid listener - must be a ComponentName");
                         return -1;
                     }
-                    String userId = getNextArg();
-                    if (userId == null) {
-                        mBinderService.setNotificationListenerAccessGranted(cn, true);
-                    } else {
-                        mBinderService.setNotificationListenerAccessGrantedForUser(
-                                cn, Integer.parseInt(userId), true);
+                    int userId = ActivityManager.getCurrentUser();
+                    if (peekNextArg() != null) {
+                        userId = Integer.parseInt(getNextArgRequired());
                     }
+                    mBinderService.setNotificationListenerAccessGrantedForUser(cn, userId, true);
                 }
                 break;
                 case "disallow_listener": {
@@ -140,13 +149,11 @@
                         pw.println("Invalid listener - must be a ComponentName");
                         return -1;
                     }
-                    String userId = getNextArg();
-                    if (userId == null) {
-                        mBinderService.setNotificationListenerAccessGranted(cn, false);
-                    } else {
-                        mBinderService.setNotificationListenerAccessGrantedForUser(
-                                cn, Integer.parseInt(userId), false);
+                    int userId = ActivityManager.getCurrentUser();
+                    if (peekNextArg() != null) {
+                        userId = Integer.parseInt(getNextArgRequired());
                     }
+                    mBinderService.setNotificationListenerAccessGrantedForUser(cn, userId, false);
                 }
                 break;
                 case "allow_assistant": {
@@ -155,7 +162,11 @@
                         pw.println("Invalid assistant - must be a ComponentName");
                         return -1;
                     }
-                    mBinderService.setNotificationAssistantAccessGranted(cn, true);
+                    int userId = ActivityManager.getCurrentUser();
+                    if (peekNextArg() != null) {
+                        userId = Integer.parseInt(getNextArgRequired());
+                    }
+                    mBinderService.setNotificationAssistantAccessGrantedForUser(cn, userId, true);
                 }
                 break;
                 case "disallow_assistant": {
@@ -164,7 +175,11 @@
                         pw.println("Invalid assistant - must be a ComponentName");
                         return -1;
                     }
-                    mBinderService.setNotificationAssistantAccessGranted(cn, false);
+                    int userId = ActivityManager.getCurrentUser();
+                    if (peekNextArg() != null) {
+                        userId = Integer.parseInt(getNextArgRequired());
+                    }
+                    mBinderService.setNotificationAssistantAccessGrantedForUser(cn, userId, false);
                 }
                 break;
                 case "suspend_package": {
@@ -176,6 +191,7 @@
                     // only use for testing
                     mDirectService.simulatePackageSuspendBroadcast(false, getNextArgRequired());
                 }
+                break;
                 case "distract_package": {
                     // only use for testing
                     // Flag values are in
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 2c47ec0..660309c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -722,7 +722,8 @@
 
         if (!channel.equals(updatedChannel)) {
             // only log if there are real changes
-            MetricsLogger.action(getChannelLog(updatedChannel, pkg));
+            MetricsLogger.action(getChannelLog(updatedChannel, pkg)
+                    .setSubtype(fromUser ? 1 : 0));
         }
 
         if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 1dada92..f4454ae 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -146,8 +146,8 @@
         if (isDumpstateBinderServiceRunningLocked()) {
             Slog.w(TAG, "'dumpstate' is already running. Cannot start a new bugreport"
                     + " while another one is currently in progress.");
-            // TODO(b/111441001): Use a new error code; add this to the documentation of the API.
-            reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
+            reportError(listener,
+                    IDumpstateListener.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
             return;
         }
 
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index dc990af..a4c04b2 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -22,6 +22,10 @@
 import android.apex.ApexInfoList;
 import android.apex.ApexSessionInfo;
 import android.apex.IApexService;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.PackageParserException;
@@ -30,6 +34,7 @@
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.File;
@@ -48,40 +53,58 @@
 class ApexManager {
     static final String TAG = "ApexManager";
     private final IApexService mApexService;
-    private final Map<String, PackageInfo> mActivePackagesCache;
+    private final Context mContext;
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private Map<String, PackageInfo> mActivePackagesCache;
 
-    ApexManager() {
+    ApexManager(Context context) {
         try {
             mApexService = IApexService.Stub.asInterface(
                 ServiceManager.getServiceOrThrow("apexservice"));
         } catch (ServiceNotFoundException e) {
             throw new IllegalStateException("Required service apexservice not available");
         }
-        mActivePackagesCache = populateActivePackagesCache();
+        mContext = context;
     }
 
-    @NonNull
-    private Map<String, PackageInfo> populateActivePackagesCache() {
-        try {
-            List<PackageInfo> list = new ArrayList<>();
-            final ApexInfo[] activePkgs = mApexService.getActivePackages();
-            for (ApexInfo ai : activePkgs) {
-                // If the device is using flattened APEX, don't report any APEX
-                // packages since they won't be managed or updated by PackageManager.
-                if ((new File(ai.packagePath)).isDirectory()) {
-                    break;
-                }
-                try {
-                    list.add(PackageParser.generatePackageInfoFromApex(
-                            new File(ai.packagePath), true /* collect certs */));
-                } catch (PackageParserException pe) {
-                    throw new IllegalStateException("Unable to parse: " + ai, pe);
-                }
+    void systemReady() {
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                onBootCompleted();
+                mContext.unregisterReceiver(this);
             }
-            return list.stream().collect(Collectors.toMap(p -> p.packageName, Function.identity()));
-        } catch (RemoteException re) {
-            Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
-            throw new RuntimeException(re);
+        }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+    }
+
+    private void populateActivePackagesCacheIfNeeded() {
+        synchronized (mLock) {
+            if (mActivePackagesCache != null) {
+                return;
+            }
+            try {
+                List<PackageInfo> list = new ArrayList<>();
+                final ApexInfo[] activePkgs = mApexService.getActivePackages();
+                for (ApexInfo ai : activePkgs) {
+                    // If the device is using flattened APEX, don't report any APEX
+                    // packages since they won't be managed or updated by PackageManager.
+                    if ((new File(ai.packagePath)).isDirectory()) {
+                        break;
+                    }
+                    try {
+                        list.add(PackageParser.generatePackageInfoFromApex(
+                                new File(ai.packagePath), true /* collect certs */));
+                    } catch (PackageParserException pe) {
+                        throw new IllegalStateException("Unable to parse: " + ai, pe);
+                    }
+                }
+                mActivePackagesCache = list.stream().collect(
+                        Collectors.toMap(p -> p.packageName, Function.identity()));
+            } catch (RemoteException re) {
+                Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
+                throw new RuntimeException(re);
+            }
         }
     }
 
@@ -96,6 +119,7 @@
      *         is not found.
      */
     @Nullable PackageInfo getActivePackage(String packageName) {
+        populateActivePackagesCacheIfNeeded();
         return mActivePackagesCache.get(packageName);
     }
 
@@ -106,6 +130,7 @@
      *         active package.
      */
     Collection<PackageInfo> getActivePackages() {
+        populateActivePackagesCacheIfNeeded();
         return mActivePackagesCache.values();
     }
 
@@ -198,6 +223,7 @@
      * @return true if APEX packages can be managed on this device, false otherwise.
      */
     boolean isApexSupported() {
+        populateActivePackagesCacheIfNeeded();
         // There is no system-wide property available to check if APEX are flattened and hence can't
         // be updated. In absence of such property, we assume that if we didn't index APEX packages
         // since they were flattened, no APEX management should be possible.
@@ -205,6 +231,21 @@
     }
 
     /**
+     * Abandons the (only) active session previously submitted.
+     *
+     * @return {@code true} upon success, {@code false} if any remote exception occurs
+     */
+    boolean abortActiveSession() {
+        try {
+            mApexService.abortActiveSession();
+            return true;
+        } catch (RemoteException re) {
+            Slog.e(TAG, "Unable to contact apexservice", re);
+            return false;
+        }
+    }
+
+    /**
      * Dumps various state information to the provided {@link PrintWriter} object.
      *
      * @param pw the {@link PrintWriter} object to send information to.
@@ -217,7 +258,7 @@
         ipw.println("Active APEX packages:");
         ipw.increaseIndent();
         try {
-            populateActivePackagesCache();
+            populateActivePackagesCacheIfNeeded();
             for (PackageInfo pi : mActivePackagesCache.values()) {
                 if (packageName != null && !packageName.equals(pi.packageName)) {
                     continue;
@@ -246,6 +287,12 @@
                     ipw.println("State: ACTIVATED");
                 } else if (si.isActivationFailed) {
                     ipw.println("State: ACTIVATION FAILED");
+                } else if (si.isSuccess) {
+                    ipw.println("State: SUCCESS");
+                } else if (si.isRollbackInProgress) {
+                    ipw.println("State: ROLLBACK IN PROGRESS");
+                } else if (si.isRolledBack) {
+                    ipw.println("State: ROLLED BACK");
                 }
                 ipw.decreaseIndent();
             }
@@ -254,4 +301,8 @@
             ipw.println("Couldn't communicate with apexd.");
         }
     }
+
+    public void onBootCompleted() {
+        populateActivePackagesCacheIfNeeded();
+    }
 }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 62c4815..9e912843 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -20,7 +20,6 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
-import android.app.AppDetailsActivity;
 import android.app.AppGlobals;
 import android.app.IApplicationThread;
 import android.app.PendingIntent;
@@ -367,7 +366,8 @@
         private ResolveInfo getHiddenAppActivityInfo(String packageName, int callingUid,
                 UserHandle user) {
             Intent intent = new Intent();
-            intent.setComponent(new ComponentName(packageName, AppDetailsActivity.class.getName()));
+            intent.setComponent(new ComponentName(packageName,
+                    PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME));
             final PackageManagerInternal pmInt =
                     LocalServices.getService(PackageManagerInternal.class);
             List<ResolveInfo> apps = pmInt.queryIntentActivities(intent,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f06da49..eeb4812 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -28,10 +28,8 @@
 import android.app.PackageDeleteObserver;
 import android.app.PackageInstallObserver;
 import android.app.admin.DevicePolicyManagerInternal;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.IntentSender.SendIntentException;
 import android.content.pm.ApplicationInfo;
@@ -141,7 +139,7 @@
 
     private final Callbacks mCallbacks;
 
-    private volatile boolean mBootCompleted = false;
+    private volatile boolean mOkToSendBroadcasts = false;
 
     /**
      * File storing persisted {@link #mSessions} metadata.
@@ -209,24 +207,13 @@
         mStagingManager = new StagingManager(pm, this, am);
     }
 
-    private void setBootCompleted()  {
-        mBootCompleted = true;
-    }
-
-    boolean isBootCompleted()  {
-        return mBootCompleted;
+    boolean okToSendBroadcasts()  {
+        return mOkToSendBroadcasts;
     }
 
     public void systemReady() {
         mAppOps = mContext.getSystemService(AppOpsManager.class);
 
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                setBootCompleted();
-                mContext.unregisterReceiver(this);
-            }
-        }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
         synchronized (mSessions) {
             readSessionsLocked();
 
@@ -269,6 +256,16 @@
         for (PackageInstallerSession session : stagedSessionsToRestore) {
             mStagingManager.restoreSession(session);
         }
+        // Broadcasts are not sent while we restore sessions on boot, since no processes would be
+        // ready to listen to them. From now on, we greedily assume that broadcasts requests are
+        // safe to send out. The worst that can happen is that a broadcast is attempted before
+        // ActivityManagerService completes its own systemReady(), in which case it will be rejected
+        // with an otherwise harmless exception.
+        // A more appropriate way to do this would be to wait until the correct  boot phase is
+        // reached, but since we are not a SystemService we can't override onBootPhase.
+        // Waiting on the BOOT_COMPLETED broadcast can take several minutes, so that's not a viable
+        // way either.
+        mOkToSendBroadcasts = true;
     }
 
     @GuardedBy("mSessions")
@@ -483,11 +480,12 @@
             }
         }
 
-        if (params.isStaged) {
+        boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0;
+        if (params.isStaged || isApex) {
             mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, TAG);
         }
 
-        if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+        if (isApex) {
             if (!mApexManager.isApexSupported()) {
                 throw new IllegalArgumentException(
                     "This device doesn't support the installation of APEX files");
@@ -821,6 +819,13 @@
     }
 
     @Override
+    public void installExistingPackage(String packageName, int installFlags, int installReason,
+            IntentSender statusReceiver, int userId) {
+        mPm.installExistingPackageAsUser(packageName, userId, installFlags, installReason,
+                statusReceiver);
+    }
+
+    @Override
     public void setPermissionsResult(int sessionId, boolean accepted) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
 
@@ -1179,7 +1184,7 @@
 
         public void onStagedSessionChanged(PackageInstallerSession session) {
             writeSessionsAsync();
-            if (mBootCompleted) {
+            if (mOkToSendBroadcasts) {
                 mPm.sendSessionUpdatedBroadcast(session.generateInfo(false),
                         session.userId);
             }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 1b71904..45b3b5b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -322,18 +322,7 @@
         public boolean handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_COMMIT:
-                    synchronized (mLock) {
-                        try {
-                            commitLocked();
-                        } catch (PackageManagerException e) {
-                            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
-                            Slog.e(TAG,
-                                    "Commit of session " + sessionId + " failed: " + completeMsg);
-                            destroyInternal();
-                            dispatchSessionFinished(e.error, completeMsg, null);
-                        }
-                    }
-
+                    handleCommit();
                     break;
                 case MSG_ON_PACKAGE_INSTALLED:
                     final SomeArgs args = (SomeArgs) msg.obj;
@@ -501,9 +490,9 @@
             if (info.childSessionIds == null) {
                 info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY;
             }
-            info.isSessionApplied = mStagedSessionApplied;
-            info.isSessionReady = mStagedSessionReady;
-            info.isSessionFailed = mStagedSessionFailed;
+            info.isStagedSessionApplied = mStagedSessionApplied;
+            info.isStagedSessionReady = mStagedSessionReady;
+            info.isStagedSessionFailed = mStagedSessionFailed;
             info.setStagedSessionErrorCode(mStagedSessionErrorCode, mStagedSessionErrorMessage);
         }
         return info;
@@ -1073,38 +1062,66 @@
         mCallback.onSessionSealedBlocking(this);
     }
 
-    @GuardedBy("mLock")
-    private void commitLocked()
-            throws PackageManagerException {
+    private void handleCommit() {
         if (params.isStaged) {
             mStagingManager.commitSession(this);
             destroyInternal();
             dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
             return;
         }
+
         if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
-            throw new PackageManagerException(
-                PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
-                "APEX packages can only be installed using staged sessions.");
+            destroyInternal();
+            dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
+                    "APEX packages can only be installed using staged sessions.", null);
+            return;
         }
+
+        // For a multiPackage session, read the child sessions
+        // outside of the lock, because reading the child
+        // sessions with the lock held could lead to deadlock
+        // (b/123391593).
+        List<PackageInstallerSession> childSessions = null;
+        if (isMultiPackage()) {
+            final int[] childSessionIds = getChildSessionIds();
+            childSessions = new ArrayList<>(childSessionIds.length);
+            for (int childSessionId : childSessionIds) {
+                childSessions.add(mSessionProvider.getSession(childSessionId));
+            }
+        }
+
+        try {
+            synchronized (mLock) {
+                commitNonStagedLocked(childSessions);
+            }
+        } catch (PackageManagerException e) {
+            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
+            Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
+            destroyInternal();
+            dispatchSessionFinished(e.error, completeMsg, null);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void commitNonStagedLocked(List<PackageInstallerSession> childSessions)
+            throws PackageManagerException {
         final PackageManagerService.ActiveInstallSession committingSession =
                 makeSessionActiveLocked();
         if (committingSession == null) {
             return;
         }
         if (isMultiPackage()) {
-            final int[] childSessionIds = getChildSessionIds();
-            List<PackageManagerService.ActiveInstallSession> childSessions =
-                    new ArrayList<>(childSessionIds.length);
+            List<PackageManagerService.ActiveInstallSession> activeChildSessions =
+                    new ArrayList<>(childSessions.size());
             boolean success = true;
             PackageManagerException failure = null;
-            for (int childSessionId : getChildSessionIds()) {
-                final PackageInstallerSession session = mSessionProvider.getSession(childSessionId);
+            for (int i = 0; i < childSessions.size(); ++i) {
+                final PackageInstallerSession session = childSessions.get(i);
                 try {
                     final PackageManagerService.ActiveInstallSession activeSession =
                             session.makeSessionActiveLocked();
                     if (activeSession != null) {
-                        childSessions.add(activeSession);
+                        activeChildSessions.add(activeSession);
                     }
                 } catch (PackageManagerException e) {
                     failure = e;
@@ -1119,7 +1136,7 @@
                 }
                 return;
             }
-            mPm.installStage(childSessions);
+            mPm.installStage(activeChildSessions);
         } else {
             mPm.installStage(committingSession);
         }
@@ -1474,12 +1491,12 @@
             // Inherit base if not overridden
             if (mResolvedBaseFile == null) {
                 mResolvedBaseFile = new File(appInfo.getBaseCodePath());
-                mResolvedInheritedFiles.add(mResolvedBaseFile);
+                resolveInheritedFile(mResolvedBaseFile);
                 // Inherit the dex metadata if present.
                 final File baseDexMetadataFile =
                         DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile);
                 if (baseDexMetadataFile != null) {
-                    mResolvedInheritedFiles.add(baseDexMetadataFile);
+                    resolveInheritedFile(baseDexMetadataFile);
                 }
                 baseApk = existingBase;
             }
@@ -1491,12 +1508,12 @@
                     final File splitFile = new File(existing.splitCodePaths[i]);
                     final boolean splitRemoved = removeSplitList.contains(splitName);
                     if (!stagedSplits.contains(splitName) && !splitRemoved) {
-                        mResolvedInheritedFiles.add(splitFile);
+                        resolveInheritedFile(splitFile);
                         // Inherit the dex metadata if present.
                         final File splitDexMetadataFile =
                                 DexMetadataHelper.findDexMetadataForFile(splitFile);
                         if (splitDexMetadataFile != null) {
-                            mResolvedInheritedFiles.add(splitDexMetadataFile);
+                            resolveInheritedFile(splitDexMetadataFile);
                         }
                     }
                 }
@@ -1610,6 +1627,17 @@
         mResolvedStagedFiles.add(stagedSignature);
     }
 
+    private void resolveInheritedFile(File origFile) {
+        mResolvedInheritedFiles.add(origFile);
+
+        // Inherit the fsverity signature file if present.
+        final File fsveritySignatureFile = new File(
+                VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
+        if (fsveritySignatureFile.exists()) {
+            mResolvedInheritedFiles.add(fsveritySignatureFile);
+        }
+    }
+
     @GuardedBy("mLock")
     private void assertApkConsistentLocked(String tag, ApkLite apk)
             throws PackageManagerException {
@@ -1839,6 +1867,15 @@
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
 
+            if (mCommitted && params.isStaged) {
+                synchronized (mLock) {
+                    mDestroyed = true;
+                }
+                mStagingManager.abortCommittedSession(this);
+
+                cleanStageDir();
+            }
+
             if (mRelinquished) {
                 Slog.d(TAG, "Ignoring abandon after commit relinquished control");
                 return;
@@ -1869,7 +1906,7 @@
     }
 
     @Override
-    public void addChildSessionId(int childSessionId) {
+    public void addChildSessionId(int childSessionId) throws RemoteException {
         final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId);
         if (childSession == null) {
             throw new RemoteException("Unable to add child.",
@@ -1884,9 +1921,12 @@
                     new PackageManagerException("Child session " + childSessionId
                             + " and parent session " + this.sessionId + " do not have consistent"
                             + " staging session settings."),
-                    false, true).rethrowAsRuntimeException();
+                    false, true);
         }
         synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+            assertPreparedAndNotSealedLocked("addChildSessionId");
+
             final int indexOfSession = mChildSessionIds.indexOfKey(childSessionId);
             if (indexOfSession >= 0) {
                 return;
@@ -1968,7 +2008,7 @@
 
         // Send broadcast to default launcher only if it's a new install
         final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
-        if (success && isNewInstall && mPm.mInstallerService.isBootCompleted()) {
+        if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) {
             mPm.sendSessionCommitBroadcast(generateInfo(), userId);
         }
 
@@ -1998,6 +2038,7 @@
             mStagedSessionErrorMessage = errorMessage;
             Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
         }
+        cleanStageDir();
         mCallback.onStagedSessionChanged(this);
     }
 
@@ -2009,7 +2050,9 @@
             mStagedSessionFailed = false;
             mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
             mStagedSessionErrorMessage = "";
+            Slog.d(TAG, "Marking session " + sessionId + " as applied");
         }
+        cleanStageDir();
         mCallback.onStagedSessionChanged(this);
     }
 
@@ -2053,9 +2096,8 @@
             }
         }
         // For staged sessions, we don't delete the directory where the packages have been copied,
-        // since these packages are supposed to be read on reboot. StagingManager is in charge of
-        // deleting these dirs when the staged session has reached a final state.
-        // TODO(b/118865310): Implement packageDir deletion in StagingManager.
+        // since these packages are supposed to be read on reboot.
+        // Those dirs are deleted when the staged session has reached a final state.
         if (stageDir != null && !params.isStaged) {
             try {
                 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
@@ -2064,6 +2106,19 @@
         }
     }
 
+    private void cleanStageDir() {
+        if (isMultiPackage()) {
+            for (int childSessionId : getChildSessionIds()) {
+                mSessionProvider.getSession(childSessionId).cleanStageDir();
+            }
+        } else {
+            try {
+                mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+            } catch (InstallerException ignored) {
+            }
+        }
+    }
+
     void dump(IndentingPrintWriter pw) {
         synchronized (mLock) {
             dumpLocked(pw);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 07d460e..20829b5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -121,7 +121,6 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
-import android.app.AppDetailsActivity;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.app.IActivityManager;
@@ -630,6 +629,7 @@
     final boolean mIsUpgrade;
     final boolean mIsPreNUpgrade;
     final boolean mIsPreNMR1Upgrade;
+    final boolean mIsPreQUpgrade;
 
     @GuardedBy("mPackages")
     private boolean mDexOptDialogShown;
@@ -1303,12 +1303,14 @@
     // Recordkeeping of restore-after-install operations that are currently in flight
     // between the Package Manager and the Backup Manager
     static class PostInstallData {
-        public InstallArgs args;
-        public PackageInstalledInfo res;
+        public final InstallArgs args;
+        public final PackageInstalledInfo res;
+        public final Runnable mPostInstallRunnable;
 
-        PostInstallData(InstallArgs _a, PackageInstalledInfo _r) {
+        PostInstallData(InstallArgs _a, PackageInstalledInfo _r, Runnable postInstallRunnable) {
             args = _a;
             res = _r;
+            mPostInstallRunnable = postInstallRunnable;
         }
     }
 
@@ -1440,7 +1442,9 @@
                     final boolean didRestore = (msg.arg2 != 0);
                     mRunningInstalls.delete(msg.arg1);
 
-                    if (data != null) {
+                    if (data != null && data.mPostInstallRunnable != null) {
+                        data.mPostInstallRunnable.run();
+                    } else if (data != null) {
                         InstallArgs args = data.args;
                         PackageInstalledInfo parentRes = data.res;
 
@@ -2396,6 +2400,7 @@
             mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;
 
             mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
+            mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q;
 
             int preUpgradeSdkVersion = ver.sdkVersion;
 
@@ -3022,6 +3027,21 @@
                 ver.fingerprint = Build.FINGERPRINT;
             }
 
+            // Grandfather existing (installed before Q) non-system apps to hide
+            // their icons in launcher.
+            if (!onlyCore && mIsPreQUpgrade) {
+                Slog.i(TAG, "Whitelisting all existing apps to hide their icons");
+                int size = mSettings.mPackages.size();
+                for (int i = 0; i < size; i++) {
+                    final PackageSetting ps = mSettings.mPackages.valueAt(i);
+                    if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                        continue;
+                    }
+                    ps.disableComponentLPw(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME,
+                            UserHandle.USER_SYSTEM);
+                }
+            }
+
             // clear only after permissions and other defaults have been updated
             mExistingSystemPackages.clear();
             mPromoteSystemApps = false;
@@ -3081,7 +3101,7 @@
                 }
             }
 
-            mApexManager = new ApexManager();
+            mApexManager = new ApexManager(context);
             mInstallerService = new PackageInstallerService(context, this, mApexManager);
             final Pair<ComponentName, String> instantAppResolverComponent =
                     getInstantAppResolverLPr();
@@ -4212,6 +4232,55 @@
         return -1;
     }
 
+    /**
+     * Check if any package sharing/holding a uid has a low enough target SDK.
+     *
+     * @param uid The uid of the packages
+     * @param higherTargetSDK The target SDK that might be higher than the searched package
+     *
+     * @return {@code true} if there is a package sharing/holding the uid with
+     * {@code package.targetSDK < higherTargetSDK}
+     */
+    private boolean hasTargetSdkInUidLowerThan(int uid, int higherTargetSDK) {
+        int userId = UserHandle.getUserId(uid);
+
+        synchronized (mPackages) {
+            Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
+            if (obj == null) {
+                return false;
+            }
+
+            if (obj instanceof PackageSetting) {
+                final PackageSetting ps = (PackageSetting) obj;
+
+                if (!ps.getInstalled(userId)) {
+                    return false;
+                }
+
+                return ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK;
+            } else if (obj instanceof SharedUserSetting) {
+                final SharedUserSetting sus = (SharedUserSetting) obj;
+
+                final int numPkgs = sus.packages.size();
+                for (int i = 0; i < numPkgs; i++) {
+                    final PackageSetting ps = sus.packages.valueAt(i);
+
+                    if (!ps.getInstalled(userId)) {
+                        continue;
+                    }
+
+                    if (ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK) {
+                        return true;
+                    }
+                }
+
+                return false;
+            } else {
+                return false;
+            }
+        }
+    }
+
     @Override
     public int[] getPackageGids(String packageName, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
@@ -5280,13 +5349,21 @@
 
     @Override
     public void grantRuntimePermission(String packageName, String permName, final int userId) {
-        mPermissionManager.grantRuntimePermission(permName, packageName, false /*overridePolicy*/,
+        boolean overridePolicy = (checkUidPermission(
+                Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid())
+                == PackageManager.PERMISSION_GRANTED);
+
+        mPermissionManager.grantRuntimePermission(permName, packageName, overridePolicy,
                 getCallingUid(), userId, mPermissionCallback);
     }
 
     @Override
     public void revokeRuntimePermission(String packageName, String permName, int userId) {
-        mPermissionManager.revokeRuntimePermission(permName, packageName, false /*overridePolicy*/,
+        boolean overridePolicy = (checkUidPermission(
+                Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid())
+                == PackageManager.PERMISSION_GRANTED);
+
+        mPermissionManager.revokeRuntimePermission(permName, packageName, overridePolicy,
                 getCallingUid(), userId, mPermissionCallback);
     }
 
@@ -5329,10 +5406,37 @@
 
     @Override
     public void updatePermissionFlags(String permName, String packageName, int flagMask,
-            int flagValues, int userId) {
+            int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
+        int callingUid = getCallingUid();
+        boolean overridePolicy = false;
+
+        if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) {
+            long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0) {
+                    if (checkAdjustPolicyFlagPermission) {
+                        mContext.enforceCallingOrSelfPermission(
+                                Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
+                                "Need " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY
+                                        + " to change policy flags");
+                    } else if (!hasTargetSdkInUidLowerThan(callingUid, Build.VERSION_CODES.Q)) {
+                        throw new IllegalArgumentException(
+                                Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " needs "
+                                        + " to be checked for packages targeting "
+                                        + Build.VERSION_CODES.Q + " or later when changing policy "
+                                        + "flags");
+                    }
+
+                    overridePolicy = true;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        }
+
         mPermissionManager.updatePermissionFlags(
-                permName, packageName, flagMask, flagValues, getCallingUid(), userId,
-                mPermissionCallback);
+                permName, packageName, flagMask, flagValues, callingUid, userId,
+                overridePolicy, mPermissionCallback);
     }
 
     /**
@@ -12683,6 +12787,11 @@
     @Override
     public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
             int installReason) {
+        return installExistingPackageAsUser(packageName, userId, installFlags, installReason, null);
+    }
+
+    int installExistingPackageAsUser(String packageName, int userId, int installFlags,
+            int installReason, IntentSender intentSender) {
         if (DEBUG_INSTALL) {
             Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId
                     + " installFlags=" + installFlags + " installReason=" + installReason);
@@ -12762,7 +12871,11 @@
                 PackageInstalledInfo res =
                         createPackageInstalledInfo(PackageManager.INSTALL_SUCCEEDED);
                 res.pkg = pkgSetting.pkg;
-                restoreAndPostInstall(userId, res, null);
+                res.newUsers = new int[]{ userId };
+                PostInstallData postInstallData = intentSender == null ? null :
+                        new PostInstallData(null, res, () -> onRestoreComplete(res.returnCode,
+                              mContext, intentSender));
+                restoreAndPostInstall(userId, res, postInstallData);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -12771,6 +12884,16 @@
         return PackageManager.INSTALL_SUCCEEDED;
     }
 
+    static void onRestoreComplete(int returnCode, Context context, IntentSender target) {
+        Intent fillIn = new Intent();
+        fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+                PackageManager.installStatusToPublicStatus(returnCode));
+        try {
+            target.sendIntent(context, 0, fillIn, null, null);
+        } catch (SendIntentException ignored) {
+        }
+    }
+
     static void setInstantAppForUser(PackageSetting pkgSetting, int userId,
             boolean instantApp, boolean fullApp) {
         // no state specified; do nothing
@@ -12867,8 +12990,14 @@
                 "setPackagesSuspendedAsUser");
 
         final int callingUid = Binder.getCallingUid();
-        if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
-                && getPackageUid(callingPackage, 0, userId) != callingUid) {
+        final int packageUid = getPackageUid(callingPackage, 0, userId);
+        final boolean allowedCallingUid = callingUid == Process.ROOT_UID
+                || callingUid == Process.SYSTEM_UID;
+        final boolean allowedPackageUid = packageUid == callingUid;
+        final boolean allowedShell = callingUid == SHELL_UID
+                && UserHandle.isSameApp(packageUid, callingUid);
+
+        if (!allowedCallingUid && !allowedShell && !allowedPackageUid) {
             throw new SecurityException("Calling package " + callingPackage + " in user "
                     + userId + " does not belong to calling uid " + callingUid);
         }
@@ -13727,7 +13856,7 @@
             }
             for (InstallRequest request : installRequests) {
                 restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
-                        new PostInstallData(request.args, request.installResult));
+                        new PostInstallData(request.args, request.installResult, null));
             }
         });
     }
@@ -17041,7 +17170,7 @@
 
             if (!legacyMode) {
                 // fs-verity is optional for now.  Only set up if signature is provided.
-                if (new File(signaturePath).exists()) {
+                if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
                     try {
                         VerityUtils.setUpFsverity(filePath, signaturePath);
                     } catch (IOException | DigestException | NoSuchAlgorithmException
@@ -19948,8 +20077,11 @@
 
     private @Nullable String getDocumenterPackageName() {
         final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+        intent.addCategory(Intent.CATEGORY_OPENABLE);
+        intent.setType("*/*");
+        final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
 
-        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, resolvedType,
                 MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
                         | MATCH_DISABLED_COMPONENTS,
                 UserHandle.myUserId());
@@ -20104,13 +20236,9 @@
         }
         // Only allow apps with CHANGE_COMPONENT_ENABLED_STATE permission to change hidden
         // app details activity
-        if (AppDetailsActivity.class.getName().equals(className)) {
-            if (mContext.checkCallingOrSelfPermission(
-                    android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE)
-                    != PackageManager.PERMISSION_GRANTED) {
-                Slog.e(TAG, "Cannot disable a protected component: " + packageName);
-                return;
-            }
+        if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)
+                && !allowedByPermission) {
+            throw new SecurityException("Cannot disable a system-generated component");
         }
 
         synchronized (mPackages) {
@@ -20578,6 +20706,7 @@
         storage.registerListener(mStorageListener);
 
         mInstallerService.systemReady();
+        mApexManager.systemReady();
         mPackageDexOptimizer.systemReady();
 
         getStorageManagerInternal().addExternalStoragePolicy(
@@ -22997,7 +23126,7 @@
         public void updatePermissionFlagsTEMP(String permName, String packageName, int flagMask,
                 int flagValues, int userId) {
             PackageManagerService.this.updatePermissionFlags(
-                    permName, packageName, flagMask, flagValues, userId);
+                    permName, packageName, flagMask, flagValues, true, userId);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 2eb762b..114810d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -300,9 +300,9 @@
                 pw.println("appPackageName = " + session.getAppPackageName()
                         + "; sessionId = " + session.getSessionId()
                         + "; isStaged = " + session.isStaged()
-                        + "; isSessionReady = " + session.isSessionReady()
-                        + "; isSessionApplied = " + session.isSessionApplied()
-                        + "; isSessionFailed = " + session.isSessionFailed() + ";");
+                        + "; isStagedSessionReady = " + session.isStagedSessionReady()
+                        + "; isStagedSessionApplied = " + session.isStagedSessionApplied()
+                        + "; isStagedSessionFailed = " + session.isStagedSessionFailed() + ";");
             }
         } catch (RemoteException e) {
             pw.println("Failure ["
@@ -1114,6 +1114,7 @@
         int userId = UserHandle.USER_SYSTEM;
         int installFlags = 0;
         String opt;
+        boolean waitTillComplete = false;
         while ((opt = getNextOption()) != null) {
             switch (opt) {
                 case "--user":
@@ -1128,6 +1129,9 @@
                     installFlags &= ~PackageManager.INSTALL_INSTANT_APP;
                     installFlags |= PackageManager.INSTALL_FULL_APP;
                     break;
+                case "--wait":
+                    waitTillComplete = true;
+                    break;
                 default:
                     pw.println("Error: Unknown option: " + opt);
                     return 1;
@@ -1140,9 +1144,23 @@
             return 1;
         }
 
+        int installReason = PackageManager.INSTALL_REASON_UNKNOWN;
         try {
+            if (waitTillComplete) {
+                final LocalIntentReceiver receiver = new LocalIntentReceiver();
+                final IPackageInstaller installer = mInterface.getPackageInstaller();
+                pw.println("Installing package " + packageName + " for user: " + userId);
+                installer.installExistingPackage(packageName, installFlags, installReason,
+                        receiver.getIntentSender(), userId);
+                final Intent result = receiver.getResult();
+                final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                        PackageInstaller.STATUS_FAILURE);
+                pw.println("Received intent for package install");
+                return status == PackageInstaller.STATUS_SUCCESS ? 0 : 1;
+            }
+
             final int res = mInterface.installExistingPackageAsUser(packageName, userId,
-                    installFlags, PackageManager.INSTALL_REASON_UNKNOWN);
+                    installFlags, installReason);
             if (res == PackageManager.INSTALL_FAILED_INVALID_URI) {
                 throw new NameNotFoundException("Package " + packageName + " doesn't exist");
             }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index ff6d7a8..9deea9e 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2173,7 +2173,8 @@
     public boolean hasShareTargets(String packageName, String packageToCheck,
             @UserIdInt int userId) {
         verifyCaller(packageName, userId);
-        enforceSystem();
+        enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS,
+                "hasShareTargets");
 
         synchronized (mLock) {
             throwIfUserLockedL(userId);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 9dfd477..cbf5e9e 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -257,7 +257,7 @@
                         + "activated");
                 return;
             }
-            if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) {
+            if (isApexSessionFailed(apexSessionInfo)) {
                 session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                         "APEX activation failed. Check logcat messages from apexd for "
                                 + "more information.");
@@ -419,7 +419,12 @@
                 if (apkChildSession == null) {
                     return false;
                 }
-                apkParentSession.addChildSessionId(apkChildSession.sessionId);
+                try {
+                    apkParentSession.addChildSessionId(apkChildSession.sessionId);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to add a child session for installing the APK files", e);
+                    return false;
+                }
             }
             return commitApkSession(apkParentSession, session.sessionId);
         }
@@ -440,11 +445,38 @@
 
     void abortSession(@NonNull PackageInstallerSession session) {
         synchronized (mStagedSessions) {
-            updateStoredSession(session);
             mStagedSessions.remove(session.sessionId);
         }
     }
 
+    void abortCommittedSession(@NonNull PackageInstallerSession session) {
+        if (session.isStagedSessionApplied()) {
+            Slog.w(TAG, "Cannot abort applied session!");
+            return;
+        }
+        if (isStagedSessionFinalized(session.sessionId)) {
+            Slog.w(TAG, "Cannot abort session because it is not active or APEXD is not reachable");
+            return;
+        }
+
+        mApexManager.abortActiveSession();
+
+        abortSession(session);
+    }
+
+    private boolean isStagedSessionFinalized(int sessionId) {
+        ApexSessionInfo session = mApexManager.getStagedSessionInfo(sessionId);
+
+        /* checking if the session is in a final state, i.e., not active anymore */
+        return session.isUnknown || session.isActivationFailed || session.isSuccess
+                || session.isRolledBack;
+    }
+
+    private static boolean isApexSessionFailed(ApexSessionInfo apexSessionInfo) {
+        return apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown
+                || apexSessionInfo.isRolledBack;
+    }
+
     @GuardedBy("mStagedSessions")
     private boolean isMultiPackageSessionComplete(@NonNull PackageInstallerSession session) {
         // This method assumes that the argument is either a parent session of a multi-package
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d0f192d..3744f68 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1324,6 +1324,7 @@
 
     @Override
     public ParcelFileDescriptor getUserIcon(int targetUserId) {
+        checkManageUsersPermission("get user icon");
         String iconPath;
         synchronized (mPackagesLock) {
             UserInfo targetUserInfo = getUserInfoNoChecks(targetUserId);
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 6369179..447234e 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -206,6 +206,11 @@
         // STOPSHIP(b/112545973): remove once feature enabled by default
         if (StorageManager.hasIsolatedStorage()) {
             MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
+
+            // STOPSHIP(b/124466734): remove these manual grants once the legacy
+            // permission logic is unified with PermissionController
+            MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+            MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
         }
     }
 
@@ -215,6 +220,11 @@
         if (StorageManager.hasIsolatedStorage()) {
             MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
             MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
+
+            // STOPSHIP(b/124466734): remove these manual grants once the legacy
+            // permission logic is unified with PermissionController
+            MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+            MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
         }
     }
 
@@ -642,16 +652,10 @@
         // Location
         if (locationPackageNames != null) {
             for (String packageName : locationPackageNames) {
-                // STOPSHIP: remove this force-granting of legacy storage
-                // permissions once b/124466734 is resolved
-                final Set<String> storageWorkaround = new ArraySet<>();
-                storageWorkaround.add(Manifest.permission.READ_EXTERNAL_STORAGE);
-                storageWorkaround.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
-
                 grantPermissionsToSystemPackage(packageName, userId,
                         CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
                         PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS,
-                        SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, storageWorkaround);
+                        SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, MEDIA_AURAL_PERMISSIONS);
                 grantSystemFixedPermissionsToSystemPackage(packageName, userId,
                         LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS);
             }
@@ -846,7 +850,7 @@
         }
         for (String packageName : packageNames) {
             grantPermissionsToSystemPackage(packageName, userId,
-                    PHONE_PERMISSIONS, LOCATION_PERMISSIONS, SMS_PERMISSIONS);
+                    PHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, SMS_PERMISSIONS);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 8df5a71..03da962 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -35,6 +35,7 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
+import static android.content.pm.PackageManager.MASK_PERMISSION_FLAGS;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.UserHandle.getAppId;
 import static android.os.UserHandle.getUid;
@@ -1031,7 +1032,8 @@
                                     updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
                                 }
 
-                                permissionsState.updatePermissionFlags(bp, userId, flags, flags);
+                                permissionsState.updatePermissionFlags(bp, userId,
+                                        MASK_PERMISSION_FLAGS, flags);
                             }
                         } break;
 
@@ -1081,7 +1083,8 @@
                                     updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
                                 }
 
-                                permissionsState.updatePermissionFlags(bp, userId, flags, flags);
+                                permissionsState.updatePermissionFlags(bp, userId,
+                                        MASK_PERMISSION_FLAGS, flags);
                             }
                         } break;
 
@@ -1198,29 +1201,23 @@
                         if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
                             BasePermission bp = mSettings.getPermissionLocked(permission);
 
-                            ps.updatePermissionFlags(bp, userId,
-                                    FLAG_PERMISSION_REVOKE_WHEN_REQUESTED
-                                            | FLAG_PERMISSION_USER_FIXED | FLAG_PERMISSION_USER_SET,
-                                    0);
-                            updatedUserIds = ArrayUtils.appendInt(updatedUserIds,
-                                    userId);
+                            int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
 
                             if ((flags & (FLAG_PERMISSION_GRANTED_BY_DEFAULT
                                     | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED))
-                                    == 0) {
-                                if (supportsRuntimePermissions) {
-                                    int revokeResult = ps.revokeRuntimePermission(bp, userId);
-                                    if (revokeResult != PERMISSION_OPERATION_FAILURE) {
-                                        if (DEBUG_PERMISSIONS) {
-                                            Slog.i(TAG, "Revoking runtime permission "
-                                                    + permission + " for " + pkgName
-                                                    + " as it is now requested");
-                                        }
+                                    == 0 && supportsRuntimePermissions) {
+                                int revokeResult = ps.revokeRuntimePermission(bp, userId);
+                                if (revokeResult != PERMISSION_OPERATION_FAILURE) {
+                                    if (DEBUG_PERMISSIONS) {
+                                        Slog.i(TAG, "Revoking runtime permission "
+                                                + permission + " for " + pkgName
+                                                + " as it is now requested");
                                     }
-                                } else {
-                                    setAppOpMode(permission, pkg, userId, MODE_IGNORED);
                                 }
 
+                                flagsToRemove |=
+                                        FLAG_PERMISSION_USER_FIXED | FLAG_PERMISSION_USER_SET;
+
                                 List<String> fgPerms = mBackgroundPermissions.get(permission);
                                 if (fgPerms != null) {
                                     int numFgPerms = fgPerms.size();
@@ -1238,6 +1235,9 @@
                                     }
                                 }
                             }
+
+                            ps.updatePermissionFlags(bp, userId, flagsToRemove, 0);
+                            updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
                         }
                     }
                 }
@@ -1935,7 +1935,7 @@
                     if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
                         updatePermissionFlags(permission, pkg.packageName,
                                 PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, callingUid,
-                                userId, callback);
+                                userId, false, callback);
                     }
                 }
             }
@@ -2441,7 +2441,8 @@
     }
 
     private void updatePermissionFlags(String permName, String packageName, int flagMask,
-            int flagValues, int callingUid, int userId, PermissionCallback callback) {
+            int flagValues, int callingUid, int userId, boolean overridePolicy,
+            PermissionCallback callback) {
         if (!mUserManagerInt.exists(userId)) {
             return;
         }
@@ -2454,6 +2455,11 @@
                 false, // requirePermissionWhenSameUser
                 "updatePermissionFlags");
 
+        if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0 && !overridePolicy) {
+            throw new SecurityException("updatePermissionFlags requires "
+                    + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
+        }
+
         // Only the system can change these flags and nothing else.
         if (callingUid != Process.SYSTEM_UID) {
             flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
@@ -2745,9 +2751,11 @@
         }
         @Override
         public void updatePermissionFlags(String permName, String packageName, int flagMask,
-                int flagValues, int callingUid, int userId, PermissionCallback callback) {
+                int flagValues, int callingUid, int userId, boolean overridePolicy,
+                PermissionCallback callback) {
             PermissionManagerService.this.updatePermissionFlags(
-                    permName, packageName, flagMask, flagValues, callingUid, userId, callback);
+                    permName, packageName, flagMask, flagValues, callingUid, userId,
+                    overridePolicy, callback);
         }
         @Override
         public boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 1dd2408..305f165 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -147,7 +147,7 @@
      */
     public abstract void updatePermissionFlags(@NonNull String permName,
             @NonNull String packageName, int flagMask, int flagValues, int callingUid, int userId,
-            @Nullable PermissionCallback callback);
+            boolean overridePolicy, @Nullable PermissionCallback callback);
     /**
      * Updates the flags for all applications by replacing the flags in the specified mask
      * with the provided flag values.
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index cf026e9..0db3d78 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -35,10 +35,12 @@
 
 /**
  * Controls the behavior of foldable devices whose screen can literally bend and fold.
+ * TODO(b/126160895): Move DisplayFoldController from PhoneWindowManager to DisplayPolicy.
  */
 class DisplayFoldController {
 
     private static final String TAG = "DisplayFoldController";
+
     private final WindowManagerInternal mWindowManagerInternal;
     private final DisplayManagerInternal mDisplayManagerInternal;
     private final int mDisplayId;
@@ -52,6 +54,8 @@
     private final DisplayInfo mNonOverrideDisplayInfo = new DisplayInfo();
     private final RemoteCallbackList<IDisplayFoldListener> mListeners = new RemoteCallbackList<>();
     private Boolean mFolded;
+    private String mFocusedApp;
+    private final DisplayFoldDurationLogger mDurationLogger = new DisplayFoldDurationLogger();
 
     DisplayFoldController(WindowManagerInternal windowManagerInternal,
             DisplayManagerInternal displayManagerInternal, int displayId, Rect foldedArea,
@@ -63,6 +67,14 @@
         mHandler = handler;
     }
 
+    void finishedGoingToSleep() {
+        mDurationLogger.onFinishedGoingToSleep();
+    }
+
+    void finishedWakingUp() {
+        mDurationLogger.onFinishedWakingUp(mFolded);
+    }
+
     void requestDeviceFolded(boolean folded) {
         mHandler.post(() -> setDeviceFolded(folded));
     }
@@ -97,6 +109,8 @@
             mWindowManagerInternal.clearForcedDisplaySize(mDisplayId);
             mDisplayManagerInternal.setDisplayOffsets(mDisplayId, 0, 0);
         }
+        mDurationLogger.setDeviceFolded(folded);
+        mDurationLogger.logFocusedAppWithFoldState(folded, mFocusedApp);
         mFolded = folded;
 
         final int n = mListeners.beginBroadcast();
@@ -167,6 +181,10 @@
         return result;
     }
 
+    void onDefaultDisplayFocusChanged(String pkg) {
+        mFocusedApp = pkg;
+    }
+
     static DisplayFoldController create(Context context, int displayId) {
         final DisplayManagerInternal displayService =
                 LocalServices.getService(DisplayManagerInternal.class);
diff --git a/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java b/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java
new file mode 100644
index 0000000..bdcd2cd
--- /dev/null
+++ b/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java
@@ -0,0 +1,117 @@
+/*
+ * 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.server.policy;
+
+import android.annotation.IntDef;
+import android.metrics.LogMaker;
+import android.os.SystemClock;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Logger for tracking duration of usage in folded vs unfolded state.
+ */
+class DisplayFoldDurationLogger {
+    static final int SCREEN_STATE_UNKNOWN = -1;
+    static final int SCREEN_STATE_OFF = 0;
+    static final int SCREEN_STATE_ON_UNFOLDED = 1;
+    static final int SCREEN_STATE_ON_FOLDED = 2;
+
+    @IntDef(flag = true, prefix = {"SCREEN_STATE_"}, value = {
+            SCREEN_STATE_UNKNOWN,
+            SCREEN_STATE_OFF,
+            SCREEN_STATE_ON_UNFOLDED,
+            SCREEN_STATE_ON_FOLDED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ScreenState {}
+
+    private @ScreenState int mScreenState = SCREEN_STATE_UNKNOWN;
+    private Long mLastChanged = null;
+
+    private static final int LOG_SUBTYPE_UNFOLDED = 0;
+    private static final int LOG_SUBTYPE_FOLDED = 1;
+    private static final int LOG_SUBTYPE_DURATION_MASK = 0x80000000;
+
+    private final MetricsLogger mLogger = new MetricsLogger();
+
+    void onFinishedWakingUp(Boolean folded) {
+        if (folded == null) {
+            mScreenState = SCREEN_STATE_UNKNOWN;
+        } else if (folded) {
+            mScreenState = SCREEN_STATE_ON_FOLDED;
+        } else {
+            mScreenState = SCREEN_STATE_ON_UNFOLDED;
+        }
+        mLastChanged = SystemClock.uptimeMillis();
+    }
+
+    void onFinishedGoingToSleep() {
+        log();
+        mScreenState = SCREEN_STATE_OFF;
+        mLastChanged = null;
+    }
+
+    void setDeviceFolded(boolean folded) {
+        // This function is called even when the screen is in ADO mode, but we're only
+        // interested in the case that the screen is actually on.
+        if (!isOn()) {
+            return;
+        }
+        log();
+        mScreenState = folded ? SCREEN_STATE_ON_FOLDED : SCREEN_STATE_ON_UNFOLDED;
+        mLastChanged = SystemClock.uptimeMillis();
+    }
+
+    void logFocusedAppWithFoldState(boolean folded, String packageName) {
+        mLogger.write(
+                new LogMaker(MetricsProto.MetricsEvent.ACTION_DISPLAY_FOLD)
+                        .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
+                        .setSubtype(folded ? LOG_SUBTYPE_FOLDED : LOG_SUBTYPE_UNFOLDED)
+                        .setPackageName(packageName));
+    }
+
+    private void log() {
+        if (mLastChanged == null) {
+            return;
+        }
+        int subtype;
+        switch (mScreenState) {
+            case SCREEN_STATE_ON_UNFOLDED:
+                subtype = LOG_SUBTYPE_UNFOLDED | LOG_SUBTYPE_DURATION_MASK;
+                break;
+            case SCREEN_STATE_ON_FOLDED:
+                subtype = LOG_SUBTYPE_FOLDED | LOG_SUBTYPE_DURATION_MASK;
+                break;
+            default:
+                return;
+        }
+        mLogger.write(
+                new LogMaker(MetricsProto.MetricsEvent.ACTION_DISPLAY_FOLD)
+                        .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
+                        .setSubtype(subtype)
+                        .setLatency(SystemClock.uptimeMillis() - mLastChanged));
+    }
+
+    private boolean isOn() {
+        return mScreenState == SCREEN_STATE_ON_UNFOLDED || mScreenState == SCREEN_STATE_ON_FOLDED;
+    }
+}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c87a81d..c0e5974 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -86,6 +86,9 @@
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_BEHAVIOR_LOCK;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_BEHAVIOR_NONE;
+import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_BEHAVIOR_SLEEP;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
 import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE;
@@ -473,8 +476,6 @@
 
     int mLidKeyboardAccessibility;
     int mLidNavigationAccessibility;
-    boolean mLidControlsScreenLock;
-    boolean mLidControlsSleep;
     private boolean mLidControlsDisplayFold;
     int mShortPressOnPowerBehavior;
     int mLongPressOnPowerBehavior;
@@ -808,7 +809,7 @@
         public void onWakeUp() {
             synchronized (mLock) {
                 if (shouldEnableWakeGestureLp()) {
-                    performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false,
+                    performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
                             "Wake Up");
                     wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture,
                             PowerManager.WAKE_REASON_GESTURE, "android.policy:GESTURE");
@@ -1195,6 +1196,11 @@
         }
     }
 
+    private int getLidBehavior() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.LID_BEHAVIOR, LID_BEHAVIOR_NONE);
+    }
+
     private int getMaxMultiPressPowerCount() {
         if (mTriplePressOnPowerBehavior != MULTI_PRESS_POWER_NOTHING) {
             return 3;
@@ -1212,21 +1218,21 @@
             break;
         case LONG_PRESS_POWER_GLOBAL_ACTIONS:
             mPowerKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                     "Power - Long Press - Global Actions");
             showGlobalActionsInternal();
             break;
         case LONG_PRESS_POWER_SHUT_OFF:
         case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
             mPowerKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                     "Power - Long Press - Shut Off");
             sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
             mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
             break;
         case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
             mPowerKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                     "Power - Long Press - Go To Voice Assist");
             final boolean keyguardActive = mKeyguardDelegate == null
                     ? false
@@ -1249,7 +1255,7 @@
             break;
         case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
             mPowerKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                     "Power - Very Long Press - Show Global Actions");
             showGlobalActionsInternal();
             break;
@@ -1400,7 +1406,7 @@
         @Override
         public void run() {
             mEndCallKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                     "End Call - Long Press - Show Global Actions");
             showGlobalActionsInternal();
         }
@@ -1667,7 +1673,7 @@
                 return;
             }
             mHomeConsumed = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                     "Home - Long Press");
             switch (mLongPressOnHomeBehavior) {
                 case LONG_PRESS_HOME_ALL_APPS:
@@ -1796,10 +1802,6 @@
                 com.android.internal.R.integer.config_lidKeyboardAccessibility);
         mLidNavigationAccessibility = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_lidNavigationAccessibility);
-        mLidControlsScreenLock = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_lidControlsScreenLock);
-        mLidControlsSleep = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_lidControlsSleep);
         mLidControlsDisplayFold = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_lidControlsDisplayFold);
 
@@ -2040,7 +2042,8 @@
 
     private boolean shouldEnableWakeGestureLp() {
         return mWakeGestureEnabledSetting && !mDefaultDisplayPolicy.isAwake()
-                && (!mLidControlsSleep || mDefaultDisplayPolicy.getLidState() != LID_CLOSED)
+                && (getLidBehavior() != LID_BEHAVIOR_SLEEP
+                || mDefaultDisplayPolicy.getLidState() != LID_CLOSED)
                 && mWakeGestureListener.isSupported();
     }
 
@@ -2316,14 +2319,15 @@
         boolean allowWhenLocked = (win.isInputMethodWindow() || imeTarget == this)
                 && showImeOverKeyguard;
 
-        if (isKeyguardLocked() && isKeyguardOccluded()) {
+        final boolean isKeyguardShowing = mKeyguardDelegate.isShowing();
+
+        if (isKeyguardShowing && isKeyguardOccluded()) {
             // Show SHOW_WHEN_LOCKED windows if Keyguard is occluded.
             allowWhenLocked |= win.canShowWhenLocked()
                     // Show error dialogs over apps that are shown on lockscreen
                     || (attrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0;
         }
 
-        boolean keyguardLocked = isKeyguardLocked();
         boolean hideDockDivider = attrs.type == TYPE_DOCK_DIVIDER
                 && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         // If AOD is showing, the IME should be hidden. However, sometimes the AOD is considered
@@ -2333,7 +2337,7 @@
         // now shown.
         final boolean hideIme = win.isInputMethodWindow()
                 && (mAodShowing || !mDefaultDisplayPolicy.isWindowManagerDrawComplete());
-        return (keyguardLocked && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY)
+        return (isKeyguardShowing && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY)
                 || hideDockDivider || hideIme;
     }
 
@@ -3237,6 +3241,14 @@
     }
 
     @Override
+    public void onDefaultDisplayFocusChangedLw(WindowState newFocus) {
+        if (mDisplayFoldController != null) {
+            mDisplayFoldController.onDefaultDisplayFocusChanged(
+                    newFocus != null ? newFocus.getOwningPackage() : null);
+        }
+    }
+
+    @Override
     public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
             throws RemoteException {
         synchronized (mLock) {
@@ -3276,7 +3288,7 @@
     }
 
     private void launchAssistLongPressAction() {
-        performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+        performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                 "Assist - Long Press");
         sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);
 
@@ -3545,7 +3557,7 @@
         if (lidOpen) {
             wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromLidSwitch,
                     PowerManager.WAKE_REASON_LID, "android.policy:LID");
-        } else if (!mLidControlsSleep) {
+        } else if (getLidBehavior() != LID_BEHAVIOR_SLEEP) {
             mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
         }
     }
@@ -4038,7 +4050,7 @@
         }
 
         if (useHapticFeedback) {
-            performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false,
+            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
                     "Virtual Key - Press");
         }
 
@@ -4436,6 +4448,9 @@
             mKeyguardDelegate.onFinishedGoingToSleep(why,
                     mCameraGestureTriggeredDuringGoingToSleep);
         }
+        if (mDisplayFoldController != null) {
+            mDisplayFoldController.finishedGoingToSleep();
+        }
         mCameraGestureTriggeredDuringGoingToSleep = false;
     }
 
@@ -4476,6 +4491,9 @@
         if (mKeyguardDelegate != null) {
             mKeyguardDelegate.onFinishedWakingUp();
         }
+        if (mDisplayFoldController != null) {
+            mDisplayFoldController.finishedWakingUp();
+        }
     }
 
     private void wakeUpFromPowerKey(long eventTime) {
@@ -4759,7 +4777,7 @@
     public void setSafeMode(boolean safeMode) {
         mSafeMode = safeMode;
         if (safeMode) {
-            performHapticFeedbackLw(null, HapticFeedbackConstants.SAFE_MODE_ENABLED, true,
+            performHapticFeedback(HapticFeedbackConstants.SAFE_MODE_ENABLED, true,
                     "Safe Mode Enabled");
         }
     }
@@ -5040,11 +5058,22 @@
         final int lidState = mDefaultDisplayPolicy.getLidState();
         if (mLidControlsDisplayFold && mDisplayFoldController != null) {
             mDisplayFoldController.requestDeviceFolded(lidState == LID_CLOSED);
-        } else if (lidState == LID_CLOSED && mLidControlsSleep) {
-            goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
-                    PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
-        } else if (lidState == LID_CLOSED && mLidControlsScreenLock) {
-            mWindowManagerFuncs.lockDeviceNow();
+        } else if (lidState == LID_CLOSED) {
+            int lidBehavior = getLidBehavior();
+            switch (lidBehavior) {
+                case LID_BEHAVIOR_LOCK:
+                    mWindowManagerFuncs.lockDeviceNow();
+                    break;
+                case LID_BEHAVIOR_SLEEP:
+                    goToSleep(SystemClock.uptimeMillis(),
+                            PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
+                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+                    break;
+                case LID_BEHAVIOR_NONE:
+                    // fall through
+                default:
+                    break;
+            }
         }
 
         synchronized (mLock) {
@@ -5236,9 +5265,14 @@
                 Settings.Global.THEATER_MODE_ON, 0) == 1;
     }
 
+    private boolean performHapticFeedback(int effectId, boolean always, String reason) {
+        return performHapticFeedback(Process.myUid(), mContext.getOpPackageName(),
+            effectId, always, reason);
+    }
+
     @Override
-    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always,
-            String reason) {
+    public boolean performHapticFeedback(int uid, String packageName, int effectId,
+            boolean always, String reason) {
         if (!mVibrator.hasVibrator()) {
             return false;
         }
@@ -5253,16 +5287,7 @@
             return false;
         }
 
-        int owningUid;
-        String owningPackage;
-        if (win != null) {
-            owningUid = win.getOwningUid();
-            owningPackage = win.getOwningPackage();
-        } else {
-            owningUid = android.os.Process.myUid();
-            owningPackage = mContext.getOpPackageName();
-        }
-        mVibrator.vibrate(owningUid, owningPackage, effect, reason, VIBRATION_ATTRIBUTES);
+        mVibrator.vibrate(uid, packageName, effect, reason, VIBRATION_ATTRIBUTES);
         return true;
     }
 
@@ -5404,8 +5429,7 @@
         pw.print(prefix); pw.print("mLidKeyboardAccessibility=");
                 pw.print(mLidKeyboardAccessibility);
                 pw.print(" mLidNavigationAccessibility="); pw.print(mLidNavigationAccessibility);
-                pw.print(" mLidControlsScreenLock="); pw.println(mLidControlsScreenLock);
-        pw.print(prefix); pw.print("mLidControlsSleep="); pw.println(mLidControlsSleep);
+                pw.print(" getLidBehavior="); pw.println(lidBehaviorToString(getLidBehavior()));
         pw.print(prefix);
                 pw.print("mLongPressOnBackBehavior=");
                 pw.println(longPressOnBackBehaviorToString(mLongPressOnBackBehavior));
@@ -5639,6 +5663,19 @@
         }
     }
 
+    private static String lidBehaviorToString(int behavior) {
+        switch (behavior) {
+            case LID_BEHAVIOR_LOCK:
+                return "LID_BEHAVIOR_LOCK";
+            case LID_BEHAVIOR_SLEEP:
+                return "LID_BEHAVIOR_SLEEP";
+            case LID_BEHAVIOR_NONE:
+                return "LID_BEHAVIOR_NONE";
+            default:
+                return Integer.toString(behavior);
+        }
+    }
+
     @Override
     public boolean setAodShowing(boolean aodShowing) {
         if (mAodShowing != aodShowing) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index d7e4b6c..d58707c 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -518,6 +518,10 @@
         public static final int LID_CLOSED = 0;
         public static final int LID_OPEN = 1;
 
+        public static final int LID_BEHAVIOR_NONE = 0;
+        public static final int LID_BEHAVIOR_SLEEP = 1;
+        public static final int LID_BEHAVIOR_LOCK = 2;
+
         public static final int CAMERA_LENS_COVER_ABSENT = -1;
         public static final int CAMERA_LENS_UNCOVERED = 0;
         public static final int CAMERA_LENS_COVERED = 1;
@@ -1300,8 +1304,8 @@
     /**
      * Call from application to perform haptic feedback on its window.
      */
-    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always,
-            String reason);
+    public boolean performHapticFeedback(int uid, String packageName, int effectId,
+            boolean always, String reason);
 
     /**
      * Called when we have started keeping the screen on because a window
@@ -1485,6 +1489,11 @@
     }
 
     /**
+     * A new window on default display has been focused.
+     */
+    default void onDefaultDisplayFocusChangedLw(WindowState newFocus) {}
+
+    /**
      * Updates the flag about whether AOD is showing.
      *
      * @return whether the value was changed.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index ae1090c..c408549 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -209,11 +209,8 @@
             mKeyguardState.reset();
             mHandler.post(() -> {
                 try {
-                    // There are no longer any keyguard windows on secondary displays, so pass
-                    // {@code null}. All that means is that showWhenLocked activities on
-                    // external displays now get to show.
                     ActivityTaskManager.getService().setLockScreenShown(true /* keyguardShowing */,
-                            false /* aodShowing */, null /* secondaryDisplaysShowing */);
+                            false /* aodShowing */);
                 } catch (RemoteException e) {
                     // Local call.
                 }
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index e6fa500..af78995 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -280,7 +280,7 @@
                 Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
                 mDynamicPowerSavingsDefaultDisableThreshold);
         final boolean isStickyAutoDisableEnabled = getGlobalSetting(
-                Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 0) != 0;
+                Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1) != 0;
         final int stickyAutoDisableThreshold = getGlobalSetting(
                 Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90);
 
diff --git a/services/core/java/com/android/server/role/TEST_MAPPING b/services/core/java/com/android/server/role/TEST_MAPPING
index 0b967be..0d7bc14 100644
--- a/services/core/java/com/android/server/role/TEST_MAPPING
+++ b/services/core/java/com/android/server/role/TEST_MAPPING
@@ -7,11 +7,14 @@
                     "include-filter": "android.cts.statsd.atom.UidAtomTests#testRoleHolder"
                 }
             ]
-        }
-    ],
-    "postsubmit": [
+        },
         {
-            "name": "CtsRoleTestCases"
+            "name": "CtsRoleTestCases",
+            "options": [
+                {
+                    "exclude-annotation": "androidx.test.filters.FlakyTest"
+                }
+            ]
         }
     ]
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index c14f126..88a5fb4 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
@@ -40,6 +41,7 @@
 import android.os.HandlerThread;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.os.SystemClock;
 import android.provider.DeviceConfig;
 import android.util.IntArray;
 import android.util.Log;
@@ -47,6 +49,7 @@
 import android.util.SparseLongArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
 import com.android.server.pm.Installer;
 
@@ -121,6 +124,13 @@
     private final RollbackPackageHealthObserver mPackageHealthObserver;
     private final AppDataRollbackHelper mAppDataRollbackHelper;
 
+    // This field stores the difference in Millis between the uptime (millis since device
+    // has booted) and current time (device wall clock) - it's used to update rollback data
+    // timestamps when the time is changed, by the user or by change of timezone.
+    // No need for guarding with lock because value is only accessed in handler thread.
+    private long  mRelativeBootTime = calculateRelativeBootTime();
+
+
     RollbackManagerServiceImpl(Context context) {
         mContext = context;
         // Note that we're calling onStart here because this object is only constructed on
@@ -217,6 +227,8 @@
                 }
             }
         }, enableRollbackFilter, null, getHandler());
+
+        registerTimeChangeReceiver();
     }
 
     @Override
@@ -268,6 +280,45 @@
                     callerPackageName, statusReceiver));
     }
 
+    private void registerTimeChangeReceiver() {
+        final BroadcastReceiver timeChangeIntentReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final long oldRelativeBootTime = mRelativeBootTime;
+                mRelativeBootTime = calculateRelativeBootTime();
+                final long timeDifference = mRelativeBootTime - oldRelativeBootTime;
+
+                synchronized (mLock) {
+                    ensureRollbackDataLoadedLocked();
+
+                    Iterator<RollbackData> iter = mAvailableRollbacks.iterator();
+                    while (iter.hasNext()) {
+                        RollbackData data = iter.next();
+
+                        data.timestamp = data.timestamp.plusMillis(timeDifference);
+                        try {
+                            mRollbackStore.saveAvailableRollback(data);
+                        } catch (IOException ioe) {
+                            // TODO: figure out the right way to deal with this, especially if
+                            //  it fails for some data and succeeds for others.
+                            Log.e(TAG, "Unable to save rollback info for : " + data.rollbackId,
+                                    ioe);
+                        }
+                    }
+
+                }
+            }
+        };
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_TIME_CHANGED);
+        mContext.registerReceiver(timeChangeIntentReceiver, filter,
+                null /* broadcastPermission */, getHandler());
+    }
+
+    private static long calculateRelativeBootTime() {
+        return System.currentTimeMillis() - SystemClock.elapsedRealtime();
+    }
+
     /**
      * Performs the actual work to commit a rollback.
      * The work is done on the current thread. This may be a long running
@@ -362,20 +413,24 @@
                 int sessionId = packageInstaller.createSession(params);
                 PackageInstaller.Session session = packageInstaller.openSession(sessionId);
 
-                File packageCode = RollbackStore.getPackageCode(data, info.getPackageName());
-                if (packageCode == null) {
+                File[] packageCodePaths = RollbackStore.getPackageCodePaths(
+                        data, info.getPackageName());
+                if (packageCodePaths == null) {
                     sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
-                            "Backup copy of package code inaccessible");
+                            "Backup copy of package inaccessible");
                     return;
                 }
 
-                try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCode,
-                        ParcelFileDescriptor.MODE_READ_ONLY)) {
-                    final long token = Binder.clearCallingIdentity();
-                    try {
-                        session.write(packageCode.getName(), 0, packageCode.length(), fd);
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
+                for (File packageCodePath : packageCodePaths) {
+                    try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
+                                ParcelFileDescriptor.MODE_READ_ONLY)) {
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            session.write(packageCodePath.getName(), 0, packageCodePath.length(),
+                                    fd);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
                     }
                 }
                 parentSession.addChildSessionId(sessionId);
@@ -434,7 +489,10 @@
             mAvailableRollbacks = null;
             mRecentlyExecutedRollbacks = null;
         }
-        getHandler().post(() -> ensureRollbackDataLoaded());
+        getHandler().post(() -> {
+            updateRollbackLifetimeDurationInMillis();
+            ensureRollbackDataLoaded();
+        });
     }
 
     @Override
@@ -528,7 +586,7 @@
                 PackageInstaller.SessionInfo session = installer.getSessionInfo(
                         data.stagedSessionId);
                 if (session != null) {
-                    if (session.isSessionApplied()) {
+                    if (session.isStagedSessionApplied()) {
                         synchronized (mLock) {
                             data.isAvailable = true;
                         }
@@ -538,7 +596,7 @@
                             Log.e(TAG, "Unable to save rollback info for : "
                                     + data.rollbackId, ioe);
                         }
-                    } else if (session.isSessionFailed()) {
+                    } else if (session.isStagedSessionFailed()) {
                         // TODO: Do we need to remove this from
                         // mAvailableRollbacks, or is it okay to leave as
                         // unavailable until the next reboot when it will go
@@ -950,7 +1008,13 @@
         }
 
         try {
-            RollbackStore.backupPackageCode(data, packageName, pkgInfo.applicationInfo.sourceDir);
+            ApplicationInfo appInfo = pkgInfo.applicationInfo;
+            RollbackStore.backupPackageCodePath(data, packageName, appInfo.sourceDir);
+            if (!ArrayUtils.isEmpty(appInfo.splitSourceDirs)) {
+                for (String sourceDir : appInfo.splitSourceDirs) {
+                    RollbackStore.backupPackageCodePath(data, packageName, sourceDir);
+                }
+            }
         } catch (IOException e) {
             Log.e(TAG, "Unable to copy package for rollback for " + packageName, e);
             return false;
@@ -966,36 +1030,45 @@
         }
 
         getHandler().post(() -> {
-            final RollbackData rollbackData = getRollbackForPackage(packageName);
-            for (int userId : userIds) {
-                if (rollbackData == null || !rollbackData.inProgress) {
-                    Log.e(TAG, "Request to restore userData for: " + packageName
-                                  + ", but no rollback in progress.");
-                    continue;
-                }
-                final PackageRollbackInfo info = getPackageRollbackInfo(rollbackData, packageName);
-                final boolean changedRollbackData = mAppDataRollbackHelper.restoreAppData(
-                        rollbackData.rollbackId, info, userId, appId, seInfo);
-
-                // We've updated metadata about this rollback, so save it to flash.
-                if (changedRollbackData) {
-                    try {
-                        mRollbackStore.saveAvailableRollback(rollbackData);
-                    } catch (IOException ioe) {
-                        // TODO(narayan): What is the right thing to do here ? This isn't a fatal
-                        // error, since it will only result in us trying to restore data again,
-                        // which will be a no-op if there's no data available.
-                        Log.e(TAG, "Unable to save available rollback: " + packageName, ioe);
-                    }
-                }
-            }
-
+            restoreUserDataInternal(packageName, userIds, appId, ceDataInode, seInfo, token);
             final PackageManagerInternal pmi = LocalServices.getService(
                     PackageManagerInternal.class);
             pmi.finishPackageInstall(token, false);
         });
     }
 
+    private void restoreUserDataInternal(String packageName, int[] userIds, int appId,
+            long ceDataInode, String seInfo, int token) {
+        final RollbackData rollbackData = getRollbackForPackage(packageName);
+        if (rollbackData == null) {
+            return;
+        }
+
+        if (!rollbackData.inProgress) {
+            Log.e(TAG, "Request to restore userData for: " + packageName
+                    + ", but no rollback in progress.");
+            return;
+        }
+
+        for (int userId : userIds) {
+            final PackageRollbackInfo info = getPackageRollbackInfo(rollbackData, packageName);
+            final boolean changedRollbackData = mAppDataRollbackHelper.restoreAppData(
+                    rollbackData.rollbackId, info, userId, appId, seInfo);
+
+            // We've updated metadata about this rollback, so save it to flash.
+            if (changedRollbackData) {
+                try {
+                    mRollbackStore.saveAvailableRollback(rollbackData);
+                } catch (IOException ioe) {
+                    // TODO(narayan): What is the right thing to do here ? This isn't a fatal
+                    // error, since it will only result in us trying to restore data again,
+                    // which will be a no-op if there's no data available.
+                    Log.e(TAG, "Unable to save available rollback: " + packageName, ioe);
+                }
+            }
+        }
+    }
+
     @Override
     public boolean notifyStagedSession(int sessionId) {
         final LinkedBlockingQueue<Boolean> result = new LinkedBlockingQueue<>();
@@ -1186,13 +1259,13 @@
         }
 
         if (pi.isStaged()) {
-            if (!pi.isSessionFailed()) {
+            if (!pi.isStagedSessionFailed()) {
                 // TODO: The session really isn't "enabled" at this point, since more work might
                 // be required post reboot.
                 // TODO: We need to make this case consistent with the call from onFinished.
                 //  Ideally, we'd call completeEnableRollback excatly once per multi-package session
                 //  with the parentSessionId only.
-                completeEnableRollback(pi.sessionId, pi.isSessionReady());
+                completeEnableRollback(pi.sessionId, pi.isStagedSessionReady());
             } else {
                 // TODO: Clean up the saved rollback when the session fails. This may need to be
                 // unified with the case where things fail post reboot.
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 3a2b69f..d24f217 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -218,10 +218,10 @@
                     && (sessionId != PackageInstaller.SessionInfo.INVALID_ID)) {
                 PackageInstaller.SessionInfo sessionInfo =
                         packageInstaller.getSessionInfo(sessionId);
-                if (sessionInfo.isSessionReady()) {
+                if (sessionInfo.isStagedSessionReady()) {
                     mContext.unregisterReceiver(listener);
                     mContext.getSystemService(PowerManager.class).reboot("Rollback staged install");
-                } else if (sessionInfo.isSessionFailed()) {
+                } else if (sessionInfo.isStagedSessionFailed()) {
                     mContext.unregisterReceiver(listener);
                 }
             }
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index be904ea..bb4e89e 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -234,9 +234,11 @@
     }
 
     /**
-     * Creates a backup copy of the apk or apex for a package.
+     * Creates a backup copy of an apk or apex for a package.
+     * For packages containing splits, this method should be called for each
+     * of the package's split apks in addition to the base apk.
      */
-    static void backupPackageCode(RollbackData data, String packageName, String codePath)
+    static void backupPackageCodePath(RollbackData data, String packageName, String codePath)
             throws IOException {
         File sourceFile = new File(codePath);
         File targetDir = new File(data.backupDir, packageName);
@@ -248,16 +250,16 @@
     }
 
     /**
-     * Returns the apk or apex file backed up for the given package.
-     * Returns null if none found.
+     * Returns the apk or apex files backed up for the given package.
+     * Includes the base apk and any splits. Returns null if none found.
      */
-    static File getPackageCode(RollbackData data, String packageName) {
+    static File[] getPackageCodePaths(RollbackData data, String packageName) {
         File targetDir = new File(data.backupDir, packageName);
         File[] files = targetDir.listFiles();
-        if (files == null || files.length != 1) {
+        if (files == null || files.length == 0) {
             return null;
         }
-        return files[0];
+        return files;
     }
 
     /**
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index 4b413e5..296a652 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -16,14 +16,13 @@
 
 package com.android.server.timezone;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.EventLogTags;
-import com.android.server.SystemService;
-import com.android.timezone.distro.DistroException;
-import com.android.timezone.distro.DistroVersion;
-import com.android.timezone.distro.StagedDistroOperation;
-import com.android.timezone.distro.TimeZoneDistro;
-import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
+import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
+import static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
+import static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN;
+import static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL;
+import static android.app.timezone.RulesState.STAGED_OPERATION_NONE;
+import static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL;
+import static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN;
 
 import android.app.timezone.Callback;
 import android.app.timezone.DistroFormatVersion;
@@ -37,6 +36,21 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.EventLogTags;
+import com.android.server.SystemService;
+import com.android.timezone.distro.DistroException;
+import com.android.timezone.distro.DistroVersion;
+import com.android.timezone.distro.StagedDistroOperation;
+import com.android.timezone.distro.TimeZoneDistro;
+import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
+
+import libcore.icu.ICU;
+import libcore.timezone.TimeZoneDataFiles;
+import libcore.timezone.TimeZoneFinder;
+import libcore.timezone.TzDataSetVersion;
+import libcore.timezone.ZoneInfoDB;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -46,18 +60,6 @@
 import java.util.Arrays;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
-import libcore.icu.ICU;
-import libcore.timezone.TzDataSetVersion;
-import libcore.timezone.TimeZoneFinder;
-import libcore.timezone.ZoneInfoDB;
-
-import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
-import static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
-import static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN;
-import static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL;
-import static android.app.timezone.RulesState.STAGED_OPERATION_NONE;
-import static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL;
-import static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN;
 
 public final class RulesManagerService extends IRulesManager.Stub {
 
@@ -96,8 +98,6 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     static final String REQUIRED_QUERY_PERMISSION =
             android.Manifest.permission.QUERY_TIME_ZONE_RULES;
-    private static final File SYSTEM_TZ_DATA_FILE = new File("/system/usr/share/zoneinfo/tzdata");
-    private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo");
 
     private final AtomicBoolean mOperationInProgress = new AtomicBoolean(false);
     private final PermissionHelper mPermissionHelper;
@@ -108,12 +108,14 @@
 
     private static RulesManagerService create(Context context) {
         RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context);
+        File baseVersionFile = new File(TimeZoneDataFiles.getRuntimeModuleTzVersionFile());
+        File tzDataDir = new File(TimeZoneDataFiles.getDataTimeZoneRootDir());
         return new RulesManagerService(
                 helper /* permissionHelper */,
                 helper /* executor */,
                 helper /* intentHelper */,
                 PackageTracker.create(context),
-                new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR));
+                new TimeZoneDistroInstaller(TAG, baseVersionFile, tzDataDir));
     }
 
     // A constructor that can be used by tests to supply mocked / faked dependencies.
@@ -143,11 +145,11 @@
     /** Like {@link #getRulesState()} without the permission check. */
     private RulesState getRulesStateInternal() {
         synchronized(this) {
-            String systemRulesVersion;
+            TzDataSetVersion baseVersion;
             try {
-                systemRulesVersion = mInstaller.getSystemRulesVersion();
+                baseVersion = mInstaller.readBaseVersion();
             } catch (IOException e) {
-                Slog.w(TAG, "Failed to read system rules", e);
+                Slog.w(TAG, "Failed to read base rules version", e);
                 return null;
             }
 
@@ -196,7 +198,7 @@
                     Slog.w(TAG, "Failed to read staged distro.", e);
                 }
             }
-            return new RulesState(systemRulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
+            return new RulesState(baseVersion.rulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
                     operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
                     distroStatus, installedDistroRulesVersion);
         }
@@ -454,13 +456,13 @@
                             pw.println("Operation in progress: " + value);
                             break;
                         }
-                        case 's': {
-                            // Report system image rules version
+                        case 'b': {
+                            // Report base rules version
                             String value = "Unknown";
                             if (rulesState != null) {
-                                value = rulesState.getSystemRulesVersion();
+                                value = rulesState.getBaseRulesVersion();
                             }
-                            pw.println("System rules version: " + value);
+                            pw.println("Base rules version: " + value);
                             break;
                         }
                         case 'c': {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 087de69..e976975 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2751,7 +2751,9 @@
                 final Configuration srcConfig = task.getConfiguration();
                 overrideConfig.colorMode = srcConfig.colorMode;
                 overrideConfig.densityDpi = srcConfig.densityDpi;
-                overrideConfig.screenLayout = srcConfig.screenLayout;
+                overrideConfig.screenLayout = srcConfig.screenLayout
+                        & (Configuration.SCREENLAYOUT_LONG_MASK
+                                | Configuration.SCREENLAYOUT_SIZE_MASK);
                 // The smallest screen width is the short side of screen bounds. Because the bounds
                 // and density won't be changed, smallestScreenWidthDp is also fixed.
                 overrideConfig.smallestScreenWidthDp = srcConfig.smallestScreenWidthDp;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 9ea819e..9f04166 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -46,6 +46,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
 import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
 import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
@@ -1457,6 +1458,14 @@
                 // This task was started because of movement of the activity based on affinity...
                 // Now that we are actually launching it, we can assign the base intent.
                 reusedActivity.getTaskRecord().setIntent(mStartActivity);
+            } else {
+                final boolean taskOnHome =
+                        (mStartActivity.intent.getFlags() & FLAG_ACTIVITY_TASK_ON_HOME) != 0;
+                if (taskOnHome) {
+                    reusedActivity.getTaskRecord().intent.addFlags(FLAG_ACTIVITY_TASK_ON_HOME);
+                } else {
+                    reusedActivity.getTaskRecord().intent.removeFlags(FLAG_ACTIVITY_TASK_ON_HOME);
+                }
             }
 
             // This code path leads to delivering a new intent, we want to make sure we schedule it
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index cb4664f..486a4ea 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3098,8 +3098,7 @@
     }
 
     @Override
-    public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing,
-            int[] secondaryDisplaysShowing) {
+    public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing) {
         if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires permission "
@@ -3116,8 +3115,7 @@
                 mH.sendMessage(msg);
             }
             try {
-                mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing,
-                        secondaryDisplaysShowing);
+                mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 91d573d..b028569 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2787,6 +2787,9 @@
     public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
         mFocusedWindow = newFocus;
         mLastFocusedWindow = lastFocus;
+        if (mDisplayContent.isDefaultDisplay) {
+            mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus);
+        }
         if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
             // If the navigation bar has been hidden or shown, we need to do another
             // layout pass to update that window.
@@ -3271,7 +3274,12 @@
         }
 
         final WindowState w = mTopFullscreenOpaqueWindowState;
-        if (w != mFocusedWindow) {
+        if (w == null || w != mFocusedWindow) {
+            return false;
+        }
+        // If the bounds of activity window is different from its parent, then reject to be seamless
+        // because the window position may change after rotation that will look like a sudden jump.
+        if (w.mAppToken != null && !w.mAppToken.matchParentBounds()) {
             return false;
         }
 
@@ -3279,8 +3287,7 @@
         // it and is in the fullscreen opaque state. Seamless rotation
         // requires freezing various Surface states and won't work well
         // with animations, so we disable it in the animation case for now.
-        if (w != null && !w.isAnimatingLw()
-                && w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) {
+        if (!w.isAnimatingLw() && w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) {
             return true;
         }
         return false;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 543f196..34a4802 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -21,6 +21,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.annotation.IntDef;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.content.ContentResolver;
@@ -49,6 +50,8 @@
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Defines the mapping between orientation and rotation of a display.
@@ -96,11 +99,36 @@
     private int mUserRotation = Surface.ROTATION_0;
 
     /**
+     * Flag that indicates this is a display that may run better when fixed to user rotation.
+     */
+    private boolean mDefaultFixedToUserRotation;
+
+    /**
+     * No overridden behavior is provided in terms of fixing rotation to user rotation. Use other
+     * flags to derive the default behavior, such as {@link WindowManagerService#mIsPc} and
+     * {@link WindowManagerService#mForceDesktopModeOnExternalDisplays}.
+     */
+    static final int FIXED_TO_USER_ROTATION_DEFAULT = 0;
+    /**
+     * Don't fix display rotation to {@link #mUserRotation} only. Always allow other factors to play
+     * a role in deciding display rotation.
+     */
+    static final int FIXED_TO_USER_ROTATION_DISABLED = 1;
+    /**
+     * Only use {@link #mUserRotation} as the display rotation.
+     */
+    static final int FIXED_TO_USER_ROTATION_ENABLED = 2;
+    @IntDef({ FIXED_TO_USER_ROTATION_DEFAULT, FIXED_TO_USER_ROTATION_DISABLED,
+            FIXED_TO_USER_ROTATION_ENABLED })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface FixedToUserRotation {}
+
+    /**
      * A flag to indicate if the display rotation should be fixed to user specified rotation
      * regardless of all other states (including app requrested orientation). {@code true} the
      * display rotation should be fixed to user specified rotation, {@code false} otherwise.
      */
-    private boolean mFixedToUserRotation;
+    private int mFixedToUserRotation = FIXED_TO_USER_ROTATION_DEFAULT;
 
     private int mDemoHdmiRotation;
     private int mDemoRotation;
@@ -208,31 +236,23 @@
         }
         mDemoRotationLock = SystemProperties.getBoolean("persist.demo.rotationlock", false);
 
-        // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
-        // http://developer.android.com/guide/practices/screens_support.html#range
-        // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen
-        // so if the orientation is forced, we need to respect that no matter what.
+        // It's physically impossible to rotate the car's screen.
         final boolean isCar = mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_AUTOMOTIVE);
-        // For TV, it's usually 960dp x 540dp, ignore the size limitation.
-        // so if the orientation is forced, we need to respect that no matter what.
+        // It's also not likely to rotate a TV screen.
         final boolean isTv = mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_LEANBACK);
+        // Not much of use to rotate the display since it's close to square.
         final boolean isCloseToSquare =
                 isNonDecorDisplayCloseToSquare(Surface.ROTATION_0, width, height);
-        final boolean forceDefaultOrientationInRes =
-                res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation);
-        final boolean forceDefaultOrienation =
-                ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv || isCloseToSquare)
-                        && forceDefaultOrientationInRes
-                        // For debug purposes the next line turns this feature off with:
-                        // $ adb shell setprop config.override_forced_orient true
-                        // $ adb shell wm size reset
-                        && !"true".equals(SystemProperties.get("config.override_forced_orient"));
-        // Configuration says we force to use the default orientation. We can fall back to fix
-        // rotation to only user rotation. As long as OEM doesn't change user rotation then the
-        // rotation of this display is effectively stuck at 0 deg.
-        setFixedToUserRotation(forceDefaultOrienation);
+        final boolean forceDesktopMode =
+                mService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay;
+        mDefaultFixedToUserRotation =
+                (isCar || isTv || mService.mIsPc || forceDesktopMode || isCloseToSquare)
+                // For debug purposes the next line turns this feature off with:
+                // $ adb shell setprop config.override_forced_orient true
+                // $ adb shell wm size reset
+                && !"true".equals(SystemProperties.get("config.override_forced_orient"));
     }
 
     private boolean isNonDecorDisplayCloseToSquare(int rotation, int width, int height) {
@@ -263,7 +283,7 @@
     }
 
     void restoreSettings(int userRotationMode, int userRotation,
-            boolean fixedToUserRotation) {
+            @FixedToUserRotation int fixedToUserRotation) {
         mFixedToUserRotation = fixedToUserRotation;
 
         // We will retrieve user rotation and user rotation mode from settings for default display.
@@ -285,14 +305,13 @@
         mUserRotation = userRotation;
     }
 
-    void setFixedToUserRotation(boolean fixedToUserRotation) {
+    void setFixedToUserRotation(@FixedToUserRotation int fixedToUserRotation) {
         if (mFixedToUserRotation == fixedToUserRotation) {
             return;
         }
 
         mFixedToUserRotation = fixedToUserRotation;
-        mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent,
-                fixedToUserRotation);
+        mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent, fixedToUserRotation);
         mService.updateRotation(true /* alwaysSendConfiguration */,
                 false /* forceRelayout */);
     }
@@ -346,7 +365,14 @@
     }
 
     boolean isFixedToUserRotation() {
-        return mFixedToUserRotation;
+        switch (mFixedToUserRotation) {
+            case FIXED_TO_USER_ROTATION_DISABLED:
+                return false;
+            case FIXED_TO_USER_ROTATION_ENABLED:
+                return true;
+            default:
+                return mDefaultFixedToUserRotation;
+        }
     }
 
     /**
@@ -355,7 +381,7 @@
      * false} is when {@link #isFixedToUserRotation()} is {@code true}.
      */
     boolean respectAppRequestedOrientation() {
-        return !mFixedToUserRotation;
+        return !isFixedToUserRotation();
     }
 
     public int getLandscapeRotation() {
@@ -461,7 +487,7 @@
      * screen is switched off.
      */
     private boolean needSensorRunning() {
-        if (mFixedToUserRotation) {
+        if (isFixedToUserRotation()) {
             // We are sure we only respect user rotation settings, so we are sure we will not
             // support sensor rotation.
             return false;
@@ -527,7 +553,7 @@
                         );
         }
 
-        if (mFixedToUserRotation) {
+        if (isFixedToUserRotation()) {
             return mUserRotation;
         }
 
@@ -739,7 +765,7 @@
         // demo, hdmi, vr, etc mode.
 
         // Determine if the rotation is currently forced.
-        if (mFixedToUserRotation) {
+        if (isFixedToUserRotation()) {
             return false; // Rotation is forced to user settings.
         }
 
@@ -899,7 +925,7 @@
         pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock);
         pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation));
         pw.println(prefix + "  mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
-        pw.println(prefix + "  mFixedToUserRotation=" + mFixedToUserRotation);
+        pw.println(prefix + "  mFixedToUserRotation=" + isFixedToUserRotation());
     }
 
     private class OrientationListener extends WindowOrientationListener {
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 5cfa7de..4617890 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -22,6 +22,7 @@
 
 import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO;
 import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED;
+import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DEFAULT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -80,7 +81,8 @@
         private boolean mShouldShowWithInsecureKeyguard = false;
         private boolean mShouldShowSystemDecors = false;
         private boolean mShouldShowIme = false;
-        private boolean mFixedToUserRotation;
+        private @DisplayRotation.FixedToUserRotation int mFixedToUserRotation =
+                FIXED_TO_USER_ROTATION_DEFAULT;
 
         private Entry(String name) {
             mName = name;
@@ -99,7 +101,7 @@
                     && !mShouldShowWithInsecureKeyguard
                     && !mShouldShowSystemDecors
                     && !mShouldShowIme
-                    && !mFixedToUserRotation;
+                    && mFixedToUserRotation == FIXED_TO_USER_ROTATION_DEFAULT;
         }
     }
 
@@ -188,7 +190,8 @@
         writeSettingsIfNeeded(entry, displayInfo);
     }
 
-    void setFixedToUserRotation(DisplayContent displayContent, boolean fixedToUserRotation) {
+    void setFixedToUserRotation(DisplayContent displayContent,
+            @DisplayRotation.FixedToUserRotation int fixedToUserRotation) {
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
         final Entry entry = getOrCreateEntry(displayInfo);
         entry.mFixedToUserRotation = fixedToUserRotation;
@@ -464,8 +467,7 @@
                     "shouldShowWithInsecureKeyguard");
             entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors");
             entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme");
-            entry.mFixedToUserRotation = getBooleanAttribute(parser,
-                    "fixedToUserRotation");
+            entry.mFixedToUserRotation = getIntAttribute(parser, "fixedToUserRotation");
             mEntries.put(name, entry);
         }
         XmlUtils.skipCurrentTag(parser);
@@ -549,9 +551,9 @@
                 if (entry.mShouldShowIme) {
                     out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme));
                 }
-                if (entry.mFixedToUserRotation) {
+                if (entry.mFixedToUserRotation != FIXED_TO_USER_ROTATION_DEFAULT) {
                     out.attribute(null, "fixedToUserRotation",
-                            Boolean.toString(entry.mFixedToUserRotation));
+                            Integer.toString(entry.mFixedToUserRotation));
                 }
                 out.endTag(null, "display");
             }
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index b5be2ac5..feb711a 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -49,7 +49,6 @@
 import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
 
 import java.io.PrintWriter;
-import java.util.Arrays;
 
 /**
  * Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are
@@ -120,18 +119,15 @@
     /**
      * Update the Keyguard showing state.
      */
-    void setKeyguardShown(boolean keyguardShowing, boolean aodShowing,
-            int[] secondaryDisplaysShowing) {
+    void setKeyguardShown(boolean keyguardShowing, boolean aodShowing) {
         boolean showingChanged = keyguardShowing != mKeyguardShowing || aodShowing != mAodShowing;
         // If keyguard is going away, but SystemUI aborted the transition, need to reset state.
         showingChanged |= mKeyguardGoingAway && keyguardShowing;
-        if (!showingChanged && Arrays.equals(secondaryDisplaysShowing,
-                mSecondaryDisplayIdsShowing)) {
+        if (!showingChanged) {
             return;
         }
         mKeyguardShowing = keyguardShowing;
         mAodShowing = aodShowing;
-        mSecondaryDisplayIdsShowing = secondaryDisplaysShowing;
         mWindowManager.setAodShowing(aodShowing);
         if (showingChanged) {
             dismissDockedStackIfNeeded();
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index c6c85fd..0480d43 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1159,7 +1159,8 @@
     /**
      * @return whether the given active task should be presented to the user through SystemUI.
      */
-    private boolean isVisibleRecentTask(TaskRecord task) {
+    @VisibleForTesting
+    boolean isVisibleRecentTask(TaskRecord task) {
         if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isVisibleRecentTask: task=" + task
                 + " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks
                 + " sessionDuration=" + mActiveTasksSessionDurationMs
@@ -1195,6 +1196,17 @@
                 }
         }
 
+        // Tasks managed by/associated with an ActivityView should be excluded from recents.
+        // singleTaskInstance is set on the VirtualDisplay managed by ActivityView
+        // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance
+        final ActivityStack stack = task.getStack();
+        if (stack != null) {
+            ActivityDisplay display = stack.getDisplay();
+            if (display != null && display.isSingleTaskInstance()) {
+                return false;
+            }
+        }
+
         // If we're in lock task mode, ignore the root task
         if (task == mService.getLockTaskController().getRootTask()) {
             return false;
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 84cd8d1..2d5c97f 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -257,7 +257,7 @@
         mOriginalWidth = originalWidth;
         mOriginalHeight = originalHeight;
 
-        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        final SurfaceControl.Transaction t = mService.mTransactionFactory.make();
         try {
             mSurfaceControl = displayContent.makeOverlay()
                     .setName("ScreenshotSurface")
@@ -267,13 +267,13 @@
 
             // In case display bounds change, screenshot buffer and surface may mismatch so set a
             // scaling mode.
-            SurfaceControl.Transaction t2 = new SurfaceControl.Transaction();
+            SurfaceControl.Transaction t2 = mService.mTransactionFactory.make();
             t2.setOverrideScalingMode(mSurfaceControl, Surface.SCALING_MODE_SCALE_TO_WINDOW);
             t2.apply(true /* sync */);
 
             // Capture a screenshot into the surface we just created.
             final int displayId = display.getDisplayId();
-            final Surface surface = new Surface();
+            final Surface surface = mService.mSurfaceFactory.make();
             surface.copyFrom(mSurfaceControl);
             if (mService.mDisplayManagerInternal.screenshot(displayId, surface)) {
                 t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 58cf73a..dc8c7b7 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -245,17 +245,13 @@
     }
 
     @Override
-    public boolean performHapticFeedback(IWindow window, int effectId,
-            boolean always) {
-        synchronized (mService.mGlobalLock) {
-            long ident = Binder.clearCallingIdentity();
-            try {
-                return mService.mPolicy.performHapticFeedbackLw(
-                        mService.windowForClientLocked(this, window, true),
+    public boolean performHapticFeedback(int effectId, boolean always) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            return mService.mPolicy.performHapticFeedback(mUid, mPackageName,
                         effectId, always, null);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/SurfaceFactory.java b/services/core/java/com/android/server/wm/SurfaceFactory.java
new file mode 100644
index 0000000..076b7df
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SurfaceFactory.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.view.Surface;
+
+/**
+ * Helper class to inject custom {@link Surface} objects into window manager.
+ */
+interface SurfaceFactory {
+    Surface make();
+};
+
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 74fb3fa..dddc6b7 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -50,6 +50,7 @@
 import android.view.WindowManager;
 import android.view.animation.Animation;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
 
 import java.io.PrintWriter;
@@ -700,24 +701,37 @@
         mWallpaperTokens.remove(token);
     }
 
+
+    @VisibleForTesting
+    boolean canScreenshotWallpaper() {
+        return canScreenshotWallpaper(getTopVisibleWallpaper());
+    }
+
+    private boolean canScreenshotWallpaper(WindowState wallpaperWindowState) {
+        if (!mService.mPolicy.isScreenOn()) {
+            if (DEBUG_SCREENSHOT) {
+                Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
+            }
+            return false;
+        }
+
+        if (wallpaperWindowState == null) {
+            if (DEBUG_SCREENSHOT) {
+                Slog.i(TAG_WM, "No visible wallpaper to screenshot");
+            }
+            return false;
+        }
+        return true;
+    }
+
     /**
      * Take a screenshot of the wallpaper if it's visible.
      *
      * @return Bitmap of the wallpaper
      */
     Bitmap screenshotWallpaperLocked() {
-        if (!mService.mPolicy.isScreenOn()) {
-            if (DEBUG_SCREENSHOT) {
-                Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
-            }
-            return null;
-        }
-
         final WindowState wallpaperWindowState = getTopVisibleWallpaper();
-        if (wallpaperWindowState == null) {
-            if (DEBUG_SCREENSHOT) {
-                Slog.i(TAG_WM, "No visible wallpaper to screenshot");
-            }
+        if (!canScreenshotWallpaper(wallpaperWindowState)) {
             return null;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 39df0e4..e19c7c6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -209,6 +209,7 @@
 import android.view.IWindowSessionCallback;
 import android.view.InputChannel;
 import android.view.InputDevice;
+import android.view.InputEvent;
 import android.view.InputEventReceiver;
 import android.view.InsetsState;
 import android.view.KeyEvent;
@@ -814,8 +815,9 @@
 
     SurfaceBuilderFactory mSurfaceBuilderFactory = SurfaceControl.Builder::new;
     TransactionFactory mTransactionFactory = SurfaceControl.Transaction::new;
+    SurfaceFactory mSurfaceFactory = Surface::new;
 
-    private final SurfaceControl.Transaction mTransaction = mTransactionFactory.make();
+    private final SurfaceControl.Transaction mTransaction;
 
     static void boostPriorityForLockedSection() {
         sThreadPriorityBooster.boost();
@@ -909,9 +911,21 @@
     public static WindowManagerService main(final Context context, final InputManagerService im,
             final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
             ActivityTaskManagerService atm) {
+        return main(context, im, showBootMsgs, onlyCore, policy, atm,
+                SurfaceControl.Transaction::new);
+    }
+
+    /**
+     * Creates and returns an instance of the WindowManagerService. This call allows the caller
+     * to override the {@link TransactionFactory} to stub functionality under test.
+     */
+    @VisibleForTesting
+    public static WindowManagerService main(final Context context, final InputManagerService im,
+            final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
+            ActivityTaskManagerService atm, TransactionFactory transactionFactory) {
         DisplayThread.getHandler().runWithScissors(() ->
                 sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
-                        atm), 0);
+                        atm, transactionFactory), 0);
         return sInstance;
     }
 
@@ -933,7 +947,7 @@
 
     private WindowManagerService(Context context, InputManagerService inputManager,
             boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
-            ActivityTaskManagerService atm) {
+            ActivityTaskManagerService atm, TransactionFactory transactionFactory) {
         installLock(this, INDEX_WINDOW);
         mGlobalLock = atm.getGlobalLock();
         mAtmService = atm;
@@ -962,6 +976,9 @@
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mDisplayWindowSettings = new DisplayWindowSettings(this);
 
+
+        mTransactionFactory = transactionFactory;
+        mTransaction = mTransactionFactory.make();
         mPolicy = policy;
         mAnimator = new WindowAnimator(this);
         mRoot = new RootWindowContainer(this);
@@ -3503,14 +3520,15 @@
         }
     }
 
-    void setRotateForApp(int displayId, boolean enabled) {
+    void setRotateForApp(int displayId,
+            @DisplayRotation.FixedToUserRotation int fixedToUserRotation) {
         synchronized (mGlobalLock) {
             final DisplayContent display = mRoot.getDisplayContent(displayId);
             if (display == null) {
                 Slog.w(TAG, "Trying to set rotate for app for a missing display.");
                 return;
             }
-            display.getDisplayRotation().setFixedToUserRotation(enabled);
+            display.getDisplayRotation().setFixedToUserRotation(fixedToUserRotation);
         }
     }
 
@@ -7403,4 +7421,16 @@
             }
         }
     }
+
+    @Override
+    public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) {
+        synchronized (mGlobalLock) {
+            mWindowPlacerLocked.performSurfacePlacementIfScheduled();
+            new SurfaceControl.Transaction()
+                    .syncInputWindows()
+                    .apply(true);
+        }
+
+        return mInputManager.injectInputEvent(ev, mode);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index d13ee45..7384bb7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -342,21 +342,24 @@
             arg = getNextArgRequired();
         }
 
-        final boolean enabled;
+        final @DisplayRotation.FixedToUserRotation  int fixedToUserRotation;
         switch (arg) {
             case "enabled":
-                enabled = true;
+                fixedToUserRotation = DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED;
                 break;
             case "disabled":
-                enabled = false;
+                fixedToUserRotation = DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED;
+                break;
+            case "default":
+                fixedToUserRotation = DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED;
                 break;
             default:
-                getErrPrintWriter().println("Error: expecting enabled or disabled, but we get "
-                        + arg);
+                getErrPrintWriter().println("Error: expecting enabled, disabled or default, but we "
+                        + "get " + arg);
                 return -1;
         }
 
-        mInternal.setRotateForApp(displayId, enabled);
+        mInternal.setRotateForApp(displayId, fixedToUserRotation);
         return 0;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 2ee58fe..cc791787 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -85,6 +85,12 @@
         return mDeferDepth > 0;
     }
 
+    void performSurfacePlacementIfScheduled() {
+        if (mTraversalScheduled) {
+            performSurfacePlacement();
+        }
+    }
+
     final void performSurfacePlacement() {
         performSurfacePlacement(false /* force */);
     }
diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
index 2ce6e6c..a4ee907 100644
--- a/services/core/java/com/android/server/wm/WindowTraceBuffer.java
+++ b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
@@ -98,9 +98,8 @@
                 ProtoOutputStream proto = new ProtoOutputStream();
                 proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
                 os.write(proto.getBytes());
-                while (!mBuffer.isEmpty()) {
-                    proto = mBuffer.poll();
-                    mBufferUsedSize -= proto.getRawSize();
+                for (ProtoOutputStream protoOutputStream : mBuffer) {
+                    proto = protoOutputStream;
                     byte[] protoBytes = proto.getBytes();
                     os.write(protoBytes);
                 }
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 3b17abc..48b9340 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -208,6 +208,7 @@
                 pw.println("  frame: Log trace once per frame");
                 pw.println("  transaction: Log each transaction");
                 pw.println("  size: Set the maximum log size (in KB)");
+                pw.println("  status: Print trace status");
                 pw.println("  level [lvl]: Set the log level between");
                 pw.println("    lvl may be one of:");
                 pw.println("      critical: Only visible windows with reduced information");
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index d178c3a..03240c0 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -36,13 +36,14 @@
 #include "android_runtime/Log.h"
 
 #include <arpa/inet.h>
+#include <cinttypes>
+#include <iomanip>
 #include <limits>
 #include <linux/in.h>
 #include <linux/in6.h>
 #include <pthread.h>
 #include <string.h>
-#include <cinttypes>
-#include <iomanip>
+#include <utils/SystemClock.h>
 
 static jobject mCallbacksObj = nullptr;
 
@@ -111,10 +112,9 @@
 using android::hardware::hidl_vec;
 using android::hardware::hidl_string;
 using android::hardware::hidl_death_recipient;
-using android::hardware::gnss::V1_0::GnssConstellationType;
-using android::hardware::gnss::V1_0::GnssLocation;
-using android::hardware::gnss::V1_0::GnssLocationFlags;
 
+using android::hardware::gnss::V1_0::GnssConstellationType;
+using android::hardware::gnss::V1_0::GnssLocationFlags;
 using android::hardware::gnss::V1_0::IAGnssRilCallback;
 using android::hardware::gnss::V1_0::IGnssBatching;
 using android::hardware::gnss::V1_0::IGnssBatchingCallback;
@@ -128,13 +128,17 @@
 using android::hardware::gnss::V1_0::IGnssXtra;
 using android::hardware::gnss::V1_0::IGnssXtraCallback;
 
+using android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
 using android::hardware::gnss::V2_0::IGnssCallback;
+
 using android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections;
 using android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection;
 using android::hardware::gnss::measurement_corrections::V1_0::ReflectingPlane;
 
 using android::hidl::base::V1_0::IBase;
 
+using GnssLocation_V1_0 = android::hardware::gnss::V1_0::GnssLocation;
+using GnssLocation_V2_0 = android::hardware::gnss::V2_0::GnssLocation;
 using IGnss_V1_0 = android::hardware::gnss::V1_0::IGnss;
 using IGnss_V1_1 = android::hardware::gnss::V1_1::IGnss;
 using IGnss_V2_0 = android::hardware::gnss::V2_0::IGnss;
@@ -204,6 +208,20 @@
 
 namespace android {
 
+namespace {
+
+// Returns true if location has lat/long information.
+bool hasLatLong(const GnssLocation_V1_0& location) {
+    return (static_cast<uint32_t>(location.gnssLocationFlags) &
+            GnssLocationFlags::HAS_LAT_LONG) != 0;
+}
+
+// Returns true if location has lat/long information.
+bool hasLatLong(const GnssLocation_V2_0& location) {
+    return hasLatLong(location.v1_0);
+}
+
+}  // namespace
 template<class T>
 class JavaMethodHelper {
  public:
@@ -216,7 +234,7 @@
            T value);
 
  private:
-    static const char *const signature_;
+    static const char* const signature_;
 };
 
 template<class T>
@@ -234,6 +252,8 @@
  public:
     JavaObject(JNIEnv* env, const char* class_name);
     JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1);
+    JavaObject(JNIEnv* env, const char* class_name, jobject object);
+
     virtual ~JavaObject();
 
     template<class T>
@@ -260,6 +280,11 @@
     object_ = env_->NewObject(clazz_, ctor, env->NewStringUTF(sz_arg_1));
 }
 
+JavaObject::JavaObject(JNIEnv* env, const char* class_name, jobject object)
+    : env_(env), object_(object) {
+    clazz_ = env_->FindClass(class_name);
+}
+
 JavaObject::~JavaObject() {
     env_->DeleteLocalRef(clazz_);
 }
@@ -303,6 +328,8 @@
 template<>
 const char *const JavaMethodHelper<int64_t>::signature_ = "(J)V";
 template<>
+const char *const JavaMethodHelper<uint64_t>::signature_ = "(J)V";
+template<>
 const char *const JavaMethodHelper<float>::signature_ = "(F)V";
 template<>
 const char *const JavaMethodHelper<double>::signature_ = "(D)V";
@@ -416,7 +443,8 @@
     return env;
 }
 
-static jobject translateLocation(JNIEnv* env, const GnssLocation& location) {
+static jobject translateGnssLocation(JNIEnv* env,
+                                     const GnssLocation_V1_0& location) {
     JavaObject object(env, "android/location/Location", "gps");
 
     uint16_t flags = static_cast<uint32_t>(location.gnssLocationFlags);
@@ -446,23 +474,33 @@
         SET(BearingAccuracyDegrees, location.bearingAccuracyDegrees);
     }
     SET(Time, location.timestamp);
+    SET(ElapsedRealtimeNanos, android::elapsedRealtimeNano());
 
     return object.get();
 }
 
-static GnssLocation createGnssLocation(
-        jint gnssLocationFlags,
-        jdouble latitudeDegrees,
-        jdouble longitudeDegrees,
-        jdouble altitudeMeters,
-        jfloat speedMetersPerSec,
-        jfloat bearingDegrees,
-        jfloat horizontalAccuracyMeters,
-        jfloat verticalAccuracyMeters,
-        jfloat speedAccuracyMetersPerSecond,
-        jfloat bearingAccuracyDegrees,
+static jobject translateGnssLocation(JNIEnv* env,
+                                     const GnssLocation_V2_0& location) {
+    JavaObject object(env, "android/location/Location",
+                      translateGnssLocation(env, location.v1_0));
+
+    const uint16_t flags = static_cast<uint16_t>(location.elapsedRealtime.flags);
+
+    // Overwrite ElapsedRealtimeNanos when available from HAL.
+    if (flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) {
+        SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
+    }
+
+    return object.get();
+}
+
+static GnssLocation_V1_0 createGnssLocation_V1_0(
+        jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees,
+        jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
+        jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+        jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
         jlong timestamp) {
-    GnssLocation location;
+    GnssLocation_V1_0 location;
     location.gnssLocationFlags = static_cast<uint16_t>(gnssLocationFlags);
     location.latitudeDegrees = static_cast<double>(latitudeDegrees);
     location.longitudeDegrees = static_cast<double>(longitudeDegrees);
@@ -478,11 +516,30 @@
     return location;
 }
 
+static GnssLocation_V2_0 createGnssLocation_V2_0(
+        jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees,
+        jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
+        jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+        jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
+        jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos) {
+    GnssLocation_V2_0 location;
+    location.v1_0 = createGnssLocation_V1_0(
+            gnssLocationFlags, latitudeDegrees, longitudeDegrees, altitudeMeters,
+            speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters,
+            verticalAccuracyMeters, speedAccuracyMetersPerSecond,
+            bearingAccuracyDegrees, timestamp);
+
+    location.elapsedRealtime.flags = static_cast<uint16_t>(elapsedRealtimeFlags);
+    location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
+
+    return location;
+}
+
 /*
  * GnssCallback class implements the callback methods for IGnss interface.
  */
 struct GnssCallback : public IGnssCallback {
-    Return<void> gnssLocationCb(const GnssLocation& location) override;
+    Return<void> gnssLocationCb(const GnssLocation_V1_0& location) override;
     Return<void> gnssStatusCb(const IGnssCallback::GnssStatusValue status) override;
     Return<void> gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) override;
     Return<void> gnssNmeaCb(int64_t timestamp, const android::hardware::hidl_string& nmea) override;
@@ -496,7 +553,13 @@
     // New in 1.1
     Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
 
+    // New in 2.0
     Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
+    Return<void> gnssLocationCb_2_0(const GnssLocation_V2_0& location) override;
+
+    // Templated implementation for gnnsLocationCb and gnnsLocationCb_2_0.
+    template <class T>
+    Return<void> gnssLocationCbImpl(const T& location);
 
     // TODO(b/73306084): Reconsider allocation cost vs threadsafety on these statics
     static const char* sNmeaString;
@@ -517,22 +580,30 @@
 const char* GnssCallback::sNmeaString = nullptr;
 size_t GnssCallback::sNmeaStringLength = 0;
 
-Return<void> GnssCallback::gnssLocationCb(const GnssLocation& location) {
+template<class T>
+Return<void> GnssCallback::gnssLocationCbImpl(const T& location) {
     JNIEnv* env = getJniEnv();
 
-    jobject jLocation = translateLocation(env, location);
-    bool hasLatLong = (static_cast<uint32_t>(location.gnssLocationFlags) &
-            GnssLocationFlags::HAS_LAT_LONG) != 0;
+    jobject jLocation = translateGnssLocation(env, location);
 
     env->CallVoidMethod(mCallbacksObj,
                         method_reportLocation,
-                        boolToJbool(hasLatLong),
+                        boolToJbool(hasLatLong(location)),
                         jLocation);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     env->DeleteLocalRef(jLocation);
     return Void();
 }
 
+Return<void> GnssCallback::gnssLocationCb(const GnssLocation_V1_0& location) {
+    return gnssLocationCbImpl<GnssLocation_V1_0>(location);
+}
+
+Return<void>
+GnssCallback::gnssLocationCb_2_0(const GnssLocation_V2_0& location) {
+    return gnssLocationCbImpl<GnssLocation_V2_0>(location);
+}
+
 Return<void> GnssCallback::gnssStatusCb(const IGnssCallback::GnssStatusValue status) {
     JNIEnv* env = getJniEnv();
     env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
@@ -681,12 +752,13 @@
     // Methods from ::android::hardware::gps::V1_0::IGnssGeofenceCallback follow.
     Return<void> gnssGeofenceTransitionCb(
             int32_t geofenceId,
-            const GnssLocation& location,
+            const GnssLocation_V1_0& location,
             GeofenceTransition transition,
             hardware::gnss::V1_0::GnssUtcTime timestamp) override;
-    Return<void> gnssGeofenceStatusCb(
+    Return<void>
+    gnssGeofenceStatusCb(
             GeofenceAvailability status,
-            const GnssLocation& location) override;
+            const GnssLocation_V1_0& location) override;
     Return<void> gnssGeofenceAddCb(int32_t geofenceId,
                                    GeofenceStatus status) override;
     Return<void> gnssGeofenceRemoveCb(int32_t geofenceId,
@@ -698,13 +770,12 @@
 };
 
 Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb(
-        int32_t geofenceId,
-        const GnssLocation& location,
+        int32_t geofenceId, const GnssLocation_V1_0& location,
         GeofenceTransition transition,
         hardware::gnss::V1_0::GnssUtcTime timestamp) {
     JNIEnv* env = getJniEnv();
 
-    jobject jLocation = translateLocation(env, location);
+    jobject jLocation = translateGnssLocation(env, location);
 
     env->CallVoidMethod(mCallbacksObj,
                         method_reportGeofenceTransition,
@@ -718,16 +789,14 @@
     return Void();
 }
 
-Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb(
-        GeofenceAvailability status,
-        const GnssLocation& location) {
+Return<void>
+GnssGeofenceCallback::gnssGeofenceStatusCb(GeofenceAvailability status,
+                                           const GnssLocation_V1_0& location) {
     JNIEnv* env = getJniEnv();
 
-    jobject jLocation = translateLocation(env, location);
+    jobject jLocation = translateGnssLocation(env, location);
 
-    env->CallVoidMethod(mCallbacksObj,
-                        method_reportGeofenceStatus,
-                        status,
+    env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, status,
                         jLocation);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     env->DeleteLocalRef(jLocation);
@@ -1296,18 +1365,18 @@
     * Methods from ::android::hardware::gps::V1_0::IGnssBatchingCallback
     * follow.
     */
-    Return<void> gnssLocationBatchCb(const hidl_vec<GnssLocation> & locations)
-        override;
+    Return<void> gnssLocationBatchCb(const hidl_vec<GnssLocation_V1_0>& locations) override;
 };
 
-Return<void> GnssBatchingCallback::gnssLocationBatchCb(const hidl_vec<GnssLocation> & locations) {
+Return<void> GnssBatchingCallback::gnssLocationBatchCb(
+    const hidl_vec<GnssLocation_V1_0>& locations) {
     JNIEnv* env = getJniEnv();
 
     jobjectArray jLocations = env->NewObjectArray(locations.size(),
             env->FindClass("android/location/Location"), nullptr);
 
     for (uint16_t i = 0; i < locations.size(); ++i) {
-        jobject jLocation = translateLocation(env, locations[i]);
+        jobject jLocation = translateGnssLocation(env, locations[i]);
         env->SetObjectArrayElement(jLocations, i, jLocation);
         env->DeleteLocalRef(jLocation);
     }
@@ -1760,7 +1829,7 @@
     agnssRilIface->setRefLocation(location);
 }
 
-static void android_location_GnssLocationProvider_agps_set_id(JNIEnv *env, jobject /* obj */,
+static void android_location_GnssLocationProvider_agps_set_id(JNIEnv* env, jobject /* obj */,
                                                              jint type, jstring  setid_string) {
     if (agnssRilIface == nullptr) {
         ALOGE("no AGPS RIL interface in agps_set_id");
@@ -1806,9 +1875,34 @@
         jfloat verticalAccuracyMeters,
         jfloat speedAccuracyMetersPerSecond,
         jfloat bearingAccuracyDegrees,
-        jlong timestamp) {
+        jlong timestamp,
+        jint elapsedRealtimeFlags,
+        jlong elapsedRealtimeNanos) {
+    if (gnssHal_V2_0 != nullptr) {
+        GnssLocation_V2_0 location = createGnssLocation_V2_0(
+                gnssLocationFlags,
+                latitudeDegrees,
+                longitudeDegrees,
+                altitudeMeters,
+                speedMetersPerSec,
+                bearingDegrees,
+                horizontalAccuracyMeters,
+                verticalAccuracyMeters,
+                speedAccuracyMetersPerSecond,
+                bearingAccuracyDegrees,
+                timestamp,
+                elapsedRealtimeFlags,
+                elapsedRealtimeNanos);
+        auto result = gnssHal_V2_0->injectBestLocation_2_0(location);
+
+        if (!result.isOk() || !result) {
+            ALOGE("%s: Gnss injectBestLocation() failed.", __func__);
+        }
+        return;
+    }
+
     if (gnssHal_V1_1 != nullptr) {
-        GnssLocation location = createGnssLocation(
+        GnssLocation_V1_0 location = createGnssLocation_V1_0(
                 gnssLocationFlags,
                 latitudeDegrees,
                 longitudeDegrees,
@@ -1821,12 +1915,14 @@
                 bearingAccuracyDegrees,
                 timestamp);
         auto result = gnssHal_V1_1->injectBestLocation(location);
+
         if (!result.isOk() || !result) {
             ALOGE("%s: Gnss injectBestLocation() failed.", __func__);
         }
-    } else {
-        ALOGE("%s: injectBestLocation() is called but gnssHal_V1_1 is not available.", __func__);
+        return;
     }
+
+    ALOGE("%s: injectBestLocation() is called but gnssHal_V1_1 is not available.", __func__);
 }
 
 static void android_location_GnssLocationProvider_inject_location(JNIEnv* /* env */,
@@ -2248,7 +2344,7 @@
             measCorrClass, "getToaGpsNanosecondsOfWeek", "()J");
 
         method_correctionsGetSingleSatCorrectionList = env->GetMethodID(
-            measCorrClass, "getSingleSatCorrectionList", "()Ljava.util.List;");
+            measCorrClass, "getSingleSatelliteCorrectionList", "()Ljava.util.List;");
     }
 
     jdouble latitudeDegreesCorr = env->CallDoubleMethod(
@@ -2285,15 +2381,15 @@
         if (firstGnssMeasurementCorrectionInjected == false) {
             jclass singleSatCorrClass = env->GetObjectClass(singleSatCorrectionObj);
             method_correctionSatFlags = env->GetMethodID(
-                singleSatCorrClass, "getSingleSatCorrectionFlags", "()I");
+                singleSatCorrClass, "getSingleSatelliteCorrectionFlags", "()I");
             method_correctionSatConstType = env->GetMethodID(
                 singleSatCorrClass, "getConstellationType", "()I");
             method_correctionSatId= env->GetMethodID(
-                singleSatCorrClass, "getSatId", "()I");
+                singleSatCorrClass, "getSatelliteId", "()I");
             method_correctionSatCarrierFreq = env->GetMethodID(
                 singleSatCorrClass, "getCarrierFrequencyHz", "()F");
             method_correctionSatIsLosProb = env->GetMethodID(
-                singleSatCorrClass,"getProbSatIsLos", "()F");
+                singleSatCorrClass,"getProbabilityLineOfSight", "()F");
             method_correctionSatEpl = env->GetMethodID(
                 singleSatCorrClass, "getExcessPathLengthMeters", "()F");
             method_correctionSatEplUnc = env->GetMethodID(
@@ -2695,45 +2791,36 @@
     {"native_init", "()Z", reinterpret_cast<void *>(android_location_GnssLocationProvider_init)},
     {"native_cleanup", "()V", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_cleanup)},
-    {"native_set_position_mode",
-            "(IIIIIZ)Z",
-            reinterpret_cast<void*>(android_location_GnssLocationProvider_set_position_mode)},
-    {"native_start", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_start)},
-    {"native_stop", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_stop)},
-    {"native_delete_aiding_data",
-            "(I)V",
-            reinterpret_cast<void*>(android_location_GnssLocationProvider_delete_aiding_data)},
+    {"native_set_position_mode", "(IIIIIZ)Z", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_set_position_mode)},
+    {"native_start", "()Z", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_start)},
+    {"native_stop", "()Z", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_stop)},
+    {"native_delete_aiding_data", "(I)V", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_delete_aiding_data)},
     {"native_read_nmea", "([BI)I", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_read_nmea)},
     {"native_inject_time", "(JJI)V", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_inject_time)},
-    {"native_inject_best_location",
-            "(IDDDFFFFFFJ)V",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_inject_best_location)},
-    {"native_inject_location",
-            "(DDF)V",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_inject_location)},
+    {"native_inject_best_location", "(IDDDFFFFFFJIJ)V", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_inject_best_location)},
+    {"native_inject_location", "(DDF)V", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_inject_location)},
     {"native_supports_xtra", "()Z", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_supports_xtra)},
-    {"native_inject_xtra_data",
-            "([BI)V",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_inject_xtra_data)},
-    {"native_agps_set_id",
-            "(ILjava/lang/String;)V",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_agps_set_id)},
-    {"native_agps_set_ref_location_cellid",
-            "(IIIII)V",
-            reinterpret_cast<void *>(
-                    android_location_GnssLocationProvider_agps_set_reference_location_cellid)},
-    {"native_set_agps_server",
-            "(ILjava/lang/String;I)V",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_set_agps_server)},
-    {"native_send_ni_response",
-            "(II)V",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_send_ni_response)},
-    {"native_get_internal_state",
-            "()Ljava/lang/String;",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_get_internal_state)},
+    {"native_inject_xtra_data", "([BI)V", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_inject_xtra_data)},
+    {"native_agps_set_id", "(ILjava/lang/String;)V", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_agps_set_id)},
+    {"native_agps_set_ref_location_cellid", "(IIIII)V", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_agps_set_reference_location_cellid)},
+    {"native_set_agps_server", "(ILjava/lang/String;I)V", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_set_agps_server)},
+    {"native_send_ni_response", "(II)V", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_send_ni_response)},
+    {"native_get_internal_state", "()Ljava/lang/String;", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_get_internal_state)},
     {"native_is_gnss_visibility_control_supported", "()Z", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_is_gnss_visibility_control_supported)},
 };
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d773929..f496e81 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -134,6 +134,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.PermissionChecker;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageDataObserver;
@@ -171,6 +172,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
+import android.os.ParcelableException;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
@@ -187,6 +189,7 @@
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.storage.StorageManager;
+import android.permission.PermissionControllerManager;
 import android.provider.CalendarContract;
 import android.provider.ContactsContract.QuickContact;
 import android.provider.ContactsInternal;
@@ -1890,6 +1893,21 @@
             return LocalServices.getService(ActivityTaskManagerInternal.class);
         }
 
+        @NonNull PermissionControllerManager getPermissionControllerManager(
+                @NonNull UserHandle user) {
+            if (user.equals(mContext.getUser())) {
+                return mContext.getSystemService(PermissionControllerManager.class);
+            } else {
+                try {
+                    return mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
+                            user).getSystemService(PermissionControllerManager.class);
+                } catch (NameNotFoundException notPossible) {
+                    // not possible
+                    throw new IllegalStateException(notPossible);
+                }
+            }
+        }
+
         UsageStatsManagerInternal getUsageStatsManagerInternal() {
             return LocalServices.getService(UsageStatsManagerInternal.class);
         }
@@ -11589,8 +11607,11 @@
     }
 
     @Override
-    public boolean setPermissionGrantState(ComponentName admin, String callerPackage,
-            String packageName, String permission, int grantState) throws RemoteException {
+    public void setPermissionGrantState(ComponentName admin, String callerPackage,
+            String packageName, String permission, int grantState, RemoteCallback callback)
+            throws RemoteException {
+        Preconditions.checkNotNull(callback);
+
         UserHandle user = mInjector.binderGetCallingUserHandle();
         synchronized (getLockObject()) {
             // Ensure the caller is a DO/PO or a permission grant state delegate.
@@ -11598,53 +11619,60 @@
                     DELEGATION_PERMISSION_GRANT);
             long ident = mInjector.binderClearCallingIdentity();
             try {
-                if (getTargetSdk(packageName, user.getIdentifier())
-                        < android.os.Build.VERSION_CODES.M) {
-                    return false;
+                boolean isPostQAdmin = getTargetSdk(callerPackage, user.getIdentifier())
+                        >= android.os.Build.VERSION_CODES.Q;
+                if (!isPostQAdmin) {
+                    // Legacy admins assume that they cannot control pre-M apps
+                    if (getTargetSdk(packageName, user.getIdentifier())
+                            < android.os.Build.VERSION_CODES.M) {
+                        callback.sendResult(null);
+                        return;
+                    }
                 }
-                if (!isRuntimePermission(permission)) {
-                    return false;
+                try {
+                    if (!isRuntimePermission(permission)) {
+                        callback.sendResult(null);
+                        return;
+                    }
+                } catch (NameNotFoundException e) {
+                    throw new RemoteException(
+                            "Cannot check if " + permission + "is a runtime permission", e, false,
+                            true);
                 }
-                final PackageManager packageManager = mInjector.getPackageManager();
-                switch (grantState) {
-                    case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED: {
-                        mInjector.getPackageManagerInternal().grantRuntimePermission(packageName,
-                                permission, user.getIdentifier(), true /* override policy */);
-                        packageManager.updatePermissionFlags(permission, packageName,
-                                PackageManager.FLAG_PERMISSION_POLICY_FIXED,
-                                PackageManager.FLAG_PERMISSION_POLICY_FIXED, user);
-                    } break;
 
-                    case DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED: {
-                        mInjector.getPackageManagerInternal().revokeRuntimePermission(packageName,
-                                permission, user.getIdentifier(), true /* override policy */);
-                        packageManager.updatePermissionFlags(permission, packageName,
-                                PackageManager.FLAG_PERMISSION_POLICY_FIXED,
-                                PackageManager.FLAG_PERMISSION_POLICY_FIXED, user);
-                    } break;
+                if (grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED
+                        || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
+                        || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) {
+                    mInjector.getPermissionControllerManager(user)
+                            .setRuntimePermissionGrantStateByDeviceAdmin(callerPackage,
+                                    packageName, permission, grantState, mContext.getMainExecutor(),
+                                    (permissionWasSet) -> {
+                                        if (isPostQAdmin && !permissionWasSet) {
+                                            callback.sendResult(null);
+                                            return;
+                                        }
 
-                    case DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT: {
-                        packageManager.updatePermissionFlags(permission, packageName,
-                                PackageManager.FLAG_PERMISSION_POLICY_FIXED, 0, user);
-                    } break;
+                                        final boolean isDelegate = (admin == null);
+                                        DevicePolicyEventLogger
+                                                .createEvent(DevicePolicyEnums
+                                                        .SET_PERMISSION_GRANT_STATE)
+                                                .setAdmin(callerPackage)
+                                                .setStrings(permission)
+                                                .setInt(grantState)
+                                                .setBoolean(isDelegate)
+                                                .write();
+
+                                        callback.sendResult(Bundle.EMPTY);
+                                    });
                 }
-            } catch (SecurityException se) {
-                return false;
-            } catch (NameNotFoundException e) {
-                return false;
+            } catch (SecurityException e) {
+                Slog.e(LOG_TAG, "Could not set permission grant state", e);
+
+                callback.sendResult(null);
             } finally {
                 mInjector.binderRestoreCallingIdentity(ident);
             }
         }
-        final boolean isDelegate = (admin == null);
-        DevicePolicyEventLogger
-                .createEvent(DevicePolicyEnums.SET_PERMISSION_GRANT_STATE)
-                .setAdmin(callerPackage)
-                .setStrings(permission)
-                .setInt(grantState)
-                .setBoolean(isDelegate)
-                .write();
-        return true;
     }
 
     @Override
@@ -11661,8 +11689,26 @@
         synchronized (getLockObject()) {
             long ident = mInjector.binderClearCallingIdentity();
             try {
-                int granted = mIPackageManager.checkPermission(permission,
-                        packageName, user.getIdentifier());
+                int granted;
+                if (getTargetSdk(callerPackage, user.getIdentifier())
+                        < android.os.Build.VERSION_CODES.Q) {
+                    // The per-Q behavior was to not check the app-ops state.
+                    granted = mIPackageManager.checkPermission(permission, packageName,
+                            user.getIdentifier());
+                } else {
+                    try {
+                        int uid = packageManager.getPackageUidAsUser(packageName,
+                                user.getIdentifier());
+
+                        // TODO: Prevent noting the app-op
+                        granted = PermissionChecker.checkPermission(mContext, permission, -1,
+                                uid, packageName);
+                    } catch (NameNotFoundException e) {
+                        throw new RemoteException(
+                                "Cannot check if " + permission + "is a runtime permission", e,
+                                false, true);
+                    }
+                }
                 int permFlags = packageManager.getPermissionFlags(permission, packageName, user);
                 if ((permFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED)
                         != PackageManager.FLAG_PERMISSION_POLICY_FIXED) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4700b96..8d88c5a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -310,6 +310,7 @@
 
     private boolean mOnlyCore;
     private boolean mFirstBoot;
+    private final int mStartCount;
     private final boolean mRuntimeRestart;
     private final long mRuntimeStartElapsedTime;
     private final long mRuntimeStartUptime;
@@ -317,6 +318,9 @@
     private static final String START_SENSOR_SERVICE = "StartSensorService";
     private static final String START_HIDL_SERVICES = "StartHidlServices";
 
+    private static final String SYSPROP_START_COUNT = "sys.system_server.start_count";
+    private static final String SYSPROP_START_ELAPSED = "sys.system_server.start_elapsed";
+    private static final String SYSPROP_START_UPTIME = "sys.system_server.start_uptime";
 
     private Future<?> mSensorServiceStart;
     private Future<?> mZygotePreload;
@@ -346,16 +350,33 @@
     public SystemServer() {
         // Check for factory test mode.
         mFactoryTestMode = FactoryTest.getMode();
-        // Remember if it's runtime restart(when sys.boot_completed is already set) or reboot
-        mRuntimeRestart = "1".equals(SystemProperties.get("sys.boot_completed"));
 
+        // Record process start information.
+        // Note SYSPROP_START_COUNT will increment by *2* on a FDE device when it fully boots;
+        // one for the password screen, second for the actual boot.
+        mStartCount = SystemProperties.getInt(SYSPROP_START_COUNT, 0) + 1;
         mRuntimeStartElapsedTime = SystemClock.elapsedRealtime();
         mRuntimeStartUptime = SystemClock.uptimeMillis();
+
+        // Remember if it's runtime restart(when sys.boot_completed is already set) or reboot
+        // We don't use "mStartCount > 1" here because it'll be wrong on a FDE device.
+        // TODO: mRuntimeRestart will *not* be set to true if the proccess crashes before
+        // sys.boot_completed is set. Fix it.
+        mRuntimeRestart = "1".equals(SystemProperties.get("sys.boot_completed"));
     }
 
     private void run() {
         try {
             traceBeginAndSlog("InitBeforeStartServices");
+
+            // Record the process start information in sys props.
+            SystemProperties.set(SYSPROP_START_COUNT, String.valueOf(mStartCount));
+            SystemProperties.set(SYSPROP_START_ELAPSED, String.valueOf(mRuntimeStartElapsedTime));
+            SystemProperties.set(SYSPROP_START_UPTIME, String.valueOf(mRuntimeStartUptime));
+
+            EventLog.writeEvent(EventLogTags.SYSTEM_SERVER_START,
+                    mStartCount, mRuntimeStartUptime, mRuntimeStartElapsedTime);
+
             // If a device's clock is before 1970 (before 0), a lot of
             // APIs crash dealing with negative numbers, notably
             // java.io.File#setLastModified, so instead we fake it and
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
index 1eb7b98..830dbbe 100644
--- a/services/net/java/android/net/NetworkStackClient.java
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -30,6 +30,7 @@
 import android.net.dhcp.DhcpServingParamsParcel;
 import android.net.dhcp.IDhcpServerCallbacks;
 import android.net.ip.IIpClientCallbacks;
+import android.net.util.SharedLog;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Process;
@@ -40,6 +41,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.io.PrintWriter;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 
@@ -61,6 +63,9 @@
     @GuardedBy("mPendingNetStackRequests")
     private INetworkStackConnector mConnector;
 
+    @GuardedBy("mLog")
+    private final SharedLog mLog = new SharedLog(TAG);
+
     private volatile boolean mNetworkStackStartRequested = false;
 
     private interface NetworkStackCallback {
@@ -129,13 +134,14 @@
     private class NetworkStackConnection implements ServiceConnection {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
+            log("Network stack service connected");
             registerNetworkStackService(service);
         }
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
             // TODO: crash/reboot the system ?
-            Slog.wtf(TAG, "Lost network stack connector");
+            logWtf("Lost network stack connector", null);
         }
     };
 
@@ -144,6 +150,7 @@
 
         ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
                 DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+        log("Network stack service registered");
 
         final ArrayList<NetworkStackCallback> requests;
         synchronized (mPendingNetStackRequests) {
@@ -166,6 +173,7 @@
      * started.
      */
     public void start(Context context) {
+        log("Starting network stack");
         mNetworkStackStartRequested = true;
         // Try to bind in-process if the library is available
         IBinder connector = null;
@@ -177,7 +185,7 @@
             connector = (IBinder) service.getMethod("makeConnector", Context.class)
                     .invoke(null, context);
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-            Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
+            logWtf("Could not create network stack connector from NetworkStackService", e);
             // TODO: crash/reboot system here ?
             return;
         } catch (ClassNotFoundException e) {
@@ -186,26 +194,28 @@
 
         // In-process network stack. Add the service to the service manager here.
         if (connector != null) {
+            log("Registering in-process network stack connector");
             registerNetworkStackService(connector);
             return;
         }
         // Start the network stack process. The service will be added to the service manager in
         // NetworkStackConnection.onServiceConnected().
+        log("Starting network stack process");
         final Intent intent = new Intent(INetworkStackConnector.class.getName());
         final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
         intent.setComponent(comp);
 
         if (comp == null) {
-            Slog.wtf(TAG, "Could not resolve the network stack with " + intent);
+            logWtf("Could not resolve the network stack with " + intent, null);
             // TODO: crash/reboot system server ?
             return;
         }
         final PackageManager pm = context.getPackageManager();
         int uid = -1;
         try {
-            uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM);
+            uid = pm.getPackageUidAsUser(comp.getPackageName(), UserHandle.USER_SYSTEM);
         } catch (PackageManager.NameNotFoundException e) {
-            Slog.wtf("Network stack package not found", e);
+            logWtf("Network stack package not found", e);
             // Fall through
         }
         if (uid != Process.NETWORK_STACK_UID) {
@@ -221,10 +231,31 @@
 
         if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
                 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
-            Slog.wtf(TAG,
-                    "Could not bind to network stack in-process, or in app with " + intent);
+            logWtf("Could not bind to network stack in-process, or in app with " + intent, null);
+            return;
             // TODO: crash/reboot system server if no network stack after a timeout ?
         }
+
+        log("Network stack service start requested");
+    }
+
+    private void log(@NonNull String message) {
+        synchronized (mLog) {
+            mLog.log(message);
+        }
+    }
+
+    private void logWtf(@NonNull String message, @Nullable Throwable e) {
+        Slog.wtf(TAG, message);
+        synchronized (mLog) {
+            mLog.e(message, e);
+        }
+    }
+
+    private void loge(@NonNull String message, @Nullable Throwable e) {
+        synchronized (mLog) {
+            mLog.e(message, e);
+        }
     }
 
     /**
@@ -243,12 +274,12 @@
             while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
                 Thread.sleep(20);
                 if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
-                    Slog.e(TAG, "Timeout waiting for NetworkStack connector");
+                    loge("Timeout waiting for NetworkStack connector", null);
                     return null;
                 }
             }
         } catch (InterruptedException e) {
-            Slog.e(TAG, "Error waiting for NetworkStack connector", e);
+            loge("Error waiting for NetworkStack connector", e);
             return null;
         }
 
@@ -286,4 +317,20 @@
 
         request.onNetworkStackConnected(connector);
     }
+
+    /**
+     * Dump NetworkStackClient logs to the specified {@link PrintWriter}.
+     */
+    public void dump(PrintWriter pw) {
+        // dump is thread-safe on SharedLog
+        mLog.dump(null, pw, null);
+
+        final int requestsQueueLength;
+        synchronized (mPendingNetStackRequests) {
+            requestsQueueLength = mPendingNetStackRequests.size();
+        }
+
+        pw.println();
+        pw.println("pendingNetStackRequests length: " + requestsQueueLength);
+    }
 }
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 8e3023b..339607b 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -275,6 +275,9 @@
 
     public void stop() {
         closeSocket();
+        // Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before
+        // the thread's termination.
+        maybeNotifyMulticastTransmitter();
         mMulticastTransmitter = null;
         mUnicastResponder = null;
     }
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index ebc816d..782196d 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -23,6 +23,7 @@
         "androidx.test.runner",
         "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
+        "truth-prebuilt",
     ],
 
     libs: [
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java
new file mode 100644
index 0000000..d32f1f7
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java
@@ -0,0 +1,421 @@
+/*
+ * 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.server.am;
+
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_1;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_2;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_STATSD_SAMPLE_RATE;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_1;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_2;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_3;
+import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_4;
+import static android.provider.DeviceConfig.ActivityManager.KEY_USE_COMPACTION;
+
+import static com.android.server.am.ActivityManagerService.Injector;
+import static com.android.server.am.AppCompactor.compactActionIntToString;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.provider.DeviceConfig;
+
+import com.android.server.appop.AppOpsService;
+import com.android.server.testables.TestableDeviceConfig;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.File;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link AppCompactor}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:AppCompactorTest
+ */
+@RunWith(MockitoJUnitRunner.class)
+public final class AppCompactorTest {
+
+    @Mock
+    private AppOpsService mAppOpsService;
+    private AppCompactor mCompactorUnderTest;
+    private HandlerThread mHandlerThread;
+    private Handler mHandler;
+    private CountDownLatch mCountDown;
+
+    @Rule
+    public TestableDeviceConfig mDeviceConfig = new TestableDeviceConfig();
+
+    @Before
+    public void setUp() {
+        mHandlerThread = new HandlerThread("");
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        ActivityManagerService ams = new ActivityManagerService(new TestInjector());
+        mCompactorUnderTest = new AppCompactor(ams,
+                new AppCompactor.PropertyChangedCallbackForTest() {
+                    @Override
+                    public void onPropertyChanged() {
+                        if (mCountDown != null) {
+                            mCountDown.countDown();
+                        }
+                    }
+                });
+    }
+
+    @After
+    public void tearDown() {
+        mHandlerThread.quit();
+        mCountDown = null;
+    }
+
+    @Test
+    public void init_setsDefaults() {
+        mCompactorUnderTest.init();
+        assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
+                AppCompactor.DEFAULT_USE_COMPACTION);
+        assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
+                compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1));
+        assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
+                compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2));
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
+        assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
+                AppCompactor.DEFAULT_STATSD_SAMPLE_RATE);
+    }
+
+    @Test
+    public void init_withDeviceConfigSetsParameters() {
+        // When the DeviceConfig already has a flag value stored (note this test will need to
+        // change if the default value changes from false).
+        assertThat(AppCompactor.DEFAULT_USE_COMPACTION).isFalse();
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_USE_COMPACTION, "true", false);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_ACTION_1,
+                Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_ACTION_2,
+                Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_THROTTLE_1,
+                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_THROTTLE_2,
+                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_THROTTLE_3,
+                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_THROTTLE_4,
+                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_STATSD_SAMPLE_RATE,
+                Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
+
+        // Then calling init will read and set that flag.
+        mCompactorUnderTest.init();
+        assertThat(mCompactorUnderTest.useCompaction()).isTrue();
+        assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue();
+
+        assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
+                compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1));
+        assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
+                compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1));
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1);
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1);
+        assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
+                AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
+    }
+
+    @Test
+    public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException {
+        assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
+                AppCompactor.DEFAULT_USE_COMPACTION);
+        // When we call init and change some the flag value...
+        mCompactorUnderTest.init();
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_USE_COMPACTION, "true", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that new flag value is updated in the implementation.
+        assertThat(mCompactorUnderTest.useCompaction()).isTrue();
+        assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue();
+
+        // And again, setting the flag the other way.
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_USE_COMPACTION, "false", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCompactorUnderTest.useCompaction()).isFalse();
+    }
+
+    @Test
+    public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
+        assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
+                AppCompactor.DEFAULT_USE_COMPACTION);
+        mCompactorUnderTest.init();
+
+        // When we push an invalid flag value...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_USE_COMPACTION, "foobar", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then we set the default.
+        assertThat(mCompactorUnderTest.useCompaction()).isEqualTo(
+                AppCompactor.DEFAULT_USE_COMPACTION);
+    }
+
+    @Test
+    public void compactAction_listensToDeviceConfigChanges() throws InterruptedException {
+        mCompactorUnderTest.init();
+
+        // When we override new values for the compaction action with reasonable values...
+
+        // There are four possible values for compactAction[Some|Full].
+        for (int i = 1; i < 5; i++) {
+            mCountDown = new CountDownLatch(2);
+            int expectedSome = (AppCompactor.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1;
+            DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                    KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false);
+            int expectedFull = (AppCompactor.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1;
+            DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                    KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false);
+            assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+            // Then the updates are reflected in the flags.
+            assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
+                    compactActionIntToString(expectedSome));
+            assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
+                    compactActionIntToString(expectedFull));
+        }
+    }
+
+    @Test
+    public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
+        mCompactorUnderTest.init();
+
+        // When we override new values for the compaction action with bad values ...
+        mCountDown = new CountDownLatch(2);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_ACTION_1, "foo", false);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_ACTION_2, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then the default values are reflected in the flag
+        assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
+                compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1));
+        assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
+                compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2));
+
+        mCountDown = new CountDownLatch(2);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_ACTION_1, "", false);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_ACTION_2, "", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo(
+                compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1));
+        assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo(
+                compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2));
+    }
+
+    @Test
+    public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException {
+        mCompactorUnderTest.init();
+
+        // When we override new reasonable throttle values after init...
+        mCountDown = new CountDownLatch(4);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_THROTTLE_1,
+                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_THROTTLE_2,
+                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_THROTTLE_3,
+                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_THROTTLE_4,
+                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then those flags values are reflected in the compactor.
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1);
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1);
+    }
+
+    @Test
+    public void compactThrottle_listensToDeviceConfigChangesBadValues()
+            throws InterruptedException {
+        mCompactorUnderTest.init();
+
+        // When one of the throttles is overridden with a bad value...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_THROTTLE_1, "foo", false);
+        // Then all the throttles have the defaults set.
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
+
+        // Repeat for each of the throttle keys.
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_THROTTLE_2, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
+
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_THROTTLE_3, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
+
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_THROTTLE_4, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_1);
+        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_2);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_3);
+        assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo(
+                AppCompactor.DEFAULT_COMPACT_THROTTLE_4);
+    }
+
+    @Test
+    public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException {
+        mCompactorUnderTest.init();
+
+        // When we override mStatsdSampleRate with a reasonable values ...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_STATSD_SAMPLE_RATE,
+                Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that override is reflected in the compactor.
+        assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
+                AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
+    }
+
+    @Test
+    public void statsdSanokeRate_listensToDeviceConfigChangesBadValues()
+            throws InterruptedException {
+        mCompactorUnderTest.init();
+
+        // When we override mStatsdSampleRate with a reasonable values ...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then that override is reflected in the compactor.
+        assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(
+                AppCompactor.DEFAULT_STATSD_SAMPLE_RATE);
+    }
+
+    @Test
+    public void statsdSanokeRate_listensToDeviceConfigChangesOutOfRangeValues()
+            throws InterruptedException {
+        mCompactorUnderTest.init();
+
+        // When we override mStatsdSampleRate with an value outside of [0..1]...
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_STATSD_SAMPLE_RATE,
+                Float.toString(-1.0f), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then the values is capped in the range.
+        assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(0.0f);
+
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
+                KEY_COMPACT_STATSD_SAMPLE_RATE,
+                Float.toString(1.01f), false);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+        // Then the values is capped in the range.
+        assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(1.0f);
+    }
+
+    private class TestInjector extends Injector {
+        @Override
+        public AppOpsService getAppOpsService(File file, Handler handler) {
+            return mAppOpsService;
+        }
+
+        @Override
+        public Handler getUiHandler(ActivityManagerService service) {
+            return mHandler;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
similarity index 60%
rename from services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
rename to services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
index 0fd5921..5db8867 100644
--- a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -16,52 +16,98 @@
 
 package com.android.server.am;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
 import android.content.ContentResolver;
+import android.os.SystemProperties;
 import android.provider.Settings;
-import android.test.mock.MockContentResolver;
 import android.text.TextUtils;
 
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
 
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.test.FakeSettingsProvider;
-
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
 
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 
 /**
- * Tests for {@link SettingsToPropertiesMapper}
- *
- *  Build/Install/Run:
- *  atest FrameworksServicesTests:SettingsToPropertiesMapperTest
+ * Test SettingsToPropertiesMapper.
  */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
 public class SettingsToPropertiesMapperTest {
     private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\-@:]*$";
     private static final String[] TEST_MAPPING = new String[] {
             Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS
     };
 
-    private TestMapper mTestMapper;
-    private MockContentResolver mMockContentResolver;
+    private MockitoSession mSession;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private ContentResolver mMockContentResolver;
+
+    private SettingsToPropertiesMapper mTestMapper;
+
+    private HashMap<String, String> mSystemSettingsMap;
+    private HashMap<String, String> mGlobalSettingsMap;
 
     @Before
-    public void setupForEach() {
-        // Use FakeSettingsProvider to not affect global state
-        mMockContentResolver = new MockContentResolver(InstrumentationRegistry.getContext());
-        mMockContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
-        mTestMapper = new TestMapper(mMockContentResolver);
+    public void setUp() throws Exception {
+        mSession =
+                ExtendedMockito.mockitoSession().initMocks(
+                        this)
+                        .strictness(Strictness.LENIENT)
+                        .spyStatic(SystemProperties.class)
+                        .spyStatic(Settings.Global.class)
+                        .spyStatic(SettingsToPropertiesMapper.class)
+                        .startMocking();
+        mSystemSettingsMap = new HashMap<>();
+        mGlobalSettingsMap = new HashMap<>();
+
+        // Mock SystemProperties setter and various getters
+        doAnswer((Answer<Void>) invocationOnMock -> {
+                    String key = invocationOnMock.getArgument(0);
+                    String value = invocationOnMock.getArgument(1);
+
+                    mSystemSettingsMap.put(key, value);
+                    return null;
+                }
+        ).when(() -> SystemProperties.set(anyString(), anyString()));
+
+        doAnswer((Answer<String>) invocationOnMock -> {
+                    String key = invocationOnMock.getArgument(0);
+
+                    String storedValue = mSystemSettingsMap.get(key);
+                    return storedValue == null ? "" : storedValue;
+                }
+        ).when(() -> SystemProperties.get(anyString()));
+
+        // Mock Settings.Global methods
+        doAnswer((Answer<String>) invocationOnMock -> {
+                    String key = invocationOnMock.getArgument(1);
+
+                    return mGlobalSettingsMap.get(key);
+                }
+        ).when(() -> Settings.Global.getString(any(), anyString()));
+
+        mTestMapper = new SettingsToPropertiesMapper(
+            mMockContentResolver, TEST_MAPPING, new String[] {});
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mSession.finishMocking();
     }
 
     @Test
@@ -108,30 +154,27 @@
 
     @Test
     public void testUpdatePropertiesFromSettings() {
-        Settings.Global.putString(mMockContentResolver,
-                Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue");
+        mGlobalSettingsMap.put(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue");
 
         String systemPropertyName = "persist.device_config.global_settings."
                 + "sqlite_compatibility_wal_flags";
 
         mTestMapper.updatePropertiesFromSettings();
-        String propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+        String propValue = mSystemSettingsMap.get(systemPropertyName);
         Assert.assertEquals("testValue", propValue);
 
-        Settings.Global.putString(mMockContentResolver,
-                Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
+        mGlobalSettingsMap.put(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
         mTestMapper.updatePropertyFromSetting(
                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
                 systemPropertyName);
-        propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+        propValue = mSystemSettingsMap.get(systemPropertyName);
         Assert.assertEquals("testValue2", propValue);
 
-        Settings.Global.putString(mMockContentResolver,
-                Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
+        mGlobalSettingsMap.put(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
         mTestMapper.updatePropertyFromSetting(
                 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
                 systemPropertyName);
-        propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+        propValue = mSystemSettingsMap.get(systemPropertyName);
         Assert.assertEquals("", propValue);
     }
 
@@ -163,71 +206,37 @@
     public void testUpdatePropertiesFromSettings_PropertyAndSettingNotPresent() {
         // Test that empty property will not not be set if setting is not set
         mTestMapper.updatePropertiesFromSettings();
-        String propValue = mTestMapper.systemPropertiesGet("TestProperty");
+        String propValue = mSystemSettingsMap.get("TestProperty");
         Assert.assertNull("Property should not be set if setting is null", propValue);
     }
 
     @Test
     public void testIsNativeFlagsResetPerformed() {
-        mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
+        mSystemSettingsMap.put("device_config.reset_performed", "true");
         Assert.assertTrue(mTestMapper.isNativeFlagsResetPerformed());
 
-        mTestMapper.systemPropertiesSet("device_config.reset_performed", "false");
+        mSystemSettingsMap.put("device_config.reset_performed", "false");
         Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed());
 
-        mTestMapper.systemPropertiesSet("device_config.reset_performed", "");
+        mSystemSettingsMap.put("device_config.reset_performed", "");
         Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed());
     }
 
     @Test
     public void testGetResetNativeCategories() {
-        mTestMapper.systemPropertiesSet("device_config.reset_performed", "");
-        Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0);
-
-        mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
-        mTestMapper.setFileContent("");
-        Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0);
-
-        mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
-        mTestMapper.setFileContent("persist.device_config.category1.flag;"
+        doReturn("persist.device_config.category1.flag;"
                 + "persist.device_config.category2.flag;persist.device_config.category3.flag;"
-                + "persist.device_config.category3.flag2");
+                + "persist.device_config.category3.flag2")
+            .when(() -> SettingsToPropertiesMapper.getResetFlagsFileContent());
+
+        mSystemSettingsMap.put("device_config.reset_performed", "");
+        Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0);
+
+        mSystemSettingsMap.put("device_config.reset_performed", "true");
         List<String> categories = Arrays.asList(mTestMapper.getResetNativeCategories());
         Assert.assertEquals(3, categories.size());
         Assert.assertTrue(categories.contains("category1"));
         Assert.assertTrue(categories.contains("category2"));
         Assert.assertTrue(categories.contains("category3"));
     }
-
-    private static class TestMapper extends SettingsToPropertiesMapper {
-        private final Map<String, String> mProps = new HashMap<>();
-
-        private String mFileContent = "";
-
-        TestMapper(ContentResolver contentResolver) {
-            super(contentResolver, TEST_MAPPING, new String[] {});
-        }
-
-        @Override
-        protected String systemPropertiesGet(String key) {
-            Preconditions.checkNotNull(key);
-            return mProps.get(key);
-        }
-
-        @Override
-        protected void systemPropertiesSet(String key, String value) {
-            Preconditions.checkNotNull(value);
-            Preconditions.checkNotNull(key);
-            mProps.put(key, value);
-        }
-
-        protected void setFileContent(String fileContent) {
-            mFileContent = fileContent;
-        }
-
-        @Override
-        protected String getResetFlagsFileContent() {
-            return mFileContent;
-        }
-    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
new file mode 100644
index 0000000..b766822
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
@@ -0,0 +1,131 @@
+/*
+ * 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.server.testables;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import android.provider.DeviceConfig;
+import android.util.Pair;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+
+import org.junit.rules.TestRule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
+/**
+ * TestableDeviceConfig uses ExtendedMockito to replace the real implementation of DeviceConfig
+ * with essentially a local HashMap in the callers process. This allows for unit testing that do not
+ * modify the real DeviceConfig on the device at all.
+ *
+ * <p>TestableDeviceConfig should be defined as a rule on your test so it can clean up after itself.
+ * Like the following:</p>
+ * <pre class="prettyprint">
+ * &#064;Rule
+ * public final TestableDeviceConfig mTestableDeviceConfig = new TestableDeviceConfig();
+ * </pre>
+ */
+public final class TestableDeviceConfig implements TestRule {
+
+    private StaticMockitoSession mMockitoSession;
+    private Map<DeviceConfig.OnPropertyChangedListener, Pair<String, Executor>>
+            mOnPropertyChangedListenerMap = new HashMap<>();
+    private Map<String, String> mKeyValueMap = new ConcurrentHashMap<>();
+
+    /**
+     * Clears out all local overrides.
+     */
+    public void clearDeviceConfig() {
+        mKeyValueMap.clear();
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        mMockitoSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .spyStatic(DeviceConfig.class)
+                .startMocking();
+
+        doAnswer((Answer<Void>) invocationOnMock -> {
+            String namespace = invocationOnMock.getArgument(0);
+            Executor executor = invocationOnMock.getArgument(1);
+            DeviceConfig.OnPropertyChangedListener onPropertyChangedListener =
+                    invocationOnMock.getArgument(2);
+            mOnPropertyChangedListenerMap.put(
+                    onPropertyChangedListener, new Pair<>(namespace, executor));
+            return null;
+        }).when(() -> DeviceConfig.addOnPropertyChangedListener(
+                anyString(), any(Executor.class),
+                any(DeviceConfig.OnPropertyChangedListener.class)));
+
+        doAnswer((Answer<Boolean>) invocationOnMock -> {
+                    String namespace = invocationOnMock.getArgument(0);
+                    String name = invocationOnMock.getArgument(1);
+                    String value = invocationOnMock.getArgument(2);
+                    mKeyValueMap.put(getKey(namespace, name), value);
+                    for (DeviceConfig.OnPropertyChangedListener listener :
+                            mOnPropertyChangedListenerMap.keySet()) {
+                        if (namespace.equals(mOnPropertyChangedListenerMap.get(listener).first)) {
+                            mOnPropertyChangedListenerMap.get(listener).second.execute(
+                                    () -> listener.onPropertyChanged(namespace, name, value));
+                        }
+                    }
+                    return true;
+                }
+        ).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean()));
+
+        doAnswer((Answer<String>) invocationOnMock -> {
+            String namespace = invocationOnMock.getArgument(0);
+            String name = invocationOnMock.getArgument(1);
+            return mKeyValueMap.get(getKey(namespace, name));
+        }).when(() -> DeviceConfig.getProperty(anyString(), anyString()));
+
+
+        return new TestWatcher() {
+            @Override
+            protected void succeeded(Description description) {
+                mMockitoSession.finishMocking();
+                mOnPropertyChangedListenerMap.clear();
+            }
+
+            @Override
+            protected void failed(Throwable e, Description description) {
+                mMockitoSession.finishMocking(e);
+                mOnPropertyChangedListenerMap.clear();
+            }
+        }.apply(base, description);
+    }
+
+    private static String getKey(String namespace, String name) {
+        return namespace + "/" + name;
+    }
+
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
new file mode 100644
index 0000000..39b5840
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.server.testables;
+
+import static android.provider.DeviceConfig.OnPropertyChangedListener;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityThread;
+import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/** Tests that ensure appropriate settings are backed up. */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TestableDeviceConfigTest {
+    private static final String sNamespace = "namespace1";
+    private static final String sKey = "key1";
+    private static final String sValue = "value1";
+    private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
+
+    @Rule
+    public TestableDeviceConfig mTestableDeviceConfig = new TestableDeviceConfig();
+
+    @Test
+    public void getProperty_empty() {
+        String result = DeviceConfig.getProperty(sNamespace, sKey);
+        assertThat(result).isNull();
+    }
+
+    @Test
+    public void setAndGetProperty_sameNamespace() {
+        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+        String result = DeviceConfig.getProperty(sNamespace, sKey);
+        assertThat(result).isEqualTo(sValue);
+    }
+
+    @Test
+    public void setAndGetProperty_differentNamespace() {
+        String newNamespace = "namespace2";
+        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+        String result = DeviceConfig.getProperty(newNamespace, sKey);
+        assertThat(result).isNull();
+    }
+
+    @Test
+    public void setAndGetProperty_multipleNamespaces() {
+        String newNamespace = "namespace2";
+        String newValue = "value2";
+        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+        DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
+        String result = DeviceConfig.getProperty(sNamespace, sKey);
+        assertThat(result).isEqualTo(sValue);
+        result = DeviceConfig.getProperty(newNamespace, sKey);
+        assertThat(result).isEqualTo(newValue);
+    }
+
+    @Test
+    public void setAndGetProperty_overrideValue() {
+        String newValue = "value2";
+        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+        DeviceConfig.setProperty(sNamespace, sKey, newValue, false);
+        String result = DeviceConfig.getProperty(sNamespace, sKey);
+        assertThat(result).isEqualTo(newValue);
+    }
+
+    @Test
+    public void testListener() throws InterruptedException {
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+
+        OnPropertyChangedListener changeListener = (namespace, name, value) -> {
+            assertThat(namespace).isEqualTo(sNamespace);
+            assertThat(name).isEqualTo(sKey);
+            assertThat(value).isEqualTo(sValue);
+            countDownLatch.countDown();
+        };
+        try {
+            DeviceConfig.addOnPropertyChangedListener(sNamespace,
+                    ActivityThread.currentApplication().getMainExecutor(), changeListener);
+            DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+            assertThat(countDownLatch.await(
+                    WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+        } finally {
+            DeviceConfig.removeOnPropertyChangedListener(changeListener);
+        }
+    }
+
+}
+
+
diff --git a/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java b/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java
deleted file mode 100644
index 63015be6..0000000
--- a/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java
+++ /dev/null
@@ -1,449 +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.server.am;
-
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_1;
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_2;
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_STATSD_SAMPLE_RATE;
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_1;
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_2;
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_3;
-import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_4;
-import static android.provider.DeviceConfig.ActivityManager.KEY_USE_COMPACTION;
-
-import static com.android.server.am.ActivityManagerService.Injector;
-import static com.android.server.am.AppCompactor.compactActionIntToString;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.provider.DeviceConfig;
-import android.support.test.uiautomator.UiDevice;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.appop.AppOpsService;
-
-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.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests for {@link AppCompactor}.
- *
- * Build/Install/Run:
- * atest FrameworksServicesTests:AppCompactorTest
- */
-@RunWith(AndroidJUnit4.class)
-public final class AppCompactorTest {
-
-    private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
-            "device_config delete activity_manager";
-
-    @Mock private AppOpsService mAppOpsService;
-    private AppCompactor mCompactorUnderTest;
-    private HandlerThread mHandlerThread;
-    private Handler mHandler;
-    private CountDownLatch mCountDown;
-
-    private static void clearDeviceConfig() throws IOException  {
-        UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        uiDevice.executeShellCommand(
-                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_USE_COMPACTION);
-        uiDevice.executeShellCommand(
-                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_ACTION_1);
-        uiDevice.executeShellCommand(
-                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_ACTION_2);
-        uiDevice.executeShellCommand(
-                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_1);
-        uiDevice.executeShellCommand(
-                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_2);
-        uiDevice.executeShellCommand(
-                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_3);
-        uiDevice.executeShellCommand(
-                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_4);
-        uiDevice.executeShellCommand(
-                CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_STATSD_SAMPLE_RATE);
-    }
-
-    @Before
-    public void setUp() throws IOException {
-        MockitoAnnotations.initMocks(this);
-        clearDeviceConfig();
-        mHandlerThread = new HandlerThread("");
-        mHandlerThread.start();
-        mHandler = new Handler(mHandlerThread.getLooper());
-        ActivityManagerService ams = new ActivityManagerService(new TestInjector());
-        mCompactorUnderTest = new AppCompactor(ams,
-                new AppCompactor.PropertyChangedCallbackForTest() {
-                    @Override
-                    public void onPropertyChanged() {
-                        if (mCountDown != null) {
-                            mCountDown.countDown();
-                        }
-                    }
-                });
-    }
-
-    @After
-    public void tearDown() throws IOException {
-        mHandlerThread.quit();
-        mCountDown = null;
-        clearDeviceConfig();
-    }
-
-    @Test
-    public void init_setsDefaults() {
-        mCompactorUnderTest.init();
-        assertThat(mCompactorUnderTest.useCompaction(),
-                is(mCompactorUnderTest.DEFAULT_USE_COMPACTION));
-        assertThat(mCompactorUnderTest.mCompactActionSome, is(
-                compactActionIntToString(mCompactorUnderTest.DEFAULT_COMPACT_ACTION_1)));
-        assertThat(mCompactorUnderTest.mCompactActionFull, is(
-                compactActionIntToString(mCompactorUnderTest.DEFAULT_COMPACT_ACTION_2)));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
-        assertThat(mCompactorUnderTest.mStatsdSampleRate,
-                is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE));
-    }
-
-    @Test
-    public void init_withDeviceConfigSetsParameters() {
-        // When the DeviceConfig already has a flag value stored (note this test will need to
-        // change if the default value changes from false).
-        assertThat(mCompactorUnderTest.DEFAULT_USE_COMPACTION, is(false));
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_USE_COMPACTION, "true", false);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_ACTION_1,
-                Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_ACTION_2,
-                Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_THROTTLE_1,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_THROTTLE_2,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_THROTTLE_3,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_THROTTLE_4,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_STATSD_SAMPLE_RATE,
-                Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
-
-        // Then calling init will read and set that flag.
-        mCompactorUnderTest.init();
-        assertThat(mCompactorUnderTest.useCompaction(), is(true));
-        assertThat(mCompactorUnderTest.mCompactionThread.isAlive(), is(true));
-
-        assertThat(mCompactorUnderTest.mCompactActionSome,
-                is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1)));
-        assertThat(mCompactorUnderTest.mCompactActionFull,
-                is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1)));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1));
-        assertThat(mCompactorUnderTest.mStatsdSampleRate,
-                is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f));
-    }
-
-    @Test
-    public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException {
-        assertThat(mCompactorUnderTest.useCompaction(),
-                is(mCompactorUnderTest.DEFAULT_USE_COMPACTION));
-        // When we call init and change some the flag value...
-        mCompactorUnderTest.init();
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_USE_COMPACTION, "true", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
-        // Then that new flag value is updated in the implementation.
-        assertThat(mCompactorUnderTest.useCompaction(), is(true));
-        assertThat(mCompactorUnderTest.mCompactionThread.isAlive(), is(true));
-
-        // And again, setting the flag the other way.
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_USE_COMPACTION, "false", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-        assertThat(mCompactorUnderTest.useCompaction(), is(false));
-    }
-
-    @Test
-    public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
-        assertThat(mCompactorUnderTest.useCompaction(),
-                is(mCompactorUnderTest.DEFAULT_USE_COMPACTION));
-        mCompactorUnderTest.init();
-
-        // When we push an invalid flag value...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_USE_COMPACTION, "foobar", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
-        // Then we set the default.
-        assertThat(mCompactorUnderTest.useCompaction(), is(AppCompactor.DEFAULT_USE_COMPACTION));
-    }
-
-    @Test
-    public void compactAction_listensToDeviceConfigChanges() throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override new values for the compaction action with reasonable values...
-
-        // There are four possible values for compactAction[Some|Full].
-        for (int i = 1; i < 5; i++) {
-            mCountDown = new CountDownLatch(2);
-            int expectedSome = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1;
-            DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                    KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false);
-            int expectedFull = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1;
-            DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                    KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false);
-            assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
-            // Then the updates are reflected in the flags.
-            assertThat(mCompactorUnderTest.mCompactActionSome,
-                    is(compactActionIntToString(expectedSome)));
-            assertThat(mCompactorUnderTest.mCompactActionFull,
-                    is(compactActionIntToString(expectedFull)));
-        }
-    }
-
-    @Test
-    public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override new values for the compaction action with bad values ...
-        mCountDown = new CountDownLatch(2);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_ACTION_1, "foo", false);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_ACTION_2, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
-        // Then the default values are reflected in the flag
-        assertThat(mCompactorUnderTest.mCompactActionSome,
-                is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)));
-        assertThat(mCompactorUnderTest.mCompactActionFull,
-                is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)));
-
-        mCountDown = new CountDownLatch(2);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_ACTION_1, "", false);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_ACTION_2, "", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
-        assertThat(mCompactorUnderTest.mCompactActionSome,
-                is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)));
-        assertThat(mCompactorUnderTest.mCompactActionFull,
-                is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)));
-    }
-
-    @Test
-    public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override new reasonable throttle values after init...
-        mCountDown = new CountDownLatch(4);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_THROTTLE_1,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_THROTTLE_2,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_THROTTLE_3,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_THROTTLE_4,
-                Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
-        // Then those flags values are reflected in the compactor.
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1));
-    }
-
-    @Test
-    public void compactThrottle_listensToDeviceConfigChangesBadValues()
-            throws IOException, InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When one of the throttles is overridden with a bad value...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_THROTTLE_1, "foo", false);
-        // Then all the throttles have the defaults set.
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
-        clearDeviceConfig();
-
-        // Repeat for each of the throttle keys.
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_THROTTLE_2, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
-        clearDeviceConfig();
-
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_THROTTLE_3, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
-        clearDeviceConfig();
-
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_THROTTLE_4, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1));
-        assertThat(mCompactorUnderTest.mCompactThrottleSomeFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullSome,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3));
-        assertThat(mCompactorUnderTest.mCompactThrottleFullFull,
-                is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4));
-    }
-
-    @Test
-    public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override mStatsdSampleRate with a reasonable values ...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_STATSD_SAMPLE_RATE,
-                Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
-        // Then that override is reflected in the compactor.
-        assertThat(mCompactorUnderTest.mStatsdSampleRate,
-                is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f));
-    }
-
-    @Test
-    public void statsdSanokeRate_listensToDeviceConfigChangesBadValues()
-            throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override mStatsdSampleRate with a reasonable values ...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
-        // Then that override is reflected in the compactor.
-        assertThat(mCompactorUnderTest.mStatsdSampleRate,
-                is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE));
-    }
-
-    @Test
-    public void statsdSanokeRate_listensToDeviceConfigChangesOutOfRangeValues()
-            throws InterruptedException {
-        mCompactorUnderTest.init();
-
-        // When we override mStatsdSampleRate with an value outside of [0..1]...
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_STATSD_SAMPLE_RATE,
-                Float.toString(-1.0f), false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
-        // Then the values is capped in the range.
-        assertThat(mCompactorUnderTest.mStatsdSampleRate, is(0.0f));
-
-        mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE,
-                KEY_COMPACT_STATSD_SAMPLE_RATE,
-                Float.toString(1.01f), false);
-        assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true));
-
-        // Then the values is capped in the range.
-        assertThat(mCompactorUnderTest.mStatsdSampleRate, is(1.0f));
-    }
-
-    private class TestInjector extends Injector {
-        @Override
-        public AppOpsService getAppOpsService(File file, Handler handler) {
-            return mAppOpsService;
-        }
-
-        @Override
-        public Handler getUiHandler(ActivityManagerService service) {
-            return mHandler;
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
index 5cb6cbb..26b1224 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
@@ -213,7 +213,8 @@
 
     @Override
     public void updatePermissionFlags(String permissionName, String packageName, int flagMask,
-        int flagValues, int userId) throws RemoteException {
+            int flagValues, boolean checkAdjustPolicyFlagPermission, int userId)
+            throws RemoteException {
 
     }
 
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 4f07d47..f4632db 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -106,6 +106,11 @@
         }
 
         @Override
+        public boolean hasBiometrics() {
+            return false;
+        }
+
+        @Override
         public int binderGetCallingUid() {
             return Process.SYSTEM_UID;
         }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 76beb8f..7e6b7da 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -6286,6 +6286,15 @@
         mManager.getShareTargets(filter);
     }
 
+    public void testHasShareTargets_permission() {
+        assertExpectException(SecurityException.class, "Missing permission", () ->
+                mManager.hasShareTargets(CALLING_PACKAGE_1));
+
+        // Has permission, now it should pass.
+        mCallerPermissions.add(permission.MANAGE_APP_PREDICTIONS);
+        mManager.hasShareTargets(CALLING_PACKAGE_1);
+    }
+
     public void testDumpsys_crossProfile() {
         prepareCrossProfileDataSet();
         dumpsysOnLogcat("test1", /* force= */ true);
diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
index 1b106dd..5c6fe0f 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
@@ -16,34 +16,9 @@
 
 package com.android.server.timezone;
 
-import com.android.timezone.distro.DistroVersion;
-import com.android.timezone.distro.StagedDistroOperation;
-import com.android.timezone.distro.TimeZoneDistro;
-import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import android.app.timezone.Callback;
-import android.app.timezone.DistroRulesVersion;
-import android.app.timezone.ICallback;
-import android.app.timezone.RulesManager;
-import android.app.timezone.RulesState;
-import android.os.ParcelFileDescriptor;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.concurrent.Executor;
-import javax.annotation.Nullable;
-
-import libcore.io.IoUtils;
-import libcore.timezone.TzDataSetVersion;
-
 import static com.android.server.timezone.RulesManagerService.REQUIRED_QUERY_PERMISSION;
 import static com.android.server.timezone.RulesManagerService.REQUIRED_UPDATER_PERMISSION;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -61,11 +36,43 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import android.app.timezone.Callback;
+import android.app.timezone.DistroRulesVersion;
+import android.app.timezone.ICallback;
+import android.app.timezone.RulesManager;
+import android.app.timezone.RulesState;
+import android.os.ParcelFileDescriptor;
+
+import com.android.timezone.distro.DistroVersion;
+import com.android.timezone.distro.StagedDistroOperation;
+import com.android.timezone.distro.TimeZoneDistro;
+import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
+
+import libcore.io.IoUtils;
+import libcore.timezone.TzDataSetVersion;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.concurrent.Executor;
+
+import javax.annotation.Nullable;
+
 /**
  * White box interaction / unit testing of the {@link RulesManagerService}.
  */
 public class RulesManagerServiceTest {
 
+    private static final int CURRENT_FORMAT_MAJOR_VERSION =
+            TzDataSetVersion.currentFormatMajorVersion();
+    private static final int CURRENT_FORMAT_MINOR_VERSION =
+            TzDataSetVersion.currentFormatMinorVersion();
+
     private RulesManagerService mRulesManagerService;
 
     private FakeExecutor mFakeExecutor;
@@ -116,8 +123,8 @@
     }
 
     @Test
-    public void getRulesState_systemRulesError() throws Exception {
-        configureDeviceCannotReadSystemRulesVersion();
+    public void getRulesState_baseVersionError() throws Exception {
+        configureDeviceCannotReadBaseVersion();
 
         assertNull(mRulesManagerService.getRulesState());
     }
@@ -126,18 +133,18 @@
     public void getRulesState_stagedInstall() throws Exception {
         configureCallerHasPermission();
 
-        configureDeviceSystemRulesVersion("2016a");
+        configureDeviceBaseVersion("2016a");
 
         DistroVersion stagedDistroVersion = new DistroVersion(
-                TzDataSetVersion.currentFormatMajorVersion(),
-                TzDataSetVersion.currentFormatMinorVersion() - 1,
+                CURRENT_FORMAT_MAJOR_VERSION,
+                CURRENT_FORMAT_MINOR_VERSION - 1,
                 "2016c",
-                3);
+                3 /* revision */);
         configureStagedInstall(stagedDistroVersion);
 
         DistroVersion installedDistroVersion = new DistroVersion(
-                TzDataSetVersion.currentFormatMajorVersion(),
-                TzDataSetVersion.currentFormatMinorVersion() - 1,
+                CURRENT_FORMAT_MAJOR_VERSION,
+                CURRENT_FORMAT_MINOR_VERSION - 1,
                 "2016b",
                 4);
         configureInstalledDistroVersion(installedDistroVersion);
@@ -158,13 +165,13 @@
     public void getRulesState_nothingStaged() throws Exception {
         configureCallerHasPermission();
 
-        configureDeviceSystemRulesVersion("2016a");
+        configureDeviceBaseVersion("2016a");
 
         configureNoStagedOperation();
 
         DistroVersion installedDistroVersion = new DistroVersion(
-                TzDataSetVersion.currentFormatMajorVersion(),
-                TzDataSetVersion.currentFormatMinorVersion() - 1,
+                CURRENT_FORMAT_MAJOR_VERSION,
+                CURRENT_FORMAT_MINOR_VERSION - 1,
                 "2016b",
                 4);
         configureInstalledDistroVersion(installedDistroVersion);
@@ -183,13 +190,13 @@
     public void getRulesState_uninstallStaged() throws Exception {
         configureCallerHasPermission();
 
-        configureDeviceSystemRulesVersion("2016a");
+        configureDeviceBaseVersion("2016a");
 
         configureStagedUninstall();
 
         DistroVersion installedDistroVersion = new DistroVersion(
-                TzDataSetVersion.currentFormatMajorVersion(),
-                TzDataSetVersion.currentFormatMinorVersion() - 1,
+                CURRENT_FORMAT_MAJOR_VERSION,
+                CURRENT_FORMAT_MINOR_VERSION - 1,
                 "2016b",
                 4);
         configureInstalledDistroVersion(installedDistroVersion);
@@ -208,8 +215,8 @@
     public void getRulesState_installedRulesError() throws Exception {
         configureCallerHasPermission();
 
-        String systemRulesVersion = "2016a";
-        configureDeviceSystemRulesVersion(systemRulesVersion);
+        String baseRulesVersion = "2016a";
+        configureDeviceBaseVersion(baseRulesVersion);
 
         configureStagedUninstall();
         configureDeviceCannotReadInstalledDistroVersion();
@@ -226,14 +233,14 @@
     public void getRulesState_stagedRulesError() throws Exception {
         configureCallerHasPermission();
 
-        String systemRulesVersion = "2016a";
-        configureDeviceSystemRulesVersion(systemRulesVersion);
+        String baseRulesVersion = "2016a";
+        configureDeviceBaseVersion(baseRulesVersion);
 
         configureDeviceCannotReadStagedDistroOperation();
 
         DistroVersion installedDistroVersion = new DistroVersion(
-                TzDataSetVersion.currentFormatMajorVersion(),
-                TzDataSetVersion.currentFormatMinorVersion() - 1,
+                CURRENT_FORMAT_MAJOR_VERSION,
+                CURRENT_FORMAT_MINOR_VERSION - 1,
                 "2016b",
                 4);
         configureInstalledDistroVersion(installedDistroVersion);
@@ -252,13 +259,13 @@
     public void getRulesState_noInstalledRules() throws Exception {
         configureCallerHasPermission();
 
-        String systemRulesVersion = "2016a";
-        configureDeviceSystemRulesVersion(systemRulesVersion);
+        String baseRulesVersion = "2016a";
+        configureDeviceBaseVersion(baseRulesVersion);
         configureNoStagedOperation();
         configureInstalledDistroVersion(null);
 
         RulesState expectedRuleState = new RulesState(
-                systemRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
+                baseRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
                 false /* operationInProgress */,
                 RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
                 RulesState.DISTRO_STATUS_NONE, null /* installedDistroRulesVersion */);
@@ -269,15 +276,15 @@
     public void getRulesState_operationInProgress() throws Exception {
         configureCallerHasPermission();
 
-        String systemRulesVersion = "2016a";
+        String baseRulesVersion = "2016a";
         String installedRulesVersion = "2016b";
         int revision = 3;
 
-        configureDeviceSystemRulesVersion(systemRulesVersion);
+        configureDeviceBaseVersion(baseRulesVersion);
 
         DistroVersion installedDistroVersion = new DistroVersion(
-                TzDataSetVersion.currentFormatMajorVersion(),
-                TzDataSetVersion.currentFormatMinorVersion() - 1,
+                CURRENT_FORMAT_MAJOR_VERSION,
+                CURRENT_FORMAT_MINOR_VERSION - 1,
                 installedRulesVersion,
                 revision);
         configureInstalledDistroVersion(installedDistroVersion);
@@ -297,7 +304,7 @@
         DistroRulesVersion expectedInstalledDistroRulesVersion =
                 new DistroRulesVersion(installedRulesVersion, revision);
         RulesState expectedRuleState = new RulesState(
-                systemRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
+                baseRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
                 true /* operationInProgress */,
                 RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */,
                 RulesState.DISTRO_STATUS_INSTALLED, expectedInstalledDistroRulesVersion);
@@ -858,11 +865,20 @@
                 .thenReturn(true);
 
         // Set up the mocks to return (arbitrary) information about the current device state.
-        when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()).thenReturn("2017a");
-        when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion()).thenReturn(
-                new DistroVersion(2, 3, "2017b", 4));
+        TzDataSetVersion baseVersion = new TzDataSetVersion(
+                CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, "2017a",
+                1 /* revision */);
+        when(mMockTimeZoneDistroInstaller.readBaseVersion()).thenReturn(baseVersion);
+        DistroVersion installedDistroVersion = new DistroVersion(
+                CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, "2017b",
+                4 /* revision */);
+        when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion())
+                .thenReturn(installedDistroVersion);
+        DistroVersion stagedDistroVersion = new DistroVersion(
+                CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, "2017c",
+                7 /* revision */);
         when(mMockTimeZoneDistroInstaller.getStagedDistroOperation()).thenReturn(
-                StagedDistroOperation.install(new DistroVersion(5, 6, "2017c", 7)));
+                StagedDistroOperation.install(stagedDistroVersion));
 
         // Do the dump call.
         String dumpedOutput = doDumpCallAndCapture(rulesManagerService, args);
@@ -973,8 +989,11 @@
         return new CheckToken(1, new PackageVersions(1, 1));
     }
 
-    private void configureDeviceSystemRulesVersion(String systemRulesVersion) throws Exception {
-        when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()).thenReturn(systemRulesVersion);
+    private void configureDeviceBaseVersion(String baseRulesVersion) throws Exception {
+        TzDataSetVersion tzDataSetVersion = new TzDataSetVersion(
+                CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, baseRulesVersion,
+                1 /* revision */);
+        when(mMockTimeZoneDistroInstaller.readBaseVersion()).thenReturn(tzDataSetVersion);
     }
 
     private void configureInstalledDistroVersion(@Nullable DistroVersion installedDistroVersion)
@@ -1002,8 +1021,8 @@
                 .thenThrow(new IOException("Simulated failure"));
     }
 
-    private void configureDeviceCannotReadSystemRulesVersion() throws Exception {
-        when(mMockTimeZoneDistroInstaller.getSystemRulesVersion())
+    private void configureDeviceCannotReadBaseVersion() throws Exception {
+        when(mMockTimeZoneDistroInstaller.readBaseVersion())
                 .thenThrow(new IOException("Simulated failure"));
     }
 
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 cc62138..31788ae 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -99,7 +99,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.MediaStore;
-import android.provider.Settings.Secure;
 import android.service.notification.Adjustment;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
@@ -2519,7 +2518,7 @@
         verify(mListeners, times(1)).migrateToXml();
         verify(mConditionProviders, times(1)).migrateToXml();
         verify(mAssistants, times(1)).migrateToXml();
-        verify(mAssistants, times(2)).ensureAssistant();
+        verify(mAssistants, never()).ensureAssistant();
     }
 
     @Test
@@ -2539,7 +2538,7 @@
         verify(mListeners, times(2)).migrateToXml();
         verify(mConditionProviders, times(2)).migrateToXml();
         verify(mAssistants, times(2)).migrateToXml();
-        verify(mAssistants, times(2)).ensureAssistant();
+        verify(mAssistants, never()).ensureAssistant();
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index a1db3e8..1319bad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -406,7 +406,7 @@
     }
 
     @Test
-    public void testFixedScreenConfigurationWhenMovingToDisplay() {
+    public void testSizeCompatMode_FixedScreenConfigurationWhenMovingToDisplay() {
         // Initialize different bounds on a new display.
         final ActivityDisplay newDisplay = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
         newDisplay.setBounds(0, 0, 1000, 2000);
@@ -431,7 +431,7 @@
     }
 
     @Test
-    public void testFixedScreenBoundsWhenDisplaySizeChanged() {
+    public void testSizeCompatMode_FixedScreenBoundsWhenDisplaySizeChanged() {
         when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
                 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
         mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
@@ -446,4 +446,28 @@
 
         assertEquals(originalBounds, mActivity.getBounds());
     }
+
+    @Test
+    public void testSizeCompatMode_FixedScreenLayoutSizeBits() {
+        final int fixedScreenLayout = Configuration.SCREENLAYOUT_LONG_NO
+                | Configuration.SCREENLAYOUT_SIZE_NORMAL;
+        mTask.getConfiguration().screenLayout = fixedScreenLayout
+                | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR;
+        mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
+        mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+        mActivity.info.maxAspectRatio = 1.5f;
+        ensureActivityConfiguration();
+
+        // The initial configuration should inherit from parent.
+        assertEquals(mTask.getConfiguration().screenLayout,
+                mActivity.getConfiguration().screenLayout);
+
+        mTask.getConfiguration().screenLayout = Configuration.SCREENLAYOUT_LAYOUTDIR_RTL
+                | Configuration.SCREENLAYOUT_LONG_YES | Configuration.SCREENLAYOUT_SIZE_LARGE;
+        mActivity.onConfigurationChanged(mTask.getConfiguration());
+
+        // The size and aspect ratio bits don't change, but the layout direction should be updated.
+        assertEquals(fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_RTL,
+                mActivity.getConfiguration().screenLayout);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 2627ec7..c072d4e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -59,8 +59,7 @@
     public void setUpOnDisplay(DisplayContent dc) {
         mStack = createTaskStackOnDisplay(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, dc);
         mTask = createTaskInStack(mStack, 0 /* userId */);
-        mToken = WindowTestUtils.createTestAppWindowToken(dc);
-        mToken.mSkipOnParentChanged = false;
+        mToken = WindowTestUtils.createTestAppWindowToken(dc, false /* skipOnParentChanged */);
 
         mTask.addChild(mToken, 0);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index afadc79..b91f3ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -55,8 +55,11 @@
 
     @Before
     public void setUp() throws Exception {
-        spyOn(mWm.mRoot);
-        doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
+        synchronized (mWm.mGlobalLock) {
+            // Hold the lock to protect the stubbing from being accessed by other threads.
+            spyOn(mWm.mRoot);
+            doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
+        }
         mDc = mWm.getDefaultDisplayContentLocked();
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index 108ee180b..a98a604 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -18,7 +18,6 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.view.SurfaceControl.Transaction;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
@@ -30,6 +29,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.view.SurfaceControl;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.server.wm.WindowTestUtils.TestAppWindowToken;
 
 import org.junit.Before;
@@ -38,8 +39,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
 
 /**
  * Animation related tests for the {@link AppWindowToken} class.
@@ -49,14 +48,11 @@
  */
 @SmallTest
 @Presubmit
-@FlakyTest(bugId = 124357362)
 public class AppWindowTokenAnimationTests extends WindowTestsBase {
 
     private TestAppWindowToken mToken;
 
     @Mock
-    private Transaction mTransaction;
-    @Mock
     private AnimationAdapter mSpec;
 
     @Before
@@ -65,7 +61,6 @@
 
         mToken = createTestAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, false /* skipOnParentChanged */);
-        mToken.setPendingTransaction(mTransaction);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 1dd72ec..2c575f5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -60,7 +60,7 @@
  * Tests for the {@link AppWindowToken} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:AppWindowTokenTests
+ *  atest WmTests:AppWindowTokenTests
  */
 @SmallTest
 @Presubmit
@@ -76,9 +76,9 @@
     public void setUp() throws Exception {
         mStack = createTaskStackOnDisplay(mDisplayContent);
         mTask = createTaskInStack(mStack, 0 /* userId */);
-        mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
+        mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent,
+                false /* skipOnParentChanged */);
 
-        mToken.mSkipOnParentChanged = false;
         mTask.addChild(mToken, 0);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index b1b8e8c..b26aa05 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -68,7 +68,6 @@
 import android.view.ViewRootImpl;
 import android.view.test.InsetsModeSession;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -97,7 +96,6 @@
 public class DisplayContentTests extends WindowTestsBase {
 
     @Test
-    @FlakyTest(detail = "Promote to presubmit when shown to be stable.")
     public void testForAllWindows() {
         final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION,
                 mDisplayContent, "exiting app");
@@ -621,7 +619,8 @@
     @Test
     public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() {
         final DisplayContent dc = createNewDisplay();
-        dc.getDisplayRotation().setFixedToUserRotation(true);
+        dc.getDisplayRotation().setFixedToUserRotation(
+                DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED);
         mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
         final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
                 ? SCREEN_ORIENTATION_PORTRAIT
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 8349ac7f..07dd93c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -25,20 +25,27 @@
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.graphics.PixelFormat;
 import android.platform.test.annotations.Presubmit;
+import android.view.Surface;
 import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
@@ -196,4 +203,33 @@
                 DisplayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar,
                         opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
     }
+
+    @Test
+    public void testShouldRotateSeamlessly() {
+        final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+        final WindowManager.LayoutParams attrs = mAppWindow.mAttrs;
+        attrs.x = attrs.y = 0;
+        attrs.height = attrs.width = WindowManager.LayoutParams.MATCH_PARENT;
+        attrs.rotationAnimation = ROTATION_ANIMATION_SEAMLESS;
+        final DisplayRotation displayRotation = mock(DisplayRotation.class);
+        doReturn(Surface.ROTATION_180).when(displayRotation).getUpsideDownRotation();
+
+        synchronized (mWm.mGlobalLock) {
+            policy.focusChangedLw(null /* lastFocus */, mAppWindow);
+            policy.applyPostLayoutPolicyLw(
+                    mAppWindow, attrs, null /* attached */, null /* imeTarget */);
+            spyOn(policy);
+            doReturn(true).when(policy).navigationBarCanMove();
+            // The focused fullscreen opaque window without override bounds should be able to be
+            // rotated seamlessly.
+            assertTrue(policy.shouldRotateSeamlessly(
+                    displayRotation, Surface.ROTATION_0, Surface.ROTATION_90));
+
+            spyOn(mAppWindow.mAppToken);
+            doReturn(false).when(mAppWindow.mAppToken).matchParentBounds();
+            // No seamless rotation if the window may be positioned with offset after rotation.
+            assertFalse(policy.shouldRotateSeamlessly(
+                    displayRotation, Surface.ROTATION_0, Surface.ROTATION_90));
+        }
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 8733674..1c10ffb0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
@@ -31,6 +32,9 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DEFAULT;
+import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED;
+import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -160,9 +164,7 @@
 
     @Test
     public void testPersistsUserRotation_LockRotation_NonDefaultDisplay() throws Exception {
-        mBuilder.mIsDefaultDisplay = false;
-
-        mBuilder.build();
+        mBuilder.setIsDefaultDisplay(false).build();
 
         freezeRotation(Surface.ROTATION_180);
 
@@ -187,9 +189,7 @@
 
     @Test
     public void testPersistsUserRotation_UnlockRotation_NonDefaultDisplay() throws Exception {
-        mBuilder.mIsDefaultDisplay = false;
-
-        mBuilder.build();
+        mBuilder.setIsDefaultDisplay(false).build();
 
         thawRotation();
 
@@ -203,14 +203,22 @@
     public void testPersistsFixedToUserRotation() throws Exception {
         mBuilder.build();
 
-        mTarget.setFixedToUserRotation(true);
+        mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
 
-        verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, true);
+        verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent,
+                FIXED_TO_USER_ROTATION_ENABLED);
 
         reset(mMockDisplayWindowSettings);
-        mTarget.setFixedToUserRotation(false);
+        mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_DISABLED);
 
-        verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, false);
+        verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent,
+                FIXED_TO_USER_ROTATION_DISABLED);
+
+        reset(mMockDisplayWindowSettings);
+        mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_DEFAULT);
+
+        verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent,
+                FIXED_TO_USER_ROTATION_DEFAULT);
     }
 
     // ========================================
@@ -355,22 +363,7 @@
         when(mMockDisplayPolicy.isAwake()).thenReturn(true);
         when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
         when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
-        mTarget.setFixedToUserRotation(true);
-        mTarget.updateOrientationListener();
-        verifyOrientationListenerRegistration(0);
-    }
-
-    @Test
-    public void testNotEnablesSensor_ForceDefaultRotation() throws Exception {
-        mBuilder.build();
-        when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
-                .thenReturn(true);
-        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
-
-        when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
-        when(mMockDisplayPolicy.isAwake()).thenReturn(true);
-        when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
-        when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+        mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
         mTarget.updateOrientationListener();
         verifyOrientationListenerRegistration(0);
     }
@@ -378,8 +371,6 @@
     @Test
     public void testNotEnablesSensor_ForceDefaultRotation_Car() throws Exception {
         mBuilder.build();
-        when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
-                .thenReturn(true);
         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
 
         when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
@@ -393,8 +384,6 @@
     @Test
     public void testNotEnablesSensor_ForceDefaultRotation_Tv() throws Exception {
         mBuilder.build();
-        when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
-                .thenReturn(true);
         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
 
         when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
@@ -405,6 +394,19 @@
         verifyOrientationListenerRegistration(0);
     }
 
+    @Test
+    public void testNotEnablesSensor_ForceDefaultRotation_Squared() throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_LOCKED, false, false);
+
+        when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+        when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+        when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+        when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+        mTarget.updateOrientationListener();
+        verifyOrientationListenerRegistration(0);
+    }
+
     private void enableOrientationSensor() {
         when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
         when(mMockDisplayPolicy.isAwake()).thenReturn(true);
@@ -513,21 +515,8 @@
     // Tests for Policy based Rotation
     // =================================
     @Test
-    public void testReturnsUserRotation_ForceDefaultRotation() throws Exception {
-        mBuilder.build();
-        when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
-                .thenReturn(true);
-        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
-
-        assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
-                Surface.ROTATION_180));
-    }
-
-    @Test
     public void testReturnsUserRotation_ForceDefaultRotation_Car() throws Exception {
         mBuilder.build();
-        when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
-                .thenReturn(true);
         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
 
         assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
@@ -537,8 +526,6 @@
     @Test
     public void testReturnsUserRotation_ForceDefaultRotation_Tv() throws Exception {
         mBuilder.build();
-        when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
-                .thenReturn(true);
         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
 
         assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
@@ -546,6 +533,15 @@
     }
 
     @Test
+    public void testReturnsUserRotation_ForceDefaultRotation_Squared() throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_LOCKED, false, false);
+
+        assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
+                Surface.ROTATION_180));
+    }
+
+    @Test
     public void testReturnsLidOpenRotation_LidOpen() throws Exception {
         mBuilder.setLidOpenRotation(Surface.ROTATION_90).build();
         configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
@@ -591,7 +587,7 @@
         mBuilder.build();
         configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
 
-        mTarget.setFixedToUserRotation(true);
+        mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
 
         freezeRotation(Surface.ROTATION_180);
 
@@ -625,7 +621,7 @@
     @Test
     public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception {
         mBuilder.build();
-        mTarget.setFixedToUserRotation(true);
+        mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
 
         assertFalse("Display rotation shouldn't respect app requested orientation if"
                 + " fixed to user rotation.", mTarget.respectAppRequestedOrientation());
@@ -647,9 +643,14 @@
                 width = 1080;
                 height = 1920;
                 break;
+            case SCREEN_ORIENTATION_LOCKED:
+                // We use locked for squared display.
+                width = 1080;
+                height = 1080;
+                break;
             default:
-                throw new IllegalArgumentException("displayOrientation needs to be either landscape"
-                        + " or portrait, but we got "
+                throw new IllegalArgumentException("displayOrientation needs to be landscape, "
+                        + "portrait or locked, but we got "
                         + ActivityInfo.screenOrientationToString(displayOrientation));
         }
 
@@ -659,6 +660,10 @@
                 .thenReturn(isCar);
         when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
                 .thenReturn(isTv);
+        when(mMockDisplayPolicy.getNonDecorDisplayWidth(anyInt(), anyInt(), anyInt(), anyInt(),
+                any())).thenReturn(width);
+        when(mMockDisplayPolicy.getNonDecorDisplayHeight(anyInt(), anyInt(), anyInt(), anyInt(),
+                any())).thenReturn(height);
 
         final int shortSizeDp = (isCar || isTv) ? 540 : 720;
         final int longSizeDp = 960;
@@ -826,6 +831,9 @@
                     .thenReturn(convertRotationToDegrees(mDeskDockRotation));
             when(mMockRes.getInteger(com.android.internal.R.integer.config_undockedHdmiRotation))
                     .thenReturn(convertRotationToDegrees(mUndockedHdmiRotation));
+            when(mMockRes.getFloat(
+                    com.android.internal.R.dimen.config_closeToSquareDisplayMaxAspectRatio))
+                    .thenReturn(1.33f);
 
             mMockSensorManager = mock(SensorManager.class);
             when(mMockContext.getSystemService(Context.SENSOR_SERVICE))
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index 992d017..2dad187 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -26,6 +26,9 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DEFAULT;
+import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED;
+import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -407,7 +410,7 @@
     }
 
     @Test
-    public void testNotFixedToUserRotationByDefault() {
+    public void testFixedToUserRotationDefault() {
         mTarget.setUserRotation(mPrimaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
                 Surface.ROTATION_0);
 
@@ -419,13 +422,14 @@
 
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
 
-        verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(false));
+        verify(displayRotation).restoreSettings(anyInt(), anyInt(),
+                eq(FIXED_TO_USER_ROTATION_DEFAULT));
         mockitoSession.finishMocking();
     }
 
     @Test
-    public void testSetFixedToUserRotation() {
-        mTarget.setFixedToUserRotation(mPrimaryDisplay, true);
+    public void testSetFixedToUserRotationDisabled() {
+        mTarget.setFixedToUserRotation(mPrimaryDisplay, FIXED_TO_USER_ROTATION_DISABLED);
 
         final MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
                 .startMocking();
@@ -435,7 +439,25 @@
 
         applySettingsToDisplayByNewInstance(mPrimaryDisplay);
 
-        verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(true));
+        verify(displayRotation).restoreSettings(anyInt(), anyInt(),
+                eq(FIXED_TO_USER_ROTATION_DISABLED));
+        mockitoSession.finishMocking();
+    }
+
+    @Test
+    public void testSetFixedToUserRotationEnabled() {
+        mTarget.setFixedToUserRotation(mPrimaryDisplay, FIXED_TO_USER_ROTATION_ENABLED);
+
+        final MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+                .startMocking();
+        final DisplayRotation displayRotation = mock(DisplayRotation.class);
+        spyOn(mPrimaryDisplay);
+        doReturn(displayRotation).when(mPrimaryDisplay).getDisplayRotation();
+
+        applySettingsToDisplayByNewInstance(mPrimaryDisplay);
+
+        verify(displayRotation).restoreSettings(anyInt(), anyInt(),
+                eq(FIXED_TO_USER_ROTATION_ENABLED));
         mockitoSession.finishMocking();
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/MockSurfaceControlBuilder.java b/services/tests/wmtests/src/com/android/server/wm/MockSurfaceControlBuilder.java
new file mode 100644
index 0000000..66139e6
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/MockSurfaceControlBuilder.java
@@ -0,0 +1,37 @@
+/*
+ * 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.server.wm;
+
+import static org.mockito.Mockito.mock;
+
+import android.view.SurfaceControl;
+
+/**
+ * Stubbed {@link SurfaceControl.Builder} class that returns a mocked SurfaceControl instance
+ * that can be used for unit testing.
+ */
+class MockSurfaceControlBuilder extends SurfaceControl.Builder {
+    @Override
+    public SurfaceControl.Builder setParent(SurfaceControl sc) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl build() {
+        return mock(SurfaceControl.class);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 3e025f6..fc1eb1c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -30,7 +30,6 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -96,6 +95,7 @@
     private TestActivityTaskManagerService mTestService;
     private ActivityDisplay mDisplay;
     private ActivityDisplay mOtherDisplay;
+    private ActivityDisplay mSingleTaskDisplay;
     private ActivityStack mStack;
     private ActivityStack mHomeStack;
     private TestTaskPersister mTaskPersister;
@@ -547,6 +547,41 @@
         assertTrimmed(mTasks.get(0), mTasks.get(1));
     }
 
+    /**
+     * Tests that tasks on singleTaskDisplay are not visible and not trimmed/removed.
+     */
+    @Test
+    public void testVisibleTasks_singleTaskDisplay() {
+        mRecentTasks.setOnlyTestVisibleRange();
+        mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */);
+
+        ActivityStack singleTaskStack = mSingleTaskDisplay.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        TaskRecord excludedTask1 = createTaskBuilder(".ExcludedTask1")
+                .setStack(singleTaskStack)
+                .build();
+
+        assertFalse("Tasks on singleTaskDisplay should not be visible recents",
+                mRecentTasks.isVisibleRecentTask(excludedTask1));
+
+        mRecentTasks.add(excludedTask1);
+
+        // Add N+1 visible tasks.
+        mRecentTasks.add(mTasks.get(0));
+        mRecentTasks.add(mTasks.get(1));
+        mRecentTasks.add(mTasks.get(2));
+        mRecentTasks.add(mTasks.get(3));
+
+        // excludedTask is not trimmed.
+        assertTrimmed(mTasks.get(0));
+
+        mRecentTasks.removeAllVisibleTasks();
+
+        // Only visible tasks removed.
+        assertTrimmed(mTasks.get(0), mTasks.get(1), mTasks.get(2), mTasks.get(3));
+    }
+
     @Test
     public void testBackStackTasks_expectNoTrim() {
         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
@@ -879,8 +914,12 @@
             super.createDefaultDisplay();
             mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
             mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1);
+            mSingleTaskDisplay = TestActivityDisplay.create(mTestStackSupervisor,
+                    DEFAULT_DISPLAY + 2);
+            mSingleTaskDisplay.setDisplayToSingleTaskInstance();
             mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
             mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
+            mRootActivityContainer.addChild(mSingleTaskDisplay, ActivityDisplay.POSITION_TOP);
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
new file mode 100644
index 0000000..d919fc8
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -0,0 +1,251 @@
+/*
+ * 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.server.wm;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.view.InputWindowHandle;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+/**
+ * Stubbed {@link android.view.SurfaceControl.Transaction} class that can be used when unit
+ * testing to avoid calls to native code.
+ */
+public class StubTransaction extends SurfaceControl.Transaction {
+    @Override
+    public void apply() {
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public void apply(boolean sync) {
+    }
+
+    @Override
+    public SurfaceControl.Transaction setVisibility(SurfaceControl sc, boolean visible) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction show(SurfaceControl sc) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction hide(SurfaceControl sc) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setPosition(SurfaceControl sc, float x, float y) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setBufferSize(SurfaceControl sc,
+            int w, int h) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setLayer(SurfaceControl sc, int z) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc, SurfaceControl relativeTo,
+            int z) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setTransparentRegionHint(SurfaceControl sc,
+            Region transparentRegion) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setAlpha(SurfaceControl sc, float alpha) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setInputWindowInfo(SurfaceControl sc,
+            InputWindowHandle handle) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction transferTouchFocus(IBinder fromToken, IBinder toToken) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setGeometry(SurfaceControl sc, Rect sourceCrop,
+            Rect destFrame, @Surface.Rotation int orientation) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setMatrix(SurfaceControl sc,
+            float dsdx, float dtdx, float dtdy, float dsdy) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setMatrix(SurfaceControl sc, Matrix matrix, float[] float9) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setColorTransform(SurfaceControl sc, float[] matrix,
+            float[] translation) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setWindowCrop(SurfaceControl sc, Rect crop) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setWindowCrop(SurfaceControl sc, int width, int height) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setLayerStack(SurfaceControl sc, int layerStack) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction deferTransactionUntil(SurfaceControl sc, IBinder handle,
+            long frameNumber) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction deferTransactionUntilSurface(SurfaceControl sc,
+            Surface barrierSurface,
+            long frameNumber) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction reparentChildren(SurfaceControl sc, IBinder newParentHandle) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction detachChildren(SurfaceControl sc) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setOverrideScalingMode(SurfaceControl sc,
+            int overrideScalingMode) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setColor(SurfaceControl sc, float[] color) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setGeometryAppliesWithResize(SurfaceControl sc) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setSecure(SurfaceControl sc, boolean isSecure) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setOpaque(SurfaceControl sc, boolean isOpaque) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setDisplaySurface(IBinder displayToken, Surface surface) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setDisplayLayerStack(IBinder displayToken, int layerStack) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setDisplayProjection(IBinder displayToken,
+            int orientation, Rect layerStackRect, Rect displayRect) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setDisplaySize(IBinder displayToken, int width, int height) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setAnimationTransaction() {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setEarlyWakeup() {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setMetadata(int key, int data) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setMetadata(int key, Parcel data) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction merge(SurfaceControl.Transaction other) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction remove(SurfaceControl sc) {
+        return this;
+    }
+}
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 712cd1e..366acea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -167,7 +167,7 @@
 
         mWindowManagerPolicy = new TestWindowManagerPolicy(this::getWindowManagerService);
         mWindowManagerService = WindowManagerService.main(
-                context, ims, false, false, mWindowManagerPolicy, atms);
+                context, ims, false, false, mWindowManagerPolicy, atms, StubTransaction::new);
 
         mWindowManagerService.onInitReady();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index b1f942e..70ed62a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -175,10 +175,14 @@
     @Test
     public void testStackOutset() {
         final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
-        spyOn(stack);
-
         final int stackOutset = 10;
-        doReturn(stackOutset).when(stack).getStackOutset();
+        // Clear the handler and hold the lock for mock, to prevent multi-thread issue.
+        waitUntilHandlersIdle();
+        synchronized (mWm.mGlobalLock) {
+            spyOn(stack);
+
+            doReturn(stackOutset).when(stack).getStackOutset();
+        }
 
         final Rect stackBounds = new Rect(200, 200, 800, 1000);
         // Update surface position and size by the given bounds.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 849772a..c3561f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -313,8 +313,8 @@
     }
 
     @Override
-    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always,
-            String reason) {
+    public boolean performHapticFeedback(int uid, String packageName, int effectId,
+            boolean always, String reason) {
         return false;
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index d07230e..6249bde 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -21,11 +21,9 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
-import static junit.framework.TestCase.assertNotNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
-import static org.junit.Assert.assertNull;
-
-import android.graphics.Bitmap;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 
@@ -37,7 +35,7 @@
  * Tests for the {@link WallpaperController} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:WallpaperControllerTests
+ *  atest WmTests:WallpaperControllerTests
  */
 @SmallTest
 @Presubmit
@@ -49,34 +47,29 @@
         synchronized (mWm.mGlobalLock) {
             // No wallpaper
             final DisplayContent dc = createNewDisplay();
-            Bitmap wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
-            assertNull(wallpaperBitmap);
+            assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
 
             // No wallpaper WSA Surface
             WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
                     true, dc, true /* ownerCanManageAppTokens */);
             WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER,
                     wallpaperWindowToken, "wallpaperWindow");
-            wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
-            assertNull(wallpaperBitmap);
+            assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
 
             // Wallpaper with not visible WSA surface.
             wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController;
             wallpaperWindow.mWinAnimator.mLastAlpha = 1;
-            wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
-            assertNull(wallpaperBitmap);
+            assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
 
             when(windowSurfaceController.getShown()).thenReturn(true);
 
             // Wallpaper with WSA alpha set to 0.
             wallpaperWindow.mWinAnimator.mLastAlpha = 0;
-            wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
-            assertNull(wallpaperBitmap);
+            assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
 
             // Wallpaper window with WSA Surface
             wallpaperWindow.mWinAnimator.mLastAlpha = 1;
-            wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
-            assertNotNull(wallpaperBitmap);
+            assertTrue(dc.mWallpaperController.canScreenshotWallpaper());
         }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index b0e20b8..b03f63b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -110,8 +110,11 @@
         // TODO: Let the insets source with new mode keep the visibility control, and remove this
         // setup code. Now mTopFullscreenOpaqueWindowState will take back the control of insets
         // visibility.
-        spyOn(mDisplayContent);
-        doNothing().when(mDisplayContent).layoutAndAssignWindowLayersIfNeeded();
+        // Hold the lock to protect the mock from accesssing by other threads.
+        synchronized (mWm.mGlobalLock) {
+            spyOn(mDisplayContent);
+            doNothing().when(mDisplayContent).layoutAndAssignWindowLayersIfNeeded();
+        }
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index 5bfa0c6..da1defa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -28,7 +28,6 @@
 import android.os.IBinder;
 import android.view.IApplicationToken;
 import android.view.IWindow;
-import android.view.SurfaceControl.Transaction;
 import android.view.WindowManager;
 
 /**
@@ -72,7 +71,6 @@
     static class TestAppWindowToken extends AppWindowToken {
         boolean mOnTop = false;
         private boolean mSkipPrepareSurfaces;
-        private Transaction mPendingTransactionOverride;
         boolean mSkipOnParentChanged = true;
 
         private TestAppWindowToken(DisplayContent dc, boolean skipOnParentChanged) {
@@ -126,17 +124,6 @@
         void setSkipPrepareSurfaces(boolean ignore) {
             mSkipPrepareSurfaces = ignore;
         }
-
-        void setPendingTransaction(Transaction transaction) {
-            mPendingTransactionOverride = transaction;
-        }
-
-        @Override
-        public Transaction getPendingTransaction() {
-            return mPendingTransactionOverride == null
-                    ? super.getPendingTransaction()
-                    : mPendingTransactionOverride;
-        }
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 0d7d085..d202e16 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -37,7 +37,10 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+
+import static org.mockito.Mockito.mock;
+
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -47,6 +50,8 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.IWindow;
+import android.view.Surface;
+import android.view.SurfaceControl.Transaction;
 import android.view.WindowManager;
 
 import com.android.server.AttributeCache;
@@ -96,10 +101,9 @@
     private MockTracker mMockTracker;
 
     /**
-     * To restore the original SurfaceControl.Transaction factory if any tests changed
-     * {@link WindowManagerService#mTransactionFactory}.
+     * Spied {@link Transaction} class than can be used to verify calls.
      */
-    private TransactionFactory mOriginalTransactionFactory;
+    Transaction mTransaction;
 
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -129,11 +133,21 @@
         // in the set up are clear. This can be removed when b/37850063 is fixed.
         try {
             mMockSession = mock(Session.class);
+            mTransaction = spy(StubTransaction.class);
 
             final Context context = getInstrumentation().getTargetContext();
 
             mWm = mSystemServicesTestRule.getWindowManagerService();
-            mOriginalTransactionFactory = mWm.mTransactionFactory;
+
+            // Setup factory classes to prevent calls to native code.
+
+            // Return a spied Transaction class than can be used to verify calls.
+            mWm.mTransactionFactory = () -> mTransaction;
+            // Return a SurfaceControl.Builder class that creates mocked SurfaceControl instances.
+            mWm.mSurfaceBuilderFactory = (unused) -> new MockSurfaceControlBuilder();
+            // Return mocked Surface instances.
+            mWm.mSurfaceFactory = () -> mock(Surface.class);
+
             beforeCreateDisplay();
 
             context.getDisplay().getDisplayInfo(mDisplayInfo);
@@ -183,7 +197,6 @@
             // stable state to clean up for consistency.
             waitUntilHandlersIdle();
 
-            mWm.mTransactionFactory = mOriginalTransactionFactory;
             final LinkedList<WindowState> nonCommonWindows = new LinkedList<>();
 
             synchronized (mWm.mGlobalLock) {
diff --git a/telecomm/java/android/telecom/CallIdentification.java b/telecomm/java/android/telecom/CallIdentification.java
index 87834fd..cde7f60 100644
--- a/telecomm/java/android/telecom/CallIdentification.java
+++ b/telecomm/java/android/telecom/CallIdentification.java
@@ -45,13 +45,13 @@
      * {@link CallIdentification} for a screened call.
      */
     public static class Builder {
-        private String mName;
-        private String mDescription;
-        private String mDetails;
+        private CharSequence mName;
+        private CharSequence mDescription;
+        private CharSequence mDetails;
         private Icon mPhoto;
         private int mNuisanceConfidence = CallIdentification.CONFIDENCE_UNKNOWN;
         private String mPackageName;
-        private String mAppName;
+        private CharSequence mAppName;
 
         /**
          * Default builder constructor.
@@ -67,7 +67,7 @@
          * @param callIdAppName The app name.
          * @hide
          */
-        public Builder(String callIdPackageName, String callIdAppName) {
+        public Builder(String callIdPackageName, CharSequence callIdAppName) {
             mPackageName = callIdPackageName;
             mAppName = callIdAppName;
         }
@@ -80,7 +80,7 @@
          * @param name The name associated with the call, or {@code null} if none is provided.
          * @return Builder instance.
          */
-        public Builder setName(@Nullable String name) {
+        public Builder setName(@Nullable CharSequence name) {
             mName = name;
             return this;
         }
@@ -97,7 +97,7 @@
          * @param description The call description, or {@code null} if none is provided.
          * @return Builder instance.
          */
-        public Builder setDescription(@Nullable String description) {
+        public Builder setDescription(@Nullable CharSequence description) {
             mDescription = description;
             return this;
         }
@@ -114,7 +114,7 @@
          * @param details The call details, or {@code null} if none is provided.
          * @return Builder instance.
          */
-        public Builder setDetails(@Nullable String details) {
+        public Builder setDetails(@Nullable CharSequence details) {
             mDetails = details;
             return this;
         }
@@ -241,10 +241,10 @@
      *                             call identification.
      * @hide
      */
-    private CallIdentification(@Nullable String name, @Nullable String description,
-            @Nullable String details, @Nullable Icon photo,
+    private CallIdentification(@Nullable CharSequence name, @Nullable CharSequence description,
+            @Nullable CharSequence details, @Nullable Icon photo,
             @NuisanceConfidence int nuisanceConfidence, @NonNull String callScreeningPackageName,
-            @NonNull String callScreeningAppName) {
+            @NonNull CharSequence callScreeningAppName) {
         mName = name;
         mDescription = description;
         mDetails = details;
@@ -254,13 +254,13 @@
         mCallScreeningPackageName = callScreeningPackageName;
     }
 
-    private String mName;
-    private String mDescription;
-    private String mDetails;
+    private CharSequence mName;
+    private CharSequence mDescription;
+    private CharSequence mDetails;
     private Icon mPhoto;
     private int mNuisanceConfidence;
     private String mCallScreeningPackageName;
-    private String mCallScreeningAppName;
+    private CharSequence mCallScreeningAppName;
 
     @Override
     public int describeContents() {
@@ -269,13 +269,13 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int i) {
-        parcel.writeString(mName);
-        parcel.writeString(mDescription);
-        parcel.writeString(mDetails);
+        parcel.writeCharSequence(mName);
+        parcel.writeCharSequence(mDescription);
+        parcel.writeCharSequence(mDetails);
         parcel.writeParcelable(mPhoto, 0);
         parcel.writeInt(mNuisanceConfidence);
         parcel.writeString(mCallScreeningPackageName);
-        parcel.writeString(mCallScreeningAppName);
+        parcel.writeCharSequence(mCallScreeningAppName);
     }
 
     /**
@@ -286,13 +286,13 @@
 
                 @Override
                 public CallIdentification createFromParcel(Parcel source) {
-                    String name = source.readString();
-                    String description = source.readString();
-                    String details = source.readString();
+                    CharSequence name = source.readCharSequence();
+                    CharSequence description = source.readCharSequence();
+                    CharSequence details = source.readCharSequence();
                     Icon photo = source.readParcelable(ClassLoader.getSystemClassLoader());
                     int nuisanceConfidence = source.readInt();
                     String callScreeningPackageName = source.readString();
-                    String callScreeningAppName = source.readString();
+                    CharSequence callScreeningAppName = source.readCharSequence();
                     return new CallIdentification(name, description, details, photo,
                             nuisanceConfidence, callScreeningPackageName, callScreeningAppName);
                 }
@@ -311,7 +311,7 @@
      *
      * @return The name associated with the number, or {@code null} if none was provided.
      */
-    public final @Nullable String getName() {
+    public final @Nullable CharSequence getName() {
         return mName;
     }
 
@@ -325,7 +325,7 @@
      *
      * @return The call description, or {@code null} if none was provided.
      */
-    public final @Nullable String getDescription() {
+    public final @Nullable CharSequence getDescription() {
         return mDescription;
     }
 
@@ -340,7 +340,7 @@
      *
      * @return The call details, or {@code null} if none was provided.
      */
-    public final @Nullable String getDetails() {
+    public final @Nullable CharSequence getDetails() {
         return mDetails;
     }
 
@@ -363,8 +363,7 @@
      *
      * @return The nuisance confidence.
      */
-    public final @NuisanceConfidence
-    int getNuisanceConfidence() {
+    public final @NuisanceConfidence int getNuisanceConfidence() {
         return mNuisanceConfidence;
     }
 
@@ -387,7 +386,7 @@
      *
      * @return The name of the app.
      */
-    public final @NonNull String getCallScreeningAppName() {
+    public final @NonNull CharSequence getCallScreeningAppName() {
         return mCallScreeningAppName;
     }
 
@@ -407,7 +406,7 @@
      * @param callScreeningAppName The app name.
      * @hide
      */
-    public void setCallScreeningAppName(@NonNull String callScreeningAppName) {
+    public void setCallScreeningAppName(@NonNull CharSequence callScreeningAppName) {
         mCallScreeningAppName = callScreeningAppName;
     }
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ad98e36..c0444bb 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2992,9 +2992,9 @@
         /* Default value is 1024 kbps */
         sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_BANDWIDTH_INT, 1024);
         /* Default value is 10 seconds */
-        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_OR_EXIT_HYSTERESIS_TIME_LONG, 10000);
+        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_ENTRY_OR_EXIT_HYSTERESIS_TIME_LONG, 10000);
         /* Default value is 10 seconds. */
-        sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG, 10000);
+        sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG, 10000);
         sDefaults.putAll(Gps.getDefaults());
         sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
                 new int[] {
diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java
index e6182ed..740b970 100644
--- a/telephony/java/android/telephony/CellSignalStrength.java
+++ b/telephony/java/android/telephony/CellSignalStrength.java
@@ -119,7 +119,7 @@
     /** @hide */
     protected static final int getAsuFromRssiDbm(int dbm) {
         if (dbm == CellInfo.UNAVAILABLE) return 99;
-        return (dbm / 2) + 113;
+        return (dbm + 113) / 2;
     }
 
     // Range for RSCP in ASU (0-96, 255) as defined in TS 27.007 8.69
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index f53cb82..6e839ab 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -26,7 +26,7 @@
  * @hide
  */
 @SystemApi
-public class DisconnectCause {
+public final class DisconnectCause {
 
     /** The disconnect cause is not valid (Not received a disconnect cause) */
     public static final int NOT_VALID                      = -1;
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index ffebc04..bb0673f 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -176,26 +176,21 @@
 
     /**
      * Listen for {@link PreciseCallState.State} of ringing, background and foreground calls.
-     * {@more}
-     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
-     * READ_PRECISE_PHONE_STATE}
      *
      * @hide
      */
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     @SystemApi
     public static final int LISTEN_PRECISE_CALL_STATE                       = 0x00000800;
 
     /**
      * Listen for {@link PreciseDataConnectionState} on the data connection (cellular).
      *
-     * {@more}
-     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
-     * READ_PRECISE_PHONE_STATE}
-     *
      * @see #onPreciseDataConnectionStateChanged
      *
      * @hide
      */
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     @SystemApi
     public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE            = 0x00001000;
 
@@ -331,12 +326,10 @@
     /**
      * Listen for call disconnect causes which contains {@link DisconnectCause} and
      * {@link PreciseDisconnectCause}.
-     * {@more}
-     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
-     * READ_PRECISE_PHONE_STATE}
      *
      * @hide
      */
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     @SystemApi
     public static final int LISTEN_CALL_DISCONNECT_CAUSES                  = 0x02000000;
 
@@ -356,13 +349,10 @@
      * Listen for IMS call disconnect causes which contains
      * {@link android.telephony.ims.ImsReasonInfo}
      *
-     * {@more}
-     * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
-     * READ_PRECISE_PHONE_STATE}
-     *
      * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo)
      * @hide
      */
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     @SystemApi
     public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES              = 0x08000000;
 
@@ -579,8 +569,9 @@
      * @param callState {@link PreciseCallState}
      * @hide
      */
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     @SystemApi
-    public void onPreciseCallStateChanged(PreciseCallState callState) {
+    public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) {
         // default implementation empty
     }
 
@@ -591,6 +582,7 @@
      *
      * @hide
      */
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     @SystemApi
     public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
         // default implementation empty
@@ -602,6 +594,7 @@
      *
      * @hide
      */
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     @SystemApi
     public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) {
         // default implementation empty
@@ -613,6 +606,7 @@
      *
      * @hide
      */
+    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
     @SystemApi
     public void onPreciseDataConnectionStateChanged(
             PreciseDataConnectionState dataConnectionState) {
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index af88748..54980a2 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -23,7 +23,7 @@
  * @hide
  */
 @SystemApi
-public class PreciseDisconnectCause {
+public final class PreciseDisconnectCause {
 
     /** The disconnect cause is not valid (Not received a disconnect cause).*/
     public static final int NOT_VALID                                        = -1;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index a1aee6d..3dc1199 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1569,6 +1569,17 @@
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public @TelephonyManager.NetworkType int getDataNetworkType() {
+        final NetworkRegistrationState iwlanRegState = getNetworkRegistrationState(
+                NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WLAN);
+        if (iwlanRegState != null
+                && iwlanRegState.getRegState() == NetworkRegistrationState.REG_STATE_HOME) {
+            // If the device is on IWLAN, return IWLAN as the network type. This is to simulate the
+            // behavior of legacy mode device. In the future caller should use
+            // getNetworkRegistrationState() to retrieve the actual data network type on cellular
+            // or on IWLAN.
+            return iwlanRegState.getAccessNetworkTechnology();
+        }
+
         final NetworkRegistrationState regState = getNetworkRegistrationState(
                 NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN);
         if (regState != null) {
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index d2ae106..d461bd0 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -215,13 +215,52 @@
      * @see android.telephony#CellSignalStrengthGsm
      */
     public @NonNull List<CellSignalStrength> getCellSignalStrengths() {
-        List<CellSignalStrength> cssList = new ArrayList<>(2); // Usually have 2 or fewer elems
-        if (mLte.isValid()) cssList.add(mLte);
-        if (mCdma.isValid()) cssList.add(mCdma);
-        if (mTdscdma.isValid()) cssList.add(mTdscdma);
-        if (mWcdma.isValid()) cssList.add(mWcdma);
-        if (mGsm.isValid()) cssList.add(mGsm);
-        if (mNr.isValid()) cssList.add(mNr);
+        return getCellSignalStrengths(CellSignalStrength.class);
+    }
+
+    /**
+     * Returns a List of CellSignalStrength Components of this SignalStrength Report.
+     *
+     * Use this API to access underlying
+     * {@link android.telephony#CellSignalStrength CellSignalStrength} objects that provide more
+     * granular information about the SignalStrength report. Only valid (non-empty)
+     * CellSignalStrengths will be returned. The order of any returned elements is not guaranteed,
+     * and the list may contain more than one instance of a CellSignalStrength type.
+     *
+     * @param clazz a class type that extends
+     *        {@link android.telephony.CellSignalStrength CellSignalStrength} to filter possible
+     *        return values.
+     * @return a List of CellSignalStrength or an empty List if there are no valid measurements.
+     *
+     * @see android.telephony#CellSignalStrength
+     * @see android.telephony#CellSignalStrengthNr
+     * @see android.telephony#CellSignalStrengthLte
+     * @see android.telephony#CellSignalStrengthTdscdma
+     * @see android.telephony#CellSignalStrengthWcdma
+     * @see android.telephony#CellSignalStrengthCdma
+     * @see android.telephony#CellSignalStrengthGsm
+     */
+    public <T extends CellSignalStrength> @NonNull List<T> getCellSignalStrengths(
+            @NonNull Class<T> clazz) {
+        List<T> cssList = new ArrayList<>(2); // Usually have 2 or fewer elems
+        if (mLte.isValid() && clazz.isAssignableFrom(CellSignalStrengthLte.class)) {
+            cssList.add((T) mLte);
+        }
+        if (mCdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthCdma.class)) {
+            cssList.add((T) mCdma);
+        }
+        if (mTdscdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthTdscdma.class)) {
+            cssList.add((T) mTdscdma);
+        }
+        if (mWcdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthWcdma.class)) {
+            cssList.add((T) mWcdma);
+        }
+        if (mGsm.isValid() && clazz.isAssignableFrom(CellSignalStrengthGsm.class)) {
+            cssList.add((T) mGsm);
+        }
+        if (mNr.isValid() && clazz.isAssignableFrom(CellSignalStrengthNr.class)) {
+            cssList.add((T) mNr);
+        }
         return cssList;
     }
 
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 94f26a8..c28d1fb 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2081,7 +2081,7 @@
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
             if (iSub != null) {
-                subId = iSub.getActiveSubIdList();
+                subId = iSub.getActiveSubIdList(/*visibleOnly*/true);
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -2866,7 +2866,7 @@
      *
      * @hide
      */
-    private boolean shouldHideSubscription(SubscriptionInfo info) {
+    public boolean shouldHideSubscription(SubscriptionInfo info) {
         if (info == null) return false;
 
         // If hasCarrierPrivileges or canManageSubscription returns true, it means caller
@@ -2874,8 +2874,14 @@
         boolean hasCarrierPrivilegePermission = (info.isEmbedded() && canManageSubscription(info))
                 || TelephonyManager.from(mContext).hasCarrierPrivileges(info.getSubscriptionId());
 
-        return (!TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic()
-                && !hasCarrierPrivilegePermission);
+        return isInvisibleSubscription(info) && !hasCarrierPrivilegePermission;
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean isInvisibleSubscription(SubscriptionInfo info) {
+        return info != null && !TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic();
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f1869b0..c1d1440 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8665,24 +8665,26 @@
 
 
     /**
-     * Returns a well-formed IETF BCP 47 language tag representing the locale from the SIM, e.g,
-     * en-US. Returns {@code null} if no locale could be derived from subscriptions.
+     * Returns a locale based on the country and language from the SIM. Returns {@code null} if
+     * no locale could be derived from subscriptions.
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      *
      * @see Locale#toLanguageTag()
-     * @see Locale#forLanguageTag(String)
      *
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    @Nullable public String getSimLocale() {
+    @Nullable public Locale getSimLocale() {
         try {
             final ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getSimLocaleForSubscriber(getSubId());
+                String languageTag = telephony.getSimLocaleForSubscriber(getSubId());
+                if (!TextUtils.isEmpty(languageTag)) {
+                    return Locale.forLanguageTag(languageTag);
+                }
             }
         } catch (RemoteException ex) {
         }
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index 19f357a..0192ffb 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -30,6 +30,7 @@
     private final String mEid;
     private final String mIccId;
     private final int mSlotIndex;
+    private final boolean mIsRemovable;
 
     public static final Creator<UiccCardInfo> CREATOR = new Creator<UiccCardInfo>() {
         @Override
@@ -49,6 +50,7 @@
         mEid = in.readString();
         mIccId = in.readString();
         mSlotIndex = in.readInt();
+        mIsRemovable = in.readByte() != 0;
     }
 
     @Override
@@ -58,6 +60,7 @@
         dest.writeString(mEid);
         dest.writeString(mIccId);
         dest.writeInt(mSlotIndex);
+        dest.writeByte((byte) (mIsRemovable ? 1 : 0));
     }
 
     @Override
@@ -65,16 +68,21 @@
         return 0;
     }
 
-    public UiccCardInfo(boolean isEuicc, int cardId, String eid, String iccId, int slotIndex) {
+    /**
+     * @hide
+     */
+    public UiccCardInfo(boolean isEuicc, int cardId, String eid, String iccId, int slotIndex,
+            boolean isRemovable) {
         this.mIsEuicc = isEuicc;
         this.mCardId = cardId;
         this.mEid = eid;
         this.mIccId = iccId;
         this.mSlotIndex = slotIndex;
+        this.mIsRemovable = isRemovable;
     }
 
     /**
-     * Return whether the UiccCardInfo is an eUICC.
+     * Return whether the UICC is an eUICC.
      * @return true if the UICC is an eUICC.
      */
     public boolean isEuicc() {
@@ -127,7 +135,17 @@
      * @hide
      */
     public UiccCardInfo getUnprivileged() {
-        return new UiccCardInfo(mIsEuicc, mCardId, null, null, mSlotIndex);
+        return new UiccCardInfo(mIsEuicc, mCardId, null, null, mSlotIndex, mIsRemovable);
+    }
+
+    /**
+     * Return whether the UICC or eUICC is removable.
+     * <p>
+     * UICCs are generally removable, but eUICCs may be removable or built in to the device.
+     * @return true if the UICC or eUICC is removable
+     */
+    public boolean isRemovable() {
+        return mIsRemovable;
     }
 
     @Override
@@ -144,12 +162,13 @@
                 && (mCardId == that.mCardId)
                 && (Objects.equals(mEid, that.mEid))
                 && (Objects.equals(mIccId, that.mIccId))
-                && (mSlotIndex == that.mSlotIndex));
+                && (mSlotIndex == that.mSlotIndex)
+                && (mIsRemovable == that.mIsRemovable));
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mSlotIndex);
+        return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mSlotIndex, mIsRemovable);
     }
 
     @Override
@@ -164,6 +183,8 @@
                 + mIccId
                 + ", mSlotIndex="
                 + mSlotIndex
+                + ", mIsRemovable="
+                + mIsRemovable
                 + ")";
     }
 }
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index a39992b..93a7da0 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -15,15 +15,15 @@
  */
 package android.telephony;
 
+import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
-import android.annotation.IntDef;
-
 /**
  * Class for the information of a UICC slot.
  * @hide
@@ -61,6 +61,7 @@
     private final @CardStateInfo int mCardStateInfo;
     private final int mLogicalSlotIdx;
     private final boolean mIsExtendedApduSupported;
+    private final boolean mIsRemovable;
 
     public static final Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
         @Override
@@ -81,6 +82,7 @@
         mCardStateInfo = in.readInt();
         mLogicalSlotIdx = in.readInt();
         mIsExtendedApduSupported = in.readByte() != 0;
+        mIsRemovable = in.readByte() != 0;
     }
 
     @Override
@@ -91,6 +93,7 @@
         dest.writeInt(mCardStateInfo);
         dest.writeInt(mLogicalSlotIdx);
         dest.writeByte((byte) (mIsExtendedApduSupported ? 1 : 0));
+        dest.writeByte((byte) (mIsRemovable ? 1 : 0));
     }
 
     @Override
@@ -98,6 +101,11 @@
         return 0;
     }
 
+    /**
+     * Construct a UiccSlotInfo.
+     * @deprecated apps should not be constructing UiccSlotInfo objects
+     */
+    @Deprecated
     public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
             @CardStateInfo int cardStateInfo, int logicalSlotIdx, boolean isExtendedApduSupported) {
         this.mIsActive = isActive;
@@ -106,6 +114,22 @@
         this.mCardStateInfo = cardStateInfo;
         this.mLogicalSlotIdx = logicalSlotIdx;
         this.mIsExtendedApduSupported = isExtendedApduSupported;
+        this.mIsRemovable = false;
+    }
+
+    /**
+     * @hide
+     */
+    public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
+            @CardStateInfo int cardStateInfo, int logicalSlotIdx, boolean isExtendedApduSupported,
+            boolean isRemovable) {
+        this.mIsActive = isActive;
+        this.mIsEuicc = isEuicc;
+        this.mCardId = cardId;
+        this.mCardStateInfo = cardStateInfo;
+        this.mLogicalSlotIdx = logicalSlotIdx;
+        this.mIsExtendedApduSupported = isExtendedApduSupported;
+        this.mIsRemovable = isRemovable;
     }
 
     public boolean getIsActive() {
@@ -136,6 +160,16 @@
         return mIsExtendedApduSupported;
     }
 
+   /**
+     * Return whether the UICC slot is for a removable UICC.
+     * <p>
+     * UICCs are generally removable, but eUICCs may be removable or built in to the device.
+     * @return true if the slot is for removable UICCs
+     */
+    public boolean isRemovable() {
+        return mIsRemovable;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
@@ -151,7 +185,8 @@
                 && (Objects.equals(mCardId, that.mCardId))
                 && (mCardStateInfo == that.mCardStateInfo)
                 && (mLogicalSlotIdx == that.mLogicalSlotIdx)
-                && (mIsExtendedApduSupported == that.mIsExtendedApduSupported);
+                && (mIsExtendedApduSupported == that.mIsExtendedApduSupported)
+                && (mIsRemovable == that.mIsRemovable);
     }
 
     @Override
@@ -163,6 +198,7 @@
         result = 31 * result + mCardStateInfo;
         result = 31 * result + mLogicalSlotIdx;
         result = 31 * result + (mIsExtendedApduSupported ? 1 : 0);
+        result = 31 * result + (mIsRemovable ? 1 : 0);
         return result;
     }
 
@@ -180,6 +216,8 @@
                 + mLogicalSlotIdx
                 + ", mIsExtendedApduSupported="
                 + mIsExtendedApduSupported
+                + ", mIsRemovable="
+                + mIsRemovable
                 + ")";
     }
 }
diff --git a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
index 837ef54..d11a0de 100644
--- a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
+++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
@@ -99,7 +99,7 @@
     public int mRttMode;
     // RTT Audio Speech Indicator
     /** @hide */
-    public boolean mHasRttAudioSpeech = false;
+    public boolean mIsReceivingRttAudio = false;
 
     /** @hide */
     public ImsStreamMediaProfile(Parcel in) {
@@ -201,7 +201,7 @@
                 ", videoQuality=" + mVideoQuality +
                 ", videoDirection=" + mVideoDirection +
                 ", rttMode=" + mRttMode +
-                ", hasRttAudioSpeech=" + mHasRttAudioSpeech + " }";
+                ", hasRttAudioSpeech=" + mIsReceivingRttAudio + " }";
     }
 
     @Override
@@ -216,7 +216,7 @@
         out.writeInt(mVideoQuality);
         out.writeInt(mVideoDirection);
         out.writeInt(mRttMode);
-        out.writeBoolean(mHasRttAudioSpeech);
+        out.writeBoolean(mIsReceivingRttAudio);
     }
 
     private void readFromParcel(Parcel in) {
@@ -225,7 +225,7 @@
         mVideoQuality = in.readInt();
         mVideoDirection = in.readInt();
         mRttMode = in.readInt();
-        mHasRttAudioSpeech = in.readBoolean();
+        mIsReceivingRttAudio = in.readBoolean();
     }
 
     public static final Creator<ImsStreamMediaProfile> CREATOR =
@@ -256,8 +256,12 @@
         mRttMode = rttMode;
     }
 
-    public void setRttAudioSpeech(boolean audioOn) {
-        mHasRttAudioSpeech = audioOn;
+    /**
+     * Sets whether the remote party is transmitting audio over the RTT call.
+     * @param audioOn true if audio is being received, false otherwise.
+     */
+    public void setReceivingRttAudio(boolean audioOn) {
+        mIsReceivingRttAudio = audioOn;
     }
 
     public int getAudioQuality() {
@@ -280,7 +284,10 @@
         return mRttMode;
     }
 
-    public boolean getRttAudioSpeech() {
-        return mHasRttAudioSpeech;
+    /**
+     * @return true if remote party is transmitting audio, false otherwise.
+     */
+    public boolean isReceivingRttAudio() {
+        return mIsReceivingRttAudio;
     }
 }
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
index d4a78ff..0bb1b43 100644
--- a/telephony/java/android/telephony/ims/Rcs1To1Thread.java
+++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
@@ -22,6 +22,8 @@
  * Rcs1To1Thread represents a single RCS conversation thread with a total of two
  * {@link RcsParticipant}s. Please see Section 5 (1-to-1 Messaging) - GSMA RCC.71 (RCS Universal
  * Profile Service Definition Document)
+ *
+ * @hide
  */
 public class Rcs1To1Thread extends RcsThread {
     private int mThreadId;
diff --git a/telephony/java/android/telephony/ims/RcsEvent.java b/telephony/java/android/telephony/ims/RcsEvent.java
index a547c5c0..994b27a 100644
--- a/telephony/java/android/telephony/ims/RcsEvent.java
+++ b/telephony/java/android/telephony/ims/RcsEvent.java
@@ -17,6 +17,8 @@
 
 /**
  * The base class for events that can happen on {@link RcsParticipant}s and {@link RcsThread}s.
+ *
+ * @hide
  */
 public abstract class RcsEvent {
     private final long mTimestamp;
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryParams.java b/telephony/java/android/telephony/ims/RcsEventQueryParams.java
index 9dbfe43..5f8fa80 100644
--- a/telephony/java/android/telephony/ims/RcsEventQueryParams.java
+++ b/telephony/java/android/telephony/ims/RcsEventQueryParams.java
@@ -37,6 +37,8 @@
  * The parameters to pass into
  * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} in order to select a
  * subset of {@link RcsEvent}s present in the message store.
+ *
+ * @hide
  */
 public final class RcsEventQueryParams implements Parcelable {
     /**
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResult.java b/telephony/java/android/telephony/ims/RcsEventQueryResult.java
index 92bda81..d6347e3 100644
--- a/telephony/java/android/telephony/ims/RcsEventQueryResult.java
+++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.java
@@ -22,6 +22,8 @@
  * The result of a {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
  * call. This class allows getting the token for querying the next batch of events in order to
  * prevent handling large amounts of data at once.
+ *
+ * @hide
  */
 public class RcsEventQueryResult {
     private RcsQueryContinuationToken mContinuationToken;
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
index 14af8ea..4742ba2 100644
--- a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
+++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
@@ -24,6 +24,8 @@
  * Pass an instance of this class to
  * {@link RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)} create an
  * {@link RcsFileTransferPart} and save it into storage.
+ *
+ * @hide
  */
 public final class RcsFileTransferCreationParams implements Parcelable {
     private String mRcsFileTransferSessionId;
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.java b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
index 9531c2e..3816cd4 100644
--- a/telephony/java/android/telephony/ims/RcsFileTransferPart.java
+++ b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
@@ -26,6 +26,8 @@
 /**
  * A part of a composite {@link RcsMessage} that holds a file transfer. Please see Section 7
  * (File Transfer) - GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide
  */
 public class RcsFileTransferPart {
     /**
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java
index 6e17bc2..8cd633b 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThread.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThread.java
@@ -29,6 +29,8 @@
  * RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join
  * or leave. Please see Section 6 (Group Chat) - GSMA RCC.71 (RCS Universal Profile Service
  * Definition Document)
+ *
+ * @hide
  */
 public class RcsGroupThread extends RcsThread {
     /**
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
index 99086aa..4a6b963 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
@@ -19,6 +19,8 @@
 
 /**
  * An event that happened on an {@link RcsGroupThread}.
+ *
+ * @hide
  */
 public abstract class RcsGroupThreadEvent extends RcsEvent {
     private final int mRcsGroupThreadId;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
index cbd762d..3c6c74f 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
@@ -22,6 +22,8 @@
 /**
  * An event that indicates an {@link RcsGroupThread}'s icon was changed. Please see R6-2-5 - GSMA
  * RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide
  */
 public final class RcsGroupThreadIconChangedEvent extends RcsGroupThreadEvent {
     private final Uri mNewIcon;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
index a2a4fab..5403253 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
@@ -21,6 +21,8 @@
 /**
  * An event that indicates an {@link RcsGroupThread}'s name was changed. Please see R6-2-5 - GSMA
  * RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide
  */
 public final class RcsGroupThreadNameChangedEvent extends RcsGroupThreadEvent {
     private final String mNewName;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
index 183cd9a..48be479 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
@@ -20,6 +20,8 @@
 /**
  * An event that indicates an RCS participant has joined an {@link RcsThread}. Please see US6-3 -
  * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide
  */
 public final class RcsGroupThreadParticipantJoinedEvent extends RcsGroupThreadEvent {
     private final RcsParticipant mJoinedParticipantId;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
index c12549b..b724a3f 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
@@ -20,6 +20,8 @@
 /**
  * An event that indicates an RCS participant has left an {@link RcsThread}. Please see US6-23 -
  * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide
  */
 public final class RcsGroupThreadParticipantLeftEvent extends RcsGroupThreadEvent {
     private RcsParticipant mLeavingParticipant;
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.java b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
index 61911ab..06e2a41 100644
--- a/telephony/java/android/telephony/ims/RcsIncomingMessage.java
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
@@ -19,6 +19,8 @@
 
 /**
  * This is a single instance of a message received over RCS.
+ *
+ * @hide
  */
 public class RcsIncomingMessage extends RcsMessage {
     /**
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
index 61dedbc..58dc1bc 100644
--- a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
@@ -24,6 +24,8 @@
  * {@link RcsIncomingMessageCreationParams} is a collection of parameters that should be passed
  * into {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} to generate an
  * {@link RcsIncomingMessage} on that {@link RcsThread}
+ *
+ * @hide
  */
 public final class RcsIncomingMessageCreationParams extends RcsMessageCreationParams implements
         Parcelable {
diff --git a/telephony/java/android/telephony/ims/RcsManager.java b/telephony/java/android/telephony/ims/RcsManager.java
index 22e4b22..63dc1ac 100644
--- a/telephony/java/android/telephony/ims/RcsManager.java
+++ b/telephony/java/android/telephony/ims/RcsManager.java
@@ -20,6 +20,8 @@
 
 /**
  * The manager class for RCS related utilities.
+ *
+ * @hide
  */
 @SystemService(Context.TELEPHONY_RCS_SERVICE)
 public class RcsManager {
diff --git a/telephony/java/android/telephony/ims/RcsMessage.java b/telephony/java/android/telephony/ims/RcsMessage.java
index 3227413..b0d0d5a 100644
--- a/telephony/java/android/telephony/ims/RcsMessage.java
+++ b/telephony/java/android/telephony/ims/RcsMessage.java
@@ -27,6 +27,8 @@
 
 /**
  * This is a single instance of a message sent or received over RCS.
+ *
+ * @hide
  */
 public abstract class RcsMessage {
     /**
diff --git a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
index c46c605..f0eea88 100644
--- a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
+++ b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
@@ -27,6 +27,8 @@
  * {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} and
  * {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to create and persist
  * {@link RcsMessage}s on an {@link RcsThread}
+ *
+ * @hide
  */
 public class RcsMessageCreationParams {
     // The globally unique id of the RcsMessage to be created.
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
index 535a597..6491ac9 100644
--- a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
@@ -31,6 +31,8 @@
  * The parameters to pass into
  * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} in order to select a
  * subset of {@link RcsMessage}s present in the message store.
+ *
+ * @hide
  */
 public final class RcsMessageQueryParams implements Parcelable {
     /**
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
index 3514b48e..e4020c1 100644
--- a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
@@ -32,6 +32,8 @@
  * The result of a {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
  * call. This class allows getting the token for querying the next batch of messages in order to
  * prevent handling large amounts of data at once.
+ *
+ * @hide
  */
 public final class RcsMessageQueryResult implements Parcelable {
     // The token to continue the query to get the next batch of results
diff --git a/telephony/java/android/telephony/ims/RcsMessageSnippet.java b/telephony/java/android/telephony/ims/RcsMessageSnippet.java
index b0b930c..9064251 100644
--- a/telephony/java/android/telephony/ims/RcsMessageSnippet.java
+++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.java
@@ -23,6 +23,8 @@
 
 /**
  * An immutable summary of the latest {@link RcsMessage} on an {@link RcsThread}
+ *
+ * @hide
  */
 public final class RcsMessageSnippet implements Parcelable {
     private final String mText;
diff --git a/telephony/java/android/telephony/ims/RcsMessageStore.java b/telephony/java/android/telephony/ims/RcsMessageStore.java
index 31f2983..3111652 100644
--- a/telephony/java/android/telephony/ims/RcsMessageStore.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStore.java
@@ -26,6 +26,8 @@
 /**
  * RcsMessageStore is the application interface to RcsProvider and provides access methods to
  * RCS related database tables.
+ *
+ * @hide
  */
 public class RcsMessageStore {
     /**
diff --git a/telephony/java/android/telephony/ims/RcsMessageStoreException.java b/telephony/java/android/telephony/ims/RcsMessageStoreException.java
index f25bb17..3b3fcf2 100644
--- a/telephony/java/android/telephony/ims/RcsMessageStoreException.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStoreException.java
@@ -19,6 +19,8 @@
 /**
  * An exception that happened on {@link RcsMessageStore} or one of the derived storage classes in
  * {@link android.telephony.ims}
+ *
+ * @hide
  */
 public class RcsMessageStoreException extends Exception {
 
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
index 06fb832..1b4bfe5 100644
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
@@ -23,6 +23,8 @@
 
 /**
  * This is a single instance of a message sent over RCS.
+ *
+ * @hide
  */
 public class RcsOutgoingMessage extends RcsMessage {
     RcsOutgoingMessage(int id) {
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
index 979634a..81e3244 100644
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
@@ -23,6 +23,8 @@
  * {@link RcsOutgoingMessageCreationParams} is a collection of parameters that should be passed
  * into {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to generate an
  * {@link RcsOutgoingMessage} on that {@link RcsThread}
+ *
+ * @hide
  */
 public final class RcsOutgoingMessageCreationParams extends RcsMessageCreationParams
         implements Parcelable {
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
index 1c87b13..2db49c6 100644
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
@@ -21,6 +21,8 @@
 /**
  * This class holds the delivery information of an {@link RcsOutgoingMessage} for each
  * {@link RcsParticipant} that the message was intended for.
+ *
+ * @hide
  */
 public class RcsOutgoingMessageDelivery {
     // The participant that this delivery is intended for
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java
index 7ba5d8e..bcf134a 100644
--- a/telephony/java/android/telephony/ims/RcsParticipant.java
+++ b/telephony/java/android/telephony/ims/RcsParticipant.java
@@ -20,6 +20,8 @@
 
 /**
  * RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s.
+ *
+ * @hide
  */
 public class RcsParticipant {
     // The row ID of this participant in the database
diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
index cc2613f..61801f3 100644
--- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
+++ b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
@@ -21,6 +21,8 @@
 /**
  * An event that indicates an {@link RcsParticipant}'s alias was changed. Please see US18-2 - GSMA
  * RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @hide
  */
 public final class RcsParticipantAliasChangedEvent extends RcsEvent {
     // The participant that changed their alias
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
index d24d079..ada9b8a 100644
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
@@ -30,6 +30,8 @@
  * The parameters to pass into
  * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} in order to select a
  * subset of {@link RcsThread}s present in the message store.
+ *
+ * @hide
  */
 public final class RcsParticipantQueryParams implements Parcelable {
     /**
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
index 505f1a5..92e2fa78 100644
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
@@ -28,6 +28,8 @@
  * The result of a {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
  * call. This class allows getting the token for querying the next batch of participants in order to
  * prevent handling large amounts of data at once.
+ *
+ * @hide
  */
 public final class RcsParticipantQueryResult implements Parcelable {
     // A token for the caller to continue their query for the next batch of results
diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
index 08643de..970c110 100644
--- a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
+++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
@@ -31,6 +31,8 @@
  * @see RcsMessageQueryResult#getContinuationToken()
  * @see RcsParticipantQueryResult#getContinuationToken()
  * @see RcsThreadQueryResult#getContinuationToken()
+ *
+ * @hide
  */
 public final class RcsQueryContinuationToken implements Parcelable {
     /**
diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java
index e015dd3..cf1dc76 100644
--- a/telephony/java/android/telephony/ims/RcsThread.java
+++ b/telephony/java/android/telephony/ims/RcsThread.java
@@ -27,6 +27,8 @@
 /**
  * RcsThread represents a single RCS conversation thread. It holds messages that were sent and
  * received and events that occurred on that thread.
+ *
+ * @hide
  */
 public abstract class RcsThread {
     /**
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
index 05a5a39..81eee40 100644
--- a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
@@ -35,6 +35,8 @@
 /**
  * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} in
  * order to select a subset of {@link RcsThread}s present in the message store.
+ *
+ * @hide
  */
 public final class RcsThreadQueryParams implements Parcelable {
     /**
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
index 1cac61d..9f2fba5 100644
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
@@ -32,6 +32,8 @@
  * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)}
  * call. This class allows getting the token for querying the next batch of threads in order to
  * prevent handling large amounts of data at once.
+ *
+ * @hide
  */
 public final class RcsThreadQueryResult implements Parcelable {
     // A token for the caller to continue their query for the next batch of results
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 75a4d82..79e0aa1 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -275,7 +275,7 @@
 
     void clearDefaultsForInactiveSubIds();
 
-    int[] getActiveSubIdList();
+    int[] getActiveSubIdList(boolean visibleOnly);
 
     int setSubscriptionProperty(int subId, String propKey, String propValue);
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index a922ab6..6de608e 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -84,6 +84,6 @@
     void notifyActiveDataSubIdChanged(int activeDataSubId);
     void notifyRadioPowerStateChanged(in int state);
     void notifyEmergencyNumberList();
-    void notifyCallQualityChanged(in CallQuality callQuality, int phoneId);
+    void notifyCallQualityChanged(in CallQuality callQuality, int phoneId, int callNetworkType);
     void notifyImsDisconnectCause(int subId, in ImsReasonInfo imsReasonInfo);
 }
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 157609c..8aa0aaf 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -44,7 +44,7 @@
 // ==========================================
 // This is only intended for inclusion in the android.test.runner-minus-junit,
 // robolectric_android-all-stub and repackaged.android.test.* libraries.
-// Must not be used elewhere.
+// Must not be used elsewhere.
 java_library_static {
     name: "android.test.base_static",
     installable: false,
@@ -61,19 +61,6 @@
     sdk_version: "current",
 }
 
-// Build the legacy-test library
-// =============================
-// This contains the junit.framework and android.test classes that were in
-// Android API level 25 excluding those from android.test.runner.
-// Also contains the com.android.internal.util.Predicate[s] classes.
-java_library {
-    name: "legacy-test",
-    installable: true,
-
-    sdk_version: "current",
-    static_libs: ["android.test.base_static"],
-}
-
 // Build the repackaged.android.test.base library
 // ==============================================
 // This contains repackaged versions of the classes from
@@ -93,8 +80,8 @@
 // ===============================================
 // This contains the android.test classes from android.test.base plus
 // the com.android.internal.util.Predicate[s] classes. This is only
-// intended for inclusion in the android.test.legacy and
-// legacy-android-test static libraries and must not be used elsewhere.
+// intended for inclusion in android.test.legacy and must not be used
+// elsewhere.
 java_library_static {
     name: "android.test.base-minus-junit",
 
diff --git a/test-legacy/Android.bp b/test-legacy/Android.bp
deleted file mode 100644
index a69f422..0000000
--- a/test-legacy/Android.bp
+++ /dev/null
@@ -1,36 +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.
-//
-
-// Build the legacy-android-test library
-// =====================================
-// This contains the android.test classes that were in Android API level 25,
-// including those from android.test.runner.
-// Also contains the com.android.internal.util.Predicate[s] classes.
-java_library_static {
-    name: "legacy-android-test",
-
-    static_libs: [
-        "android.test.base-minus-junit",
-        "android.test.runner-minus-junit",
-        "android.test.mock_static",
-    ],
-
-    no_framework_libs: true,
-    libs: [
-        "framework",
-        "junit",
-    ],
-}
diff --git a/test-legacy/Android.mk b/test-legacy/Android.mk
index da47de0..af26c5b 100644
--- a/test-legacy/Android.mk
+++ b/test-legacy/Android.mk
@@ -24,35 +24,16 @@
 # Built against the SDK so that it can be statically included in APKs
 # without breaking link type checks.
 #
-# This builds directly from the source rather than simply statically
-# including the android.test.base-minus-junit and
-# android.test.runner-minus-junit libraries because the latter library
-# cannot itself be built against the SDK. That is because it uses on
-# an internal method (setTestContext) on the AndroidTestCase class.
-# That class is provided by both the android.test.base-minus-junit and
-# the current SDK and as the latter is first on the classpath its
-# version is used. Unfortunately, it does not provide the internal
-# method and so compilation fails.
-#
-# Building from source avoids that because the compiler will use the
-# source version of AndroidTestCase instead of the one from the current
-# SDK.
-#
-# The use of the internal method does not prevent this from being
-# statically included because the class that provides the method is
-# also included in this library.
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := android.test.legacy
 
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, ../test-base/src/android) \
-    $(call all-java-files-under, ../test-base/src/com) \
-    $(call all-java-files-under, ../test-runner/src/android) \
-
 LOCAL_SDK_VERSION := current
 
 LOCAL_JAVA_LIBRARIES := junit android.test.mock.stubs
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android.test.base-minus-junit \
+    android.test.runner-minus-junit \
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 43b765d..e1d6e01 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -30,19 +30,3 @@
     srcs_lib_whitelist_pkgs: ["android"],
     compile_dex: true,
 }
-
-// Build the android.test.mock_static library
-// ==========================================
-// This is only intended for inclusion in the legacy-android-test.
-// Must not be used elewhere.
-java_library_static {
-    name: "android.test.mock_static",
-
-    java_version: "1.8",
-    srcs: ["src/**/*.java"],
-
-    no_framework_libs: true,
-    libs: [
-        "framework",
-    ],
-}
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index 0cb8f22..6765316 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -1,6 +1,10 @@
 // Signature format: 2.0
 package android.test.mock {
 
+  public class MockContext extends android.content.Context {
+    method public android.view.Display getDisplay();
+  }
+
   @Deprecated public class MockPackageManager extends android.content.pm.PackageManager {
     method public boolean arePermissionsIndividuallyControlled();
     method public String getDefaultBrowserPackageNameAsUser(int);
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index db5053e..3521202 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -45,7 +45,7 @@
 
 // Build the android.test.runner-minus-junit library
 // =================================================
-// This is only intended for inclusion in the legacy-android-test static
+// This is only intended for inclusion in the android.test.legacy static
 // library and must not be used elsewhere.
 java_library {
     name: "android.test.runner-minus-junit",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
index 60bd60f..fece8ba 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
@@ -49,7 +49,7 @@
         @Override
         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
             mContent.setLeftTopRightBottom(0, 0, width, height);
-            RecordingCanvas canvas = mContent.startRecording();
+            RecordingCanvas canvas = mContent.beginRecording();
             canvas.drawColor(Color.WHITE);
             Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
             paint.setColor(Color.BLACK);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
index 8bd7d79..08d5d4f 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
@@ -60,14 +60,14 @@
         outline.setAlpha(1f);
         childNode.setOutline(outline);
         {
-            Canvas canvas = childNode.startRecording();
+            Canvas canvas = childNode.beginRecording();
             canvas.drawColor(Color.BLUE);
         }
         childNode.endRecording();
         childNode.setElevation(20f);
 
         {
-            Canvas canvas = mContent.startRecording();
+            Canvas canvas = mContent.beginRecording();
             canvas.drawColor(Color.WHITE);
             canvas.enableZ();
             canvas.drawRenderNode(childNode);
diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk
index 9e5d8ce..db9376b 100644
--- a/tests/RollbackTest/Android.mk
+++ b/tests/RollbackTest/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
 LOCAL_MANIFEST_FILE := TestApp/Av1.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v1
 LOCAL_PACKAGE_NAME := RollbackTestAppAv1
 include $(BUILD_PACKAGE)
 ROLLBACK_TEST_APP_AV1 := $(LOCAL_INSTALLED_MODULE)
@@ -32,6 +33,7 @@
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
 LOCAL_MANIFEST_FILE := TestApp/Av2.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2
 LOCAL_PACKAGE_NAME := RollbackTestAppAv2
 include $(BUILD_PACKAGE)
 ROLLBACK_TEST_APP_AV2 := $(LOCAL_INSTALLED_MODULE)
@@ -43,6 +45,7 @@
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
 LOCAL_MANIFEST_FILE := TestApp/ACrashingV2.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2
 LOCAL_PACKAGE_NAME := RollbackTestAppACrashingV2
 include $(BUILD_PACKAGE)
 ROLLBACK_TEST_APP_A_CRASHING_V2 := $(LOCAL_INSTALLED_MODULE)
@@ -54,6 +57,7 @@
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
 LOCAL_MANIFEST_FILE := TestApp/Bv1.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v1
 LOCAL_PACKAGE_NAME := RollbackTestAppBv1
 include $(BUILD_PACKAGE)
 ROLLBACK_TEST_APP_BV1 := $(LOCAL_INSTALLED_MODULE)
@@ -65,10 +69,39 @@
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
 LOCAL_MANIFEST_FILE := TestApp/Bv2.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2
 LOCAL_PACKAGE_NAME := RollbackTestAppBv2
 include $(BUILD_PACKAGE)
 ROLLBACK_TEST_APP_BV2 := $(LOCAL_INSTALLED_MODULE)
 
+# RollbackTestAppASplitV1.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Av1.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v1
+LOCAL_PACKAGE_NAME := RollbackTestAppASplitV1
+LOCAL_PACKAGE_SPLITS := anydpi
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_A_SPLIT_V1 := $(LOCAL_INSTALLED_MODULE)
+ROLLBACK_TEST_APP_A_SPLIT_V1_SPLIT := $(installed_apk_splits)
+
+# RollbackTestAppASplitV2.apk
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src)
+LOCAL_MANIFEST_FILE := TestApp/Av2.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2
+LOCAL_PACKAGE_NAME := RollbackTestAppASplitV2
+LOCAL_PACKAGE_SPLITS := anydpi
+include $(BUILD_PACKAGE)
+ROLLBACK_TEST_APP_A_SPLIT_V2 := $(LOCAL_INSTALLED_MODULE)
+ROLLBACK_TEST_APP_A_SPLIT_V2_SPLIT := $(installed_apk_splits)
+
 # RollbackTest
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(call all-java-files-under, RollbackTest/src)
@@ -82,6 +115,10 @@
   $(ROLLBACK_TEST_APP_A_CRASHING_V2) \
   $(ROLLBACK_TEST_APP_BV1) \
   $(ROLLBACK_TEST_APP_BV2) \
+  $(ROLLBACK_TEST_APP_A_SPLIT_V1) \
+  $(ROLLBACK_TEST_APP_A_SPLIT_V1_SPLIT) \
+  $(ROLLBACK_TEST_APP_A_SPLIT_V2) \
+  $(ROLLBACK_TEST_APP_A_SPLIT_V2_SPLIT) \
   $(ROLLBACK_TEST_APEX_V1) \
   $(ROLLBACK_TEST_APEX_V2)
 LOCAL_MANIFEST_FILE := RollbackTest/AndroidManifest.xml
@@ -103,5 +140,9 @@
 ROLLBACK_TEST_APP_AV1 :=
 ROLLBACK_TEST_APP_AV2 :=
 ROLLBACK_TEST_APP_A_CRASHING_V2 :=
+ROLLBACK_TEST_APP_A_SPLIT_V1 :=
+ROLLBACK_TEST_APP_A_SPLIT_V1_SPLIT :=
+ROLLBACK_TEST_APP_A_SPLIT_V2 :=
+ROLLBACK_TEST_APP_A_SPLIT_V2_SPLIT :=
 ROLLBACK_TEST_APP_BV1 :=
 ROLLBACK_TEST_APP_BV2 :=
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index bd0881f..7be83ed 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -32,6 +32,7 @@
 import android.content.rollback.RollbackManager;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.provider.DeviceConfig;
 import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
@@ -331,6 +332,64 @@
     }
 
     /**
+     * Test the scheduling aspect of rollback expiration.
+     */
+    @Test
+    public void testRollbackExpiresAfterLifetime() throws Exception {
+        long expirationTime = TimeUnit.SECONDS.toMillis(30);
+        long defaultExpirationTime = TimeUnit.HOURS.toMillis(48);
+        RollbackManager rm = RollbackTestUtils.getRollbackManager();
+
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS,
+                    Manifest.permission.WRITE_DEVICE_CONFIG);
+
+            DeviceConfig.setProperty(DeviceConfig.Rollback.BOOT_NAMESPACE,
+                    DeviceConfig.Rollback.ROLLBACK_LIFETIME_IN_MILLIS,
+                    Long.toString(expirationTime), false /* makeDefault*/);
+
+            // Pull the new expiration time from DeviceConfig
+            rm.reloadPersistedData();
+
+            // Uninstall TEST_APP_A
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // Install v1 of the app (without rollbacks enabled).
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
+            assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // Upgrade from v1 to v2, with rollbacks enabled.
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
+            assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
+
+            // Check that the rollback data has not expired
+            Thread.sleep(1000);
+            RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+                    rm.getAvailableRollbacks(), TEST_APP_A);
+            assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+
+            // Give it a little more time, but still not the long enough to expire
+            Thread.sleep(expirationTime / 2);
+            rollback = getUniqueRollbackInfoForPackage(
+                rm.getAvailableRollbacks(), TEST_APP_A);
+            assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
+
+            // Check that the data has expired after the expiration time (with a buffer of 1 second)
+            Thread.sleep(expirationTime / 2);
+            assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A));
+        } finally {
+            DeviceConfig.setProperty(DeviceConfig.Rollback.BOOT_NAMESPACE,
+                    DeviceConfig.Rollback.ROLLBACK_LIFETIME_IN_MILLIS,
+                    Long.toString(defaultExpirationTime), false /* makeDefault*/);
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
      * Test explicit expiration of rollbacks.
      * Does not test the scheduling aspects of rollback expiration.
      */
@@ -442,6 +501,39 @@
     }
 
     /**
+     * Test rollback of apks involving splits.
+     */
+    @Test
+    public void testRollbackWithSplits() throws Exception {
+        try {
+            RollbackTestUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.MANAGE_ROLLBACKS);
+
+            RollbackTestUtils.uninstall(TEST_APP_A);
+            RollbackTestUtils.installSplit(false,
+                    "RollbackTestAppASplitV1.apk",
+                    "RollbackTestAppASplitV1_anydpi.apk");
+            processUserData(TEST_APP_A);
+
+            RollbackTestUtils.installSplit(true,
+                    "RollbackTestAppASplitV2.apk",
+                    "RollbackTestAppASplitV2_anydpi.apk");
+            processUserData(TEST_APP_A);
+
+            RollbackManager rm = RollbackTestUtils.getRollbackManager();
+            RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+                    rm.getAvailableRollbacks(), TEST_APP_A);
+            assertNotNull(rollback);
+            RollbackTestUtils.rollback(rollback.getRollbackId());
+            processUserData(TEST_APP_A);
+        } finally {
+            RollbackTestUtils.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
      * Test restrictions on rollback broadcast sender.
      * A random app should not be able to send a ROLLBACK_COMMITTED broadcast.
      */
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index 097d33d..f28714c 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -130,6 +130,19 @@
      */
     static void install(String resourceName, boolean enableRollback)
             throws InterruptedException, IOException {
+        installSplit(enableRollback, resourceName);
+    }
+
+    /**
+     * Installs the apk with the given name and its splits.
+     *
+     * @param enableRollback if rollback should be enabled.
+     * @param resourceNames names of class loader resources for the apk and
+     *        its splits to install.
+     * @throws AssertionError if the installation fails.
+     */
+    static void installSplit(boolean enableRollback, String... resourceNames)
+            throws InterruptedException, IOException {
         Context context = InstrumentationRegistry.getContext();
         PackageInstaller.Session session = null;
         PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
@@ -142,12 +155,14 @@
         session = packageInstaller.openSession(sessionId);
 
         ClassLoader loader = RollbackTest.class.getClassLoader();
-        try (OutputStream packageInSession = session.openWrite(resourceName, 0, -1);
-             InputStream is = loader.getResourceAsStream(resourceName);) {
-            byte[] buffer = new byte[4096];
-            int n;
-            while ((n = is.read(buffer)) >= 0) {
-                packageInSession.write(buffer, 0, n);
+        for (String resourceName : resourceNames) {
+            try (OutputStream packageInSession = session.openWrite(resourceName, 0, -1);
+                    InputStream is = loader.getResourceAsStream(resourceName);) {
+                byte[] buffer = new byte[4096];
+                int n;
+                while ((n = is.read(buffer)) >= 0) {
+                    packageInSession.write(buffer, 0, n);
+                }
             }
         }
 
@@ -345,7 +360,7 @@
                 PackageInstaller.SessionInfo info =
                         intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
                 if (info != null && info.getSessionId() == sessionId) {
-                    if (info.isSessionReady() || info.isSessionFailed()) {
+                    if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
                         try {
                             sessionStatus.put(info);
                         } catch (InterruptedException e) {
@@ -365,13 +380,13 @@
         PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId);
 
         try {
-            if (info.isSessionReady() || info.isSessionFailed()) {
+            if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
                 sessionStatus.put(info);
             }
 
             info = sessionStatus.take();
             context.unregisterReceiver(sessionUpdatedReceiver);
-            if (info.isSessionFailed()) {
+            if (info.isStagedSessionFailed()) {
                 throw new AssertionError(info.getStagedSessionErrorMessage());
             }
         } catch (InterruptedException e) {
diff --git a/tests/RollbackTest/TestApp/ACrashingV2.xml b/tests/RollbackTest/TestApp/ACrashingV2.xml
index 5708d23..77bfd4e 100644
--- a/tests/RollbackTest/TestApp/ACrashingV2.xml
+++ b/tests/RollbackTest/TestApp/ACrashingV2.xml
@@ -23,7 +23,6 @@
     <uses-sdk android:minSdkVersion="19" />
 
     <application android:label="Rollback Test App A v2">
-        <meta-data android:name="version" android:value="2" />
         <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
                   android:exported="true" />
         <activity android:name="com.android.tests.rollback.testapp.CrashingMainActivity">
diff --git a/tests/RollbackTest/TestApp/Av1.xml b/tests/RollbackTest/TestApp/Av1.xml
index 996d831..63729fb 100644
--- a/tests/RollbackTest/TestApp/Av1.xml
+++ b/tests/RollbackTest/TestApp/Av1.xml
@@ -23,7 +23,6 @@
     <uses-sdk android:minSdkVersion="19" />
 
     <application android:label="Rollback Test App A v1">
-        <meta-data android:name="version" android:value="1" />
         <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
                   android:exported="true" />
         <activity android:name="com.android.tests.rollback.testapp.MainActivity">
diff --git a/tests/RollbackTest/TestApp/Av2.xml b/tests/RollbackTest/TestApp/Av2.xml
index 21c7260..f0e909f 100644
--- a/tests/RollbackTest/TestApp/Av2.xml
+++ b/tests/RollbackTest/TestApp/Av2.xml
@@ -23,7 +23,6 @@
     <uses-sdk android:minSdkVersion="19" />
 
     <application android:label="Rollback Test App A v2">
-        <meta-data android:name="version" android:value="2" />
         <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
                   android:exported="true" />
         <activity android:name="com.android.tests.rollback.testapp.MainActivity">
diff --git a/tests/RollbackTest/TestApp/Bv1.xml b/tests/RollbackTest/TestApp/Bv1.xml
index de0fd0d..ca9c2ec 100644
--- a/tests/RollbackTest/TestApp/Bv1.xml
+++ b/tests/RollbackTest/TestApp/Bv1.xml
@@ -23,7 +23,6 @@
     <uses-sdk android:minSdkVersion="19" />
 
     <application android:label="Rollback Test App B v1">
-        <meta-data android:name="version" android:value="1" />
         <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
                   android:exported="true" />
         <activity android:name="com.android.tests.rollback.testapp.MainActivity">
diff --git a/tests/RollbackTest/TestApp/Bv2.xml b/tests/RollbackTest/TestApp/Bv2.xml
index 6c2e66a..bd3e613 100644
--- a/tests/RollbackTest/TestApp/Bv2.xml
+++ b/tests/RollbackTest/TestApp/Bv2.xml
@@ -23,7 +23,6 @@
     <uses-sdk android:minSdkVersion="19" />
 
     <application android:label="Rollback Test App B v2">
-        <meta-data android:name="version" android:value="2" />
         <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData"
                   android:exported="true" />
         <activity android:name="com.android.tests.rollback.testapp.MainActivity">
diff --git a/tests/RollbackTest/TestApp/res_v1/values-anydpi/values.xml b/tests/RollbackTest/TestApp/res_v1/values-anydpi/values.xml
new file mode 100644
index 0000000..90d3da2
--- /dev/null
+++ b/tests/RollbackTest/TestApp/res_v1/values-anydpi/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <integer name="split_version">1</integer>
+</resources>
diff --git a/tests/RollbackTest/TestApp/res_v1/values/values.xml b/tests/RollbackTest/TestApp/res_v1/values/values.xml
new file mode 100644
index 0000000..0447c74
--- /dev/null
+++ b/tests/RollbackTest/TestApp/res_v1/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <integer name="app_version">1</integer>
+    <integer name="split_version">0</integer>
+</resources>
diff --git a/tests/RollbackTest/TestApp/res_v2/values-anydpi/values.xml b/tests/RollbackTest/TestApp/res_v2/values-anydpi/values.xml
new file mode 100644
index 0000000..9a1aa7f
--- /dev/null
+++ b/tests/RollbackTest/TestApp/res_v2/values-anydpi/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <integer name="split_version">2</integer>
+</resources>
diff --git a/tests/RollbackTest/TestApp/res_v2/values/values.xml b/tests/RollbackTest/TestApp/res_v2/values/values.xml
new file mode 100644
index 0000000..fd988f5
--- /dev/null
+++ b/tests/RollbackTest/TestApp/res_v2/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <integer name="app_version">2</integer>
+    <integer name="split_version">0</integer>
+</resources>
diff --git a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java
index fde6a83..38c658e 100644
--- a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java
+++ b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java
@@ -19,9 +19,7 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
+import android.content.res.Resources;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -35,6 +33,8 @@
  */
 public class ProcessUserData extends BroadcastReceiver {
 
+    private static final String TAG = "RollbackTestApp";
+
     /**
      * Exception thrown in case of issue with user data.
      */
@@ -66,14 +66,19 @@
      * @throws UserDataException in case of problems with app user data.
      */
     public void processUserData(Context context) throws UserDataException {
-        int appVersion = 0;
-        try {
-            ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
-                    context.getPackageName(), PackageManager.GET_META_DATA);
-            Bundle bundle = appInfo.metaData;
-            appVersion = bundle.getInt("version");
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new UserDataException("Unable to get app version info", e);
+        Resources res = context.getResources();
+        String packageName = context.getPackageName();
+
+        int appVersionId = res.getIdentifier("app_version", "integer", packageName);
+        int appVersion = res.getInteger(appVersionId);
+
+        int splitVersionId = res.getIdentifier("split_version", "integer", packageName);
+        int splitVersion = res.getInteger(splitVersionId);
+
+        // Make sure the app version and split versions are compatible.
+        if (appVersion != splitVersion) {
+            throw new UserDataException("Split version " + splitVersion
+                    + " does not match app version " + appVersion);
         }
 
         // Read the version of the app's user data and ensure it is compatible
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index a10fb4e..ed524f6 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -154,6 +154,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.SparseArray;
 
 import com.android.internal.net.VpnConfig;
 import com.android.internal.util.ArrayUtils;
@@ -748,6 +749,10 @@
         // mExpectations is non-empty.
         private boolean mExpectingAdditions;
 
+        // Used to collect the networks requests managed by this factory. This is a duplicate of
+        // the internal information stored in the NetworkFactory (which is private).
+        private SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
+
         public MockNetworkFactory(Looper looper, Context context, String logTag,
                 NetworkCapabilities filter) {
             super(looper, context, logTag, filter);
@@ -800,6 +805,7 @@
                 }
 
                 // Add the request.
+                mNetworkRequests.put(request.requestId, request);
                 super.handleAddRequest(request, score, factorySerialNumber);
                 mExpectations.notify();
             }
@@ -817,11 +823,17 @@
                 }
 
                 // Remove the request.
+                mNetworkRequests.remove(request.requestId);
                 super.handleRemoveRequest(request);
                 mExpectations.notify();
             }
         }
 
+        // Trigger releasing the request as unfulfillable
+        public void triggerUnfulfillable(NetworkRequest r) {
+            super.releaseRequestAsUnfulfillableByAnyFactory(r);
+        }
+
         private void assertNoExpectations() {
             if (mExpectations.size() != 0) {
                 fail("Can't add expectation, " + mExpectations.size() + " already pending");
@@ -861,9 +873,11 @@
             assertEquals(msg, 0, count);
         }
 
-        public void waitForNetworkRequests(final int count) throws InterruptedException {
+        public SparseArray<NetworkRequest> waitForNetworkRequests(final int count)
+                throws InterruptedException {
             waitForRequests();
             assertEquals(count, getMyRequestCount());
+            return mNetworkRequests;
         }
     }
 
@@ -3533,6 +3547,55 @@
         networkCallback.assertNoCallback();
     }
 
+    /**
+     * Validate the callback flow for a factory releasing a request as unfulfillable.
+     */
+    @Test
+    public void testUnfulfillableNetworkRequest() throws Exception {
+        NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
+                NetworkCapabilities.TRANSPORT_WIFI).build();
+        final TestNetworkCallback networkCallback = new TestNetworkCallback();
+
+        final HandlerThread handlerThread = new HandlerThread("testUnfulfillableNetworkRequest");
+        handlerThread.start();
+        NetworkCapabilities filter = new NetworkCapabilities()
+                .addTransportType(TRANSPORT_WIFI)
+                .addCapability(NET_CAPABILITY_INTERNET);
+        final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+                mServiceContext, "testFactory", filter);
+        testFactory.setScoreFilter(40);
+
+        // Register the factory and expect it to receive the default request.
+        testFactory.expectAddRequestsWithScores(0);
+        testFactory.register();
+        SparseArray<NetworkRequest> requests = testFactory.waitForNetworkRequests(1);
+
+        assertEquals(1, requests.size()); // have 1 request at this point
+        int origRequestId = requests.valueAt(0).requestId;
+
+        // Now file the test request and expect it.
+        testFactory.expectAddRequestsWithScores(0);
+        mCm.requestNetwork(nr, networkCallback);
+        requests = testFactory.waitForNetworkRequests(2); // have 2 requests at this point
+
+        int newRequestId = 0;
+        for (int i = 0; i < requests.size(); ++i) {
+            if (requests.valueAt(i).requestId != origRequestId) {
+                newRequestId = requests.valueAt(i).requestId;
+                break;
+            }
+        }
+
+        // Simulate the factory releasing the request as unfulfillable and expect onUnavailable!
+        testFactory.expectRemoveRequests(1);
+        testFactory.triggerUnfulfillable(requests.get(newRequestId));
+        networkCallback.expectCallback(CallbackState.UNAVAILABLE, null);
+        testFactory.waitForRequests();
+
+        testFactory.unregister();
+        handlerThread.quit();
+    }
+
     private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
 
         public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 0032960..ffc1a92 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -44,6 +44,7 @@
 
 Maybe<ResourceName> ToResourceName(
     const android::ResTable::resource_name& name_in) {
+  // TODO: Remove this when ResTable and AssetManager(1) are removed from AAPT2
   ResourceName name_out;
   if (!name_in.package) {
     return {};
@@ -79,6 +80,41 @@
   return name_out;
 }
 
+Maybe<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& name_in) {
+  ResourceName name_out;
+  if (!name_in.package) {
+    return {};
+  }
+
+  name_out.package = std::string(name_in.package, name_in.package_len);
+
+  const ResourceType* type;
+  if (name_in.type16) {
+    type = ParseResourceType(
+        util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len)));
+  } else if (name_in.type) {
+    type = ParseResourceType(StringPiece(name_in.type, name_in.type_len));
+  } else {
+    return {};
+  }
+
+  if (!type) {
+    return {};
+  }
+
+  name_out.type = *type;
+
+  if (name_in.entry16) {
+    name_out.entry =
+        util::Utf16ToUtf8(StringPiece16(name_in.entry16, name_in.entry_len));
+  } else if (name_in.entry) {
+    name_out.entry = std::string(name_in.entry, name_in.entry_len);
+  } else {
+    return {};
+  }
+  return name_out;
+}
+
 bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref,
                        bool* out_private) {
   if (str.empty()) {
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index e282fd58..a8a3120 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -20,6 +20,7 @@
 #include <functional>
 #include <memory>
 
+#include "androidfw/AssetManager2.h"
 #include "androidfw/ConfigDescription.h"
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/StringPiece.h"
@@ -78,6 +79,12 @@
     const android::ResTable::resource_name& name);
 
 /**
+ * Convert an android::AssetManager2::ResourceName to an aapt::ResourceName struct.
+ */
+Maybe<ResourceName> ToResourceName(
+    const android::AssetManager2::ResourceName& name_in);
+
+/**
  * Returns a boolean value if the string is equal to TRUE, true, True, FALSE,
  * false, or False.
  */
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 0512bdc..bec6c69 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -769,7 +769,10 @@
     auto collection = util::make_unique<io::FileCollection>();
 
     // Collect data from the path for each input file.
-    for (const std::string& arg : args) {
+    std::vector<std::string> sorted_args = args;
+    std::sort(sorted_args.begin(), sorted_args.end());
+
+    for (const std::string& arg : sorted_args) {
       collection->InsertFile(arg);
     }
 
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 22edd2f..a7b8d25 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -717,28 +717,20 @@
   return true;
 }
 
-static int32_t FindFrameworkAssetManagerCookie(const android::AssetManager& assets) {
+static android::ApkAssetsCookie FindFrameworkAssetManagerCookie(
+    const android::AssetManager2& assets) {
   using namespace android;
 
   // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so
   // we're looking for the first attribute resource in the system package.
-  const ResTable& table = assets.getResources(true);
-  Res_value val;
-  ssize_t idx = table.getResource(0x01010000, &val, true);
-  if (idx != NO_ERROR) {
-    // Try as a bag.
-    const ResTable::bag_entry* entry;
-    ssize_t cnt = table.lockBag(0x01010000, &entry);
-    if (cnt >= 0) {
-      idx = entry->stringBlock;
-    }
-    table.unlockBag(entry);
-  }
+  Res_value val{};
+  ResTable_config config{};
+  uint32_t type_spec_flags;
+  ApkAssetsCookie idx = assets.GetResource(0x01010000, true /** may_be_bag */,
+                                           0 /** density_override */, &val, &config,
+                                           &type_spec_flags);
 
-  if (idx < 0) {
-    return 0;
-  }
-  return table.getTableCookie(idx);
+  return idx;
 }
 
 class Linker {
@@ -750,17 +742,17 @@
         file_collection_(util::make_unique<io::FileCollection>()) {
   }
 
-  void ExtractCompileSdkVersions(android::AssetManager* assets) {
+  void ExtractCompileSdkVersions(android::AssetManager2* assets) {
     using namespace android;
 
-    int32_t cookie = FindFrameworkAssetManagerCookie(*assets);
-    if (cookie == 0) {
+    android::ApkAssetsCookie cookie = FindFrameworkAssetManagerCookie(*assets);
+    if (cookie == android::kInvalidCookie) {
       // No Framework assets loaded. Not a failure.
       return;
     }
 
     std::unique_ptr<Asset> manifest(
-        assets->openNonAsset(cookie, kAndroidManifestPath, Asset::AccessMode::ACCESS_BUFFER));
+        assets->OpenNonAsset(kAndroidManifestPath, cookie, Asset::AccessMode::ACCESS_BUFFER));
     if (manifest == nullptr) {
       // No errors.
       return;
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index 51cc903..e15f935 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -79,6 +79,7 @@
     return nullptr;
   }
 
+  std::vector<std::string> sorted_files;
   while (struct dirent *entry = readdir(d.get())) {
     std::string prefix_path = root.to_string();
     file::AppendPath(&prefix_path, entry->d_name);
@@ -105,10 +106,15 @@
         continue;
       }
 
-      collection->InsertFile(full_path);
+      sorted_files.push_back(full_path);
     }
   }
 
+  std::sort(sorted_files.begin(), sorted_files.end());
+  for (const std::string& full_path : sorted_files) {
+    collection->InsertFile(full_path);
+  }
+
   return collection;
 }
 
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index a844a43..78e0074 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -20,9 +20,11 @@
 
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
-#include "androidfw/AssetManager.h"
+#include "androidfw/Asset.h"
+#include "androidfw/AssetManager2.h"
 #include "androidfw/ConfigDescription.h"
 #include "androidfw/ResourceTypes.h"
+#include "androidfw/ResourceUtils.h"
 
 #include "NameMangler.h"
 #include "Resource.h"
@@ -30,6 +32,7 @@
 #include "ValueVisitor.h"
 #include "util/Util.h"
 
+using ::android::ApkAssets;
 using ::android::ConfigDescription;
 using ::android::StringPiece;
 using ::android::StringPiece16;
@@ -214,51 +217,75 @@
 }
 
 bool AssetManagerSymbolSource::AddAssetPath(const StringPiece& path) {
-  int32_t cookie = 0;
-  return assets_.addAssetPath(android::String8(path.data(), path.size()), &cookie);
+  if (std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path.data())) {
+    apk_assets_.push_back(std::move(apk));
+
+    std::vector<const ApkAssets*> apk_assets;
+    for (const std::unique_ptr<const ApkAssets>& apk_asset : apk_assets_) {
+      apk_assets.push_back(apk_asset.get());
+    }
+
+    asset_manager_.SetApkAssets(apk_assets, true /* invalidate_caches */,
+                                false /* filter_incompatible_configs */);
+    return true;
+  }
+  return false;
 }
 
 std::map<size_t, std::string> AssetManagerSymbolSource::GetAssignedPackageIds() const {
   std::map<size_t, std::string> package_map;
-  const android::ResTable& table = assets_.getResources(false);
-  const size_t package_count = table.getBasePackageCount();
-  for (size_t i = 0; i < package_count; i++) {
-    package_map[table.getBasePackageId(i)] =
-        util::Utf16ToUtf8(android::StringPiece16(table.getBasePackageName(i).string()));
-  }
+  asset_manager_.ForEachPackage([&package_map](const std::string& name, uint8_t id) -> bool {
+    package_map.insert(std::make_pair(id, name));
+    return true;
+  });
+
   return package_map;
 }
 
 bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const {
-  return assets_.getResources(false).isPackageDynamic(packageId);
+  if (packageId == 0) {
+    return true;
+  }
+
+  for (const std::unique_ptr<const ApkAssets>& assets : apk_assets_) {
+    for (const std::unique_ptr<const android::LoadedPackage>& loaded_package
+         : assets->GetLoadedArsc()->GetPackages()) {
+      if (packageId == loaded_package->GetPackageId() && loaded_package->IsDynamic()) {
+        return true;
+      }
+    }
+  }
+
+  return false;
 }
 
 static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable(
-    const android::ResTable& table, ResourceId id) {
-  // Try as a bag.
-  const android::ResTable::bag_entry* entry;
-  ssize_t count = table.lockBag(id.id, &entry);
-  if (count < 0) {
-    table.unlockBag(entry);
+    android::AssetManager2& am, ResourceId id) {
+  if (am.GetApkAssets().empty()) {
+    return {};
+  }
+
+  const android::ResolvedBag* bag = am.GetBag(id.id);
+  if (bag == nullptr) {
     return nullptr;
   }
 
   // We found a resource.
   std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>(id);
 
-  // Check to see if it is an attribute.
-  for (size_t i = 0; i < (size_t)count; i++) {
-    if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
-      s->attribute = std::make_shared<Attribute>(entry[i].map.value.data);
+  const size_t count = bag->entry_count;
+  for (uint32_t i = 0; i < count; i++) {
+    if (bag->entries[i].key == android::ResTable_map::ATTR_TYPE) {
+      s->attribute = std::make_shared<Attribute>(bag->entries[i].value.data);
       break;
     }
   }
 
   if (s->attribute) {
-    for (size_t i = 0; i < (size_t)count; i++) {
-      const android::ResTable_map& map_entry = entry[i].map;
-      if (Res_INTERNALID(map_entry.name.ident)) {
-        switch (map_entry.name.ident) {
+    for (size_t i = 0; i < count; i++) {
+      const android::ResolvedBag::Entry& map_entry = bag->entries[i];
+      if (Res_INTERNALID(map_entry.key)) {
+        switch (map_entry.key) {
           case android::ResTable_map::ATTR_MIN:
             s->attribute->min_int = static_cast<int32_t>(map_entry.value.data);
             break;
@@ -269,74 +296,65 @@
         continue;
       }
 
-      android::ResTable::resource_name entry_name;
-      if (!table.getResourceName(map_entry.name.ident, false, &entry_name)) {
-        table.unlockBag(entry);
+      android::AssetManager2::ResourceName name;
+      if (!am.GetResourceName(map_entry.key, &name)) {
         return nullptr;
       }
 
-      Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(entry_name);
+      Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(name);
       if (!parsed_name) {
         return nullptr;
       }
 
       Attribute::Symbol symbol;
       symbol.symbol.name = parsed_name.value();
-      symbol.symbol.id = ResourceId(map_entry.name.ident);
+      symbol.symbol.id = ResourceId(map_entry.key);
       symbol.value = map_entry.value.data;
       s->attribute->symbols.push_back(std::move(symbol));
     }
   }
-  table.unlockBag(entry);
+
   return s;
 }
 
 std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
     const ResourceName& name) {
-  const android::ResTable& table = assets_.getResources(false);
+  const std::string mangled_entry = NameMangler::MangleEntry(name.package, name.entry);
 
-  const std::u16string package16 = util::Utf8ToUtf16(name.package);
-  const std::u16string type16 = util::Utf8ToUtf16(to_string(name.type));
-  const std::u16string entry16 = util::Utf8ToUtf16(name.entry);
-  const std::u16string mangled_entry16 =
-      util::Utf8ToUtf16(NameMangler::MangleEntry(name.package, name.entry));
-
+  bool found = false;
+  ResourceId res_id = 0;
   uint32_t type_spec_flags;
-  ResourceId res_id;
 
   // There can be mangled resources embedded within other packages. Here we will
   // look into each package and look-up the mangled name until we find the resource.
-  const size_t count = table.getBasePackageCount();
-  for (size_t i = 0; i < count; i++) {
-    const android::String16 package_name = table.getBasePackageName(i);
-    StringPiece16 real_package16 = package16;
-    StringPiece16 real_entry16 = entry16;
-    std::u16string scratch_entry16;
-    if (StringPiece16(package_name) != package16) {
-      real_entry16 = mangled_entry16;
-      real_package16 = package_name.string();
+  asset_manager_.ForEachPackage([&](const std::string& package_name, uint8_t id) -> bool {
+    ResourceName real_name(name.package, name.type, name.entry);
+
+    if (package_name != name.package) {
+      real_name.entry = mangled_entry;
+      real_name.package = package_name;
     }
 
-    type_spec_flags = 0;
-    res_id = table.identifierForName(real_entry16.data(), real_entry16.size(), type16.data(),
-                                     type16.size(), real_package16.data(), real_package16.size(),
-                                     &type_spec_flags);
-    if (res_id.is_valid()) {
-      break;
+    res_id = asset_manager_.GetResourceId(real_name.to_string());
+    if (res_id.is_valid() && asset_manager_.GetResourceFlags(res_id.id, &type_spec_flags)) {
+      found = true;
+      return false;
     }
-  }
 
-  if (!res_id.is_valid()) {
+    return true;
+  });
+
+  if (!found) {
     return {};
   }
 
   std::unique_ptr<SymbolTable::Symbol> s;
   if (name.type == ResourceType::kAttr) {
-    s = LookupAttributeInTable(table, res_id);
+    s = LookupAttributeInTable(asset_manager_, res_id);
   } else {
     s = util::make_unique<SymbolTable::Symbol>();
     s->id = res_id;
-    s->is_dynamic = table.isResourceDynamic(res_id.id);
+    s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id());
   }
 
   if (s) {
@@ -346,13 +364,13 @@
   return {};
 }
 
-static Maybe<ResourceName> GetResourceName(const android::ResTable& table,
+static Maybe<ResourceName> GetResourceName(android::AssetManager2& am,
                                            ResourceId id) {
-  android::ResTable::resource_name res_name = {};
-  if (!table.getResourceName(id.id, true, &res_name)) {
+  android::AssetManager2::ResourceName name;
+  if (!am.GetResourceName(id.id, &name)) {
     return {};
   }
-  return ResourceUtils::ToResourceName(res_name);
+  return ResourceUtils::ToResourceName(name);
 }
 
 std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
@@ -361,22 +379,30 @@
     // Exit early and avoid the error logs from AssetManager.
     return {};
   }
-  const android::ResTable& table = assets_.getResources(false);
-  Maybe<ResourceName> maybe_name = GetResourceName(table, id);
+
+  if (apk_assets_.empty()) {
+    return {};
+  }
+
+  Maybe<ResourceName> maybe_name = GetResourceName(asset_manager_, id);
   if (!maybe_name) {
     return {};
   }
 
-  uint32_t type_spec_flags = 0;
-  table.getResourceFlags(id.id, &type_spec_flags);
 
+  uint32_t type_spec_flags = 0;
+  if (!asset_manager_.GetResourceFlags(id.id, &type_spec_flags)) {
+    return {};
+  }
+
+  ResourceName& name = maybe_name.value();
   std::unique_ptr<SymbolTable::Symbol> s;
-  if (maybe_name.value().type == ResourceType::kAttr) {
-    s = LookupAttributeInTable(table, id);
+  if (name.type == ResourceType::kAttr) {
+    s = LookupAttributeInTable(asset_manager_, id);
   } else {
     s = util::make_unique<SymbolTable::Symbol>();
     s->id = id;
-    s->is_dynamic = table.isResourceDynamic(id.id);
+    s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id());
   }
 
   if (s) {
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 2d8bd02..6997cd6 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -22,7 +22,8 @@
 #include <vector>
 
 #include "android-base/macros.h"
-#include "androidfw/AssetManager.h"
+#include "androidfw/Asset.h"
+#include "androidfw/AssetManager2.h"
 #include "utils/JenkinsHash.h"
 #include "utils/LruCache.h"
 
@@ -201,12 +202,13 @@
   std::unique_ptr<SymbolTable::Symbol> FindByReference(
       const Reference& ref) override;
 
-  android::AssetManager* GetAssetManager() {
-    return &assets_;
+  android::AssetManager2* GetAssetManager() {
+    return &asset_manager_;
   }
 
  private:
-  android::AssetManager assets_;
+  android::AssetManager2 asset_manager_;
+  std::vector<std::unique_ptr<const android::ApkAssets>> apk_assets_;
 
   DISALLOW_COPY_AND_ASSIGN(AssetManagerSymbolSource);
 };
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index 1f59d70..ddc2101 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -76,40 +76,54 @@
   EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull());
 }
 
-TEST(SymbolTableTest, FindByNameWhenSymbolIsMangledInResTable) {
+using SymbolTableTestFixture = CommandTestFixture;
+TEST_F(SymbolTableTestFixture, FindByNameWhenSymbolIsMangledInResTable) {
   using namespace android;
+  StdErrDiagnostics diag;
 
-  std::unique_ptr<IAaptContext> context =
-      test::ContextBuilder()
-          .SetCompilationPackage("com.android.app")
-          .SetPackageId(0x7f)
-          .SetPackageType(PackageType::kApp)
-          .SetMinSdkVersion(SDK_LOLLIPOP_MR1)
-          .SetNameManglerPolicy(NameManglerPolicy{"com.android.app"})
-          .Build();
+  // Create a static library.
+  const std::string static_lib_compiled_files_dir = GetTestPath("static-lib-compiled");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+         R"(<?xml version="1.0" encoding="utf-8"?>
+         <resources>
+             <item type="id" name="foo"/>
+        </resources>)",
+         static_lib_compiled_files_dir, &diag));
 
-  // Create a ResourceTable with a mangled resource, simulating a static library being merged into
-  // the main application package.
-  std::unique_ptr<ResourceTable> table =
-      test::ResourceTableBuilder()
-          .AddSimple("com.android.app:id/" + NameMangler::MangleEntry("com.android.lib", "foo"),
-                     ResourceId(0x7f020000))
-          .AddSimple("com.android.app:id/bar", ResourceId(0x7f020001))
-          .Build();
+  const std::string static_lib_apk = GetTestPath("static_lib.apk");
+  std::vector<std::string> link_args = {
+      "--manifest", GetDefaultManifest("com.android.lib"),
+      "--min-sdk-version", "22",
+      "--static-lib",
+      "-o", static_lib_apk,
+  };
+  ASSERT_TRUE(Link(link_args, static_lib_compiled_files_dir, &diag));
 
-  BigBuffer buffer(1024u);
-  TableFlattener flattener({}, &buffer);
-  ASSERT_TRUE(flattener.Consume(context.get(), table.get()));
+  // Merge the static library into the main application package. The static library resources will
+  // be mangled with the library package name.
+  const std::string app_compiled_files_dir = GetTestPath("app-compiled");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+      R"(<?xml version="1.0" encoding="utf-8"?>
+         <resources>
+             <item type="id" name="bar"/>
+        </resources>)",
+        app_compiled_files_dir, &diag));
 
-  std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
+  const std::string out_apk = GetTestPath("out.apk");
+  link_args = {
+      "--manifest", GetDefaultManifest("com.android.app"),
+      "--min-sdk-version", "22",
+      "-o", out_apk,
+      static_lib_apk
+  };
+  ASSERT_TRUE(Link(link_args, app_compiled_files_dir, &diag));
 
   // Construct the test AssetManager.
   auto asset_manager_source = util::make_unique<AssetManagerSymbolSource>();
-  ResTable& res_table = const_cast<ResTable&>(
-      asset_manager_source->GetAssetManager()->getResources(false /*required*/));
-  ASSERT_THAT(res_table.add(data.get(), buffer.size()), Eq(NO_ERROR));
+  asset_manager_source->AddAssetPath(out_apk);
 
-  SymbolTable symbol_table(context->GetNameMangler());
+  NameMangler name_mangler(NameManglerPolicy{"com.android.app"});
+  SymbolTable symbol_table(&name_mangler);
   symbol_table.AppendSource(std::move(asset_manager_source));
 
   EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull());
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index 3fcdfb7..a51b4a4 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -37,6 +37,8 @@
 
 namespace aapt {
 
+const char* CommandTestFixture::kDefaultPackageName = "com.aapt.command.test";
+
 void ClearDirectory(const android::StringPiece& path) {
   const std::string root_dir = path.to_string();
   std::unique_ptr<DIR, decltype(closedir)*> dir(opendir(root_dir.data()), closedir);
@@ -124,12 +126,12 @@
   return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
 }
 
-std::string CommandTestFixture::GetDefaultManifest() {
+std::string CommandTestFixture::GetDefaultManifest(const char* package_name) {
   const std::string manifest_file = GetTestPath("AndroidManifest.xml");
-  CHECK(WriteFile(manifest_file, R"(
+  CHECK(WriteFile(manifest_file, android::base::StringPrintf(R"(
       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.aapt.command.test">
-      </manifest>)"));
+          package="%s">
+      </manifest>)", package_name)));
   return manifest_file;
 }
 
diff --git a/tools/aapt2/test/Fixture.h b/tools/aapt2/test/Fixture.h
index 3079c75..fce2aeb 100644
--- a/tools/aapt2/test/Fixture.h
+++ b/tools/aapt2/test/Fixture.h
@@ -81,7 +81,7 @@
             IDiagnostics* diag);
 
   // Creates a minimal android manifest within the test directory and returns the file path.
-  std::string GetDefaultManifest();
+  std::string GetDefaultManifest(const char* package_name = kDefaultPackageName);
 
   // Returns pointer to data inside APK files
   std::unique_ptr<io::IData> OpenFileAsData(LoadedApk* apk,
@@ -91,6 +91,7 @@
   void AssertLoadXml(LoadedApk* apk, const io::IData* data,
                      android::ResXMLTree* out_tree);
 
+  static const char* kDefaultPackageName;
  private:
   DISALLOW_COPY_AND_ASSIGN(CommandTestFixture);
 };
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 604b257..5d57de6 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -102,12 +102,21 @@
 #endif
 
 bool mkdirs(const std::string& path) {
-  #ifdef _WIN32
-  // Start after the drive path if present. Calling mkdir with only the drive will cause an error.
-  size_t current_pos = 1u;
-  if (path.size() >= 3 && path[1] == ':' &&
-        (path[2] == '\\' || path[2] == '/')) {
-    current_pos = 3u;
+ #ifdef _WIN32
+  // Start after the long path prefix if present.
+  bool require_drive = false;
+  size_t current_pos = 0u;
+  if (util::StartsWith(path, R"(\\?\)")) {
+    require_drive = true;
+    current_pos = 4u;
+  }
+
+  // Start after the drive path if present.
+  if (path.size() >= 3 && path[current_pos + 1] == ':' &&
+       (path[current_pos + 2] == '\\' || path[current_pos + 2] == '/')) {
+    current_pos += 3u;
+  } else if (require_drive) {
+    return false;
   }
  #else
   // Start after the first character so that we don't consume the root '/'.
diff --git a/tools/aapt2/util/Files_test.cpp b/tools/aapt2/util/Files_test.cpp
index 202cc26..6c38080 100644
--- a/tools/aapt2/util/Files_test.cpp
+++ b/tools/aapt2/util/Files_test.cpp
@@ -19,6 +19,7 @@
 #include <sstream>
 
 #include "android-base/stringprintf.h"
+#include "android-base/utf8.h"
 
 #include "test/Test.h"
 
@@ -65,5 +66,40 @@
   EXPECT_EQ(expected_path_, base);
 }
 
+#ifdef _WIN32
+TEST_F(FilesTest, WindowsMkdirsLongPath) {
+  // Creating directory paths longer than the Windows maximum path length (260 charatcers) should
+  // succeed.
+  const std::string kDirName = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+  const size_t kRecursiveDepth = 10u;
+
+  // Recursively create the test file path and clean up the created directories after the files have
+  // been created.
+  std::function<void(std::string, size_t)> CreateResursiveDirs =
+      [&kDirName, &CreateResursiveDirs](std::string current_path, const size_t n) -> void {
+    AppendPath(&current_path, kDirName);
+
+    if (n == 0) {
+      ASSERT_TRUE(file::mkdirs(current_path)) << "Failed to create path " << current_path;
+    } else {
+      CreateResursiveDirs(current_path, n - 1);
+    }
+
+    // Clean up the created directories.
+    _rmdir(current_path.data());
+  };
+
+  CreateResursiveDirs(
+      android::base::StringPrintf(R"(\\?\%s)", android::base::GetExecutableDirectory().data()),
+      kRecursiveDepth);
+}
+
+TEST_F(FilesTest, WindowsMkdirsLongPathMissingDrive) {
+  ASSERT_FALSE(file::mkdirs(R"(\\?\local\path\to\file)"));
+  ASSERT_FALSE(file::mkdirs(R"(\\?\:local\path\to\file)"));
+  ASSERT_FALSE(file::mkdirs(R"(\\?\\local\path\to\file)"));
+}
+#endif
+
 }  // namespace files
 }  // namespace aapt
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index af9fdfb..089b59a 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -137,6 +137,16 @@
     private boolean mOsuAp;
 
     /**
+     * Fully qualified domain name of a Passpoint configuration
+     */
+    private String mFqdn;
+
+    /**
+     * Name of Passpoint credential provider
+     */
+    private String mProviderFriendlyName;
+
+    /**
      * If connected to a network suggestion or specifier, store the package name of the app,
      * else null.
      */
@@ -223,6 +233,8 @@
         setEphemeral(false);
         setOsuAp(false);
         setNetworkSuggestionOrSpecifierPackageName(null);
+        setFQDN(null);
+        setProviderFriendlyName(null);
         txBad = 0;
         txSuccess = 0;
         rxSuccess = 0;
@@ -257,6 +269,8 @@
             mNetworkSuggestionOrSpecifierPackageName =
                     source.mNetworkSuggestionOrSpecifierPackageName;
             mOsuAp = source.mOsuAp;
+            mFqdn = source.mFqdn;
+            mProviderFriendlyName = source.mProviderFriendlyName;
             txBad = source.txBad;
             txRetries = source.txRetries;
             txSuccess = source.txSuccess;
@@ -504,6 +518,34 @@
     }
 
     /** {@hide} */
+    @SystemApi
+    public boolean isPasspointAp() {
+        return mFqdn != null && mProviderFriendlyName != null;
+    }
+
+    /** {@hide} */
+    public void setFQDN(@Nullable String fqdn) {
+        mFqdn = fqdn;
+    }
+
+    /** {@hide} */
+    @SystemApi
+    public @Nullable String getFqdn() {
+        return mFqdn;
+    }
+
+    /** {@hide} */
+    public void setProviderFriendlyName(@Nullable String providerFriendlyName) {
+        mProviderFriendlyName = providerFriendlyName;
+    }
+
+    /** {@hide} */
+    @SystemApi
+    public @Nullable String getProviderFriendlyName() {
+        return mProviderFriendlyName;
+    }
+
+    /** {@hide} */
     public void setNetworkSuggestionOrSpecifierPackageName(@Nullable String packageName) {
         mNetworkSuggestionOrSpecifierPackageName = packageName;
     }
@@ -677,6 +719,8 @@
         mSupplicantState.writeToParcel(dest, flags);
         dest.writeInt(mOsuAp ? 1 : 0);
         dest.writeString(mNetworkSuggestionOrSpecifierPackageName);
+        dest.writeString(mFqdn);
+        dest.writeString(mProviderFriendlyName);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -716,6 +760,8 @@
                 info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in);
                 info.mOsuAp = in.readInt() != 0;
                 info.mNetworkSuggestionOrSpecifierPackageName = in.readString();
+                info.mFqdn = in.readString();
+                info.mProviderFriendlyName = in.readString();
                 return info;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7caace6..6c645fc 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1738,10 +1738,7 @@
      * @deprecated This is no longer supported.
      */
     @Deprecated
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.NETWORK_SETTINGS,
-            android.Manifest.permission.NETWORK_SETUP_WIZARD
-    })
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void removePasspointConfiguration(String fqdn) {
         try {
             if (!mService.removePasspointConfiguration(fqdn, mContext.getOpPackageName())) {
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index f931ad2..479adbc 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -26,6 +26,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.regex.PatternSyntaxException;
 
 /**
  * A class representing a Wi-Fi P2p configuration for setting up a connection
@@ -252,7 +253,12 @@
          * Specify the network name, a.k.a. group name,
          * for creating or joining a group.
          * <p>
-         *     Must be called - an empty network name is not valid.
+         * A network name shall begin with "DIRECT-xy". x and y are selected
+         * from the following character set: upper case letters, lower case
+         * letters and numbers.
+         * <p>
+         *     Must be called - an empty network name or an network name
+         *     not conforming to the P2P Group ID naming rule is not valid.
          *
          * @param networkName network name of a group.
          * @return The builder to facilitate chaining
@@ -263,6 +269,14 @@
                 throw new IllegalArgumentException(
                         "network name must be non-empty.");
             }
+            try {
+                if (!networkName.matches("^DIRECT-[a-zA-Z0-9]{2}.*")) {
+                    throw new IllegalArgumentException(
+                            "network name must starts with the prefix DIRECT-xy.");
+                }
+            } catch (PatternSyntaxException e) {
+                // can never happen (fixed pattern)
+            }
             mNetworkName = networkName;
             return this;
         }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index 22dc2ed..ab7bb68 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -17,14 +17,13 @@
 package android.net.wifi.p2p;
 
 import android.annotation.UnsupportedAppUsage;
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.util.Log;
 
 import java.util.Objects;
-
-import java.util.regex.Pattern;
 import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * A class representing a Wi-Fi p2p device
@@ -360,7 +359,9 @@
             deviceCapability = source.deviceCapability;
             groupCapability = source.groupCapability;
             status = source.status;
-            wfdInfo = new WifiP2pWfdInfo(source.wfdInfo);
+            if (source.wfdInfo != null) {
+                wfdInfo = new WifiP2pWfdInfo(source.wfdInfo);
+            }
         }
     }
 
diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
index 948dcfa..b303496 100644
--- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
@@ -36,6 +36,8 @@
     private static final long TEST_TX_BAD = 3;
     private static final long TEST_RX_SUCCESS = 4;
     private static final String TEST_PACKAGE_NAME = "com.test.example";
+    private static final String TEST_FQDN = "test.com";
+    private static final String TEST_PROVIDER_NAME = "test";
 
     /**
      *  Verify parcel write/read with WifiInfo.
@@ -49,6 +51,8 @@
         writeWifiInfo.rxSuccess = TEST_RX_SUCCESS;
         writeWifiInfo.setTrusted(true);
         writeWifiInfo.setOsuAp(true);
+        writeWifiInfo.setFQDN(TEST_FQDN);
+        writeWifiInfo.setProviderFriendlyName(TEST_PROVIDER_NAME);
         writeWifiInfo.setNetworkSuggestionOrSpecifierPackageName(TEST_PACKAGE_NAME);
 
         Parcel parcel = Parcel.obtain();
@@ -64,6 +68,9 @@
         assertEquals(TEST_RX_SUCCESS, readWifiInfo.rxSuccess);
         assertTrue(readWifiInfo.isTrusted());
         assertTrue(readWifiInfo.isOsuAp());
+        assertTrue(readWifiInfo.isPasspointAp());
         assertEquals(TEST_PACKAGE_NAME, readWifiInfo.getNetworkSuggestionOrSpecifierPackageName());
+        assertEquals(TEST_FQDN, readWifiInfo.getFqdn());
+        assertEquals(TEST_PROVIDER_NAME, readWifiInfo.getProviderFriendlyName());
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java
new file mode 100644
index 0000000..560c88e
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.net.wifi.p2p;
+
+import static org.junit.Assert.fail;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit test harness for {@link android.net.wifi.p2p.WifiP2pConfig}
+ */
+@SmallTest
+public class WifiP2pConfigTest {
+    /**
+     * Check network name setter
+     */
+    @Test
+    public void testBuilderInvalidNetworkName() throws Exception {
+        WifiP2pConfig.Builder b = new WifiP2pConfig.Builder();
+
+        // sunny case
+        try {
+            b.setNetworkName("DIRECT-ab-Hello");
+        } catch (IllegalArgumentException e) {
+            fail("Unexpected IllegalArgumentException");
+        }
+
+        // sunny case, no trailing string
+        try {
+            b.setNetworkName("DIRECT-WR");
+        } catch (IllegalArgumentException e) {
+            fail("Unexpected IllegalArgumentException");
+        }
+
+        // less than 9 characters.
+        try {
+            b.setNetworkName("DIRECT-z");
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) { }
+
+        // not starts with DIRECT-xy.
+        try {
+            b.setNetworkName("ABCDEFGHIJK");
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) { }
+
+        // not starts with uppercase DIRECT-xy
+        try {
+            b.setNetworkName("direct-ab");
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) { }
+
+        // x and y are not selected from upper case letters, lower case letters or
+        // numbers.
+        try {
+            b.setNetworkName("direct-a?");
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) { }
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
index f61e6b7..17ee755 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
@@ -30,6 +30,31 @@
 public class WifiP2pDeviceTest {
 
     /**
+     * Compare two p2p devices.
+     *
+     * @param devA is the first device to be compared
+     * @param devB is the second device to be compared
+     */
+    private void compareWifiP2pDevices(WifiP2pDevice devA, WifiP2pDevice devB) {
+        assertEquals(devA.deviceName, devB.deviceName);
+        assertEquals(devA.deviceAddress, devB.deviceAddress);
+        assertEquals(devA.primaryDeviceType, devB.primaryDeviceType);
+        assertEquals(devA.secondaryDeviceType, devB.secondaryDeviceType);
+        assertEquals(devA.wpsConfigMethodsSupported, devB.wpsConfigMethodsSupported);
+        assertEquals(devA.deviceCapability, devB.deviceCapability);
+        assertEquals(devA.groupCapability, devB.groupCapability);
+        assertEquals(devA.status, devB.status);
+        if (devA.wfdInfo != null) {
+            assertEquals(devA.wfdInfo.isWfdEnabled(), devB.wfdInfo.isWfdEnabled());
+            assertEquals(devA.wfdInfo.getDeviceInfoHex(), devB.wfdInfo.getDeviceInfoHex());
+            assertEquals(devA.wfdInfo.getControlPort(), devB.wfdInfo.getControlPort());
+            assertEquals(devA.wfdInfo.getMaxThroughput(), devB.wfdInfo.getMaxThroughput());
+        } else {
+            assertEquals(devA.wfdInfo, devB.wfdInfo);
+        }
+    }
+
+    /**
      * Check equals and hashCode consistency
      */
     @Test
@@ -42,4 +67,52 @@
         assertTrue(dev_a.equals(dev_b));
         assertEquals(dev_a.hashCode(), dev_b.hashCode());
     }
+
+    /**
+     * Check the copy constructor with default values.
+     */
+    @Test
+    public void testCopyConstructorWithDefaultValues() throws Exception {
+        WifiP2pDevice device = new WifiP2pDevice();
+        WifiP2pDevice copy = new WifiP2pDevice(device);
+        compareWifiP2pDevices(device, copy);
+    }
+
+    /**
+     * Check the copy constructor with updated values.
+     */
+    @Test
+    public void testCopyConstructorWithUpdatedValues() throws Exception {
+        WifiP2pDevice device = new WifiP2pDevice();
+        device.deviceName = "deviceName";
+        device.deviceAddress = "11:22:33:44:55:66";
+        device.primaryDeviceType = "primaryDeviceType";
+        device.secondaryDeviceType = "secondaryDeviceType";
+        device.wpsConfigMethodsSupported = 0x0008;
+        device.deviceCapability = 1;
+        device.groupCapability = 1;
+        device.status = WifiP2pDevice.CONNECTED;
+        device.wfdInfo = new WifiP2pWfdInfo();
+        WifiP2pDevice copy = new WifiP2pDevice(device);
+        compareWifiP2pDevices(device, copy);
+    }
+
+    /**
+     * Check the copy constructor when the wfdInfo of the source object is null.
+     */
+    @Test
+    public void testCopyConstructorWithNullWfdInfo() throws Exception {
+        WifiP2pDevice device = new WifiP2pDevice();
+        device.deviceName = "deviceName";
+        device.deviceAddress = "11:22:33:44:55:66";
+        device.primaryDeviceType = "primaryDeviceType";
+        device.secondaryDeviceType = "secondaryDeviceType";
+        device.wpsConfigMethodsSupported = 0x0008;
+        device.deviceCapability = 1;
+        device.groupCapability = 1;
+        device.status = WifiP2pDevice.CONNECTED;
+        device.wfdInfo = null;
+        WifiP2pDevice copy = new WifiP2pDevice(device);
+        compareWifiP2pDevices(device, copy);
+    }
 }