Merge "Only rotate seamlessly if the app window fills its parent"
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 327b65b..fce28d9 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 {
@@ -11375,6 +11375,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 +11457,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 +11476,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 +11664,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";
@@ -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();
@@ -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";
@@ -43339,11 +43343,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 +43362,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);
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index c245b6b..b580deb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1305,8 +1305,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 {
@@ -1558,6 +1558,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();
@@ -1576,6 +1577,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);
@@ -1591,8 +1593,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);
@@ -1602,6 +1604,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);
@@ -3037,21 +3040,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);
   }
@@ -3066,7 +3069,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);
@@ -3081,14 +3084,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
@@ -3097,17 +3100,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 {
@@ -4665,7 +4668,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 {
diff --git a/api/test-current.txt b/api/test-current.txt
index 1981d68..ba497d4 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();
   }
@@ -518,12 +548,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 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 {
@@ -969,6 +1004,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 {
@@ -1313,6 +1392,7 @@
 
   public class Build {
     method public static boolean is64BitAbi(String);
+    field public static final boolean IS_EMULATOR;
   }
 
   public static class Build.VERSION {
@@ -1893,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";
   }
@@ -1911,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";
@@ -2391,6 +2473,10 @@
     method public E valueAtUnchecked(int);
   }
 
+  public class TimeUtils {
+    method public static String formatDuration(long);
+  }
+
 }
 
 package android.util.proto {
@@ -2612,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);
   }
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/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/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 7f7972a..aaff76e 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
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a63350c..91fc188 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2387,8 +2387,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;
     }
 
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/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 8e5fc30..d3e3507 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2469,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 b792ad2..b607f9a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
@@ -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,
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/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 1f3a000..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();
 
     /**
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index fac3266..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();
     }
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/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index b0d16cd..f87ce82 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.
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/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/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/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 47b1eef..650d217 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/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index cd7bbfd..9d58064 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -519,7 +519,7 @@
         }
 
         @Override
-        public void scheduleRequest(@NonNull PendingRequest<RemoteService,
+        public void scheduleRequest(@NonNull BasePendingRequest<RemoteService,
                 IPermissionController> pendingRequest) {
             super.scheduleRequest(pendingRequest);
         }
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..868a36b 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.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 77e896e..1588718 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 =
@@ -10780,6 +10796,7 @@
         *
         * @hide
         */
+       @TestApi
        public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices";
 
         /**
@@ -13045,6 +13062,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>
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/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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 89c6703..be6b56c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6996,7 +6996,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;
         }
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/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/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/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..e628f19 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -240,9 +240,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 +401,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.
@@ -456,10 +453,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,15 +583,10 @@
             }
         }
 
-        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,
@@ -626,16 +617,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 +646,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 +666,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,6 +726,19 @@
     }
 
     /**
+     * Returns the locale string for the current resources configuration.
+     */
+    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();
+        }
+    }
+
+    /**
      * Helper class to store the information from which RemoteActions are built.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
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/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..732553b 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -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/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/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 15ceca9..73e6789 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -572,214 +572,246 @@
 }
 
 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,
+                                   const std::vector<std::string>& volume_labels,
+                                   bool mount_all_obbs, const std::string& sandbox_id,
+                                   userid_t user_id, uid_t uid, fail_fn_t fail_fn) {
+  for (auto& label : volume_labels) {
+    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
@@ -789,126 +821,97 @@
         const std::vector<std::string>& packages_for_uid,
         const std::vector<std::string>& visible_vol_ids, 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 pkgSandboxDir = StringPrintf("/mnt/user/%d/package", user_id);
+      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)));
+      }
+
+      HandleMountModeInstaller(mount_mode, user_id, sandbox_id, fail_fn);
+
+      PreparePkgSpecificDirs(packages_for_uid, visible_vol_ids,
+          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 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()));
+    }
+
+    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)));
+    }
+  }
 }
 
 static bool NeedsNoRandomizeWorkaround() {
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 5497b86..f74fc21 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -1622,6 +1622,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;
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6571cd2..eb69535 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2673,11 +2673,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>
 
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..8df0e19 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" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 3a1a4fc..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,
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/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/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/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/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/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..445edd1 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);
         }
     }
 
@@ -417,24 +445,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) {
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/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/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/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/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_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/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/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/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/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..b635033 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -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,8 +180,11 @@
         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);
 
@@ -195,6 +206,8 @@
 
         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 +239,15 @@
             activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
             return view.onApplyWindowInsets(insets);
         });
+
+        if (!mShowOnTop) {
+            removeView(mPointerView);
+            if (mUseFooter) {
+                removeView(viewWrapper);
+                addView(viewWrapper);
+            }
+            addView(mPointerView);
+        }
     }
 
     /**
@@ -332,7 +354,11 @@
 
             // Use notification view
             mNotifRow = mEntry.getRow();
-            addView(mNotifRow);
+            if (mShowOnTop) {
+                addView(mNotifRow);
+            } else {
+                addView(mNotifRow, mUseFooter ? 0 : 1);
+            }
         }
         updateView();
     }
@@ -345,6 +371,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 +395,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 +456,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);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index e20be8e..167bf47 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(
@@ -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/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 7312cbc..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;
@@ -70,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;
@@ -96,8 +94,6 @@
 
     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);
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/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/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/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 4a4e247..0aed63d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -31,7 +31,6 @@
 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;
@@ -76,7 +75,6 @@
     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;
@@ -90,10 +88,8 @@
         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();
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/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 4584306..b3e5f69 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
@@ -7007,6 +7013,10 @@
     // 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;
+
     // ---- End Q Constants, all Q constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
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/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 364e537..2e99654 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -1085,6 +1085,7 @@
                             if (remoteService != null) {
                                 remoteService.destroy();
                             }
+                            mRemoteAugmentedAutofillService = null;
                         }
                     }, mMaster.isInstantServiceAllowed(), mMaster.verbose);
         }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index de9896e..f9fda64 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,8 +37,6 @@
         extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService,
         IContentCaptureService> {
 
-    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
-
     private final IBinder mServerCallback;
 
     RemoteContentCaptureService(Context context, String serviceInterface,
@@ -65,12 +62,6 @@
         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;
-    }
-
     @Override // from RemoteService
     protected void handleOnConnectedStateChanged(boolean state) {
         if (state && getTimeoutIdleBindMillis() != PERMANENT_BOUND_TIMEOUT_MS) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 915c131..ed459db 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);
         }
     }
 
@@ -2641,11 +2642,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 +2802,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 +2975,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 +3002,7 @@
             int callingUid) {
         NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
         if (nri != null) {
-            handleReleaseNetworkRequest(nri.request, callingUid);
+            handleReleaseNetworkRequest(nri.request, callingUid, /* callOnUnavailable */ false);
         }
     }
 
@@ -3066,7 +3085,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 +3096,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 +3530,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/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index cd9d84c..a5eab85 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2811,6 +2811,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()) {
@@ -4056,6 +4057,11 @@
 
         @Override
         public String[] getVisibleVolumesForUser(int userId) {
+            synchronized (mLock) {
+                if (!ArrayUtils.contains(mSystemUnlockedUsers, userId)) {
+                    return EmptyArray.STRING;
+                }
+            }
             final ArrayList<String> visibleVolsForUser = new ArrayList<>();
             for (int i = mVisibleVols.size() - 1; i >= 0; --i) {
                 final VolumeInfo vol = mVisibleVols.get(i);
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/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/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/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 924018e..aa85c83 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -407,6 +407,10 @@
             return new SyntheticPasswordManager(getContext(), storage, getUserManager());
         }
 
+        public boolean hasBiometrics() {
+            return BiometricManager.hasBiometrics(mContext);
+        }
+
         public int binderGetCallingUid() {
             return Binder.getCallingUid();
         }
@@ -2434,7 +2438,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/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index dc990af..4dd58e0 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -30,6 +30,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,7 +49,9 @@
 class ApexManager {
     static final String TAG = "ApexManager";
     private final IApexService mApexService;
-    private final Map<String, PackageInfo> mActivePackagesCache;
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private Map<String, PackageInfo> mActivePackagesCache;
 
     ApexManager() {
         try {
@@ -57,31 +60,35 @@
         } catch (ServiceNotFoundException e) {
             throw new IllegalStateException("Required service apexservice not available");
         }
-        mActivePackagesCache = populateActivePackagesCache();
     }
 
-    @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);
-                }
+    private void populateActivePackagesCacheIfNeeded() {
+        synchronized (mLock) {
+            if (mActivePackagesCache != null) {
+                return;
             }
-            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);
+            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 +103,7 @@
      *         is not found.
      */
     @Nullable PackageInfo getActivePackage(String packageName) {
+        populateActivePackagesCacheIfNeeded();
         return mActivePackagesCache.get(packageName);
     }
 
@@ -106,6 +114,7 @@
      *         active package.
      */
     Collection<PackageInfo> getActivePackages() {
+        populateActivePackagesCacheIfNeeded();
         return mActivePackagesCache.values();
     }
 
@@ -205,6 +214,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 +241,7 @@
         ipw.println("Active APEX packages:");
         ipw.increaseIndent();
         try {
-            populateActivePackagesCache();
+            populateActivePackagesCacheIfNeeded();
             for (PackageInfo pi : mActivePackagesCache.values()) {
                 if (packageName != null && !packageName.equals(pi.packageName)) {
                     continue;
@@ -254,4 +278,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..cced0f4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -209,8 +209,10 @@
         mStagingManager = new StagingManager(pm, this, am);
     }
 
-    private void setBootCompleted()  {
+    private void onBootCompleted()  {
         mBootCompleted = true;
+        // Tell APEX manager about it as well
+        mApexManager.onBootCompleted();
     }
 
     boolean isBootCompleted()  {
@@ -223,7 +225,7 @@
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                setBootCompleted();
+                onBootCompleted();
                 mContext.unregisterReceiver(this);
             }
         }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
@@ -483,11 +485,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 +824,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);
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 1b71904..7ed49fc 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);
+
+                //TODO(b/123624108): delete staging dir
+            }
+
             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;
@@ -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);
     }
 
@@ -2064,6 +2107,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 c3b84eb..e7e3e83 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;
@@ -12767,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);
@@ -12846,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);
@@ -12855,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
@@ -12951,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);
         }
@@ -13811,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));
             }
         });
     }
@@ -17125,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
@@ -20032,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());
@@ -20188,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) {
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/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 9dfd477..e956b74 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -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,32 @@
 
     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;
+    }
+
     @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/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c87a81d..0759419 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;
     }
 
@@ -3276,7 +3280,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 +3549,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 +4042,7 @@
         }
 
         if (useHapticFeedback) {
-            performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false,
+            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
                     "Virtual Key - Press");
         }
 
@@ -4759,7 +4763,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 +5044,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 +5251,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 +5273,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 +5415,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 +5649,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..5cd0014 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
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index c14f126..d2c0ee3 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -528,7 +528,7 @@
                 PackageInstaller.SessionInfo session = installer.getSessionInfo(
                         data.stagedSessionId);
                 if (session != null) {
-                    if (session.isSessionApplied()) {
+                    if (session.isStagedSessionApplied()) {
                         synchronized (mLock) {
                             data.isAvailable = true;
                         }
@@ -538,7 +538,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
@@ -966,36 +966,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 +1195,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/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/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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d4430da4..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;
@@ -3519,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);
         }
     }
 
@@ -7419,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/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index d178c3a..fe94168 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -2248,7 +2248,7 @@
             measCorrClass, "getToaGpsNanosecondsOfWeek", "()J");
 
         method_correctionsGetSingleSatCorrectionList = env->GetMethodID(
-            measCorrClass, "getSingleSatCorrectionList", "()Ljava.util.List;");
+            measCorrClass, "getSingleSatelliteCorrectionList", "()Ljava.util.List;");
     }
 
     jdouble latitudeDegreesCorr = env->CallDoubleMethod(
@@ -2285,15 +2285,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(
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 74d4739..bf71318 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/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/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/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index b1b8e8c..69f7ced 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -621,7 +621,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/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/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/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/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/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index 097d33d..def5b8e 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -345,7 +345,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 +365,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/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/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/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/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);
+    }
 }