Merge "Add missing Looper::setForThread"
diff --git a/.gitignore b/.gitignore
index 45884c4..d7aebc6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 /.idea
 *.iml
+*.sw*
diff --git a/Android.bp b/Android.bp
index 183121b..072007b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -209,6 +209,7 @@
         "core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl",
         "core/java/android/hardware/usb/IUsbManager.aidl",
         "core/java/android/hardware/usb/IUsbSerialReader.aidl",
+        "core/java/android/net/ICaptivePortal.aidl",
         "core/java/android/net/IConnectivityManager.aidl",
         "core/java/android/hardware/ISensorPrivacyListener.aidl",
         "core/java/android/hardware/ISensorPrivacyManager.aidl",
@@ -773,7 +774,7 @@
         "android.hardware.vibrator-V1.3-java",
         "android.hardware.wifi-V1.0-java-constants",
         "networkstack-aidl-interfaces-java",
-        "netd_aidl_interface-java",
+        "netd_aidl_parcelables-java",
         "devicepolicyprotosnano",
     ],
 
@@ -893,7 +894,6 @@
     srcs: [
         "core/java/android/net/ApfCapabilitiesParcelable.aidl",
         "core/java/android/net/DhcpResultsParcelable.aidl",
-        "core/java/android/net/ICaptivePortal.aidl",
         "core/java/android/net/INetworkMonitor.aidl",
         "core/java/android/net/INetworkMonitorCallbacks.aidl",
         "core/java/android/net/IIpMemoryStore.aidl",
@@ -1187,6 +1187,15 @@
     "org/apache/http/params",
 ]
 
+// Make the api/current.txt file available for use by modules in other
+// directories.
+filegroup {
+    name: "frameworks-base-api-current.txt",
+    srcs: [
+        "api/current.txt",
+    ],
+}
+
 framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " +
      "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " +
      "-overview $(location core/java/overview.html) " +
diff --git a/Android.mk b/Android.mk
index 9a91dd1..c58f7af 100644
--- a/Android.mk
+++ b/Android.mk
@@ -77,8 +77,6 @@
 
 # Run this for checkbuild
 checkbuild: doc-comment-check-docs
-# Check comment when you are updating the API
-update-api: doc-comment-check-docs
 
 # ==== hiddenapi lists =======================================
 ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)
diff --git a/api/current.txt b/api/current.txt
index 4238ca6..15c4fdf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14096,6 +14096,34 @@
     ctor @Deprecated public EmbossMaskFilter(float[], float, float, float);
   }
 
+  public class HardwareRenderer {
+    ctor public HardwareRenderer();
+    method public void clearContent();
+    method public android.graphics.HardwareRenderer.FrameRenderRequest createRenderRequest();
+    method public void destroy();
+    method public boolean isOpaque();
+    method public void notifyFramePending();
+    method public void setContentRoot(@Nullable android.graphics.RenderNode);
+    method public void setLightSourceAlpha(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+    method public void setLightSourceGeometry(float, float, float, float);
+    method public void setName(String);
+    method public void setOpaque(boolean);
+    method public void setStopped(boolean);
+    method public void setSurface(@Nullable android.view.Surface);
+    field public static final int SYNC_CONTEXT_IS_STOPPED = 4; // 0x4
+    field public static final int SYNC_FRAME_DROPPED = 8; // 0x8
+    field public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 2; // 0x2
+    field public static final int SYNC_OK = 0; // 0x0
+    field public static final int SYNC_REDRAW_REQUESTED = 1; // 0x1
+  }
+
+  public final class HardwareRenderer.FrameRenderRequest {
+    method public android.graphics.HardwareRenderer.FrameRenderRequest setFrameCommitCallback(@NonNull java.util.concurrent.Executor, @NonNull Runnable);
+    method public android.graphics.HardwareRenderer.FrameRenderRequest setVsyncTime(long);
+    method public android.graphics.HardwareRenderer.FrameRenderRequest setWaitForPresent(boolean);
+    method public int syncAndDraw();
+  }
+
   public final class ImageDecoder implements java.lang.AutoCloseable {
     method public void close();
     method @AnyThread @NonNull public static android.graphics.ImageDecoder.Source createSource(@NonNull android.content.res.Resources, int);
@@ -27445,6 +27473,7 @@
     method public void seekTo(long);
     method public void sendCustomAction(@NonNull android.media.session.PlaybackState.CustomAction, @Nullable android.os.Bundle);
     method public void sendCustomAction(@NonNull String, @Nullable android.os.Bundle);
+    method public void setPlaybackSpeed(float);
     method public void setRating(android.media.Rating);
     method public void skipToNext();
     method public void skipToPrevious();
@@ -27495,6 +27524,7 @@
     method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
     method public void onRewind();
     method public void onSeekTo(long);
+    method public void onSetPlaybackSpeed(float);
     method public void onSetRating(@NonNull android.media.Rating);
     method public void onSkipToNext();
     method public void onSkipToPrevious();
@@ -35050,7 +35080,7 @@
     method public android.os.PowerManager.WakeLock newWakeLock(int, String);
     method public void reboot(String);
     method public void registerThermalStatusCallback(@NonNull android.os.PowerManager.ThermalStatusCallback, @NonNull java.util.concurrent.Executor);
-    method public void unregisterThermalStatusCallback(android.os.PowerManager.ThermalStatusCallback);
+    method public void unregisterThermalStatusCallback(@NonNull android.os.PowerManager.ThermalStatusCallback);
     field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
     field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
     field public static final String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
@@ -41953,7 +41983,6 @@
 
   public class VoiceInteractionService extends android.app.Service {
     ctor public VoiceInteractionService();
-    method public final void clearTranscription(boolean);
     method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
     method public int getDisabledShowContext();
     method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
@@ -41963,8 +41992,7 @@
     method public void onReady();
     method public void onShutdown();
     method public void setDisabledShowContext(int);
-    method public final void setTranscription(@NonNull String);
-    method public final void setVoiceState(int);
+    method public final void setUiHints(@NonNull android.os.Bundle);
     method public void showSession(android.os.Bundle, int);
     field public static final String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
     field public static final String SERVICE_META_DATA = "android.voice_interaction";
@@ -44395,6 +44423,7 @@
     method public String getMccString();
     method public String getMncString();
     method @Nullable public String getMobileNetworkOperator();
+    method public int getUarfcn();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR;
   }
@@ -45069,12 +45098,12 @@
     method public boolean canChangeDtmfToneLength();
     method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
     method public android.telephony.TelephonyManager createForSubscriptionId(int);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
     method public int getCallState();
     method public int getCardIdForDefaultEuicc();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig();
     method public int getCarrierIdFromSimMccMnc();
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.CellLocation getCellLocation();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.telephony.CellLocation getCellLocation();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList(int);
     method public int getDataActivity();
@@ -45104,7 +45133,7 @@
     method public int getPhoneCount();
     method public int getPhoneType();
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState();
+    method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
     method @Nullable public android.telephony.SignalStrength getSignalStrength();
     method public int getSimCarrierId();
     method @Nullable public CharSequence getSimCarrierIdName();
@@ -45146,8 +45175,8 @@
     method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
     method public void listen(android.telephony.PhoneStateListener, int);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+    method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
     method public void sendDialerSpecialCode(String);
     method public String sendEnvelopeWithStatus(String);
     method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
@@ -48998,6 +49027,7 @@
   }
 
   public final class StatsLog {
+    method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public static boolean logBinaryPushStateChanged(@NonNull String, long, int, int, @NonNull long[]);
     method public static boolean logEvent(int);
     method public static boolean logStart(int);
     method public static boolean logStop(int);
@@ -53472,6 +53502,7 @@
     method public void close();
     method @NonNull public final android.view.contentcapture.ContentCaptureSession createContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext);
     method public final void destroy();
+    method @Nullable public final android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
     method public final android.view.contentcapture.ContentCaptureSessionId getContentCaptureSessionId();
     method @NonNull public android.view.autofill.AutofillId newAutofillId(@NonNull android.view.autofill.AutofillId, long);
     method @NonNull public final android.view.ViewStructure newVirtualViewStructure(@NonNull android.view.autofill.AutofillId, long);
@@ -53479,6 +53510,7 @@
     method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId);
     method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence);
     method public final void notifyViewsDisappeared(@NonNull android.view.autofill.AutofillId, @NonNull long[]);
+    method public final void setContentCaptureContext(@Nullable android.view.contentcapture.ContentCaptureContext);
   }
 
   public final class ContentCaptureSessionId implements android.os.Parcelable {
@@ -55739,6 +55771,7 @@
     method public int getDropDownVerticalOffset();
     method public int getDropDownWidth();
     method protected android.widget.Filter getFilter();
+    method public int getInputMethodMode();
     method @Deprecated public android.widget.AdapterView.OnItemClickListener getItemClickListener();
     method @Deprecated public android.widget.AdapterView.OnItemSelectedListener getItemSelectedListener();
     method public int getListSelection();
@@ -55763,6 +55796,7 @@
     method public void setDropDownHorizontalOffset(int);
     method public void setDropDownVerticalOffset(int);
     method public void setDropDownWidth(int);
+    method public void setInputMethodMode(int);
     method public void setListSelection(int);
     method public void setOnDismissListener(android.widget.AutoCompleteTextView.OnDismissListener);
     method public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener);
diff --git a/api/removed.txt b/api/removed.txt
index f5bd434..c4ed871 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -558,7 +558,7 @@
 
   public class TelephonyManager {
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
-    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
+    method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
   }
 
 }
diff --git a/api/system-current.txt b/api/system-current.txt
index 48a450a..fb2b9e1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -285,9 +285,11 @@
     method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String);
     method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser();
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
+    method @NonNull public java.util.Collection<java.util.Locale> getSupportedLocales();
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
     method @RequiresPermission(android.Manifest.permission.KILL_UID) public void killUid(int, String);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
+    method public void setDeviceLocales(@NonNull android.os.LocaleList);
     method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle);
   }
@@ -312,6 +314,7 @@
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(String, int, String, int);
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(String, int, int);
     field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
+    field public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
     field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
     field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
     field public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot";
@@ -545,15 +548,17 @@
   }
 
   public class StatusBarManager {
-    method public android.util.Pair<java.lang.Integer,java.lang.Integer> getDisableFlags();
+    method public android.app.StatusBarManager.DisableInfo getDisableInfo();
     method public void setDisabledForSetup(boolean);
-    field public static final int DISABLE2_NONE = 0; // 0x0
-    field public static final int DISABLE_EXPAND = 65536; // 0x10000
-    field public static final int DISABLE_HOME = 2097152; // 0x200000
-    field public static final int DISABLE_NONE = 0; // 0x0
-    field public static final int DISABLE_NOTIFICATION_ALERTS = 262144; // 0x40000
-    field public static final int DISABLE_RECENT = 16777216; // 0x1000000
-    field public static final int DISABLE_SEARCH = 33554432; // 0x2000000
+  }
+
+  public static final class StatusBarManager.DisableInfo {
+    method public boolean areNoComponentsDisabled();
+    method public boolean isNavigateToHomeDisabled();
+    method public boolean isNotificationPeekingDisabled();
+    method public boolean isRecentsDisabled();
+    method public boolean isSearchDisabled();
+    method public boolean isStatusBarExpansionDisabled();
   }
 
   public final class Vr2dDisplayProperties implements android.os.Parcelable {
@@ -1366,6 +1371,7 @@
     field public static final String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
     field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
     field public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+    field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION";
     field public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
     field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
     field public static final String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
@@ -1376,6 +1382,7 @@
     field public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
     field public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
     field public static final String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
+    field public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES";
     field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_APP_PERMISSION_USAGE = "android.intent.action.REVIEW_APP_PERMISSION_USAGE";
     field public static final String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
     field public static final String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
@@ -1580,7 +1587,7 @@
     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(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[], @android.content.pm.PackageManager.DistractionRestriction 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);
     method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String);
     method @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo);
@@ -1658,9 +1665,6 @@
     method public abstract void onDexModuleRegistered(String, boolean, String);
   }
 
-  @IntDef(flag=true, prefix={"RESTRICTION_"}, value={android.content.pm.PackageManager.RESTRICTION_NONE, android.content.pm.PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, android.content.pm.PackageManager.RESTRICTION_HIDE_NOTIFICATIONS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.DistractionRestriction {
-  }
-
   public static interface PackageManager.OnPermissionsChangedListener {
     method public void onPermissionsChanged(int);
   }
@@ -3929,7 +3933,7 @@
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
     method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean);
-    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle);
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.net.Network, android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
@@ -5305,6 +5309,10 @@
     field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
   }
 
+  public final class LocaleList implements android.os.Parcelable {
+    method public static boolean isPseudoLocale(@Nullable android.icu.util.ULocale);
+  }
+
   public final class NativeHandle implements java.io.Closeable {
     ctor public NativeHandle();
     ctor public NativeHandle(@NonNull java.io.FileDescriptor, boolean);
@@ -5783,8 +5791,10 @@
   }
 
   public static interface DeviceConfig.Rollback {
+    field public static final String BOOT_NAMESPACE = "rollback_boot";
     field public static final String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout";
     field public static final String NAMESPACE = "rollback";
+    field public static final String ROLLBACK_LIFETIME_IN_MILLIS = "rollback_lifetime_in_millis";
   }
 
   public static interface DeviceConfig.Runtime {
@@ -5946,6 +5956,7 @@
   public final class Settings {
     field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
     field public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS";
+    field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
     field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
   }
 
@@ -6301,20 +6312,11 @@
   }
 
   public abstract class PresentationParams {
-    method public int getFlags();
-    method @Nullable public android.service.autofill.augmented.PresentationParams.Area getFullArea();
     method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea();
-    field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
-    field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
-    field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
-    field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
-    field public static final int FLAG_HOST_IME = 16; // 0x10
-    field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
   }
 
   public abstract static class PresentationParams.Area {
     method @NonNull public android.graphics.Rect getBounds();
-    method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSubArea(@NonNull android.graphics.Rect);
   }
 
 }
@@ -6348,7 +6350,8 @@
     method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
     method public void onDisconnected();
     method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest);
-    method public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method @Deprecated public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
     field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService";
   }
 
@@ -6974,6 +6977,7 @@
     field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
     field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
     field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
+    field public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
     field public static final int TTY_MODE_FULL = 1; // 0x1
     field public static final int TTY_MODE_HCO = 2; // 0x2
     field public static final int TTY_MODE_OFF = 0; // 0x0
@@ -7860,7 +7864,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
     method public boolean needsOtaServiceProvisioning();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
-    method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+    method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
@@ -8248,6 +8252,7 @@
     method public int getServiceType();
     method public static int getVideoStateFromCallType(int);
     method public static int getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile);
+    method public boolean hasKnownUserIntentEmergency();
     method public boolean isEmergencyCallTesting();
     method public boolean isVideoCall();
     method public boolean isVideoPaused();
@@ -8260,6 +8265,7 @@
     method public void setEmergencyCallTesting(boolean);
     method public void setEmergencyServiceCategories(int);
     method public void setEmergencyUrns(java.util.List<java.lang.String>);
+    method public void setHasKnownUserIntentEmergency(boolean);
     method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
     method public void updateCallType(android.telephony.ims.ImsCallProfile);
     method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -9302,7 +9308,8 @@
 package android.view.autofill {
 
   public final class AutofillManager {
-    method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method @Deprecated public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method public void setAugmentedAutofillWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
   }
 
 }
@@ -9324,6 +9331,7 @@
 
   public final class ContentCaptureEvent implements android.os.Parcelable {
     method public int describeContents();
+    method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
     method public long getEventTime();
     method @Nullable public android.view.autofill.AutofillId getId();
     method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds();
@@ -9332,6 +9340,7 @@
     method @Nullable public android.view.contentcapture.ViewNode getViewNode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
+    field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6
     field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
     field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
     field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
diff --git a/api/test-current.txt b/api/test-current.txt
index 1a7e4cb..2cb01ea 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -936,7 +936,7 @@
   }
 
   public class ConnectivityManager {
-    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle);
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.net.Network, android.os.Bundle);
     field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
     field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
   }
@@ -996,6 +996,7 @@
     method public int[] getCapabilities();
     method public int[] getTransportTypes();
     method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities);
+    field public static final int TRANSPORT_TEST = 7; // 0x7
   }
 
   public class NetworkStack {
@@ -1815,6 +1816,7 @@
 
   public final class Settings {
     field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
+    field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
     field public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; // 0x1
   }
 
@@ -2073,20 +2075,11 @@
   }
 
   public abstract class PresentationParams {
-    method public int getFlags();
-    method @Nullable public android.service.autofill.augmented.PresentationParams.Area getFullArea();
     method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea();
-    field public static final int FLAG_HINT_GRAVITY_BOTTOM = 2; // 0x2
-    field public static final int FLAG_HINT_GRAVITY_LEFT = 4; // 0x4
-    field public static final int FLAG_HINT_GRAVITY_RIGHT = 8; // 0x8
-    field public static final int FLAG_HINT_GRAVITY_TOP = 1; // 0x1
-    field public static final int FLAG_HOST_IME = 16; // 0x10
-    field public static final int FLAG_HOST_SYSTEM = 32; // 0x20
   }
 
   public abstract static class PresentationParams.Area {
     method @NonNull public android.graphics.Rect getBounds();
-    method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSubArea(@NonNull android.graphics.Rect);
   }
 
 }
@@ -2110,7 +2103,8 @@
     method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
     method public void onDisconnected();
     method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest);
-    method public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method @Deprecated public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
     field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService";
   }
 
@@ -2699,7 +2693,8 @@
   }
 
   public final class AutofillManager {
-    method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method @Deprecated public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method public void setAugmentedAutofillWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
     field public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES = "smart_suggestion_supported_modes";
     field public static final int FLAG_SMART_SUGGESTION_OFF = 0; // 0x0
     field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1
@@ -2725,6 +2720,7 @@
 
   public final class ContentCaptureEvent implements android.os.Parcelable {
     method public int describeContents();
+    method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
     method public long getEventTime();
     method @Nullable public android.view.autofill.AutofillId getId();
     method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds();
@@ -2733,6 +2729,7 @@
     method @Nullable public android.view.contentcapture.ViewNode getViewNode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
+    field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6
     field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
     field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
     field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 46917e4..a6c7cae 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -110,13 +110,30 @@
     } else {
         mShuttingDown = true;
     }
+    ALOGD("%sAnimationStartTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+            elapsedRealtime());
+}
+
+BootAnimation::~BootAnimation() {
+    if (mAnimation != nullptr) {
+        releaseAnimation(mAnimation);
+        mAnimation = nullptr;
+    }
+    ALOGD("%sAnimationStopTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+            elapsedRealtime());
 }
 
 void BootAnimation::onFirstRef() {
     status_t err = mSession->linkToComposerDeath(this);
     SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
     if (err == NO_ERROR) {
-        run("BootAnimation", PRIORITY_DISPLAY);
+        // Load the animation content -- this can be slow (eg 200ms)
+        // called before waitForSurfaceFlinger() in main() to avoid wait
+        ALOGD("%sAnimationPreloadTiming start time: %" PRId64 "ms",
+                mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
+        preloadAnimation();
+        ALOGD("%sAnimationPreloadStopTiming start time: %" PRId64 "ms",
+                mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
     }
 }
 
@@ -306,6 +323,20 @@
     mFlingerSurface = s;
     mTargetInset = -1;
 
+    return NO_ERROR;
+}
+
+bool BootAnimation::preloadAnimation() {
+    findBootAnimationFile();
+    if (!mZipFileName.isEmpty()) {
+        mAnimation = loadAnimation(mZipFileName);
+        return (mAnimation != nullptr);
+    }
+
+    return false;
+}
+
+void BootAnimation::findBootAnimationFile() {
     // If the device has encryption turned on or is in process
     // of being encrypted we show the encrypted boot animation.
     char decrypt[PROPERTY_VALUE_MAX];
@@ -320,7 +351,7 @@
         for (const char* f : encryptedBootFiles) {
             if (access(f, R_OK) == 0) {
                 mZipFileName = f;
-                return NO_ERROR;
+                return;
             }
         }
     }
@@ -332,10 +363,9 @@
     for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) {
         if (access(f, R_OK) == 0) {
             mZipFileName = f;
-            return NO_ERROR;
+            return;
         }
     }
-    return NO_ERROR;
 }
 
 bool BootAnimation::threadLoop()
@@ -790,8 +820,6 @@
         }
     }
 
-    mCallbacks->init(animation.parts);
-
     zip->endIteration(cookie);
 
     return true;
@@ -799,13 +827,25 @@
 
 bool BootAnimation::movie()
 {
-    Animation* animation = loadAnimation(mZipFileName);
-    if (animation == NULL)
+    if (mAnimation == nullptr) {
+        mAnimation = loadAnimation(mZipFileName);
+    }
+
+    if (mAnimation == nullptr)
         return false;
 
+    // mCallbacks->init() may get called recursively,
+    // this loop is needed to get the same results
+    for (const Animation::Part& part : mAnimation->parts) {
+        if (part.animation != nullptr) {
+            mCallbacks->init(part.animation->parts);
+        }
+    }
+    mCallbacks->init(mAnimation->parts);
+
     bool anyPartHasClock = false;
-    for (size_t i=0; i < animation->parts.size(); i++) {
-        if(validClock(animation->parts[i])) {
+    for (size_t i=0; i < mAnimation->parts.size(); i++) {
+        if(validClock(mAnimation->parts[i])) {
             anyPartHasClock = true;
             break;
         }
@@ -846,7 +886,7 @@
     bool clockFontInitialized = false;
     if (mClockEnabled) {
         clockFontInitialized =
-            (initFont(&animation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
+            (initFont(&mAnimation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
         mClockEnabled = clockFontInitialized;
     }
 
@@ -855,7 +895,7 @@
         mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
     }
 
-    playAnimation(*animation);
+    playAnimation(*mAnimation);
 
     if (mTimeCheckThread != nullptr) {
         mTimeCheckThread->requestExit();
@@ -863,10 +903,11 @@
     }
 
     if (clockFontInitialized) {
-        glDeleteTextures(1, &animation->clockFont.texture.name);
+        glDeleteTextures(1, &mAnimation->clockFont.texture.name);
     }
 
-    releaseAnimation(animation);
+    releaseAnimation(mAnimation);
+    mAnimation = nullptr;
 
     return false;
 }
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 19616cb..dc19fb0 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -115,6 +115,7 @@
     };
 
     explicit BootAnimation(sp<Callbacks> callbacks);
+    virtual ~BootAnimation();
 
     sp<SurfaceComposerClient> session() const;
 
@@ -155,6 +156,8 @@
     void releaseAnimation(Animation*) const;
     bool parseAnimationDesc(Animation&);
     bool preloadZip(Animation &animation);
+    void findBootAnimationFile();
+    bool preloadAnimation();
 
     void checkExit();
 
@@ -182,6 +185,7 @@
     SortedVector<String8> mLoadedFiles;
     sp<TimeCheckThread> mTimeCheckThread = nullptr;
     sp<Callbacks> mCallbacks;
+    Animation* mAnimation = nullptr;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index a52a5e9..6c7b3e5 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -44,14 +44,16 @@
         sp<ProcessState> proc(ProcessState::self());
         ProcessState::self()->startThreadPool();
 
+        // create the boot animation object (may take up to 200ms for 2MB zip)
+        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
+
         waitForSurfaceFlinger();
 
-        // create the boot animation object
-        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
+        boot->run("BootAnimation", PRIORITY_DISPLAY);
+
         ALOGV("Boot animation set up. Joining pool.");
 
         IPCThreadState::self()->joinThreadPool();
     }
-    ALOGV("Boot animation exit");
     return 0;
 }
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index faf2053..f408655 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -76,6 +76,7 @@
         "src/external/SubsystemSleepStatePuller.cpp",
         "src/external/PowerStatsPuller.cpp",
         "src/external/ResourceHealthManagerPuller.cpp",
+        "src/external/TrainInfoPuller.cpp",
         "src/external/StatsPullerManager.cpp",
         "src/external/puller_util.cpp",
         "src/logd/LogEvent.cpp",
@@ -238,6 +239,7 @@
         "tests/guardrail/StatsdStats_test.cpp",
         "tests/metrics/metrics_test_helper.cpp",
         "tests/statsd_test_util.cpp",
+        "tests/storage/StorageManager_test.cpp",
         "tests/e2e/WakelockDuration_e2e_test.cpp",
         "tests/e2e/MetricActivation_e2e_test.cpp",
         "tests/e2e/MetricConditionLink_e2e_test.cpp",
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index b478fed..fb603b9 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -31,6 +31,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionController.h>
+#include <cutils/multiuser.h>
 #include <dirent.h>
 #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
 #include <private/android_filesystem_config.h>
@@ -47,6 +48,7 @@
 
 using android::base::StringPrintf;
 using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_INT64;
 using android::util::FIELD_TYPE_MESSAGE;
 
 namespace android {
@@ -62,6 +64,8 @@
 
 // for StatsDataDumpProto
 const int FIELD_ID_REPORTS_LIST = 1;
+// for TrainInfo experiment id serialization
+const int FIELD_ID_EXPERIMENT_ID = 1;
 
 static binder::Status ok() {
     return binder::Status::ok();
@@ -1167,6 +1171,56 @@
     return Status::ok();
 }
 
+Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainName,
+                                                    int64_t trainVersionCode, int options,
+                                                    int32_t state,
+                                                    const std::vector<int64_t>& experimentIds) {
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+    // For testing
+    if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) {
+        return ok();
+    }
+
+    // Caller must be granted these permissions
+    if (!checkCallingPermission(String16(kPermissionDump))) {
+        return exception(binder::Status::EX_SECURITY,
+                         StringPrintf("UID %d lacks permission %s", uid, kPermissionDump));
+    }
+    if (!checkCallingPermission(String16(kPermissionUsage))) {
+        return exception(binder::Status::EX_SECURITY,
+                         StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage));
+    }
+    // TODO: add verifier permission
+
+    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;
+
+    ProtoOutputStream proto;
+    for (const auto& expId : experimentIds) {
+        proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID,
+                    (long long)expId);
+    }
+
+    vector<uint8_t> buffer;
+    buffer.resize(proto.size());
+    size_t pos = 0;
+    auto iter = proto.data();
+    while (iter.readBuffer() != NULL) {
+        size_t toRead = iter.currentToRead();
+        std::memcpy(&(buffer[pos]), iter.readBuffer(), toRead);
+        pos += toRead;
+        iter.rp()->move(toRead);
+    }
+    LogEvent event(std::string(String8(trainName).string()), trainVersionCode, requiresStaging,
+                   rollbackEnabled, requiresLowLatencyMonitor, state, buffer, userId);
+    mProcessor->OnLogEvent(&event);
+    StorageManager::writeTrainInfo(trainVersionCode, buffer);
+    return Status::ok();
+}
+
 hardware::Return<void> StatsService::reportSpeakerImpedance(
         const SpeakerImpedance& speakerImpedance) {
     LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), speakerImpedance);
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 7f10d74..d24565a 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -31,6 +31,7 @@
 #include <android/frameworks/stats/1.0/types.h>
 #include <android/os/BnStatsManager.h>
 #include <android/os/IStatsCompanionService.h>
+#include <android/os/IStatsManager.h>
 #include <binder/IResultReceiver.h>
 #include <utils/Looper.h>
 
@@ -186,6 +187,13 @@
     virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override;
 
     /**
+     * Binder call to log BinaryPushStateChanged atom.
+     */
+    virtual Status sendBinaryPushStateChangedAtom(
+            const android::String16& trainName, int64_t trainVersionCode, int options,
+            int32_t state, const std::vector<int64_t>& experimentIds) override;
+
+    /**
      * Binder call to get SpeakerImpedance atom.
      */
     virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 29f67c7..57c32c7 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -302,6 +302,7 @@
         RoleHolder role_holder = 10049;
         DangerousPermissionState dangerous_permission_state = 10050;
         TrainInfo train_info = 10051;
+        TimeZoneDataInfo time_zone_data_info = 10052;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -3156,6 +3157,13 @@
     optional int64 order_id = 2;
 }
 
+/**
+ * Potential experiment ids that goes with a train install.
+ */
+message TrainExperimentIds {
+    repeated int64 experiment_id = 1;
+}
+
 /*
  * Logs when a binary push state changes.
  * Logged by the installer via public api.
@@ -3182,8 +3190,14 @@
         INSTALL_FAILURE = 6;
         INSTALL_CANCELLED = 7;
         INSTALLER_ROLLBACK_REQUESTED = 8;
+        INSTALLER_ROLLBACK_SUCCESS = 9;
+        INSTALLER_ROLLBACK_FAILURE = 10;
     }
     optional State state = 6;
+    // Possible experiment ids for monitoring this push.
+    optional TrainExperimentIds experiment_ids = 7 [(log_mode) = MODE_BYTES];
+    // user id
+    optional int32 user_id = 8;
 }
 
 /** Represents USB port overheat event. */
@@ -5511,13 +5525,6 @@
 }
 
 /**
- * Potential experiment ids that goes with a train install.
- */
-message TrainExperimentIds {
-    repeated int64 experiment_id = 1;
-}
-
-/**
  * Pulls the ongoing mainline install train version code.
  * Pulled from StatsCompanionService
  */
@@ -5558,3 +5565,11 @@
   // [0,100] progress for the assist gesture.
   optional int32 progress = 1;
 }
+
+/*
+ * Information about the time zone data on a device.
+ */
+message TimeZoneDataInfo {
+    // A version identifier for the data set on device. e.g. "2018i"
+    optional string tzdb_version = 1;
+}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index ed72b29..d70c851 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -29,10 +29,11 @@
 #include "../statscompanion_util.h"
 #include "PowerStatsPuller.h"
 #include "ResourceHealthManagerPuller.h"
-#include "StatsCompanionServicePuller.h"
 #include "StatsCallbackPuller.h"
+#include "StatsCompanionServicePuller.h"
 #include "StatsPullerManager.h"
 #include "SubsystemSleepStatePuller.h"
+#include "TrainInfoPuller.h"
 #include "statslog.h"
 
 #include <iostream>
@@ -152,7 +153,7 @@
                   new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
         // temperature
         {android::util::TEMPERATURE,
-          {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}},
+         {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}},
         // binder_calls
         {android::util::BINDER_CALLS,
          {.additiveFields = {4, 5, 6, 8, 12},
@@ -231,6 +232,11 @@
         // PermissionState.
         {android::util::DANGEROUS_PERMISSION_STATE,
          {.puller = new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE)}},
+        // TrainInfo.
+        {android::util::TRAIN_INFO, {.puller = new TrainInfoPuller()}},
+        // TimeZoneDataInfo.
+        {android::util::TIME_ZONE_DATA_INFO,
+         {.puller = new StatsCompanionServicePuller(android::util::TIME_ZONE_DATA_INFO)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp
new file mode 100644
index 0000000..9d09242
--- /dev/null
+++ b/cmds/statsd/src/external/TrainInfoPuller.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#define DEBUG false  // STOPSHIP if true
+#include "Log.h"
+
+#include "external/StatsPuller.h"
+
+#include "TrainInfoPuller.h"
+#include "logd/LogEvent.h"
+#include "stats_log_util.h"
+#include "statslog.h"
+#include "storage/StorageManager.h"
+
+using std::make_shared;
+using std::shared_ptr;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TrainInfoPuller::TrainInfoPuller() :
+    StatsPuller(android::util::TRAIN_INFO) {
+}
+
+bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+    InstallTrainInfo trainInfo;
+    bool ret = StorageManager::readTrainInfo(trainInfo);
+    if (!ret) {
+        ALOGW("Failed to read train info.");
+        return false;
+    }
+    auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
+    data->push_back(event);
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/TrainInfoPuller.h b/cmds/statsd/src/external/TrainInfoPuller.h
new file mode 100644
index 0000000..615d023
--- /dev/null
+++ b/cmds/statsd/src/external/TrainInfoPuller.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads train info from disk.
+ */
+class TrainInfoPuller : public StatsPuller {
+ public:
+  TrainInfoPuller();
+
+ private:
+  bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index b433c41..d661ee8 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -478,6 +478,11 @@
     getAtomMetricStats(metricId).bucketDropped++;
 }
 
+void StatsdStats::noteBucketUnknownCondition(int64_t metricId) {
+    lock_guard<std::mutex> lock(mLock);
+    getAtomMetricStats(metricId).bucketUnknownCondition++;
+}
+
 void StatsdStats::noteConditionChangeInNextBucket(int64_t metricId) {
     lock_guard<std::mutex> lock(mLock);
     getAtomMetricStats(metricId).conditionChangeInNextBucket++;
@@ -497,7 +502,7 @@
             std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs);
 }
 
-StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int metricId) {
+StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int64_t metricId) {
     auto atomMetricStatsIter = mAtomMetricStats.find(metricId);
     if (atomMetricStatsIter != mAtomMetricStats.end()) {
         return atomMetricStatsIter->second;
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 5275c8f..e039be2 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -409,6 +409,11 @@
     void noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs);
 
     /**
+     * Number of buckets with unknown condition.
+     */
+    void noteBucketUnknownCondition(int64_t metricId);
+
+    /**
      * Reset the historical stats. Including all stats in icebox, and the tracked stats about
      * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
      * to collect stats after reset() has been called.
@@ -458,6 +463,7 @@
         long bucketDropped = 0;
         int64_t minBucketBoundaryDelayNs = 0;
         int64_t maxBucketBoundaryDelayNs = 0;
+        long bucketUnknownCondition = 0;
     } AtomMetricStats;
 
 private:
@@ -488,7 +494,7 @@
     std::map<int, PulledAtomStats> mPulledAtomStats;
 
     // Maps metric ID to its stats. The size is capped by the number of metrics.
-    std::map<int, AtomMetricStats> mAtomMetricStats;
+    std::map<int64_t, AtomMetricStats> mAtomMetricStats;
 
     struct LogLossStats {
         LogLossStats(int32_t sec, int32_t count, int32_t error)
@@ -532,7 +538,7 @@
      * Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference
      * will live as long as `this`.
      */
-    StatsdStats::AtomMetricStats& getAtomMetricStats(int metricId);
+    StatsdStats::AtomMetricStats& getAtomMetricStats(int64_t metricId);
 
     FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
     FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd);
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 40a4070..dec36b5 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -20,6 +20,8 @@
 #include "stats_log_util.h"
 #include "statslog.h"
 
+#include <binder/IPCThreadState.h>
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -180,6 +182,25 @@
     }
 }
 
+LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging,
+                   bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state,
+                   const std::vector<uint8_t>& experimentIds, int32_t userId) {
+    mLogdTimestampNs = getWallClockNs();
+    mElapsedTimestampNs = getElapsedRealtimeNs();
+    mTagId = android::util::BINARY_PUSH_STATE_CHANGED;
+    mLogUid = android::IPCThreadState::self()->getCallingUid();
+
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value((int)requiresStaging)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value((int)rollbackEnabled)));
+    mValues.push_back(
+            FieldValue(Field(mTagId, getSimpleField(5)), Value((int)requiresLowLatencyMonitor)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)), Value(state)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)), Value(experimentIds)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(8)), Value(userId)));
+}
+
 LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
                    const SpeakerImpedance& speakerImpedance) {
     mLogdTimestampNs = wallClockTimestampNs;
@@ -340,6 +361,16 @@
     }
 }
 
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                   const InstallTrainInfo& trainInfo) {
+    mLogdTimestampNs = wallClockTimestampNs;
+    mElapsedTimestampNs = elapsedTimestampNs;
+    mTagId = android::util::TRAIN_INFO;
+    mValues.push_back(
+            FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode)));
+    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainInfo.experimentIds)));
+}
+
 LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, 0) {}
 
 LogEvent::LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 784376a..111a619 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -55,6 +55,11 @@
     int32_t mUid;
     std::string mTag;
 };
+
+struct InstallTrainInfo {
+    int64_t trainVersionCode;
+    std::vector<uint8_t> experimentIds;
+};
 /**
  * Wrapper for the log_msg structure.
  */
@@ -97,6 +102,11 @@
                       const std::map<int32_t, std::string>& string_map,
                       const std::map<int32_t, float>& float_map);
 
+    // Constructs a BinaryPushStateChanged LogEvent from API call.
+    explicit LogEvent(const std::string& trainName, int64_t trainVersionCode, bool requiresStaging,
+                      bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state,
+                      const std::vector<uint8_t>& experimentIds, int32_t userId);
+
     explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
                       const SpeakerImpedance& speakerImpedance);
 
@@ -127,6 +137,9 @@
     explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
                       const VendorAtom& vendorAtom);
 
+    explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+                      const InstallTrainInfo& installTrainInfo);
+
     ~LogEvent();
 
     /**
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 350745b..5707de5 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -250,7 +250,7 @@
 void CountMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                    const int64_t eventTime) {
     VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
-    mCondition = conditionMet;
+    mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
 }
 
 bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 6c1c47b..da6b97c 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -433,7 +433,7 @@
 void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                       const int64_t eventTime) {
     VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
-    mCondition = conditionMet;
+    mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
     flushIfNeededLocked(eventTime);
     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
         for (auto& pair : whatIt.second) {
@@ -767,12 +767,13 @@
                            !mSameConditionDimensionsInTracker,
                            !mHasLinksToAllConditionDimensionsInTracker,
                            &dimensionKeysInCondition);
-        condition = (conditionState == ConditionState::kTrue);
+        condition = conditionState == ConditionState::kTrue;
         if (mDimensionsInCondition.empty() && condition) {
             dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
         }
     } else {
-        condition = mCondition;
+        // TODO: The unknown condition state is not handled here, we should fix it.
+        condition = mCondition == ConditionState::kTrue;
         if (condition) {
             dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
         }
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 1b830a3..ec561b5 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -137,6 +137,7 @@
 
     FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition);
     FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition);
+    FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState);
     FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade);
     FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket);
     FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade);
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 7e695a6..3b4af65 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -132,7 +132,7 @@
 void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                    const int64_t eventTime) {
     VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
-    mCondition = conditionMet;
+    mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
 }
 
 void EventMetricProducer::onMatchedLogEventInternalLocked(
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index d56a355..837d532 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -320,15 +320,15 @@
         // When the metric wants to do random sampling and there is already one gauge atom for the
         // current bucket, do not do it again.
         case GaugeMetric::RANDOM_ONE_SAMPLE: {
-            triggerPuller = mCondition && mCurrentSlicedBucket->empty();
+            triggerPuller = mCondition == ConditionState::kTrue && mCurrentSlicedBucket->empty();
             break;
         }
         case GaugeMetric::CONDITION_CHANGE_TO_TRUE: {
-            triggerPuller = mCondition;
+            triggerPuller = mCondition == ConditionState::kTrue;
             break;
         }
         case GaugeMetric::FIRST_N_SAMPLES: {
-            triggerPuller = mCondition;
+            triggerPuller = mCondition == ConditionState::kTrue;
             break;
         }
         default:
@@ -364,7 +364,7 @@
                                                    const int64_t eventTimeNs) {
     VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId);
     flushIfNeededLocked(eventTimeNs);
-    mCondition = conditionMet;
+    mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
     if (mIsPulled && mTriggerAtomId == -1) {
         pullAndMatchEventsLocked(eventTimeNs);
     }  // else: Push mode. No need to proactively pull the gauge data.
@@ -377,7 +377,7 @@
     flushIfNeededLocked(eventTimeNs);
     // If the condition is sliced, mCondition is true if any of the dimensions is true. And we will
     // pull for every dimension.
-    mCondition = overallCondition;
+    mCondition = overallCondition ? ConditionState::kTrue : ConditionState::kFalse;
     if (mIsPulled && mTriggerAtomId == -1) {
         pullAndMatchEventsLocked(eventTimeNs);
     }  // else: Push mode. No need to proactively pull the gauge data.
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 1107568..4cf5333 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -45,7 +45,8 @@
                            &dimensionKeysInCondition);
         condition = (conditionState == ConditionState::kTrue);
     } else {
-        condition = mCondition;
+        // TODO: The unknown condition state is not handled here, we should fix it.
+        condition = mCondition == ConditionState::kTrue;
     }
 
     if (mDimensionsInCondition.empty() && condition) {
@@ -107,7 +108,8 @@
     if (it == mEventActivationMap.end()) {
         return;
     }
-    if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT) {
+    if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT &&
+        it->second.state == ActivationState::kNotActive) {
         it->second.state = ActivationState::kActiveOnBoot;
         return;
     }
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 849cb76..8ab3b06 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -59,7 +59,7 @@
           mTimeBaseNs(timeBaseNs),
           mCurrentBucketStartTimeNs(timeBaseNs),
           mCurrentBucketNum(0),
-          mCondition(conditionIndex >= 0 ? false : true),
+          mCondition(conditionIndex >= 0 ? ConditionState::kUnknown : ConditionState::kTrue),
           mConditionSliced(false),
           mWizard(wizard),
           mConditionTrackerIndex(conditionIndex),
@@ -315,7 +315,7 @@
 
     int64_t mBucketSizeNs;
 
-    bool mCondition;
+    ConditionState mCondition;
 
     bool mConditionSliced;
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 851ae99..bc7c872 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -154,7 +154,7 @@
     // Adjust start for partial bucket
     mCurrentBucketStartTimeNs = startTimeNs;
     // Kicks off the puller immediately if condition is true and diff based.
-    if (mIsPulled && mCondition && mUseDiff) {
+    if (mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
         pullAndMatchEventsLocked(startTimeNs);
     }
     VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
@@ -341,17 +341,21 @@
     flushIfNeededLocked(eventTimeNs);
 
     // Pull on condition changes.
-    if (mIsPulled && (mCondition != condition)) {
+    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 && !condition) {
+    if (mUseDiff && mCondition == ConditionState::kTrue && condition == ConditionState::kFalse) {
         resetBase();
     }
 
-    mCondition = condition;
+    mCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
 }
 
 void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
@@ -372,7 +376,7 @@
 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) {
+    if (mCondition == ConditionState::kTrue) {
         if (!pullSuccess) {
             // If the pull failed, we won't be able to compute a diff.
             invalidateCurrentBucket();
@@ -725,6 +729,10 @@
 }
 
 void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs) {
+    if (mCondition == ConditionState::kUnknown) {
+        StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
+    }
+
     VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
          (int)mCurrentSlicedBucket.size());
     int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index f7428a5..09b8fed 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -425,6 +425,7 @@
       optional int64 bucket_dropped = 8;
       optional int64 min_bucket_boundary_delay_ns = 9;
       optional int64 max_bucket_boundary_delay_ns = 10;
+      optional int64 bucket_unknown_condition = 11;
     }
     repeated AtomMetricStats atom_metric_stats = 17;
 
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 01043a2..ca645e1 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -84,6 +84,7 @@
 const int FIELD_ID_BUCKET_DROPPED = 8;
 const int FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS = 9;
 const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10;
+const int FIELD_ID_BUCKET_UNKNOWN_CONDITION = 11;
 
 namespace {
 
@@ -489,11 +490,11 @@
     protoOutput->end(token);
 }
 
-void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair,
+void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair,
                                   util::ProtoOutputStream *protoOutput) {
     uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_METRIC_STATS |
                                         FIELD_COUNT_REPEATED);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (int32_t)pair.first);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED,
                        (long long)pair.second.hardDimensionLimitReached);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED,
@@ -512,6 +513,8 @@
                        (long long)pair.second.minBucketBoundaryDelayNs);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS,
                        (long long)pair.second.maxBucketBoundaryDelayNs);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION,
+                       (long long)pair.second.bucketUnknownCondition);
     protoOutput->end(token);
 }
 
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index dcea0e6..59d4865 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -74,7 +74,7 @@
                               util::ProtoOutputStream* protoOutput);
 
 // Helper function to write AtomMetricStats to ProtoOutputStream
-void writeAtomMetricStatsToStream(const std::pair<int, StatsdStats::AtomMetricStats> &pair,
+void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair,
                                   util::ProtoOutputStream *protoOutput);
 
 template<class T>
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 90f641a..165e57c 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -38,10 +38,13 @@
 
 #define STATS_DATA_DIR "/data/misc/stats-data"
 #define STATS_SERVICE_DIR "/data/misc/stats-service"
+#define TRAIN_INFO_DIR "/data/misc/train-info"
 
 // for ConfigMetricsReportList
 const int FIELD_ID_REPORTS = 2;
 
+std::mutex StorageManager::sTrainInfoMutex;
+
 using android::base::StringPrintf;
 using std::unique_ptr;
 
@@ -92,6 +95,71 @@
     close(fd);
 }
 
+bool StorageManager::writeTrainInfo(int64_t trainVersionCode,
+                                    const std::vector<uint8_t>& experimentIds) {
+    std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+
+    deleteAllFiles(TRAIN_INFO_DIR);
+
+    string file_name = StringPrintf("%s/%lld", TRAIN_INFO_DIR, (long long)trainVersionCode);
+
+    int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
+    if (fd == -1) {
+        VLOG("Attempt to access %s but failed", file_name.c_str());
+        return false;
+    }
+
+    size_t result = write(fd, experimentIds.data(), experimentIds.size());
+    if (result == experimentIds.size()) {
+        VLOG("Successfully wrote %s", file_name.c_str());
+    } else {
+        VLOG("Failed to write %s", file_name.c_str());
+        return false;
+    }
+
+    result = fchown(fd, AID_STATSD, AID_STATSD);
+    if (result) {
+        VLOG("Failed to chown %s to statsd", file_name.c_str());
+        return false;
+    }
+
+    close(fd);
+    return true;
+}
+
+bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
+    std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+
+    unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
+
+    if (dir == NULL) {
+        VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
+        return false;
+    }
+
+    dirent* de;
+    while ((de = readdir(dir.get()))) {
+        char* name = de->d_name;
+        if (name[0] == '.') {
+            continue;
+        }
+        trainInfo.trainVersionCode = StrToInt64(name);
+        string fullPath = StringPrintf("%s/%s", TRAIN_INFO_DIR, name);
+        int fd = open(fullPath.c_str(), O_RDONLY | O_CLOEXEC);
+        if (fd != -1) {
+            string str;
+            if (android::base::ReadFdToString(fd, &str)) {
+                close(fd);
+                std::copy(str.begin(), str.end(), std::back_inserter(trainInfo.experimentIds));
+                VLOG("Read train info file successful: %s", fullPath.c_str());
+                return true;
+            }
+        }
+        close(fd);
+    }
+    return false;
+}
+
 void StorageManager::deleteFile(const char* file) {
     if (remove(file) != 0) {
         VLOG("Attempt to delete %s but is not found", file);
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index dcf3bb6..d6df867 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -29,6 +29,11 @@
 
 using android::util::ProtoOutputStream;
 
+struct TrainInfo {
+    int64_t trainVersionCode;
+    std::vector<uint8_t> experimentIds;
+};
+
 class StorageManager : public virtual RefBase {
 public:
     /**
@@ -37,6 +42,16 @@
     static void writeFile(const char* file, const void* buffer, int numBytes);
 
     /**
+     * Writes train info.
+     */
+    static bool writeTrainInfo(int64_t trainVersionCode, const std::vector<uint8_t>& experimentIds);
+
+    /**
+     * Reads train info.
+     */
+    static bool readTrainInfo(InstallTrainInfo& trainInfo);
+
+    /**
      * Reads the file content to the buffer.
      */
     static bool readFileToString(const char* file, string* content);
@@ -109,6 +124,8 @@
      * Prints disk usage statistics about a directory related to statsd.
      */
     static void printDirStats(int out, const char* path);
+
+    static std::mutex sTrainInfoMutex;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc
index e0cbd5d..a98ecd5 100644
--- a/cmds/statsd/statsd.rc
+++ b/cmds/statsd/statsd.rc
@@ -27,3 +27,4 @@
     mkdir /data/misc/stats-data/ 0770 statsd system
     mkdir /data/misc/stats-service/ 0770 statsd system
     mkdir /data/misc/stats-active-metric/ 0770 statsd system
+    mkdir /data/misc/train-info/ 0770 statsd system
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 5f3aae3..4579ca6 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -297,7 +297,8 @@
 
     // Setup a simple config, no activation
     StatsdConfig config1;
-    config1.set_id(12341);
+    int64_t cfgId1 = 12341;
+    config1.set_id(cfgId1);
     config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
     auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
     *config1.add_atom_matcher() = wakelockAcquireMatcher;
@@ -314,14 +315,12 @@
     countMetric2->set_what(wakelockAcquireMatcher.id());
     countMetric2->set_bucket(FIVE_MINUTES);
 
-    ConfigKey cfgKey1(uid, 12341);
-    long timeBase1 = 1;
-    sp<StatsLogProcessor> processor =
-            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
+    ConfigKey cfgKey1(uid, cfgId1);
 
     // Add another config, with two metrics, one with activation
     StatsdConfig config2;
-    config2.set_id(12342);
+    int64_t cfgId2 = 12342;
+    config2.set_id(cfgId2);
     config2.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
     *config2.add_atom_matcher() = wakelockAcquireMatcher;
 
@@ -344,11 +343,12 @@
     metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
     metric3ActivationTrigger->set_ttl_seconds(100);
 
-    ConfigKey cfgKey2(uid, 12342);
+    ConfigKey cfgKey2(uid, cfgId2);
 
     // Add another config, with two metrics, both with activations
     StatsdConfig config3;
-    config3.set_id(12342);
+    int64_t cfgId3 = 12343;
+    config3.set_id(cfgId3);
     config3.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
     *config3.add_atom_matcher() = wakelockAcquireMatcher;
 
@@ -376,14 +376,37 @@
     metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
     metric6ActivationTrigger->set_ttl_seconds(200);
 
-    ConfigKey cfgKey3(uid, 12343);
+    ConfigKey cfgKey3(uid, cfgId3);
 
-    processor->OnConfigUpdated(2, cfgKey2, config2);
-    processor->OnConfigUpdated(3, cfgKey3, config3);
+    sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> subscriberAlarmMonitor;
+    vector<int64_t> activeConfigsBroadcast;
 
-    EXPECT_EQ(3, processor->mMetricsManagers.size());
-    auto it = processor->mMetricsManagers.find(cfgKey1);
-    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    long timeBase1 = 1;
+    int broadcastCount = 0;
+    StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
+            timeBase1, [](const ConfigKey& key) { return true; },
+            [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
+                    const vector<int64_t>& activeConfigs) {
+                broadcastCount++;
+                EXPECT_EQ(broadcastUid, uid);
+                activeConfigsBroadcast.clear();
+                activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
+                        activeConfigs.begin(), activeConfigs.end());
+                return true;
+            });
+
+    processor.OnConfigUpdated(1, cfgKey1, config1);
+    processor.OnConfigUpdated(2, cfgKey2, config2);
+    processor.OnConfigUpdated(3, cfgKey3, config3);
+
+    EXPECT_EQ(3, processor.mMetricsManagers.size());
+
+    // Expect the first config and both metrics in it to be active.
+    auto it = processor.mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor.mMetricsManagers.end());
     auto& metricsManager1 = it->second;
     EXPECT_TRUE(metricsManager1->isActive());
 
@@ -407,8 +430,9 @@
     auto& metricProducer2 = *metricIt;
     EXPECT_TRUE(metricProducer2->isActive());
 
-    it = processor->mMetricsManagers.find(cfgKey2);
-    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active.
+    it = processor.mMetricsManagers.find(cfgKey2);
+    EXPECT_TRUE(it != processor.mMetricsManagers.end());
     auto& metricsManager2 = it->second;
     EXPECT_TRUE(metricsManager2->isActive());
 
@@ -432,8 +456,9 @@
     auto& metricProducer4 = *metricIt;
     EXPECT_TRUE(metricProducer4->isActive());
 
-    it = processor->mMetricsManagers.find(cfgKey3);
-    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    // Expect the third config and both metrics in it to be inactive.
+    it = processor.mMetricsManagers.find(cfgKey3);
+    EXPECT_TRUE(it != processor.mMetricsManagers.end());
     auto& metricsManager3 = it->second;
     EXPECT_FALSE(metricsManager3->isActive());
 
@@ -457,10 +482,30 @@
     auto& metricProducer6 = *metricIt;
     EXPECT_FALSE(metricProducer6->isActive());
 
+    // No broadcast for active configs should have happened yet.
+    EXPECT_EQ(broadcastCount, 0);
+
+    // Activate all 3 metrics that were not active.
     std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
     auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
 
+    // Assert that all 3 configs are active.
+    EXPECT_TRUE(metricsManager1->isActive());
+    EXPECT_TRUE(metricsManager2->isActive());
+    EXPECT_TRUE(metricsManager3->isActive());
+
+    // A broadcast should have happened, and all 3 configs should be active in the broadcast.
+    EXPECT_EQ(broadcastCount, 1);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 3);
+    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1)
+            != activeConfigsBroadcast.end());
+    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2)
+            != activeConfigsBroadcast.end());
+    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3)
+            != activeConfigsBroadcast.end());
+
+    // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns.
     int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
     EXPECT_TRUE(metricProducer3->isActive());
     int64_t ttl3 = metricProducer3->getRemainingTtlNs(shutDownTime);
@@ -472,8 +517,9 @@
     int64_t ttl6 = metricProducer6->getRemainingTtlNs(shutDownTime);
     EXPECT_EQ(100 + 100 * NS_PER_SEC, ttl6);
 
-    processor->WriteMetricsActivationToDisk(timeBase1 + 100 * NS_PER_SEC);
+    processor.WriteMetricsActivationToDisk(shutDownTime);
 
+    // Create a second StatsLogProcessor and push the same 3 configs.
     long timeBase2 = 1000;
     sp<StatsLogProcessor> processor2 =
             CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
@@ -481,6 +527,8 @@
     processor2->OnConfigUpdated(timeBase2, cfgKey3, config3);
 
     EXPECT_EQ(3, processor2->mMetricsManagers.size());
+
+    // First config and both metrics are active.
     it = processor2->mMetricsManagers.find(cfgKey1);
     EXPECT_TRUE(it != processor2->mMetricsManagers.end());
     auto& metricsManager1001 = it->second;
@@ -506,6 +554,7 @@
     auto& metricProducer1002 = *metricIt;
     EXPECT_TRUE(metricProducer1002->isActive());
 
+    // Second config is active. Metric 3 is inactive, metric 4 is active.
     it = processor2->mMetricsManagers.find(cfgKey2);
     EXPECT_TRUE(it != processor2->mMetricsManagers.end());
     auto& metricsManager1002 = it->second;
@@ -531,6 +580,7 @@
     auto& metricProducer1004 = *metricIt;
     EXPECT_TRUE(metricProducer1004->isActive());
 
+    // Config 3 is inactive. both metrics are inactive.
     it = processor2->mMetricsManagers.find(cfgKey3);
     EXPECT_TRUE(it != processor2->mMetricsManagers.end());
     auto& metricsManager1003 = it->second;
@@ -557,6 +607,7 @@
     auto& metricProducer1006 = *metricIt;
     EXPECT_FALSE(metricProducer1006->isActive());
 
+    // Assert that all 3 metrics with activation are inactive and that the ttls were properly set.
     EXPECT_FALSE(metricProducer1003->isActive());
     const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second;
     EXPECT_EQ(100 * NS_PER_SEC, activation1003.ttl_ns);
@@ -572,12 +623,16 @@
 
     processor2->LoadMetricsActivationFromDisk();
 
+    // After loading activations from disk, assert that all 3 metrics are active.
     EXPECT_TRUE(metricProducer1003->isActive());
     EXPECT_EQ(timeBase2 + ttl3 - activation1003.ttl_ns, activation1003.activation_ns);
     EXPECT_TRUE(metricProducer1005->isActive());
     EXPECT_EQ(timeBase2 + ttl5 - activation1005.ttl_ns, activation1005.activation_ns);
     EXPECT_TRUE(metricProducer1006->isActive());
     EXPECT_EQ(timeBase2 + ttl6 - activation1006.ttl_ns, activation1003.activation_ns);
+
+    // Make sure no more broadcasts have happened.
+    EXPECT_EQ(broadcastCount, 1);
 }
 
 TEST(StatsLogProcessorTest, TestActivationOnBoot) {
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
index 29e86f3..85d8a56 100644
--- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -66,17 +66,42 @@
     auto config = CreateStatsdConfig();
 
     int64_t bucketStartTimeNs = 10000000000;
-    int64_t bucketSizeNs =
-        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
 
-    ConfigKey cfgKey;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-    sp<MetricProducer> metricProducer =
-        processor->mMetricsManagers.begin()->second->mAllMetricProducers[0];
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> subscriberAlarmMonitor;
+    vector<int64_t> activeConfigsBroadcast;
+
+    long timeBase1 = 1;
+    int broadcastCount = 0;
+    StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
+            bucketStartTimeNs, [](const ConfigKey& key) { return true; },
+            [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
+                    const vector<int64_t>& activeConfigs) {
+                broadcastCount++;
+                EXPECT_EQ(broadcastUid, uid);
+                activeConfigsBroadcast.clear();
+                activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
+                        activeConfigs.begin(), activeConfigs.end());
+                return true;
+            });
+
+    processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
+
+    EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
     auto& eventActivationMap = metricProducer->mEventActivationMap;
 
+    EXPECT_FALSE(metricsManager->isActive());
     EXPECT_FALSE(metricProducer->mIsActive);
     // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
     // triggered by screen on event (tracker index 2).
@@ -93,13 +118,19 @@
     std::unique_ptr<LogEvent> event;
 
     event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
+    EXPECT_FALSE(metricsManager->isActive());
     EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 0);
 
     // Activated by battery save mode.
     event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
+    EXPECT_TRUE(metricsManager->isActive());
     EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 1);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
     EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
     EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
     EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -109,12 +140,13 @@
 
     // First processed event.
     event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
 
     // Activated by screen on event.
     event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
                                           bucketStartTimeNs + 20);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
+    EXPECT_TRUE(metricsManager->isActive());
     EXPECT_TRUE(metricProducer->mIsActive);
     EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
     EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
@@ -126,7 +158,8 @@
     // 2nd processed event.
     // The activation by screen_on event expires, but the one by battery save mode is still active.
     event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
+    EXPECT_TRUE(metricsManager->isActive());
     EXPECT_TRUE(metricProducer->mIsActive);
     EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
     EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
@@ -134,15 +167,21 @@
     EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive);
     EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20);
     EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+    // No new broadcast since the config should still be active.
+    EXPECT_EQ(broadcastCount, 1);
 
     // 3rd processed event.
     event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
 
     // All activations expired.
     event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
+    EXPECT_FALSE(metricsManager->isActive());
     EXPECT_FALSE(metricProducer->mIsActive);
+    // New broadcast since the config is no longer active.
+    EXPECT_EQ(broadcastCount, 2);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 0);
     EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive);
     EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
     EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -153,8 +192,12 @@
     // Re-activate.
     event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
                                           bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
+    EXPECT_TRUE(metricsManager->isActive());
     EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(broadcastCount, 3);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+    EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
     EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive);
     EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
     EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -163,11 +206,11 @@
     EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
 
     event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-    processor->OnLogEvent(event.get());
+    processor.OnLogEvent(event.get());
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+    processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
                             ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index b540964..b294cad 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -117,6 +117,7 @@
     DurationMetricProducer durationProducer(
             kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    durationProducer.mCondition = ConditionState::kFalse;
 
     EXPECT_FALSE(durationProducer.mCondition);
     EXPECT_FALSE(durationProducer.isConditionSliced());
@@ -140,6 +141,51 @@
     EXPECT_EQ(1LL, buckets2[0].mDuration);
 }
 
+TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) {
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
+
+    DurationMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+    int tagId = 1;
+    LogEvent event1(tagId, bucketStartTimeNs + 1);
+    event1.init();
+    LogEvent event2(tagId, bucketStartTimeNs + 2);
+    event2.init();
+    LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1);
+    event3.init();
+    LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3);
+    event4.init();
+
+    FieldMatcher dimensions;
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
+            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+    EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition);
+    EXPECT_FALSE(durationProducer.isConditionSliced());
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
+    durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+
+    durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
+    durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
+    durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
+    durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
+    EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
+    const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+    EXPECT_EQ(1UL, buckets2.size());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
+    EXPECT_EQ(1LL, buckets2[0].mDuration);
+}
+
 TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) {
     /**
      * The duration starts from the first bucket, through the two partial buckets (10-70sec),
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 572b199..7e7ffed 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -2176,7 +2176,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = true;
+    valueProducer.mCondition = ConditionState::kTrue;
 
     vector<shared_ptr<LogEvent>> allData;
     valueProducer.onDataPulled(allData, /** succeed */ false, bucketStartTimeNs);
@@ -2226,7 +2226,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = false;
+    valueProducer.mCondition = ConditionState::kFalse;
 
     // Max delay is set to 0 so pull will exceed max delay.
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
@@ -2257,7 +2257,7 @@
                                       eventMatcherWizard, tagId, bucket2StartTimeNs,
                                       bucket2StartTimeNs, pullerManager);
 
-    valueProducer.mCondition = false;
+    valueProducer.mCondition = ConditionState::kFalse;
 
     // Event should be skipped since it is from previous bucket.
     // Pull should not be called.
@@ -2299,7 +2299,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = false;
+    valueProducer.mCondition = ConditionState::kFalse;
     valueProducer.mHasGlobalBase = false;
 
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
@@ -2351,7 +2351,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = true;
+    valueProducer.mCondition = ConditionState::kTrue;
 
     // Bucket start.
     vector<shared_ptr<LogEvent>> allData;
@@ -2429,7 +2429,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = false;
+    valueProducer.mCondition = ConditionState::kFalse;
     valueProducer.onConditionChanged(true, bucket2StartTimeNs + 2);
     EXPECT_EQ(true, valueProducer.mCurrentBucketIsInvalid);
     EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
@@ -2481,7 +2481,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = true;
+    valueProducer.mCondition = ConditionState::kTrue;
 
     // Bucket start.
     vector<shared_ptr<LogEvent>> allData;
@@ -2564,7 +2564,7 @@
                                       eventMatcherWizard, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
-    valueProducer.mCondition = true;
+    valueProducer.mCondition = ConditionState::kTrue;
 
     // Bucket start.
     vector<shared_ptr<LogEvent>> allData;
diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp
new file mode 100644
index 0000000..f66de05
--- /dev/null
+++ b/cmds/statsd/tests/storage/StorageManager_test.cpp
@@ -0,0 +1,52 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include "src/storage/StorageManager.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using namespace testing;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+using testing::Contains;
+
+TEST(StorageManagerTest, TrainInfoReadWriteTest) {
+    InstallTrainInfo trainInfo;
+    trainInfo.trainVersionCode = 12345;
+    const char* expIds = "test_ids";
+    trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds));
+
+    StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.experimentIds);
+
+    InstallTrainInfo result;
+    StorageManager::readTrainInfo(result);
+    EXPECT_EQ(trainInfo.trainVersionCode, result.trainVersionCode);
+    EXPECT_EQ(trainInfo.experimentIds.size(), result.experimentIds.size());
+    EXPECT_EQ(trainInfo.experimentIds, result.experimentIds);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index b72ce89..dc2ed4c 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -542,8 +542,6 @@
 Landroid/net/IConnectivityManager;->getTetheredIfaces()[Ljava/lang/String;
 Landroid/net/IConnectivityManager;->getTetheringErroredIfaces()[Ljava/lang/String;
 Landroid/net/IConnectivityManager;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V
-Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd;
-Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V
 Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V
 Landroid/net/INetworkPolicyListener$Stub;-><init>()V
 Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager;
@@ -2825,7 +2823,6 @@
 Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;-><init>()V
 Lcom/android/internal/telephony/GsmCdmaCall;->attachFake(Lcom/android/internal/telephony/Connection;Lcom/android/internal/telephony/Call$State;)V
 Lcom/android/internal/telephony/GsmCdmaCallTracker;->clearDisconnected()V
-Lcom/android/internal/telephony/GsmCdmaCallTracker;->dialThreeWay(Ljava/lang/String;)Lcom/android/internal/telephony/Connection;
 Lcom/android/internal/telephony/GsmCdmaCallTracker;->disableDataCallInEmergencyCall(Ljava/lang/String;)V
 Lcom/android/internal/telephony/GsmCdmaCallTracker;->fakeHoldForegroundBeforeDial()V
 Lcom/android/internal/telephony/GsmCdmaCallTracker;->getPhone()Lcom/android/internal/telephony/GsmCdmaPhone;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ca3c726..5d4f988 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -55,6 +55,7 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
@@ -68,6 +69,7 @@
 import android.util.Singleton;
 import android.util.Size;
 
+import com.android.internal.app.LocalePicker;
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.os.RoSystemProperties;
 import com.android.internal.os.TransferPipe;
@@ -84,7 +86,9 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * <p>
@@ -3451,6 +3455,35 @@
     }
 
     /**
+     * Sets the current locales of the device. Calling app must have the permission
+     * {@code android.permission.CHANGE_CONFIGURATION} and
+     * {@code android.permission.WRITE_SETTINGS}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void setDeviceLocales(@NonNull LocaleList locales) {
+        LocalePicker.updateLocales(locales);
+    }
+
+    /**
+     * Returns a list of supported locales by this system. It includes all locales that are
+     * selectable by the user, potentially including locales that the framework does not have
+     * translated resources for. To get locales that the framework has translated resources for, use
+     * {@code Resources.getSystem().getAssets().getLocales()} instead.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @NonNull Collection<Locale> getSupportedLocales() {
+        ArrayList<Locale> locales = new ArrayList<>();
+        for (String localeTag : LocalePicker.getSupportedLocales(mContext)) {
+            locales.add(Locale.forLanguageTag(localeTag));
+        }
+        return locales;
+    }
+
+    /**
      * Get the device configuration attributes.
      */
     public ConfigurationInfo getDeviceConfigurationInfo() {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 64b94a9..f76f7b9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -848,6 +848,7 @@
     /** @hide Has a legacy (non-isolated) view of storage. */
     public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
     /** @hide Interact with accessibility. */
+    @SystemApi
     public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
 
     // Warning: If an permission is added here it also has to be added to
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1a728c1..6908ca2 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2101,7 +2101,8 @@
     }
 
     private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
-            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
+            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo,
+            String[] overlayDirs) {
         final String[] splitResDirs;
         final ClassLoader classLoader;
         try {
@@ -2113,7 +2114,7 @@
         return ResourcesManager.getInstance().getResources(activityToken,
                 pi.getResDir(),
                 splitResDirs,
-                pi.getOverlayDirs(),
+                overlayDirs,
                 pi.getApplicationInfo().sharedLibraryFiles,
                 displayId,
                 overrideConfig,
@@ -2131,9 +2132,11 @@
                     new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null);
 
             final int displayId = getDisplayId();
-
+            // overlayDirs is retrieved directly from ApplicationInfo since ActivityThread may have
+            // a LoadedApk containing Resources with stale overlays for a remote application.
+            final String[] overlayDirs = application.resourceDirs;
             c.setResources(createResources(mActivityToken, pi, null, displayId, null,
-                    getDisplayAdjustments(displayId).getCompatibilityInfo()));
+                    getDisplayAdjustments(displayId).getCompatibilityInfo(), overlayDirs));
             if (c.mResources != null) {
                 return c;
             }
@@ -2168,7 +2171,7 @@
             final int displayId = getDisplayId();
 
             c.setResources(createResources(mActivityToken, pi, null, displayId, null,
-                    getDisplayAdjustments(displayId).getCompatibilityInfo()));
+                    getDisplayAdjustments(displayId).getCompatibilityInfo(), pi.getOverlayDirs()));
             if (c.mResources != null) {
                 return c;
             }
@@ -2218,7 +2221,8 @@
 
         final int displayId = getDisplayId();
         context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
-                overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
+                overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
+                mPackageInfo.getOverlayDirs()));
         return context;
     }
 
@@ -2233,7 +2237,8 @@
 
         final int displayId = display.getDisplayId();
         context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
-                null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
+                null, getDisplayAdjustments(displayId).getCompatibilityInfo(),
+                mPackageInfo.getOverlayDirs()));
         context.mDisplay = display;
         return context;
     }
@@ -2416,7 +2421,7 @@
         ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
                 null, null, 0, null, null);
         context.setResources(createResources(null, packageInfo, null, displayId, null,
-                packageInfo.getCompatibilityInfo()));
+                packageInfo.getCompatibilityInfo(), packageInfo.getOverlayDirs()));
         context.updateDisplay(displayId);
         return context;
     }
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 1878d84..077652c 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -42,12 +42,10 @@
 public class StatusBarManager {
 
     /** @hide */
-    @SystemApi
     public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND;
     /** @hide */
     public static final int DISABLE_NOTIFICATION_ICONS = View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS;
     /** @hide */
-    @SystemApi
     public static final int DISABLE_NOTIFICATION_ALERTS
             = View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS;
 
@@ -59,17 +57,14 @@
     /** @hide */
     public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO;
     /** @hide */
-    @SystemApi
     public static final int DISABLE_HOME = View.STATUS_BAR_DISABLE_HOME;
     /** @hide */
-    @SystemApi
     public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT;
     /** @hide */
     public static final int DISABLE_BACK = View.STATUS_BAR_DISABLE_BACK;
     /** @hide */
     public static final int DISABLE_CLOCK = View.STATUS_BAR_DISABLE_CLOCK;
     /** @hide */
-    @SystemApi
     public static final int DISABLE_SEARCH = View.STATUS_BAR_DISABLE_SEARCH;
 
     /** @hide */
@@ -78,7 +73,6 @@
             View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_RECENT;
 
     /** @hide */
-    @SystemApi
     public static final int DISABLE_NONE = 0x00000000;
 
     /** @hide */
@@ -122,7 +116,6 @@
     public static final int DISABLE2_ROTATE_SUGGESTIONS = 1 << 4;
 
     /** @hide */
-    @SystemApi
     public static final int DISABLE2_NONE = 0x00000000;
 
     /** @hide */
@@ -387,14 +380,14 @@
     }
 
     /**
-     * Get the currently applied StatusBar disable flags
+     * Get this app's currently requested disabled components
      *
-     * @return a pair of Integers in the form of (disable, disable2)
+     * @return a new DisableInfo
      *
      * @hide
      */
     @SystemApi
-    public Pair<Integer, Integer> getDisableFlags() {
+    public DisableInfo getDisableInfo() {
         try {
             final int userId = Binder.getCallingUserHandle().getIdentifier();
             final IStatusBarService svc = getService();
@@ -403,7 +396,7 @@
                 flags = svc.getDisableFlags(mToken, userId);
             }
 
-            return new Pair<Integer, Integer>(flags[0], flags[1]);
+            return new DisableInfo(flags[0], flags[1]);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
@@ -416,4 +409,180 @@
         if (state == WINDOW_STATE_SHOWING) return "WINDOW_STATE_SHOWING";
         return "WINDOW_STATE_UNKNOWN";
     }
+
+    /**
+     * DisableInfo describes this app's requested state of the StatusBar with regards to which
+     * components are enabled/disabled
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class DisableInfo {
+
+        private boolean mStatusBarExpansion;
+        private boolean mNavigateHome;
+        private boolean mNotificationPeeking;
+        private boolean mRecents;
+        private boolean mSearch;
+
+        /** @hide */
+        public DisableInfo(int flags1, int flags2) {
+            mStatusBarExpansion = (flags1 & DISABLE_EXPAND) != 0;
+            mNavigateHome = (flags1 & DISABLE_HOME) != 0;
+            mNotificationPeeking = (flags1 & DISABLE_NOTIFICATION_ALERTS) != 0;
+            mRecents = (flags1 & DISABLE_RECENT) != 0;
+            mSearch = (flags1 & DISABLE_SEARCH) != 0;
+        }
+
+        /** @hide */
+        public DisableInfo() {}
+
+        /**
+         * @return {@code true} if expanding the notification shade is disabled
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean isStatusBarExpansionDisabled() {
+            return mStatusBarExpansion;
+        }
+
+        /** * @hide */
+        public void setStatusBarExpansionDisabled(boolean disabled) {
+            mStatusBarExpansion = disabled;
+        }
+
+        /**
+         * @return {@code true} if navigation home is disabled
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean isNavigateToHomeDisabled() {
+            return mNavigateHome;
+        }
+
+        /** * @hide */
+        public void setNagivationHomeDisabled(boolean disabled) {
+            mNavigateHome = disabled;
+        }
+
+        /**
+         * @return {@code true} if notification peeking (heads-up notification) is disabled
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean isNotificationPeekingDisabled() {
+            return mNotificationPeeking;
+        }
+
+        /** @hide */
+        public void setNotificationPeekingDisabled(boolean disabled) {
+            mNotificationPeeking = disabled;
+        }
+
+        /**
+         * @return {@code true} if mRecents/overview is disabled
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean isRecentsDisabled() {
+            return mRecents;
+        }
+
+        /**  @hide */
+        public void setRecentsDisabled(boolean disabled) {
+            mRecents = disabled;
+        }
+
+        /**
+         * @return {@code true} if mSearch is disabled
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean isSearchDisabled() {
+            return mSearch;
+        }
+
+        /** @hide */
+        public void setSearchDisabled(boolean disabled) {
+            mSearch = disabled;
+        }
+
+        /**
+         * @return {@code true} if no components are disabled (default state)
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean areNoComponentsDisabled() {
+            return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents
+                    && !mSearch;
+        }
+
+        /** @hide */
+        public void setEnableAll() {
+            mStatusBarExpansion = false;
+            mNavigateHome = false;
+            mNotificationPeeking = false;
+            mRecents = false;
+            mSearch = false;
+        }
+
+        /**
+         * @return {@code true} if all status bar components are disabled
+         *
+         * @hide
+         */
+        public boolean areAllComponentsDisabled() {
+            return mStatusBarExpansion && mNavigateHome && mNotificationPeeking
+                    && mRecents && mSearch;
+        }
+
+        /** @hide */
+        public void setDisableAll() {
+            mStatusBarExpansion = true;
+            mNavigateHome = true;
+            mNotificationPeeking = true;
+            mRecents = true;
+            mSearch = true;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("DisableInfo: ");
+            sb.append(" mStatusBarExpansion=").append(mStatusBarExpansion ? "disabled" : "enabled");
+            sb.append(" mNavigateHome=").append(mNavigateHome ? "disabled" : "enabled");
+            sb.append(" mNotificationPeeking=")
+                    .append(mNotificationPeeking ? "disabled" : "enabled");
+            sb.append(" mRecents=").append(mRecents ? "disabled" : "enabled");
+            sb.append(" mSearch=").append(mSearch ? "disabled" : "enabled");
+
+            return sb.toString();
+
+        }
+
+        /**
+         * Convert a DisableInfo to equivalent flags
+         * @return a pair of equivalent disable flags
+         *
+         * @hide
+         */
+        public Pair<Integer, Integer> toFlags() {
+            int disable1 = DISABLE_NONE;
+            int disable2 = DISABLE2_NONE;
+
+            if (mStatusBarExpansion) disable1 |= DISABLE_EXPAND;
+            if (mNavigateHome) disable1 |= DISABLE_HOME;
+            if (mNotificationPeeking) disable1 |= DISABLE_NOTIFICATION_ALERTS;
+            if (mRecents) disable1 |= DISABLE_RECENT;
+            if (mSearch) disable1 |= DISABLE_SEARCH;
+
+            return new Pair<Integer, Integer>(disable1, disable2);
+        }
+    }
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 807b7f2..b9f56b1 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -101,13 +101,11 @@
 import android.net.IEthernetManager;
 import android.net.IIpMemoryStore;
 import android.net.IIpSecService;
-import android.net.INetd;
 import android.net.INetworkPolicyManager;
 import android.net.IpMemoryStore;
 import android.net.IpSecManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkScoreManager;
-import android.net.NetworkStack;
 import android.net.NetworkWatchlistManager;
 import android.net.lowpan.ILowpanManager;
 import android.net.lowpan.LowpanManager;
@@ -330,21 +328,13 @@
                 return new ConnectivityManager(context, service);
             }});
 
-        registerService(Context.NETD_SERVICE, INetd.class, new StaticServiceFetcher<INetd>() {
+        registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
             @Override
-            public INetd createService() throws ServiceNotFoundException {
-                return INetd.Stub.asInterface(
-                        ServiceManager.getServiceOrThrow(Context.NETD_SERVICE));
+            public IBinder createService() throws ServiceNotFoundException {
+                return ServiceManager.getServiceOrThrow(Context.NETD_SERVICE);
             }
         });
 
-        registerService(Context.NETWORK_STACK_SERVICE, NetworkStack.class,
-                new StaticServiceFetcher<NetworkStack>() {
-                    @Override
-                    public NetworkStack createService() {
-                        return new NetworkStack();
-                    }});
-
         registerService(Context.IP_MEMORY_STORE_SERVICE, IpMemoryStore.class,
                 new CachedServiceFetcher<IpMemoryStore>() {
                     @Override
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 957a484..25bfba2 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -49,7 +49,6 @@
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
-import android.net.NetworkStack;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -3647,11 +3646,10 @@
     public static final String NETD_SERVICE = "netd";
 
     /**
-     * Use with {@link #getSystemService(String)} to retrieve a
-     * {@link NetworkStack} for communicating with the network stack
+     * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+     * {@link NetworkStackClient} IBinder for communicating with the network stack
      * @hide
-     * @see #getSystemService(String)
-     * @see NetworkStack
+     * @see NetworkStackClient
      */
     public static final String NETWORK_STACK_SERVICE = "network_stack";
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d781a96..a5e7e95 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1790,6 +1790,35 @@
             "android.intent.action.MANAGE_APP_PERMISSIONS";
 
     /**
+     * Activity action: Launch UI to manage a specific permissions of an app.
+     * <p>
+     * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permission
+     * will be managed by the launched UI.
+     * </p>
+     * <p>
+     * Input: {@link #EXTRA_PERMISSION_NAME} specifies the (individual) permission
+     * that should be managed by the launched UI.
+     * </p>
+     * <p>
+     * <li> {@link #EXTRA_USER} specifies the UserHandle of the user that owns the app.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @see #EXTRA_PACKAGE_NAME
+     * @see #EXTRA_PERMISSION_NAME
+     * @see #EXTRA_USER
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_APP_PERMISSION =
+            "android.intent.action.MANAGE_APP_PERMISSION";
+
+    /**
      * Activity action: Launch UI to manage permissions.
      * <p>
      * Input: Nothing.
@@ -2080,6 +2109,22 @@
     public static final String ACTION_REVIEW_APP_PERMISSION_USAGE =
             "android.intent.action.REVIEW_APP_PERMISSION_USAGE";
 
+    /**
+     * Activity action: Launch UI to review running accessibility services.
+     * <p>
+     * Input: Nothing.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES =
+            "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent broadcast actions (see action variable).
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index eaf6c5a..3ea78df 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5910,24 +5910,24 @@
     /**
      * Flag to denote no restrictions. This should be used to clear any restrictions that may have
      * been previously set for the package.
-     * @see PackageManager.DistractionRestriction
      * @hide
+     * @see #setDistractingPackageRestrictions(String[], int)
      */
     @SystemApi
     public static final int RESTRICTION_NONE = 0x0;
 
     /**
      * Flag to denote that a package should be hidden from any suggestions to the user.
-     * @see PackageManager.DistractionRestriction
      * @hide
+     * @see #setDistractingPackageRestrictions(String[], int)
      */
     @SystemApi
     public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 0x00000001;
 
     /**
      * Flag to denote that a package's notifications should be hidden.
-     * @see PackageManager.DistractionRestriction
      * @hide
+     * @see #setDistractingPackageRestrictions(String[], int)
      */
     @SystemApi
     public static final int RESTRICTION_HIDE_NOTIFICATIONS = 0x00000002;
@@ -5939,7 +5939,6 @@
      * @see #setDistractingPackageRestrictions(String[], int)
      * @hide
      */
-    @SystemApi
     @IntDef(flag = true, prefix = {"RESTRICTION_"}, value = {
             RESTRICTION_NONE,
             RESTRICTION_HIDE_FROM_SUGGESTIONS,
@@ -5957,14 +5956,16 @@
      * <p>The caller must hold {@link android.Manifest.permission#SUSPEND_APPS} to use this API.
      *
      * @param packages Packages to mark as distracting.
-     * @param restrictionFlags Any combination of {@link DistractionRestriction restrictions} to
-     *                         impose on the given packages. {@link #RESTRICTION_NONE} can be used
-     *                         to clear any existing restrictions.
+     * @param restrictionFlags Any combination of restrictions to impose on the given packages.
+     *                         {@link #RESTRICTION_NONE} can be used to clear any existing
+     *                         restrictions.
      * @return A list of packages that could not have the {@code restrictionFlags} set. The system
      * may prevent restricting critical packages to preserve normal device function.
      *
-     * @see DistractionRestriction
      * @hide
+     * @see #RESTRICTION_NONE
+     * @see #RESTRICTION_HIDE_FROM_SUGGESTIONS
+     * @see #RESTRICTION_HIDE_NOTIFICATIONS
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.SUSPEND_APPS)
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index cfe35b0..270e387 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -163,6 +163,30 @@
     }
 
     /**
+     * Provider for default home
+     */
+    public interface DefaultHomeProvider {
+
+        /**
+         * Get the package name of the default home.
+         *
+         * @param userId the user id
+         *
+         * @return the package name of the default home, or {@code null} if none
+         */
+        @Nullable
+        String getDefaultHome(@UserIdInt int userId);
+
+        /**
+         * Set the package name of the default home.
+         *
+         * @param packageName package name of the default home, or {@code null} to remove
+         * @param userId the user id
+         */
+        void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId);
+    }
+
+    /**
      * Sets the location provider packages provider.
      * @param provider The packages provider.
      */
@@ -886,4 +910,11 @@
      * @param provider the provider
      */
     public abstract void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider);
+
+    /**
+     * Sets the default home provider.
+     *
+     * @param provider the provider
+     */
+    public abstract void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider);
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0abd5ea..0f67262 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4848,7 +4848,7 @@
     }
 
     /**
-     * Sets every the max aspect ratio of every child activity that doesn't already have an aspect
+     * Sets every the min aspect ratio of every child activity that doesn't already have an aspect
      * ratio set.
      */
     private void setMinAspectRatio(Package owner) {
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index d257c03..a696eeb 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -156,21 +156,21 @@
     }
 
     /**
-     * Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
+     * Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
      *
      * @param token an opaque token returned by password confirmation.
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
-    public void resetTimeout(byte[] token) {
+    public void resetLockout(byte[] token) {
         if (mService != null) {
             try {
-                mService.resetTimeout(token);
+                mService.resetLockout(token);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
         } else {
-            Slog.w(TAG, "resetTimeout(): Service not connected");
+            Slog.w(TAG, "resetLockout(): Service not connected");
         }
     }
 
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index a20e2bf..4971911 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -49,8 +49,8 @@
     // Client lifecycle is still managed in <Biometric>Service.
     void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId);
 
-    // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
-    void resetTimeout(in byte [] token);
+    // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
+    void resetLockout(in byte [] token);
 
     // TODO(b/123378871): Remove when moved.
     // CDCA needs to send results to BiometricService if it was invoked using BiometricPrompt's
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index efe24e5..55b340f 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -107,6 +107,11 @@
             mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
                     new Face(null, faceId, deviceId)).sendToTarget();
         }
+
+        @Override
+        public void onEnumerated(long deviceId, int faceId, int remaining) {
+            // TODO: Finish. Low priority since it's not used.
+        }
     };
 
     /**
@@ -474,25 +479,6 @@
     }
 
     /**
-     * Reset the lockout timer when asked to do so by keyguard.
-     *
-     * @param token an opaque token returned by password confirmation.
-     * @hide
-     */
-    @RequiresPermission(MANAGE_BIOMETRIC)
-    public void resetTimeout(byte[] token) {
-        if (mService != null) {
-            try {
-                mService.resetTimeout(token);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        } else {
-            Log.w(TAG, "resetTimeout(): Service not connected!");
-        }
-    }
-
-    /**
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index f67760a..9609e99 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -86,8 +86,8 @@
     // Gets the authenticator ID for face
     long getAuthenticatorId(String opPackageName);
 
-    // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
-    void resetTimeout(in byte [] cryptoToken);
+    // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
+    void resetLockout(in byte [] token);
 
     // Add a callback which gets notified when the face lockout period expired.
     void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index b88574b..cec9fd8 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -28,4 +28,5 @@
     void onAuthenticationFailed(long deviceId);
     void onError(long deviceId, int error, int vendorCode);
     void onRemoved(long deviceId, int faceId, int remaining);
+    void onEnumerated(long deviceId, int faceId, int remaining);
 }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 80d404d..d0622c8 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -18,7 +18,6 @@
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.MANAGE_FINGERPRINT;
-import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_FINGERPRINT;
 
@@ -724,26 +723,6 @@
     }
 
     /**
-     * Reset the lockout timer when asked to do so by keyguard.
-     *
-     * @param token an opaque token returned by password confirmation.
-     *
-     * @hide
-     */
-    @RequiresPermission(RESET_FINGERPRINT_LOCKOUT)
-    public void resetTimeout(byte[] token) {
-        if (mService != null) {
-            try {
-                mService.resetTimeout(token);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        } else {
-            Slog.w(TAG, "resetTimeout(): Service not connected!");
-        }
-    }
-
-    /**
      * @hide
      */
     public void addLockoutResetCallback(final LockoutResetCallback callback) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 2aca55a..6d195ae 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -678,11 +678,20 @@
     @Deprecated
     public static final int TYPE_VPN = 17;
 
-    /** {@hide} */
-    public static final int MAX_RADIO_TYPE   = TYPE_VPN;
+    /**
+     * A network that is exclusively meant to be used for testing
+     *
+     * @deprecated Use {@link NetworkCapabilities} instead.
+     * @hide
+     */
+    @Deprecated
+    public static final int TYPE_TEST = 18; // TODO: Remove this once NetworkTypes are unused.
 
     /** {@hide} */
-    public static final int MAX_NETWORK_TYPE = TYPE_VPN;
+    public static final int MAX_RADIO_TYPE = TYPE_TEST;
+
+    /** {@hide} */
+    public static final int MAX_NETWORK_TYPE = TYPE_TEST;
 
     private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
 
@@ -3927,15 +3936,16 @@
      *
      * <p>This endpoint is exclusively for use by the NetworkStack and is protected by the
      * corresponding permission.
+     * @param network Network on which the captive portal was detected.
      * @param appExtras Extras to include in the app start intent.
      * @hide
      */
     @SystemApi
     @TestApi
     @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
-    public void startCaptivePortalApp(Bundle appExtras) {
+    public void startCaptivePortalApp(Network network, Bundle appExtras) {
         try {
-            mService.startCaptivePortalAppInternal(appExtras);
+            mService.startCaptivePortalAppInternal(network, appExtras);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 872671f..87c62d2 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -168,7 +168,7 @@
     void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
     void setAvoidUnvalidated(in Network network);
     void startCaptivePortalApp(in Network network);
-    void startCaptivePortalAppInternal(in Bundle appExtras);
+    void startCaptivePortalAppInternal(in Network network, in Bundle appExtras);
 
     boolean getAvoidBadWifi();
     int getMultipathPreference(in Network Network);
diff --git a/core/java/android/net/INetworkMonitor.aidl b/core/java/android/net/INetworkMonitor.aidl
index 41f969a..c94cdde 100644
--- a/core/java/android/net/INetworkMonitor.aidl
+++ b/core/java/android/net/INetworkMonitor.aidl
@@ -34,6 +34,7 @@
 
     void start();
     void launchCaptivePortalApp();
+    void notifyCaptivePortalAppFinished(int response);
     void forceReevaluation(int uid);
     void notifyPrivateDnsChanged(in PrivateDnsConfigParcel config);
     void notifyDnsResponse(int returnCode);
diff --git a/core/java/android/net/INetworkMonitorCallbacks.aidl b/core/java/android/net/INetworkMonitorCallbacks.aidl
index 5146585..2c61511 100644
--- a/core/java/android/net/INetworkMonitorCallbacks.aidl
+++ b/core/java/android/net/INetworkMonitorCallbacks.aidl
@@ -26,5 +26,4 @@
     void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config);
     void showProvisioningNotification(String action, String packageName);
     void hideProvisioningNotification();
-    void logCaptivePortalLoginEvent(int eventId, String packageName);
 }
\ No newline at end of file
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
index 62cf7d7..b9d49c1 100644
--- a/core/java/android/net/InterfaceConfiguration.java
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -36,8 +36,9 @@
     private LinkAddress mAddr;
     private HashSet<String> mFlags = Sets.newHashSet();
 
-    private static final String FLAG_UP = INetd.IF_STATE_UP;
-    private static final String FLAG_DOWN = INetd.IF_STATE_DOWN;
+    // Must be kept in sync with constant in INetd.aidl
+    private static final String FLAG_UP = "up";
+    private static final String FLAG_DOWN = "down";
 
     private static final  String[] EMPTY_STRING_ARRAY = new String[0];
 
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 7e9bda1..1d2d81d 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -597,6 +597,7 @@
             TRANSPORT_VPN,
             TRANSPORT_WIFI_AWARE,
             TRANSPORT_LOWPAN,
+            TRANSPORT_TEST,
     })
     public @interface Transport { }
 
@@ -635,10 +636,18 @@
      */
     public static final int TRANSPORT_LOWPAN = 6;
 
+    /**
+     * Indicates this network uses a Test-only virtual interface as a transport.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final int TRANSPORT_TEST = 7;
+
     /** @hide */
     public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
     /** @hide */
-    public static final int MAX_TRANSPORT = TRANSPORT_LOWPAN;
+    public static final int MAX_TRANSPORT = TRANSPORT_TEST;
 
     /** @hide */
     public static boolean isValidTransport(@Transport int transportType) {
@@ -652,7 +661,8 @@
         "ETHERNET",
         "VPN",
         "WIFI_AWARE",
-        "LOWPAN"
+        "LOWPAN",
+        "TEST"
     };
 
     /**
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 5ab34e9..bf27262 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -92,16 +92,6 @@
     public static final int MASK_ALL_NETWORKS     = 0b11110000;
 
     public static final int FIREWALL_RULE_DEFAULT = 0;
-    public static final int FIREWALL_RULE_ALLOW = INetd.FIREWALL_RULE_ALLOW;
-    public static final int FIREWALL_RULE_DENY = INetd.FIREWALL_RULE_DENY;
-
-    public static final int FIREWALL_TYPE_WHITELIST = INetd.FIREWALL_WHITELIST;
-    public static final int FIREWALL_TYPE_BLACKLIST = INetd.FIREWALL_BLACKLIST;
-
-    public static final int FIREWALL_CHAIN_NONE = INetd.FIREWALL_CHAIN_NONE;
-    public static final int FIREWALL_CHAIN_DOZABLE = INetd.FIREWALL_CHAIN_DOZABLE;
-    public static final int FIREWALL_CHAIN_STANDBY = INetd.FIREWALL_CHAIN_STANDBY;
-    public static final int FIREWALL_CHAIN_POWERSAVE = INetd.FIREWALL_CHAIN_POWERSAVE;
 
     public static final String FIREWALL_CHAIN_NAME_NONE = "none";
     public static final String FIREWALL_CHAIN_NAME_DOZABLE = "dozable";
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index ca49438..dbb894f 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -15,46 +15,17 @@
  */
 package android.net;
 
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.SystemService;
 import android.annotation.TestApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.dhcp.IDhcpServerCallbacks;
-import android.net.ip.IIpClientCallbacks;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
 
 /**
- * Service used to communicate with the network stack, which is running in a separate module.
+ *
+ * Constants for client code communicating with the network stack service.
  * @hide
  */
-@SystemService(Context.NETWORK_STACK_SERVICE)
 @SystemApi
 @TestApi
 public class NetworkStack {
-    private static final String TAG = NetworkStack.class.getSimpleName();
-
     /**
      * Permission granted only to the NetworkStack APK, defined in NetworkStackStub with signature
      * protection level.
@@ -65,235 +36,5 @@
     public static final String PERMISSION_MAINLINE_NETWORK_STACK =
             "android.permission.MAINLINE_NETWORK_STACK";
 
-    private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
-
-    @NonNull
-    @GuardedBy("mPendingNetStackRequests")
-    private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
-    @Nullable
-    @GuardedBy("mPendingNetStackRequests")
-    private INetworkStackConnector mConnector;
-
-    private volatile boolean mNetworkStackStartRequested = false;
-
-    private interface NetworkStackCallback {
-        void onNetworkStackConnected(INetworkStackConnector connector);
-    }
-
-    /** @hide */
-    public NetworkStack() { }
-
-    /**
-     * Create a DHCP server according to the specified parameters.
-     *
-     * <p>The server will be returned asynchronously through the provided callbacks.
-     * @hide
-     */
-    public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
-            final IDhcpServerCallbacks cb) {
-        requestConnector(connector -> {
-            try {
-                connector.makeDhcpServer(ifName, params, cb);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            }
-        });
-    }
-
-    /**
-     * Create an IpClient on the specified interface.
-     *
-     * <p>The IpClient will be returned asynchronously through the provided callbacks.
-     * @hide
-     */
-    public void makeIpClient(String ifName, IIpClientCallbacks cb) {
-        requestConnector(connector -> {
-            try {
-                connector.makeIpClient(ifName, cb);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            }
-        });
-    }
-
-    /**
-     * Create a NetworkMonitor.
-     *
-     * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
-     * @hide
-     */
-    public void makeNetworkMonitor(
-            NetworkParcelable network, String name, INetworkMonitorCallbacks cb) {
-        requestConnector(connector -> {
-            try {
-                connector.makeNetworkMonitor(network, name, cb);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            }
-        });
-    }
-
-    private class NetworkStackConnection implements ServiceConnection {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            registerNetworkStackService(service);
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            // TODO: crash/reboot the system ?
-            Slog.wtf(TAG, "Lost network stack connector");
-        }
-    };
-
-    private void registerNetworkStackService(@NonNull IBinder service) {
-        final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service);
-
-        ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
-                DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
-
-        final ArrayList<NetworkStackCallback> requests;
-        synchronized (mPendingNetStackRequests) {
-            requests = new ArrayList<>(mPendingNetStackRequests);
-            mPendingNetStackRequests.clear();
-            mConnector = connector;
-        }
-
-        for (NetworkStackCallback r : requests) {
-            r.onNetworkStackConnected(connector);
-        }
-    }
-
-    /**
-     * Start the network stack. Should be called only once on device startup.
-     *
-     * <p>This method will start the network stack either in the network stack process, or inside
-     * the system server on devices that do not support the network stack module. The network stack
-     * connector will then be delivered asynchronously to clients that requested it before it was
-     * started.
-     * @hide
-     */
-    public void start(Context context) {
-        mNetworkStackStartRequested = true;
-        // Try to bind in-process if the library is available
-        IBinder connector = null;
-        try {
-            final Class service = Class.forName(
-                    "com.android.server.NetworkStackService",
-                    true /* initialize */,
-                    context.getClassLoader());
-            connector = (IBinder) service.getMethod("makeConnector", Context.class)
-                    .invoke(null, context);
-        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-            Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
-            // TODO: crash/reboot system here ?
-            return;
-        } catch (ClassNotFoundException e) {
-            // Normal behavior if stack is provided by the app: fall through
-        }
-
-        // In-process network stack. Add the service to the service manager here.
-        if (connector != null) {
-            registerNetworkStackService(connector);
-            return;
-        }
-        // Start the network stack process. The service will be added to the service manager in
-        // NetworkStackConnection.onServiceConnected().
-        final Intent intent = new Intent(INetworkStackConnector.class.getName());
-        final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
-        intent.setComponent(comp);
-
-        if (comp == null) {
-            Slog.wtf(TAG, "Could not resolve the network stack with " + intent);
-            // TODO: crash/reboot system server ?
-            return;
-        }
-
-        final PackageManager pm = context.getPackageManager();
-        int uid = -1;
-        try {
-            uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM);
-        } catch (PackageManager.NameNotFoundException e) {
-            Slog.wtf("Network stack package not found", e);
-            // Fall through
-        }
-
-        if (uid != Process.NETWORK_STACK_UID) {
-            throw new SecurityException("Invalid network stack UID: " + uid);
-        }
-
-        final int hasPermission =
-                pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName());
-        if (hasPermission != PERMISSION_GRANTED) {
-            throw new SecurityException(
-                    "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK);
-        }
-
-        if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
-                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
-            Slog.wtf(TAG,
-                    "Could not bind to network stack in-process, or in app with " + intent);
-            // TODO: crash/reboot system server if no network stack after a timeout ?
-        }
-    }
-
-    /**
-     * For non-system server clients, get the connector registered by the system server.
-     */
-    private INetworkStackConnector getRemoteConnector() {
-        // Block until the NetworkStack connector is registered in ServiceManager.
-        // <p>This is only useful for non-system processes that do not have a way to be notified of
-        // registration completion. Adding a callback system would be too heavy weight considering
-        // that the connector is registered on boot, so it is unlikely that a client would request
-        // it before it is registered.
-        // TODO: consider blocking boot on registration and simplify much of the logic in this class
-        IBinder connector;
-        try {
-            final long before = System.currentTimeMillis();
-            while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
-                Thread.sleep(20);
-                if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
-                    Slog.e(TAG, "Timeout waiting for NetworkStack connector");
-                    return null;
-                }
-            }
-        } catch (InterruptedException e) {
-            Slog.e(TAG, "Error waiting for NetworkStack connector", e);
-            return null;
-        }
-
-        return INetworkStackConnector.Stub.asInterface(connector);
-    }
-
-    private void requestConnector(@NonNull NetworkStackCallback request) {
-        // TODO: PID check.
-        final int caller = Binder.getCallingUid();
-        if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) {
-            // Don't even attempt to obtain the connector and give a nice error message
-            throw new SecurityException(
-                    "Only the system server should try to bind to the network stack.");
-        }
-
-        if (!mNetworkStackStartRequested) {
-            // The network stack is not being started in this process, e.g. this process is not
-            // the system server. Get a remote connector registered by the system server.
-            final INetworkStackConnector connector = getRemoteConnector();
-            synchronized (mPendingNetStackRequests) {
-                mConnector = connector;
-            }
-            request.onNetworkStackConnected(connector);
-            return;
-        }
-
-        final INetworkStackConnector connector;
-        synchronized (mPendingNetStackRequests) {
-            connector = mConnector;
-            if (connector == null) {
-                mPendingNetStackRequests.add(request);
-                return;
-            }
-        }
-
-        request.onNetworkStackConnected(connector);
-    }
+    private NetworkStack() {}
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index ab6dd7c..b7e65b9 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -46,6 +46,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -262,6 +263,7 @@
     private static final long BYTES_PER_KB = 1024;
     private static final long BYTES_PER_MB = 1048576; // 1024^2
     private static final long BYTES_PER_GB = 1073741824; //1024^3
+    public static final double MILLISECONDS_IN_HOUR = 3600 * 1000;
 
     private static final String VERSION_DATA = "vers";
     private static final String UID_DATA = "uid";
@@ -482,6 +484,13 @@
          * yield a value of 0 if the device doesn't support power calculations.
          */
         public abstract LongCounter getPowerCounter();
+
+        /**
+         * @return a non-null {@link LongCounter} representing total power monitored on the rails
+         * in mAms (miliamps-milliseconds). The counter may always yield a value of 0 if the device
+         * doesn't support power rail monitoring.
+         */
+        public abstract LongCounter getMonitoredRailChargeConsumedMaMs();
     }
 
     /**
@@ -1526,6 +1535,9 @@
         // The charge of the battery in micro-Ampere-hours.
         public int batteryChargeUAh;
 
+        public double modemRailChargeMah;
+        public double wifiRailChargeMah;
+
         // Constants from SCREEN_BRIGHTNESS_*
         public static final int STATE_BRIGHTNESS_SHIFT = 0;
         public static final int STATE_BRIGHTNESS_MASK = 0x7;
@@ -1738,6 +1750,8 @@
                     | ((((int)batteryVoltage)<<16)&0xffff0000);
             dest.writeInt(bat);
             dest.writeInt(batteryChargeUAh);
+            dest.writeDouble(modemRailChargeMah);
+            dest.writeDouble(wifiRailChargeMah);
             dest.writeInt(states);
             dest.writeInt(states2);
             if (wakelockTag != null) {
@@ -1767,6 +1781,8 @@
             batteryTemperature = (short)(bat2&0xffff);
             batteryVoltage = (char)((bat2>>16)&0xffff);
             batteryChargeUAh = src.readInt();
+            modemRailChargeMah = src.readDouble();
+            wifiRailChargeMah = src.readDouble();
             states = src.readInt();
             states2 = src.readInt();
             if ((bat&0x10000000) != 0) {
@@ -1807,6 +1823,8 @@
             batteryTemperature = 0;
             batteryVoltage = 0;
             batteryChargeUAh = 0;
+            modemRailChargeMah = 0;
+            wifiRailChargeMah = 0;
             states = 0;
             states2 = 0;
             wakelockTag = null;
@@ -1835,6 +1853,8 @@
             batteryTemperature = o.batteryTemperature;
             batteryVoltage = o.batteryVoltage;
             batteryChargeUAh = o.batteryChargeUAh;
+            modemRailChargeMah = o.modemRailChargeMah;
+            wifiRailChargeMah = o.wifiRailChargeMah;
             states = o.states;
             states2 = o.states2;
             if (o.wakelockTag != null) {
@@ -1867,6 +1887,8 @@
                     && batteryTemperature == o.batteryTemperature
                     && batteryVoltage == o.batteryVoltage
                     && batteryChargeUAh == o.batteryChargeUAh
+                    && modemRailChargeMah == o.modemRailChargeMah
+                    && wifiRailChargeMah == o.wifiRailChargeMah
                     && states == o.states
                     && states2 == o.states2
                     && currentTime == o.currentTime;
@@ -3311,7 +3333,8 @@
 
         if (counter.getIdleTimeCounter().getCountLocked(which) != 0
                 || counter.getRxTimeCounter().getCountLocked(which) != 0
-                || counter.getPowerCounter().getCountLocked(which) != 0) {
+                || counter.getPowerCounter().getCountLocked(which) != 0
+                || counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which) != 0) {
             return true;
         }
 
@@ -3345,7 +3368,10 @@
         pw.print(",");
         pw.print(counter.getRxTimeCounter().getCountLocked(which));
         pw.print(",");
-        pw.print(counter.getPowerCounter().getCountLocked(which) / (1000 * 60 * 60));
+        pw.print(counter.getPowerCounter().getCountLocked(which) / (MILLISECONDS_IN_HOUR));
+        pw.print(",");
+        pw.print(counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which)
+                / (MILLISECONDS_IN_HOUR));
         for (LongCounter c : counter.getTxTimeCounters()) {
             pw.print(",");
             pw.print(c.getCountLocked(which));
@@ -3370,7 +3396,10 @@
         proto.write(ControllerActivityProto.RX_DURATION_MS,
                 counter.getRxTimeCounter().getCountLocked(which));
         proto.write(ControllerActivityProto.POWER_MAH,
-                counter.getPowerCounter().getCountLocked(which) / (1000 * 60 * 60));
+                counter.getPowerCounter().getCountLocked(which) / (MILLISECONDS_IN_HOUR));
+        proto.write(ControllerActivityProto.MONITORED_RAIL_CHARGE_MAH,
+                counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which)
+                        / (MILLISECONDS_IN_HOUR));
 
         long tToken;
         LongCounter[] txCounters = counter.getTxTimeCounters();
@@ -3400,6 +3429,8 @@
         final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
         final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
         final long powerDrainMaMs = counter.getPowerCounter().getCountLocked(which);
+        final long monitoredRailChargeConsumedMaMs =
+                counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
         // Battery real time
         final long totalControllerActivityTimeMs
             = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
@@ -3522,10 +3553,22 @@
             sb.append("     ");
             sb.append(controllerName);
             sb.append(" Battery drain: ").append(
-                BatteryStatsHelper.makemAh(powerDrainMaMs / (double) (1000*60*60)));
+                    BatteryStatsHelper.makemAh(powerDrainMaMs / MILLISECONDS_IN_HOUR));
             sb.append("mAh");
             pw.println(sb.toString());
         }
+
+        if (monitoredRailChargeConsumedMaMs > 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     ");
+            sb.append(controllerName);
+            sb.append(" Monitored rail energy drain: ").append(
+                    new DecimalFormat("#.##").format(
+                            monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR));
+            sb.append(" mAh");
+            pw.println(sb.toString());
+        }
     }
 
     /**
@@ -6103,6 +6146,8 @@
         int oldTemp = -1;
         int oldVolt = -1;
         int oldChargeMAh = -1;
+        double oldModemRailChargeMah = -1;
+        double oldWifiRailChargeMah = -1;
         long lastTime = -1;
 
         void reset() {
@@ -6114,6 +6159,8 @@
             oldTemp = -1;
             oldVolt = -1;
             oldChargeMAh = -1;
+            oldModemRailChargeMah = -1;
+            oldWifiRailChargeMah = -1;
         }
 
         public void printNextItem(PrintWriter pw, HistoryItem rec, long baseTime, boolean checkin,
@@ -6299,6 +6346,16 @@
                     item.append(checkin ? ",Bcc=" : " charge=");
                     item.append(oldChargeMAh);
                 }
+                if (oldModemRailChargeMah != rec.modemRailChargeMah) {
+                    oldModemRailChargeMah = rec.modemRailChargeMah;
+                    item.append(checkin ? ",Mrc=" : " modemRailChargemAh=");
+                    item.append(new DecimalFormat("#.##").format(oldModemRailChargeMah));
+                }
+                if (oldWifiRailChargeMah != rec.wifiRailChargeMah) {
+                    oldWifiRailChargeMah = rec.wifiRailChargeMah;
+                    item.append(checkin ? ",Wrc=" : " wifiRailChargemAh=");
+                    item.append(new DecimalFormat("#.##").format(oldWifiRailChargeMah));
+                }
                 printBitDescriptions(item, oldState, rec.states, rec.wakelockTag,
                         HISTORY_STATE_DESCRIPTIONS, !checkin);
                 printBitDescriptions(item, oldState2, rec.states2, null,
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8813e40..cc241b3 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -25,16 +25,11 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
-import android.gamedriver.GameDriverProto.Blacklist;
-import android.gamedriver.GameDriverProto.Blacklists;
 import android.opengl.EGL14;
 import android.provider.Settings;
-import android.util.Base64;
 import android.util.Log;
 import android.widget.Toast;
 
-import com.android.framework.protobuf.InvalidProtocolBufferException;
-
 import dalvik.system.VMRuntime;
 
 import java.io.File;
@@ -69,8 +64,7 @@
     private static final String ANGLE_RULES_FILE = "a4a_rules.json";
     private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
     private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
-    private static final String GAME_DRIVER_BLACKLIST_FLAG = "blacklist";
-    private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP;
+    private static final String GAME_DRIVER_WHITELIST_ALL = "*";
 
     private ClassLoader mClassLoader;
     private String mLayerPath;
@@ -630,43 +624,23 @@
             final boolean isOptIn =
                     getGlobalSettingsString(null, coreSettings,
                             Settings.Global.GAME_DRIVER_OPT_IN_APPS).contains(packageName);
-            if (!isOptIn
-                    && !getGlobalSettingsString(null, coreSettings,
-                    Settings.Global.GAME_DRIVER_WHITELIST).contains(packageName)) {
+            final List<String> whitelist = getGlobalSettingsString(null, coreSettings,
+                    Settings.Global.GAME_DRIVER_WHITELIST);
+            if (!isOptIn && whitelist.indexOf(GAME_DRIVER_WHITELIST_ALL) != 0
+                    && !whitelist.contains(packageName)) {
                 if (DEBUG) {
                     Log.w(TAG, packageName + " is not on the whitelist.");
                 }
                 return false;
             }
 
-            if (!isOptIn) {
-                // At this point, the application is on the whitelist only, check whether it's
-                // on the blacklist, terminate early when it's on the blacklist.
-                try {
-                    // TODO(b/121350991) Switch to DeviceConfig with property listener.
-                    final String base64String =
-                            coreSettings.getString(Settings.Global.GAME_DRIVER_BLACKLIST);
-                    if (base64String != null && !base64String.isEmpty()) {
-                        final Blacklists blacklistsProto =
-                                Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS));
-                        final List<Blacklist> blacklists = blacklistsProto.getBlacklistsList();
-                        final long driverVersionCode = driverAppInfo.longVersionCode;
-                        for (Blacklist blacklist : blacklists) {
-                            if (blacklist.getVersionCode() == driverVersionCode) {
-                                for (String pkgName : blacklist.getPackageNamesList()) {
-                                    if (pkgName == packageName) {
-                                        return false;
-                                    }
-                                }
-                                break;
-                            }
-                        }
-                    }
-                } catch (InvalidProtocolBufferException e) {
-                    if (DEBUG) {
-                        Log.w(TAG, "Can't parse blacklist, skip and continue...");
-                    }
-                }
+            // If the application is not opted-in and check whether it's on the blacklist,
+            // terminate early if it's on the blacklist and fallback to system driver.
+            if (!isOptIn
+                    && getGlobalSettingsString(null, coreSettings,
+                                               Settings.Global.GAME_DRIVER_BLACKLIST)
+                            .contains(ai.packageName)) {
+                return false;
             }
         }
 
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index f1bba1a..6d4c5a0 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -196,4 +196,25 @@
     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS
     */
    oneway void unregisterPullerCallback(int atomTag, String packageName);
+
+    /**
+     * The install requires staging.
+     */
+    const int FLAG_REQUIRE_STAGING = 0x01;
+
+    /**
+     * Rollback is enabled with this install.
+     */
+    const int FLAG_ROLLBACK_ENABLED = 0x02;
+
+    /**
+     * Requires low latency monitoring.
+     */
+    const int FLAG_REQUIRE_LOW_LATENCY_MONITOR = 0x04;
+
+    /**
+     * Logs an event for binary push for module updates.
+     */
+     oneway void sendBinaryPushStateChangedAtom(in String trainName, in long trainVersionCode,
+         in int options, in int state, in long[] experimentId);
 }
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index 87e1b7d..1420e2f 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.annotation.SystemApi;
 import android.content.LocaleProto;
 import android.icu.util.ULocale;
 import android.util.proto.ProtoOutputStream;
@@ -324,6 +325,15 @@
         return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale);
     }
 
+    /**
+     * Returns true if locale is a pseudo-locale, false otherwise.
+     * {@hide}
+     */
+    @SystemApi
+    public static boolean isPseudoLocale(@Nullable ULocale locale) {
+        return isPseudoLocale(locale != null ? locale.toLocale() : null);
+    }
+
     @IntRange(from=0, to=1)
     private static int matchScore(Locale supported, Locale desired) {
         if (supported.equals(desired)) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 2ecf9d1..cfe2d28 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1784,7 +1784,7 @@
      *
      * see {@link #registerThermalStatusCallback}
      */
-    public void unregisterThermalStatusCallback(ThermalStatusCallback callback) {
+    public void unregisterThermalStatusCallback(@NonNull ThermalStatusCallback callback) {
         Preconditions.checkNotNull(callback, "callback cannnot be null");
         synchronized (this) {
             if (mThermalService == null) {
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 8492b0c..3ee54ce 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -32,6 +32,7 @@
 import android.provider.Settings;
 import android.telephony.euicc.EuiccManager;
 import android.text.TextUtils;
+import android.text.format.DateFormat;
 import android.util.Log;
 import android.view.Display;
 import android.view.WindowManager;
@@ -762,7 +763,8 @@
 
         String reasonArg = null;
         if (!TextUtils.isEmpty(reason)) {
-            reasonArg = "--reason=" + sanitizeArg(reason);
+            String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString();
+            reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
         }
 
         final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
diff --git a/core/java/android/os/connectivity/WifiBatteryStats.java b/core/java/android/os/connectivity/WifiBatteryStats.java
index e5341ee..3639c71 100644
--- a/core/java/android/os/connectivity/WifiBatteryStats.java
+++ b/core/java/android/os/connectivity/WifiBatteryStats.java
@@ -44,6 +44,7 @@
   private long[] mTimeInStateMs;
   private long[] mTimeInSupplicantStateMs;
   private long[] mTimeInRxSignalStrengthLevelMs;
+  private long mMonitoredRailChargeConsumedMaMs;
 
   public static final Parcelable.Creator<WifiBatteryStats> CREATOR = new
       Parcelable.Creator<WifiBatteryStats>() {
@@ -77,6 +78,7 @@
     out.writeLongArray(mTimeInStateMs);
     out.writeLongArray(mTimeInRxSignalStrengthLevelMs);
     out.writeLongArray(mTimeInSupplicantStateMs);
+    out.writeLong(mMonitoredRailChargeConsumedMaMs);
   }
 
   public void readFromParcel(Parcel in) {
@@ -96,6 +98,7 @@
     in.readLongArray(mTimeInStateMs);
     in.readLongArray(mTimeInRxSignalStrengthLevelMs);
     in.readLongArray(mTimeInSupplicantStateMs);
+    mMonitoredRailChargeConsumedMaMs = in.readLong();
   }
 
   public long getLoggingDurationMs() {
@@ -162,6 +165,10 @@
     return mTimeInSupplicantStateMs;
   }
 
+  public long getMonitoredRailChargeConsumedMaMs() {
+    return mMonitoredRailChargeConsumedMaMs;
+  }
+
   public void setLoggingDurationMs(long t) {
     mLoggingDurationMs = t;
     return;
@@ -245,6 +252,11 @@
     return;
   }
 
+  public void setMonitoredRailChargeConsumedMaMs(long monitoredRailEnergyConsumedMaMs) {
+    mMonitoredRailChargeConsumedMaMs = monitoredRailEnergyConsumedMaMs;
+    return;
+  }
+
   public int describeContents() {
     return 0;
   }
@@ -274,6 +286,7 @@
     Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0);
     mTimeInSupplicantStateMs = new long[BatteryStats.NUM_WIFI_SUPPL_STATES];
     Arrays.fill(mTimeInSupplicantStateMs, 0);
+    mMonitoredRailChargeConsumedMaMs = 0;
     return;
   }
 }
\ No newline at end of file
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 104b61d..7eb0300 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -316,10 +316,17 @@
     public interface Rollback {
         String NAMESPACE = "rollback";
 
+        String BOOT_NAMESPACE = "rollback_boot";
+
         /**
          * Timeout in milliseconds for enabling package rollback.
          */
         String ENABLE_ROLLBACK_TIMEOUT = "enable_rollback_timeout";
+
+       /**
+        * The lifetime duration of rollback packages in millis
+        */
+        String ROLLBACK_LIFETIME_IN_MILLIS = "rollback_lifetime_in_millis";
     }
 
     /**
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 5f1c560..5708342 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -853,10 +853,8 @@
     private static final String PATH_DOCUMENT = "document";
     private static final String PATH_CHILDREN = "children";
     private static final String PATH_SEARCH = "search";
-    // TODO(b/72055774): make private again once ScopedAccessProvider is refactored
-    /** {@hide} */
     @UnsupportedAppUsage
-    public static final String PATH_TREE = "tree";
+    private static final String PATH_TREE = "tree";
 
     private static final String PARAM_QUERY = "query";
     private static final String PARAM_MANAGE = "manage";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d75c5e2..d925d7e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1657,6 +1657,30 @@
     public static final String ACTION_STORAGE_VOLUME_ACCESS_SETTINGS =
             "android.settings.STORAGE_VOLUME_ACCESS_SETTINGS";
 
+
+    /**
+     * Activity Action: Show screen that let user select enable (or disable) Content Capture.
+     * <p>
+     * Input: Nothing.
+     *
+     * <p>
+     * Output: {@link android.app.Activity#RESULT_OK} if user enabled Content Capture,
+     * {@link android.app.Activity#RESULT_CANCELED} if user disabled it, cancelled, or if the caller
+     * is not the Content Capture service associated with the user.
+     *
+     * <p>
+     * <b>NOTE: </b> Caller should call
+     * {@link android.view.contentcapture.ContentCaptureManager#isContentCaptureFeatureEnabled()}
+     * first to check whether the feature is already enabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE =
+            "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
+
     // End of Intent actions for Settings
 
     /**
@@ -6103,7 +6127,7 @@
          * Indicates which clock face to show on lock screen and AOD while docked.
          * @hide
          */
-        private static final String DOCKED_CLOCK_FACE = "docked_clock_face";
+        public static final String DOCKED_CLOCK_FACE = "docked_clock_face";
 
         /**
          * Set by the system to track if the user needs to see the call to action for
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 81d066d..8695da2 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -61,8 +61,6 @@
  */
 @SystemApi
 @TestApi
-// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-// in the same package as the test, and that module is compiled with SDK=test_current
 public abstract class AugmentedAutofillService extends Service {
 
     private static final String TAG = AugmentedAutofillService.class.getSimpleName();
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index f2a7a35..b989dd9 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -31,8 +31,6 @@
  */
 @SystemApi
 @TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillCallback {
 
     private static final String TAG = FillCallback.class.getSimpleName();
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index d7bc893..67f23d5 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -38,10 +38,8 @@
  */
 @SystemApi
 @TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillController {
-    private static final String TAG = "FillController";
+    private static final String TAG = FillController.class.getSimpleName();
 
     private final AutofillProxy mProxy;
 
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index af9905f..9a97bb2 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -31,8 +31,6 @@
 @SystemApi
 // TODO(b/123100811): pass a requestId and/or sessionId?
 @TestApi
-// TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-// in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillRequest {
 
     final AutofillProxy mProxy;
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index f1e904a..2ac406c 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -30,8 +30,6 @@
  */
 @SystemApi
 @TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillResponse {
 
     private final FillWindow mFillWindow;
@@ -53,8 +51,6 @@
      */
     @SystemApi
     @TestApi
-    //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-    //in the same package as the test, and that module is compiled with SDK=test_current
     public static final class Builder {
 
         private FillWindow mFillWindow;
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 40e3a12..6e06754 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -63,10 +63,8 @@
  */
 @SystemApi
 @TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
 public final class FillWindow implements AutoCloseable {
-    private static final String TAG = "FillWindow";
+    private static final String TAG = FillWindow.class.getSimpleName();
 
     private final Object mLock = new Object();
     private final CloseGuard mCloseGuard = CloseGuard.get();
diff --git a/core/java/android/service/autofill/augmented/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java
index 1fb9032..334487d 100644
--- a/core/java/android/service/autofill/augmented/PresentationParams.java
+++ b/core/java/android/service/autofill/augmented/PresentationParams.java
@@ -15,32 +15,19 @@
  */
 package android.service.autofill.augmented;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.graphics.Rect;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
-import android.util.DebugUtils;
 import android.view.View;
 
 import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 
 /**
  * Abstraction of a "Smart Suggestion" component responsible to embed the autofill UI provided by
- * the intelligence service.
- *
- * <p>The Smart Suggestion can embed the autofill UI in 3 distinct places:
- *
- * <ul>
- *   <li>A small area associated with suggestions (like a small strip in the top of the IME),
- *   returned by {@link #getSuggestionArea()}
- *   <li>The full area (like the full IME window), returned by {@link #getFullArea()}
- *   <li>A subset of the aforementioned areas, returned by {@link Area#getSubArea(Rect)}
- * </ul>
+ * the augmented autofill service.
  *
  * <p>The Smart Suggestion is represented by a {@link Area} object that contains the
  * dimensions the smart suggestion window, so the service can use it to calculate the size of the
@@ -50,54 +37,8 @@
  */
 @SystemApi
 @TestApi
-//TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-//in the same package as the test, and that module is compiled with SDK=test_current
 public abstract class PresentationParams {
 
-    /**
-     * Flag indicating the Smart Suggestion is hosted in the top of its container.
-     */
-    public static final int FLAG_HINT_GRAVITY_TOP = 0x1;
-
-    /**
-     * Flag indicating the Smart Suggestion is hosted in the bottom of its container.
-     */
-    public static final int FLAG_HINT_GRAVITY_BOTTOM = 0x2;
-
-    /**
-     * Flag indicating the Smart Suggestion is hosted in the left of its container.
-     */
-    public static final int FLAG_HINT_GRAVITY_LEFT = 0x4;
-
-    /**
-     * Flag indicating the Smart Suggestion is hosted in the right of its container.
-     */
-    public static final int FLAG_HINT_GRAVITY_RIGHT = 0x8;
-
-    /**
-     * Flag indicating the Smart Suggestion is hosted by the IME.
-     */
-    public static final int FLAG_HOST_IME = 0x10;
-
-    /**
-     * Flag indicating the Smart Suggestion is hosted by the Android System as a floating popup
-     * window.
-     */
-    public static final int FLAG_HOST_SYSTEM = 0x20;
-
-    /** @hide */
-    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
-            FLAG_HINT_GRAVITY_TOP,
-            FLAG_HINT_GRAVITY_BOTTOM,
-            FLAG_HINT_GRAVITY_LEFT,
-            FLAG_HINT_GRAVITY_RIGHT,
-            FLAG_HOST_IME,
-            FLAG_HOST_SYSTEM
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface Flags {}
-
-
     // /** @hide */
     PresentationParams() {}
 
@@ -112,40 +53,7 @@
         return null;
     }
 
-    /**
-     * Gets the full area for the of the Smart Suggestion provider.
-     *
-     * @return full dimensions, or {@code null} if the Smart Suggestion provider does not support
-     * embeding the UI on its full area.
-     */
-    @Nullable
-    public Area getFullArea() {
-        return null;
-    }
-
-    /**
-     * Gets flags associated with the Smart Suggestion.
-     *
-     * @return any combination of {@link #FLAG_HINT_GRAVITY_TOP},
-     * {@link #FLAG_HINT_GRAVITY_BOTTOM}, {@link #FLAG_HINT_GRAVITY_LEFT},
-     * {@link #FLAG_HINT_GRAVITY_RIGHT}, {@link #FLAG_HOST_IME}, or
-     * {@link #FLAG_HOST_SYSTEM},
-     */
-    public @Flags int getFlags() {
-        return 0;
-    }
-
-    /** @hide */
-    void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
-        final int flags = getFlags();
-        if (flags > 0) {
-            pw.print(prefix); pw.print("flags: "); pw.println(flagsToString(flags));
-        }
-    }
-
-    private static String flagsToString(int flags) {
-        return DebugUtils.flagsToString(PresentationParams.class, "FLAG_", flags);
-    }
+    abstract void dump(String prefix, PrintWriter pw);
 
     /**
      * Area associated with a {@link PresentationParams Smart Suggestions} provider.
@@ -154,8 +62,6 @@
      */
     @SystemApi
     @TestApi
-    //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-    //in the same package as the test, and that module is compiled with SDK=test_current
     public abstract static class Area {
 
         /** @hide */
@@ -176,24 +82,6 @@
             return mBounds;
         }
 
-        /**
-         * Gets a subarea limited by given boundaries.
-         *
-         * @param bounds boundaries relative to this Area.
-         *
-         * @return new subarea, or {@code null} if the Smart Suggestion host does not support such
-         * subaarea.
-         *
-         * @throws IllegalArgumentException if the {@code bounds} is not fully-contained inside this
-         * full Area.
-         *
-         */
-        @Nullable
-        public Area getSubArea(@NonNull Rect bounds) {
-            // TODO(b/123100712): implement / check boundaries / throw IAE / add unit test
-            return null;
-        }
-
         @Override
         public String toString() {
             return mBounds.toString();
@@ -220,13 +108,7 @@
         }
 
         @Override
-        public int getFlags() {
-            return FLAG_HOST_SYSTEM | FLAG_HINT_GRAVITY_BOTTOM;
-        }
-
-        @Override
         void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
-            super.dump(prefix, pw);
             pw.print(prefix); pw.print("area: "); pw.println(mSuggestionArea);
         }
     }
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index c98f09e..ae70775 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -49,7 +49,9 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A service used to capture the content of the screen to provide contextual data in other areas of
@@ -164,17 +166,9 @@
     }
 
     /**
-     * Explicitly limits content capture to the given packages and activities.
-     *
-     * <p>To reset the whitelist, call it passing {@code null} to both arguments.
-     *
-     * <p>Useful when the service wants to restrict content capture to a category of apps, like
-     * chat apps. For example, if the service wants to support view captures on all activities of
-     * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
-     * it would call: {@code setContentCaptureWhitelist(Arrays.asList("ChatApp1"),
-     * Arrays.asList(new ComponentName("ChatApp2", "act1"),
-     * new ComponentName("ChatApp2", "act2")));}
+     * @deprecated use {@link #setContentCaptureWhitelist(Set, Set)} instead
      */
+    @Deprecated
     public final void setContentCaptureWhitelist(@Nullable List<String> packages,
             @Nullable List<ComponentName> activities) {
         final IContentCaptureServiceCallback callback = mCallback;
@@ -190,6 +184,27 @@
     }
 
     /**
+     * Explicitly limits content capture to the given packages and activities.
+     *
+     * <p>To reset the whitelist, call it passing {@code null} to both arguments.
+     *
+     * <p>Useful when the service wants to restrict content capture to a category of apps, like
+     * chat apps. For example, if the service wants to support view captures on all activities of
+     * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
+     * it would call: {@code setContentCaptureWhitelist(Sets.newArraySet("ChatApp1"),
+     * Sets.newArraySet(new ComponentName("ChatApp2", "act1"),
+     * new ComponentName("ChatApp2", "act2")));}
+     */
+    public final void setContentCaptureWhitelist(@Nullable Set<String> packages,
+            @Nullable Set<ComponentName> activities) {
+        setContentCaptureWhitelist(toList(packages), toList(activities));
+    }
+
+    private <T> ArrayList<T> toList(@Nullable Set<T> set) {
+        return set == null ? null : new ArrayList<T>(set);
+    }
+
+    /**
      * Called when the Android system connects to service.
      *
      * <p>You should generally do initialization here rather than in {@link #onCreate}.
@@ -339,7 +354,7 @@
             }
             switch (event.getType()) {
                 case ContentCaptureEvent.TYPE_SESSION_STARTED:
-                    final ContentCaptureContext clientContext = event.getClientContext();
+                    final ContentCaptureContext clientContext = event.getContentCaptureContext();
                     clientContext.setParentSessionId(event.getParentSessionId());
                     mSessionUids.put(sessionIdString, uid);
                     onCreateContentCaptureSession(clientContext, sessionId);
@@ -383,8 +398,8 @@
         }
         final Integer rightUid = mSessionUids.get(sessionId);
         if (rightUid == null) {
-            if (DEBUG) {
-                Log.d(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
+            if (VERBOSE) {
+                Log.v(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
                         + ": " + mSessionUids);
             }
             // Just ignore, as the session could have been finished already
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 551bb8a..954dc39 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -372,6 +372,13 @@
     /**
      * @hide
      */
+    public void clearPackageContext() {
+        mContext = null;
+    }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public Context getPackageContext(Context context) {
         if (mContext == null) {
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index ea2a25d..e3e63e5 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -342,37 +342,17 @@
     }
 
     /**
-     * Requests that the voice state UI indicate the given state.
+     * Provide hints to be reflected in the system UI.
      *
-     * @param state value indicating whether the assistant is listening, fulfilling, etc.
+     * @param hints Arguments used to show UI.
      */
-    public final void setVoiceState(int state) {
-        try {
-            mSystemService.setVoiceState(mInterface, state);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+    public final void setUiHints(@NonNull Bundle hints) {
+        if (hints == null) {
+            throw new IllegalArgumentException("Hints must be non-null");
         }
-    }
 
-    /**
-     * Displays the given voice transcription contents.
-     */
-    public final void setTranscription(@NonNull String transcription) {
         try {
-            mSystemService.setTranscription(mInterface, transcription);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Hides transcription.
-     *
-     * @param immediate if {@code true}, remove before transcription animation completes.
-     */
-    public final void clearTranscription(boolean immediate) {
-        try {
-            mSystemService.clearTranscription(mInterface, immediate);
+            mSystemService.setUiHints(mInterface, hints);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java
index be772af..5754014 100644
--- a/core/java/android/text/style/DynamicDrawableSpan.java
+++ b/core/java/android/text/style/DynamicDrawableSpan.java
@@ -77,6 +77,13 @@
      */
     public static final int ALIGN_BASELINE = 1;
 
+    /**
+     * A constant indicating that this span should be vertically centered between
+     * the top and the lowest descender.
+     * @hide
+     */
+    public static final int ALIGN_CENTER = 2;
+
     protected final int mVerticalAlignment;
 
     @UnsupportedAppUsage
@@ -142,6 +149,8 @@
         int transY = bottom - b.getBounds().bottom;
         if (mVerticalAlignment == ALIGN_BASELINE) {
             transY -= paint.getFontMetricsInt().descent;
+        } else if (mVerticalAlignment == ALIGN_CENTER) {
+            transY = (bottom - top) / 2 - b.getBounds().height() / 2;
         }
 
         canvas.translate(x, transY);
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index ace4bf4..29ced3e 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -16,7 +16,14 @@
 
 package android.util;
 
+import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+
+import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.app.IActivityManager;
+import android.content.Context;
 import android.os.IStatsManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -31,7 +38,10 @@
 
     private static IStatsManager sService;
 
-    private StatsLog() {}
+    private static Object sLogLock = new Object();
+
+    private StatsLog() {
+    }
 
     /**
      * Logs a start event.
@@ -40,11 +50,13 @@
      * @return True if the log request was sent to statsd.
      */
     public static boolean logStart(int label) {
-        synchronized (StatsLog.class) {
+        synchronized (sLogLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging start");
+                    if (DEBUG) {
+                        Slog.d(TAG, "Failed to find statsd when logging start");
+                    }
                     return false;
                 }
                 service.sendAppBreadcrumbAtom(label,
@@ -52,7 +64,9 @@
                 return true;
             } catch (RemoteException e) {
                 sService = null;
-                if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging start");
+                if (DEBUG) {
+                    Slog.d(TAG, "Failed to connect to statsd when logging start");
+                }
                 return false;
             }
         }
@@ -65,18 +79,22 @@
      * @return True if the log request was sent to statsd.
      */
     public static boolean logStop(int label) {
-        synchronized (StatsLog.class) {
+        synchronized (sLogLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging stop");
+                    if (DEBUG) {
+                        Slog.d(TAG, "Failed to find statsd when logging stop");
+                    }
                     return false;
                 }
                 service.sendAppBreadcrumbAtom(label, StatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP);
                 return true;
             } catch (RemoteException e) {
                 sService = null;
-                if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging stop");
+                if (DEBUG) {
+                    Slog.d(TAG, "Failed to connect to statsd when logging stop");
+                }
                 return false;
             }
         }
@@ -89,11 +107,13 @@
      * @return True if the log request was sent to statsd.
      */
     public static boolean logEvent(int label) {
-        synchronized (StatsLog.class) {
+        synchronized (sLogLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    if (DEBUG) Slog.d(TAG, "Failed to find statsd when logging event");
+                    if (DEBUG) {
+                        Slog.d(TAG, "Failed to find statsd when logging event");
+                    }
                     return false;
                 }
                 service.sendAppBreadcrumbAtom(
@@ -101,7 +121,51 @@
                 return true;
             } catch (RemoteException e) {
                 sService = null;
-                if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when logging event");
+                if (DEBUG) {
+                    Slog.d(TAG, "Failed to connect to statsd when logging event");
+                }
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Logs an event for binary push for module updates.
+     *
+     * @param trainName        name of install train.
+     * @param trainVersionCode version code of the train.
+     * @param options          optional flags about this install.
+     * @param state            current install state.
+     * @param experimentIds    experiment ids.
+     * @return True if the log request was sent to statsd.
+     */
+    @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS})
+    public static boolean logBinaryPushStateChanged(@NonNull String trainName,
+            long trainVersionCode, int options, int state,
+            @NonNull long[] experimentIds) {
+        synchronized (sLogLock) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Failed to find statsd when logging event");
+                    }
+                    return false;
+                }
+                int userId = IActivityManager.Stub.asInterface(
+                        ServiceManager.getService("activity"))
+                        .getCurrentUser()
+                        .id;
+                service.sendBinaryPushStateChangedAtom(
+                        trainName, trainVersionCode, options, state, experimentIds);
+                return true;
+            } catch (RemoteException e) {
+                sService = null;
+                if (DEBUG) {
+                    Slog.d(TAG,
+                            "Failed to connect to StatsCompanionService when logging "
+                                    + "BinaryPushStateChanged");
+                }
                 return false;
             }
         }
@@ -118,7 +182,7 @@
     /**
      * Add a log to the stats log.
      *
-     * @param id The id of the atom
+     * @param id     The id of the atom
      * @param params The parameters of the atom's message.
      */
     public static void write(int id, @NonNull Object... params) {
@@ -128,4 +192,13 @@
                         (boolean) params[4], (int) params[5]);
         }
     }
+
+    private static void enforceDumpCallingPermission(Context context) {
+        context.enforceCallingPermission(android.Manifest.permission.DUMP, "Need DUMP permission.");
+    }
+
+    private static void enforcesageStatsCallingPermission(Context context) {
+        context.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS,
+                "Need PACKAGE_USAGE_STATS permission.");
+    }
 }
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 62ed901..f37c916 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -336,7 +336,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             final View root = findViewByAccessibilityId(accessibilityViewId);
-            if (root != null) {
+            if (root != null && isShown(root)) {
                 mPrefetcher.prefetchAccessibilityNodeInfos(
                         root, virtualDescendantId, flags, infos, arguments);
             }
@@ -448,7 +448,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             final View root = findViewByAccessibilityId(accessibilityViewId);
-            if (root != null) {
+            if (root != null && isShown(root)) {
                 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
                 if (provider != null) {
                     infos = provider.findAccessibilityNodeInfosByText(text,
@@ -531,7 +531,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             final View root = findViewByAccessibilityId(accessibilityViewId);
-            if (root != null) {
+            if (root != null && isShown(root)) {
                 switch (focusType) {
                     case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: {
                         View host = mViewRootImpl.mAccessibilityFocusedHost;
@@ -621,7 +621,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             final View root = findViewByAccessibilityId(accessibilityViewId);
-            if (root != null) {
+            if (root != null && isShown(root)) {
                 View nextView = root.focusSearch(direction);
                 if (nextView != null) {
                     next = nextView.createAccessibilityNodeInfo();
@@ -676,7 +676,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             final View target = findViewByAccessibilityId(accessibilityViewId);
-            if (target != null) {
+            if (target != null && isShown(target)) {
                 if (action == R.id.accessibilityActionClickOnClickableSpan) {
                     // Handle this hidden action separately
                     succeeded = handleClickableSpanActionUiThread(
@@ -759,9 +759,7 @@
         if (accessibilityId == AccessibilityNodeInfo.ROOT_ITEM_ID) {
             return mViewRootImpl.mView;
         } else {
-            final View foundView =
-                    AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
-            return isShown(foundView) ? foundView : null;
+            return AccessibilityNodeIdManager.getInstance().findView(accessibilityId);
         }
     }
 
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 1dbc46b..6cfc9f2 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -25,6 +25,7 @@
 import android.graphics.RecordingCanvas;
 import android.graphics.RenderNode;
 import android.os.Build;
+import android.os.Handler;
 import android.util.SparseIntArray;
 
 import com.android.internal.util.VirtualRefBasePtr;
@@ -84,6 +85,7 @@
 
     private VirtualRefBasePtr mNativePtr;
 
+    private Handler mHandler;
     private RenderNode mTarget;
     private View mViewTarget;
     private int mRenderProperty = -1;
@@ -222,6 +224,9 @@
     private void moveToRunningState() {
         mState = STATE_RUNNING;
         if (mNativePtr != null) {
+            if (mHandler == null) {
+                mHandler = new Handler();
+            }
             nStart(mNativePtr.get());
         }
         notifyStartListeners();
@@ -497,7 +502,7 @@
     // Called by native
     @UnsupportedAppUsage
     private static void callOnFinished(RenderNodeAnimator animator) {
-        animator.onFinished();
+        animator.mHandler.post(animator::onFinished);
     }
 
     @Override
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 47b206ca..2097812 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -453,7 +453,7 @@
      */
     void destroyHardwareResources(View view) {
         destroyResources(view);
-        destroyHardwareResources();
+        clearContent();
     }
 
     private static void destroyResources(View view) {
@@ -735,7 +735,9 @@
             if (callback != null) {
                 setFrameCallback(callback);
             }
-            syncAndDrawFrame(vsync);
+            createRenderRequest()
+                    .setVsyncTime(vsync)
+                    .syncAndDraw();
         }
     }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ba1b517..2b440dc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9487,7 +9487,7 @@
      * Gets the session used to notify Content Capture events.
      *
      * @return session explicitly set by {@link #setContentCaptureSession(ContentCaptureSession)},
-     * inherited by ancestore, default session or {@code null} if content capture is disabled for
+     * inherited by ancestors, default session or {@code null} if content capture is disabled for
      * this view.
      */
     @Nullable
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1a782ee..b1fee2d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3404,21 +3404,25 @@
                     .captureFrameCommitCallbacks();
             if (mReportNextDraw) {
                 usingAsyncReport = true;
-                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
-                    // TODO: Use the frame number
-                    pendingDrawFinished();
-                    if (commitCallbacks != null) {
-                        for (int i = 0; i < commitCallbacks.size(); i++) {
-                            commitCallbacks.get(i).run();
-                        }
-                    }
-                });
+                final Handler handler = mAttachInfo.mHandler;
+                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) ->
+                        handler.post(() -> {
+                            // TODO: Use the frame number
+                            pendingDrawFinished();
+                            if (commitCallbacks != null) {
+                                for (int i = 0; i < commitCallbacks.size(); i++) {
+                                    commitCallbacks.get(i).run();
+                                }
+                            }
+                        }));
             } else if (commitCallbacks != null && commitCallbacks.size() > 0) {
-                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
-                    for (int i = 0; i < commitCallbacks.size(); i++) {
-                        commitCallbacks.get(i).run();
-                    }
-                });
+                final Handler handler = mAttachInfo.mHandler;
+                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) ->
+                        handler.post(() -> {
+                            for (int i = 0; i < commitCallbacks.size(); i++) {
+                                commitCallbacks.get(i).run();
+                            }
+                        }));
             }
         }
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 5e5c826..a6b40ed 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -78,6 +78,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 //TODO: use java.lang.ref.Cleaner once Android supports Java 9
 import sun.misc.Cleaner;
@@ -1780,12 +1781,20 @@
     }
 
     /**
+     * @deprecated use {@link #setAugmentedAutofillWhitelist(Set, Set)} instead.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @Deprecated
+    public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
+            @Nullable List<ComponentName> activities) {
+        // TODO(b/123100824): implement
+    }
+
+    /**
      * Explicitly limits augmented autofill to the given packages and activities.
      *
-     * <p>When the whitelist is set, it overrides the values passed to
-     * {@link #setActivityAugmentedAutofillEnabled(ComponentName, boolean)}
-     * and {@link #setPackageAugmentedAutofillEnabled(String, boolean)}.
-     *
      * <p>To reset the whitelist, call it passing {@code null} to both arguments.
      *
      * <p>Useful when the service wants to restrict augmented autofill to a category of apps, like
@@ -1803,10 +1812,8 @@
      */
     @SystemApi
     @TestApi
-    //TODO(b/122654591): @TestApi is needed because CtsAutoFillServiceTestCases hosts the service
-    //in the same package as the test, and that module is compiled with SDK=test_current
-    public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
-            @Nullable List<ComponentName> activities) {
+    public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages,
+            @Nullable Set<ComponentName> activities) {
         // TODO(b/123100824): implement
     }
 
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index acb81e0..13e8a65 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -20,10 +20,6 @@
 import android.view.autofill.AutofillId;
 import android.view.contentcapture.ViewNode.ViewStructureImpl;
 
-import com.android.internal.util.Preconditions;
-
-import java.io.PrintWriter;
-
 /**
  * A session that is explicitly created by the app (and hence is a descendant of
  * {@link MainContentCaptureSession}).
@@ -35,21 +31,11 @@
     @NonNull
     private final ContentCaptureSession mParent;
 
-    /**
-     * {@link ContentCaptureContext} set by client, or {@code null} when it's the
-     * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the
-     * context.
-     *
-     * @hide
-     */
-    @NonNull
-    private final ContentCaptureContext mClientContext;
-
     /** @hide */
     protected ChildContentCaptureSession(@NonNull ContentCaptureSession parent,
             @NonNull ContentCaptureContext clientContext) {
+        super(clientContext);
         mParent = parent;
-        mClientContext = Preconditions.checkNotNull(clientContext);
     }
 
     @Override
@@ -73,6 +59,11 @@
     }
 
     @Override
+    public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
+        getMainCaptureSession().notifyContextUpdated(mId, context);
+    }
+
+    @Override
     void onDestroy() {
         getMainCaptureSession().notifyChildSessionFinished(mParent.mId, mId);
     }
@@ -101,13 +92,4 @@
     boolean isContentCaptureEnabled() {
         return getMainCaptureSession().isContentCaptureEnabled();
     }
-
-    @Override
-    void dump(String prefix, PrintWriter pw) {
-        if (mClientContext != null) {
-            // NOTE: we don't dump clientContent because it could have PII
-            pw.print(prefix); pw.println("hasClientContext");
-        }
-        super.dump(prefix, pw);
-    }
 }
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 22254cd..9cdbefa 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -91,13 +91,22 @@
      */
     public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5;
 
+    /**
+     * Called after a call to
+     * {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}.
+     *
+     * <p>The passed context is available through {@link #getContentCaptureContext()}.
+     */
+    public static final int TYPE_CONTEXT_UPDATED = 6;
+
     /** @hide */
     @IntDef(prefix = { "TYPE_" }, value = {
             TYPE_VIEW_APPEARED,
             TYPE_VIEW_DISAPPEARED,
             TYPE_VIEW_TEXT_CHANGED,
             TYPE_INITIAL_VIEW_TREE_APPEARING,
-            TYPE_INITIAL_VIEW_TREE_APPEARED
+            TYPE_INITIAL_VIEW_TREE_APPEARED,
+            TYPE_CONTEXT_UPDATED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType{}
@@ -193,12 +202,13 @@
     }
 
     /**
-     * Used by {@link #TYPE_SESSION_STARTED}.
+     * Gets the {@link ContentCaptureContext} set calls to
+     * {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}.
      *
-     * @hide
+     * <p>Only set on {@link #TYPE_CONTEXT_UPDATED} events.
      */
     @Nullable
-    public ContentCaptureContext getClientContext() {
+    public ContentCaptureContext getContentCaptureContext() {
         return mClientContext;
     }
 
@@ -220,8 +230,8 @@
      * Gets the type of the event.
      *
      * @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED},
-     * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING}, or
-     * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}.
+     * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING},
+     * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}, or {@link #TYPE_CONTEXT_UPDATED}.
      */
     public @EventType int getType() {
         return mType;
@@ -299,6 +309,10 @@
         if (mText != null) {
             pw.print(", text="); pw.println(getSanitizedString(mText));
         }
+        if (mClientContext != null) {
+            pw.print(", context="); mClientContext.dump(pw); pw.println();
+
+        }
     }
 
     @Override
@@ -325,6 +339,9 @@
         if (mText != null) {
             string.append(", text=").append(getSanitizedString(mText));
         }
+        if (mClientContext != null) {
+            string.append(", context=").append(mClientContext);
+        }
         return string.append(']').toString();
     }
 
@@ -345,7 +362,7 @@
         if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) {
             parcel.writeString(mParentSessionId);
         }
-        if (mType == TYPE_SESSION_STARTED) {
+        if (mType == TYPE_SESSION_STARTED || mType == TYPE_CONTEXT_UPDATED) {
             parcel.writeParcelable(mClientContext, flags);
         }
     }
@@ -375,7 +392,7 @@
             if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) {
                 event.setParentSessionId(parcel.readString());
             }
-            if (type == TYPE_SESSION_STARTED) {
+            if (type == TYPE_SESSION_STARTED || type == TYPE_CONTEXT_UPDATED) {
                 event.setClientContext(parcel.readParcelable(null));
             }
             return event;
@@ -404,6 +421,8 @@
                 return "INITIAL_VIEW_HIERARCHY_STARTED";
             case TYPE_INITIAL_VIEW_TREE_APPEARED:
                 return "INITIAL_VIEW_HIERARCHY_FINISHED";
+            case TYPE_CONTEXT_UPDATED:
+                return "CONTEXT_UPDATED";
             default:
                 return "UKNOWN_TYPE: " + type;
         }
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index e028961..b8d3fa6 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -166,6 +166,14 @@
     private ContentCaptureSessionId mContentCaptureSessionId;
 
     /**
+     * {@link ContentCaptureContext} set by client, or {@code null} when it's the
+     * {@link ContentCaptureManager#getMainContentCaptureSession() default session} for the
+     * context.
+     */
+    @Nullable
+    private ContentCaptureContext mClientContext;
+
+    /**
      * List of children session.
      */
     @Nullable
@@ -183,6 +191,12 @@
         mId = Preconditions.checkNotNull(id);
     }
 
+    // Used by ChildCOntentCaptureSession
+    ContentCaptureSession(@NonNull ContentCaptureContext initialContext) {
+        this();
+        mClientContext = Preconditions.checkNotNull(initialContext);
+    }
+
     /** @hide */
     @NonNull
     abstract MainContentCaptureSession getMainCaptureSession();
@@ -240,6 +254,30 @@
     abstract void flush(@FlushReason int reason);
 
     /**
+     * Sets the {@link ContentCaptureContext} associated with the session.
+     *
+     * <p>Typically used to change the context associated with the default session from an activity.
+     */
+    public final void setContentCaptureContext(@Nullable ContentCaptureContext context) {
+        mClientContext = context;
+        updateContentCaptureContext(context);
+    }
+
+    abstract void updateContentCaptureContext(@Nullable ContentCaptureContext context);
+
+    /**
+     * Gets the {@link ContentCaptureContext} associated with the session.
+     *
+     * @return context set on constructor or by
+     *         {@link #setContentCaptureContext(ContentCaptureContext)}, or {@code null} if never
+     *         explicitly set.
+     */
+    @Nullable
+    public final ContentCaptureContext getContentCaptureContext() {
+        return mClientContext;
+    }
+
+    /**
      * Destroys this session, flushing out all pending notifications to the service.
      *
      * <p>Once destroyed, any new notification will be dropped.
@@ -424,6 +462,9 @@
     @CallSuper
     void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
         pw.print(prefix); pw.print("id: "); pw.println(mId);
+        if (mClientContext != null) {
+            pw.print(prefix); mClientContext.dump(pw); pw.println();
+        }
         synchronized (mLock) {
             pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
             if (mChildren != null && !mChildren.isEmpty()) {
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 810c967..d949f45 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -15,6 +15,7 @@
  */
 package android.view.contentcapture;
 
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARING;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
@@ -269,11 +270,12 @@
     private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
         final int eventType = event.getType();
         if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
-        if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED) {
+        if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED
+                && eventType != ContentCaptureEvent.TYPE_CONTEXT_UPDATED) {
             // TODO(b/120494182): comment when this could happen (dialogs?)
             Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
                     + ContentCaptureEvent.getTypeAsString(eventType)
-                    + "): session not started yet");
+                    + "): dropping because session not started yet");
             return;
         }
         if (mDisabled.get()) {
@@ -476,6 +478,11 @@
         }
     }
 
+    @Override
+    public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
+        notifyContextUpdated(mId, context);
+    }
+
     /**
      * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
      */
@@ -613,6 +620,12 @@
         }
     }
 
+    void notifyContextUpdated(@NonNull String sessionId,
+            @Nullable ContentCaptureContext context) {
+        sendEvent(new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
+                .setClientContext(context));
+    }
+
     @Override
     void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
         pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2171fc5..3555822 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -760,7 +760,7 @@
      * <p>
      * The {@code encoding} parameter specifies whether the data is base64 or URL
      * encoded. If the data is base64 encoded, the value of the encoding
-     * parameter must be 'base64'. HTML can be encoded with {@link
+     * parameter must be {@code "base64"}. HTML can be encoded with {@link
      * android.util.Base64#encodeToString(byte[],int)} like so:
      * <pre>
      * String unencodedHtml =
@@ -768,11 +768,15 @@
      * String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING);
      * webView.loadData(encodedHtml, "text/html", "base64");
      * </pre>
-     * <p>
+     * <p class="note">
      * For all other values of {@code encoding} (including {@code null}) it is assumed that the
      * data uses ASCII encoding for octets inside the range of safe URL characters and use the
      * standard %xx hex encoding of URLs for octets outside that range. See <a
      * href="https://tools.ietf.org/html/rfc3986#section-2.2">RFC 3986</a> for more information.
+     * Applications targeting {@link android.os.Build.VERSION_CODES#Q} or later must either use
+     * base64 or encode any {@code #} characters in the content as {@code %23}, otherwise they
+     * will be treated as the end of the content and the remaining text used as a document
+     * fragment identifier.
      * <p>
      * The {@code mimeType} parameter specifies the format of the data.
      * If WebView can't handle the specified MIME type, it will download the data.
@@ -820,7 +824,8 @@
      * <p>
      * If the base URL uses the data scheme, this method is equivalent to
      * calling {@link #loadData(String,String,String) loadData()} and the
-     * historyUrl is ignored, and the data will be treated as part of a data: URL.
+     * historyUrl is ignored, and the data will be treated as part of a data: URL,
+     * including the requirement that the content be URL-encoded or base64 encoded.
      * If the base URL uses any other scheme, then the data will be loaded into
      * the WebView as a plain string (i.e. not part of a data URL) and any URL-encoded
      * entities in the string will not be decoded.
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 904a862..89e205c 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1230,9 +1230,16 @@
      * Ensures that the drop down is not obscuring the IME.
      * @param visible whether the ime should be in front. If false, the ime is pushed to
      * the background.
+     *
+     * This method is deprecated. Please use the following methods instead.
+     * Use {@link #setInputMethodMode} to ensure that the drop down is not obscuring the IME.
+     * Use {@link #showDropDown()} to show the drop down immediately
+     * A combination of {@link #isDropDownAlwaysVisible()} and {@link #enoughToFilter()} to decide
+     * whether to manually trigger {@link #showDropDown()} or not.
+     *
      * @hide internal used only here and SearchDialog
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768913)
     public void ensureImeVisible(boolean visible) {
         mPopup.setInputMethodMode(visible
                 ? ListPopupWindow.INPUT_METHOD_NEEDED : ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
@@ -1242,14 +1249,39 @@
     }
 
     /**
-     * @hide internal used only here and SearchDialog
+     * This method is deprecated. Please use {@link #getInputMethodMode()} instead.
+     *
+     * @hide This API is not being used and can be removed.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public boolean isInputMethodNotNeeded() {
         return mPopup.getInputMethodMode() == ListPopupWindow.INPUT_METHOD_NOT_NEEDED;
     }
 
     /**
+     * Returns the input method mode used by the auto complete dropdown.
+     */
+    public int getInputMethodMode() {
+        return mPopup.getInputMethodMode();
+    }
+
+    /**
+     * Use this method to specify when the IME should be displayed. This function can be used to
+     * prevent the dropdown from obscuring the IME.
+     *
+     * @param mode speficies the input method mode. use one of the following values:
+     *
+     * {@link ListPopupWindow#INPUT_METHOD_FROM_FOCUSABLE} IME Displayed if the auto-complete box is
+     * focusable.
+     * {@link ListPopupWindow#INPUT_METHOD_NEEDED} Always display the IME.
+     * {@link ListPopupWindow#INPUT_METHOD_NOT_NEEDED}. The auto-complete suggestions are always
+     * displayed, even if the suggestions cover/hide the input method.
+     */
+    public void setInputMethodMode(int mode) {
+        mPopup.setInputMethodMode(mode);
+    }
+
+    /**
      * <p>Displays the drop down on screen.</p>
      */
     public void showDropDown() {
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 29f070e..8113b40 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -199,7 +199,9 @@
     private boolean mMaxInitialized;
 
     private int mBehavior;
-    @UnsupportedAppUsage
+    // Better to define a Drawable that implements Animatable if you want to modify animation
+    // characteristics programatically.
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124052713)
     private int mDuration;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private boolean mIndeterminate;
@@ -284,7 +286,6 @@
             }
         }
 
-
         mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration);
 
         mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth);
@@ -709,7 +710,17 @@
     /**
      * Define the drawable used to draw the progress bar in indeterminate mode.
      *
+     * <p>For the Drawable to animate, it must implement {@link Animatable}, or override
+     * {@link Drawable#onLevelChange(int)}.  A Drawable that implements Animatable will be animated
+     * via that interface and therefore provides the greatest amount of customization. A Drawable
+     * that only overrides onLevelChange(int) is animated directly by ProgressBar and only the
+     * animation {@link android.R.styleable#ProgressBar_indeterminateDuration duration},
+         * {@link android.R.styleable#ProgressBar_indeterminateBehavior repeating behavior}, and
+     * {@link #setInterpolator(Interpolator) interpolator} can be modified, and only before the
+     * indeterminate animation begins.
+     *
      * @param d the new drawable
+     * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable
      * @see #getIndeterminateDrawable()
      * @see #setIndeterminate(boolean)
      */
@@ -1774,10 +1785,21 @@
 
     /**
      * Sets the acceleration curve for the indeterminate animation.
-     * The interpolator is loaded as a resource from the specified context.
+     *
+     * <p>The interpolator is loaded as a resource from the specified context. Defaults to a linear
+     * interpolation.
+     *
+     * <p>The interpolator only affects the indeterminate animation if the
+     * {@link #setIndeterminateDrawable(Drawable) supplied indeterminate drawable} does not
+     * implement {@link Animatable}.
+     *
+     * <p>This call must be made before the indeterminate animation starts for it to have an affect.
      *
      * @param context The application environment
      * @param resID The resource identifier of the interpolator to load
+     * @attr ref android.R.styleable#ProgressBar_interpolator
+     * @see #setInterpolator(Interpolator)
+     * @see #getInterpolator()
      */
     public void setInterpolator(Context context, @InterpolatorRes int resID) {
         setInterpolator(AnimationUtils.loadInterpolator(context, resID));
@@ -1787,7 +1809,17 @@
      * Sets the acceleration curve for the indeterminate animation.
      * Defaults to a linear interpolation.
      *
+     * <p>The interpolator only affects the indeterminate animation if the
+     * {@link #setIndeterminateDrawable(Drawable) supplied indeterminate drawable} does not
+     * implement {@link Animatable}.
+     *
+     * <p>This call must be made before the indeterminate animation starts for it to have
+     * an affect.
+     *
      * @param interpolator The interpolator which defines the acceleration curve
+     * @attr ref android.R.styleable#ProgressBar_interpolator
+     * @see #setInterpolator(Context, int)
+     * @see #getInterpolator()
      */
     public void setInterpolator(Interpolator interpolator) {
         mInterpolator = interpolator;
@@ -1797,6 +1829,9 @@
      * Gets the acceleration curve type for the indeterminate animation.
      *
      * @return the {@link Interpolator} associated to this animation
+     * @attr ref android.R.styleable#ProgressBar_interpolator
+     * @see #setInterpolator(Context, int)
+     * @see #setInterpolator(Interpolator)
      */
     @InspectableProperty
     public Interpolator getInterpolator() {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 8ebcef5..2009fd50 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -46,6 +46,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
+import android.content.res.Configuration;
 import android.database.Cursor;
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
@@ -486,6 +487,27 @@
         }
     }
 
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        int width = -1;
+        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            width = getResources().getDimensionPixelSize(R.dimen.chooser_preview_width);
+        }
+
+        updateLayoutWidth(R.id.content_preview_text_layout, width);
+        updateLayoutWidth(R.id.content_preview_title_layout, width);
+        updateLayoutWidth(R.id.content_preview_file_layout, width);
+    }
+
+    private void updateLayoutWidth(int layoutResourceId, int width) {
+        View view = findViewById(layoutResourceId);
+        LayoutParams params = view.getLayoutParams();
+        params.width = width;
+        view.setLayoutParams(params);
+    }
+
     private void displayContentPreview(@ContentPreviewType int previewType, Intent targetIntent) {
         switch (previewType) {
             case CONTENT_PREVIEW_TEXT:
@@ -601,7 +623,14 @@
     private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) {
         String fileName = null;
         boolean hasThumbnail = false;
-        Cursor cursor = resolver.query(uri, null, null, null, null);
+        Cursor cursor = null;
+
+        try {
+            cursor = resolver.query(uri, null, null, null, null);
+        } catch (SecurityException e) {
+            Log.w(TAG, "Error loading file preview", e);
+        }
+
         if (cursor != null && cursor.getCount() > 0) {
             int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
             int flagsIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS);
@@ -633,40 +662,50 @@
         // due to permissions issues
         findViewById(R.id.file_copy_button).setVisibility(View.GONE);
 
-        ContentResolver resolver = getContentResolver();
-        TextView fileNameView = findViewById(R.id.content_preview_filename);
         String action = targetIntent.getAction();
         if (Intent.ACTION_SEND.equals(action)) {
             Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
-
-            FileInfo fileInfo = extractFileInfo(uri, resolver);
-            fileNameView.setText(fileInfo.name);
-
-            if (fileInfo.hasThumbnail) {
-                loadUriIntoView(R.id.content_preview_file_thumbnail, uri);
-            } else {
-                ImageView fileIconView = findViewById(R.id.content_preview_file_icon);
-                fileIconView.setVisibility(View.VISIBLE);
-                fileIconView.setImageResource(R.drawable.ic_doc_generic);
-            }
+            loadFileUriIntoView(uri);
         } else {
             List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
-            if (uris.size() == 0) {
+            int uriCount = uris.size();
+
+            if (uriCount == 0) {
                 contentPreviewLayout.setVisibility(View.GONE);
                 Log.i(TAG,
-                        "Appears to be no uris available in EXTRA_STREAM, removing preview area");
+                        "Appears to be no uris available in EXTRA_STREAM, removing "
+                                + "preview area");
                 return;
+            } else if (uriCount == 1) {
+                loadFileUriIntoView(uris.get(0));
+            } else {
+                FileInfo fileInfo = extractFileInfo(uris.get(0), getContentResolver());
+                int remUriCount = uriCount - 1;
+                String fileName = getResources().getQuantityString(R.plurals.file_count,
+                        remUriCount, fileInfo.name, remUriCount);
+
+                TextView fileNameView = findViewById(R.id.content_preview_filename);
+                fileNameView.setText(fileName);
+
+                ImageView fileIconView = findViewById(R.id.content_preview_file_icon);
+                fileIconView.setVisibility(View.VISIBLE);
+                fileIconView.setImageResource(R.drawable.ic_file_copy);
             }
+        }
+    }
 
-            FileInfo fileInfo = extractFileInfo(uris.get(0), resolver);
-            int remFileCount = uris.size() - 1;
-            String fileName = getResources().getQuantityString(R.plurals.file_count,
-                    remFileCount, fileInfo.name, remFileCount);
+    private void loadFileUriIntoView(Uri uri) {
+        FileInfo fileInfo = extractFileInfo(uri, getContentResolver());
 
-            fileNameView.setText(fileName);
+        TextView fileNameView = findViewById(R.id.content_preview_filename);
+        fileNameView.setText(fileInfo.name);
+
+        if (fileInfo.hasThumbnail) {
+            loadUriIntoView(R.id.content_preview_file_thumbnail, uri);
+        } else {
             ImageView fileIconView = findViewById(R.id.content_preview_file_icon);
             fileIconView.setVisibility(View.VISIBLE);
-            fileIconView.setImageResource(R.drawable.ic_file_copy);
+            fileIconView.setImageResource(R.drawable.ic_doc_generic);
         }
     }
 
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 8dde44e..9ce7ed1 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -153,17 +153,7 @@
      in IVoiceActionCheckCallback callback);
 
     /**
-     * Sets the transcribed voice to the given string.
+     * Provide hints for showing UI.
      */
-    void setTranscription(IVoiceInteractionService service, String transcription);
-
-    /**
-     * Indicates that the transcription session is finished.
-     */
-    void clearTranscription(IVoiceInteractionService service, boolean immediate);
-
-    /**
-     * Sets the voice state indication based upon the given value.
-     */
-    void setVoiceState(IVoiceInteractionService service, int state);
+    void setUiHints(in IVoiceInteractionService service, in Bundle hints);
 }
diff --git a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
index 674ad5b..bc757e2 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
@@ -16,6 +16,8 @@
 
  package com.android.internal.app;
 
+ import android.os.Bundle;
+
  oneway interface IVoiceInteractionSessionListener {
     /**
      * Called when a voice session is shown.
@@ -28,18 +30,7 @@
     void onVoiceSessionHidden();
 
     /**
-     * Called when voice assistant transcription has been updated to the given string.
+     * Called when UI hints were received.
      */
-    void onTranscriptionUpdate(in String transcription);
-
-    /**
-     * Called when voice transcription is completed.
-     */
-    void onTranscriptionComplete(in boolean immediate);
-
-    /**
-     * Called when the voice assistant's state has changed. Values are from
-     * VoiceInteractionService's VOICE_STATE* constants.
-     */
-    void onVoiceStateChange(in int state);
+    void onSetUiHints(in Bundle args);
  }
\ No newline at end of file
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 52e1748..4ff9948 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -187,6 +187,8 @@
     static final int MSG_REPORT_RESET_STATS = 4;
     static final long DELAY_UPDATE_WAKELOCKS = 5*1000;
 
+    private static final double MILLISECONDS_IN_HOUR = 3600 * 1000;
+
     private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
     private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
 
@@ -252,6 +254,9 @@
     private static final long RPM_STATS_UPDATE_FREQ_MS = 1000;
     /** Last time that RPM stats were updated by updateRpmStatsLocked. */
     private long mLastRpmStatsUpdateTimeMs = -RPM_STATS_UPDATE_FREQ_MS;
+
+    /** Container for Rail Energy Data stats. */
+    private final RailStats mTmpRailStats = new RailStats();
     /**
      * Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader},
      * {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader},
@@ -327,6 +332,15 @@
         public String getSubsystemLowPowerStats();
     }
 
+    /** interface to update rail information for power monitor */
+    public interface RailEnergyDataCallback {
+        /** Function to fill the map for the rail data stats
+         * Used for power monitoring feature
+         * @param railStats
+         */
+        void fillRailDataStats(RailStats railStats);
+    }
+
     public static abstract class UserInfoProvider {
         private int[] userIds;
         protected abstract @Nullable int[] getUserIds();
@@ -361,6 +375,8 @@
         }
     };
 
+    public final RailEnergyDataCallback mRailEnergyDataCallback;
+
     /**
      * This handler is running on {@link BackgroundThread}.
      */
@@ -593,7 +609,9 @@
         int UPDATE_RADIO = 0x04;
         int UPDATE_BT = 0x08;
         int UPDATE_RPM = 0x10; // 16
-        int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM;
+        int UPDATE_RAIL = 0x20; // 32
+        int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM
+                | UPDATE_RAIL;
 
         Future<?> scheduleSync(String reason, int flags);
         Future<?> scheduleCpuSyncDueToRemovedUid(int uid);
@@ -1078,6 +1096,7 @@
         mBatteryStatsHistory = null;
         mHandler = null;
         mPlatformIdleStateCallback = null;
+        mRailEnergyDataCallback = null;
         mUserInfoProvider = null;
         mConstants = new Constants(mHandler);
         clearHistoryLocked();
@@ -3005,6 +3024,7 @@
         private final LongSamplingCounter mRxTimeMillis;
         private final LongSamplingCounter[] mTxTimeMillis;
         private final LongSamplingCounter mPowerDrainMaMs;
+        private final LongSamplingCounter mMonitoredRailChargeConsumedMaMs;
 
         public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) {
             mIdleTimeMillis = new LongSamplingCounter(timeBase);
@@ -3016,6 +3036,7 @@
                 mTxTimeMillis[i] = new LongSamplingCounter(timeBase);
             }
             mPowerDrainMaMs = new LongSamplingCounter(timeBase);
+            mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase);
         }
 
         public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) {
@@ -3033,6 +3054,7 @@
                 mTxTimeMillis[i] = new LongSamplingCounter(timeBase, in);
             }
             mPowerDrainMaMs = new LongSamplingCounter(timeBase, in);
+            mMonitoredRailChargeConsumedMaMs = new LongSamplingCounter(timeBase, in);
         }
 
         public void readSummaryFromParcel(Parcel in) {
@@ -3048,6 +3070,7 @@
                 counter.readSummaryFromParcelLocked(in);
             }
             mPowerDrainMaMs.readSummaryFromParcelLocked(in);
+            mMonitoredRailChargeConsumedMaMs.readSummaryFromParcelLocked(in);
         }
 
         @Override
@@ -3065,6 +3088,7 @@
                 counter.writeSummaryFromParcelLocked(dest);
             }
             mPowerDrainMaMs.writeSummaryFromParcelLocked(dest);
+            mMonitoredRailChargeConsumedMaMs.writeSummaryFromParcelLocked(dest);
         }
 
         @Override
@@ -3078,6 +3102,7 @@
                 counter.writeToParcel(dest);
             }
             mPowerDrainMaMs.writeToParcel(dest);
+            mMonitoredRailChargeConsumedMaMs.writeToParcel(dest);
         }
 
         public void reset(boolean detachIfReset) {
@@ -3089,6 +3114,7 @@
                 counter.reset(detachIfReset);
             }
             mPowerDrainMaMs.reset(detachIfReset);
+            mMonitoredRailChargeConsumedMaMs.reset(detachIfReset);
         }
 
         public void detach() {
@@ -3100,6 +3126,7 @@
                 counter.detach();
             }
             mPowerDrainMaMs.detach();
+            mMonitoredRailChargeConsumedMaMs.detach();
         }
 
         /**
@@ -3154,6 +3181,15 @@
         public LongSamplingCounter getPowerCounter() {
             return mPowerDrainMaMs;
         }
+
+        /**
+         * @return a LongSamplingCounter, measuring actual monitored rail energy consumed
+         * milli-ampere milli-seconds (mAmS).
+         */
+        @Override
+        public LongSamplingCounter getMonitoredRailChargeConsumedMaMs() {
+            return mMonitoredRailChargeConsumedMaMs;
+        }
     }
 
     /** Get Resource Power Manager stats. Create a new one if it doesn't already exist. */
@@ -3497,6 +3533,8 @@
             if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUAh=" + cur.batteryChargeUAh);
             dest.writeInt(cur.batteryChargeUAh);
         }
+        dest.writeDouble(cur.modemRailChargeMah);
+        dest.writeDouble(cur.wifiRailChargeMah);
     }
 
     private int buildBatteryLevelInt(HistoryItem h) {
@@ -3747,6 +3785,8 @@
         if ((firstToken&DELTA_BATTERY_CHARGE_FLAG) != 0) {
             cur.batteryChargeUAh = src.readInt();
         }
+        cur.modemRailChargeMah = src.readDouble();
+        cur.wifiRailChargeMah = src.readDouble();
     }
 
     @Override
@@ -10111,12 +10151,12 @@
     }
 
     public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb,
-            UserInfoProvider userInfoProvider) {
-        this(new SystemClocks(), systemDir, handler, cb, userInfoProvider);
+            RailEnergyDataCallback railStatsCb, UserInfoProvider userInfoProvider) {
+        this(new SystemClocks(), systemDir, handler, cb, railStatsCb, userInfoProvider);
     }
 
     private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler,
-            PlatformIdleStateCallback cb,
+            PlatformIdleStateCallback cb, RailEnergyDataCallback railStatsCb,
             UserInfoProvider userInfoProvider) {
         init(clocks);
 
@@ -10218,6 +10258,7 @@
         clearHistoryLocked();
         updateDailyDeadlineLocked();
         mPlatformIdleStateCallback = cb;
+        mRailEnergyDataCallback = railStatsCb;
         mUserInfoProvider = userInfoProvider;
     }
 
@@ -10238,6 +10279,7 @@
         mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer);
         readFromParcel(p);
         mPlatformIdleStateCallback = null;
+        mRailEnergyDataCallback = null;
     }
 
     public void setPowerProfileLocked(PowerProfile profile) {
@@ -10934,6 +10976,8 @@
             mWakeupReasonStats.clear();
         }
 
+        mTmpRailStats.reset();
+
         mLastHistoryStepDetails = null;
         mLastStepCpuUserTime = mLastStepCpuSystemTime = 0;
         mCurStepCpuUserTime = mCurStepCpuSystemTime = 0;
@@ -11321,6 +11365,16 @@
                     mWifiActivity.getPowerCounter().addCountLocked(
                             (long) (info.getControllerEnergyUsed() / opVolt));
                 }
+                // Converting uWs to mAms.
+                // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms
+                long monitoredRailChargeConsumedMaMs =
+                        (long) (mTmpRailStats.getWifiTotalEnergyUseduWs() / opVolt);
+                mWifiActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked(
+                        monitoredRailChargeConsumedMaMs);
+                mHistoryCur.wifiRailChargeMah +=
+                        (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
+                addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+                mTmpRailStats.resetWifiTotalEnergyUsed();
             }
         }
     }
@@ -11411,9 +11465,18 @@
 
                     // We store the power drain as mAms.
                     mModemActivity.getPowerCounter().addCountLocked((long) energyUsed);
+                    // Converting uWs to mAms.
+                    // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms
+                    long monitoredRailChargeConsumedMaMs =
+                            (long) (mTmpRailStats.getCellularTotalEnergyUseduWs() / opVolt);
+                    mModemActivity.getMonitoredRailChargeConsumedMaMs().addCountLocked(
+                            monitoredRailChargeConsumedMaMs);
+                    mHistoryCur.modemRailChargeMah +=
+                            (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR);
+                    addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+                    mTmpRailStats.resetCellularTotalEnergyUsed();
                 }
             }
-
             final long elapsedRealtimeMs = mClocks.elapsedRealtime();
             long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
                     elapsedRealtimeMs * 1000);
@@ -11812,6 +11875,16 @@
     }
 
     /**
+     * Read and record Rail Energy data.
+     */
+    public void updateRailStatsLocked() {
+        if (mRailEnergyDataCallback == null || !mTmpRailStats.isRailStatsAvailable()) {
+            return;
+        }
+        mRailEnergyDataCallback.fillRailDataStats(mTmpRailStats);
+    }
+
+    /**
      * Read and distribute kernel wake lock use across apps.
      */
     public void updateKernelWakelocksLocked() {
@@ -12950,6 +13023,8 @@
         final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
         final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
         final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
+        final long monitoredRailChargeConsumedMaMs =
+                counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
         long[] timeInRatMs = new long[BatteryStats.NUM_DATA_CONNECTION_TYPES];
         for (int i = 0; i < timeInRatMs.length; i++) {
            timeInRatMs[i] = getPhoneDataConnectionTime(i, rawRealTime, which) / 1000;
@@ -12979,58 +13054,62 @@
         s.setTimeInRatMs(timeInRatMs);
         s.setTimeInRxSignalStrengthLevelMs(timeInRxSignalStrengthLevelMs);
         s.setTxTimeMs(txTimeMs);
+        s.setMonitoredRailChargeConsumedMaMs(monitoredRailChargeConsumedMaMs);
         return s;
     }
 
-     /*@hide */
-     public WifiBatteryStats getWifiBatteryStats() {
-         WifiBatteryStats s = new WifiBatteryStats();
-         final int which = STATS_SINCE_CHARGED;
-         final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
-         final ControllerActivityCounter counter = getWifiControllerActivity();
-         final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
-         final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
-         final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
-         final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which);
-         final long totalControllerActivityTimeMs
-             = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
-         final long sleepTimeMs
-             = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs);
-         final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
-         long numAppScanRequest = 0;
-         for (int i = 0; i < mUidStats.size(); i++) {
-             numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which);
-         }
-         long[] timeInStateMs = new long[NUM_WIFI_STATES];
-         for (int i=0; i<NUM_WIFI_STATES; i++) {
+    /*@hide */
+    public WifiBatteryStats getWifiBatteryStats() {
+        WifiBatteryStats s = new WifiBatteryStats();
+        final int which = STATS_SINCE_CHARGED;
+        final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
+        final ControllerActivityCounter counter = getWifiControllerActivity();
+        final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
+        final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
+        final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
+        final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which);
+        final long totalControllerActivityTimeMs
+                = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
+        final long sleepTimeMs
+                = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs);
+        final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
+        final long monitoredRailChargeConsumedMaMs =
+                counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
+        long numAppScanRequest = 0;
+        for (int i = 0; i < mUidStats.size(); i++) {
+            numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which);
+        }
+        long[] timeInStateMs = new long[NUM_WIFI_STATES];
+        for (int i=0; i<NUM_WIFI_STATES; i++) {
             timeInStateMs[i] = getWifiStateTime(i, rawRealTime, which) / 1000;
-         }
-         long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES];
-         for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
-             timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000;
-         }
-         long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS];
-         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
-             timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000;
-         }
-         s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
-         s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000);
-         s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
-         s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
-         s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
-         s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
-         s.setSleepTimeMs(sleepTimeMs);
-         s.setIdleTimeMs(idleTimeMs);
-         s.setRxTimeMs(rxTimeMs);
-         s.setTxTimeMs(txTimeMs);
-         s.setScanTimeMs(scanTimeMs);
-         s.setEnergyConsumedMaMs(energyConsumedMaMs);
-         s.setNumAppScanRequest(numAppScanRequest);
-         s.setTimeInStateMs(timeInStateMs);
-         s.setTimeInSupplicantStateMs(timeInSupplStateMs);
-         s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs);
-         return s;
-     }
+        }
+        long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES];
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000;
+        }
+        long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS];
+        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000;
+        }
+        s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
+        s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000);
+        s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
+        s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
+        s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
+        s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
+        s.setSleepTimeMs(sleepTimeMs);
+        s.setIdleTimeMs(idleTimeMs);
+        s.setRxTimeMs(rxTimeMs);
+        s.setTxTimeMs(txTimeMs);
+        s.setScanTimeMs(scanTimeMs);
+        s.setEnergyConsumedMaMs(energyConsumedMaMs);
+        s.setNumAppScanRequest(numAppScanRequest);
+        s.setTimeInStateMs(timeInStateMs);
+        s.setTimeInSupplicantStateMs(timeInSupplStateMs);
+        s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs);
+        s.setMonitoredRailChargeConsumedMaMs(monitoredRailChargeConsumedMaMs);
+        return s;
+    }
 
     /*@hide */
     public GpsBatteryStats getGpsBatteryStats() {
diff --git a/core/java/com/android/internal/os/RailStats.java b/core/java/com/android/internal/os/RailStats.java
new file mode 100644
index 0000000..ff00831
--- /dev/null
+++ b/core/java/com/android/internal/os/RailStats.java
@@ -0,0 +1,147 @@
+/*
+ * 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.internal.os;
+
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import java.util.Map;
+
+/** Rail Stats Power Monitoring Class */
+public final class RailStats {
+    private static final String TAG = "RailStats";
+
+    private static final String WIFI_SUBSYSTEM = "wifi";
+    private static final String CELLULAR_SUBSYSTEM = "cellular";
+
+    private Map<Long, RailInfoData> mRailInfoData = new ArrayMap<>();
+
+    private long mCellularTotalEnergyUseduWs = 0;
+    private long mWifiTotalEnergyUseduWs = 0;
+    private boolean mRailStatsAvailability = true;
+
+    /** Updates the rail data map of all power monitor rails being monitored
+     * Function is called from native side
+     * @param index
+     * @param railName
+     * @param subSystemName
+     * @param timestampSinceBootMs
+     * @param energyUsedSinceBootuWs
+     */
+    public void updateRailData(long index, String railName, String subSystemName,
+            long timestampSinceBootMs, long energyUsedSinceBootuWs) {
+        if (!(subSystemName.equals(WIFI_SUBSYSTEM) || subSystemName.equals(CELLULAR_SUBSYSTEM))) {
+            return;
+        }
+        RailInfoData node = mRailInfoData.get(index);
+        if (node == null) {
+            mRailInfoData.put(index, new RailInfoData(index, railName, subSystemName,
+                    timestampSinceBootMs, energyUsedSinceBootuWs));
+            if (subSystemName.equals(WIFI_SUBSYSTEM)) {
+                mWifiTotalEnergyUseduWs += energyUsedSinceBootuWs;
+                return;
+            }
+            if (subSystemName.equals(CELLULAR_SUBSYSTEM)) {
+                mCellularTotalEnergyUseduWs += energyUsedSinceBootuWs;
+            }
+            return;
+        }
+        long timeSinceLastLogMs = timestampSinceBootMs - node.timestampSinceBootMs;
+        long energyUsedSinceLastLoguWs = energyUsedSinceBootuWs - node.energyUsedSinceBootuWs;
+        if (timeSinceLastLogMs < 0 || energyUsedSinceLastLoguWs < 0) {
+            energyUsedSinceLastLoguWs = node.energyUsedSinceBootuWs;
+        }
+        node.timestampSinceBootMs = timestampSinceBootMs;
+        node.energyUsedSinceBootuWs = energyUsedSinceBootuWs;
+        if (subSystemName.equals(WIFI_SUBSYSTEM)) {
+            mWifiTotalEnergyUseduWs += energyUsedSinceLastLoguWs;
+            return;
+        }
+        if (subSystemName.equals(CELLULAR_SUBSYSTEM)) {
+            mCellularTotalEnergyUseduWs += energyUsedSinceLastLoguWs;
+        }
+    }
+
+    /** resets the cellular total energy used aspect.
+     */
+    public void resetCellularTotalEnergyUsed() {
+        mCellularTotalEnergyUseduWs = 0;
+    }
+
+    /** resets the wifi total energy used aspect.
+     */
+    public void resetWifiTotalEnergyUsed() {
+        mWifiTotalEnergyUseduWs = 0;
+    }
+
+    public long getCellularTotalEnergyUseduWs() {
+        return mCellularTotalEnergyUseduWs;
+    }
+
+    public long getWifiTotalEnergyUseduWs() {
+        return mWifiTotalEnergyUseduWs;
+    }
+
+    /** reset the total energy subsystems
+     *
+     */
+    public void reset() {
+        mCellularTotalEnergyUseduWs = 0;
+        mWifiTotalEnergyUseduWs = 0;
+    }
+
+    public RailStats getRailStats() {
+        return this;
+    }
+
+    public void setRailStatsAvailability(boolean railStatsAvailability) {
+        mRailStatsAvailability = railStatsAvailability;
+    }
+
+    public boolean isRailStatsAvailable() {
+        return mRailStatsAvailability;
+    }
+
+    /** Container class to contain rail data information */
+    public static class RailInfoData {
+        private static final String TAG = "RailInfoData";
+        public long index;
+        public String railName;
+        public String subSystemName;
+        public long timestampSinceBootMs;
+        public long energyUsedSinceBootuWs;
+
+        private RailInfoData(long index, String railName, String subSystemName,
+                long timestampSinceBootMs, long energyUsedSinceBoot) {
+            this.index = index;
+            this.railName = railName;
+            this.subSystemName = subSystemName;
+            this.timestampSinceBootMs = timestampSinceBootMs;
+            this.energyUsedSinceBootuWs = energyUsedSinceBoot;
+        }
+
+        /** print the rail data
+         *
+         */
+        public void printData() {
+            Slog.d(TAG, "Index = " + index);
+            Slog.d(TAG, "RailName = " + railName);
+            Slog.d(TAG, "SubSystemName = " + subSystemName);
+            Slog.d(TAG, "TimestampSinceBootMs = " + timestampSinceBootMs);
+            Slog.d(TAG, "EnergyUsedSinceBootuWs = " + energyUsedSinceBootuWs);
+        }
+    }
+}
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 774c224..7ff15f2 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -6,4 +6,4 @@
 per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
 
 # Zygote
-per-file com_android_inernal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com
+per-file com_android_internal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index e8172172f..8f00759 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -355,9 +355,16 @@
         colorType = kN32_SkColorType;
     }
 
+    sk_sp<SkColorSpace> colorSpace;
+    if (colorType == kAlpha_8_SkColorType) {
+        colorSpace = nullptr;
+    } else {
+        colorSpace = GraphicsJNI::getNativeColorSpace(colorSpacePtr);
+    }
+
     SkBitmap bitmap;
     bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType,
-                GraphicsJNI::getNativeColorSpace(colorSpacePtr)));
+                colorSpace));
 
     sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap);
     if (!nativeBitmap) {
@@ -385,15 +392,17 @@
         case kRGB_565_SkColorType:
             dstInfo = dstInfo.makeAlphaType(kOpaque_SkAlphaType);
             break;
-        case kRGBA_F16_SkColorType:
-            // The caller does not have an opportunity to pass a dst color space.  Assume that
-            // they want linear sRGB.
-            dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGBLinear());
+        case kAlpha_8_SkColorType:
+            dstInfo = dstInfo.makeColorSpace(nullptr);
             break;
         default:
             break;
     }
 
+    if (!dstInfo.colorSpace() && dstCT != kAlpha_8_SkColorType) {
+        dstInfo = dstInfo.makeColorSpace(SkColorSpace::MakeSRGB());
+    }
+
     if (!dst->setInfo(dstInfo)) {
         return false;
     }
@@ -608,14 +617,6 @@
     return static_cast<jint>(bitmap->getGenerationID());
 }
 
-static jboolean Bitmap_isConfigF16(JNIEnv* env, jobject, jlong bitmapHandle) {
-    LocalScopedBitmap bitmap(bitmapHandle);
-    if (bitmap->info().colorType() == kRGBA_F16_SkColorType) {
-        return JNI_TRUE;
-    }
-    return JNI_FALSE;
-}
-
 static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) {
     LocalScopedBitmap bitmap(bitmapHandle);
     if (bitmap->info().alphaType() == kPremul_SkAlphaType) {
@@ -684,9 +685,7 @@
     const SkAlphaType alphaType = (SkAlphaType)p->readInt32();
     const uint32_t    colorSpaceSize = p->readUint32();
     sk_sp<SkColorSpace> colorSpace;
-    if (kRGBA_F16_SkColorType == colorType) {
-        colorSpace = SkColorSpace::MakeSRGBLinear();
-    } else if (colorSpaceSize > 0) {
+    if (colorSpaceSize > 0) {
         if (colorSpaceSize > kMaxColorSpaceSerializedBytes) {
             ALOGD("Bitmap_createFromParcel: Serialized SkColorSpace is larger than expected: "
                     "%d bytes\n", colorSpaceSize);
@@ -811,7 +810,7 @@
     p->writeInt32(bitmap.colorType());
     p->writeInt32(bitmap.alphaType());
     SkColorSpace* colorSpace = bitmap.colorSpace();
-    if (colorSpace != nullptr && bitmap.colorType() != kRGBA_F16_SkColorType) {
+    if (colorSpace != nullptr) {
         sk_sp<SkData> data = colorSpace->serialize();
         size_t size = data->size();
         p->writeUint32(size);
@@ -924,44 +923,14 @@
     return colorSpace == srgbLinear.get() ? JNI_TRUE : JNI_FALSE;
 }
 
-static jboolean Bitmap_getColorSpace(JNIEnv* env, jobject, jlong bitmapHandle,
-        jfloatArray xyzArray, jfloatArray paramsArray) {
-
+static jobject Bitmap_computeColorSpace(JNIEnv* env, jobject, jlong bitmapHandle) {
     LocalScopedBitmap bitmapHolder(bitmapHandle);
-    if (!bitmapHolder.valid()) return JNI_FALSE;
+    if (!bitmapHolder.valid()) return nullptr;
 
     SkColorSpace* colorSpace = bitmapHolder->info().colorSpace();
-    if (colorSpace == nullptr) return JNI_FALSE;
+    if (colorSpace == nullptr) return nullptr;
 
-    skcms_Matrix3x3 xyzMatrix;
-    if (!colorSpace->toXYZD50(&xyzMatrix)) return JNI_FALSE;
-
-    jfloat* xyz = env->GetFloatArrayElements(xyzArray, NULL);
-    xyz[0] = xyzMatrix.vals[0][0];
-    xyz[1] = xyzMatrix.vals[1][0];
-    xyz[2] = xyzMatrix.vals[2][0];
-    xyz[3] = xyzMatrix.vals[0][1];
-    xyz[4] = xyzMatrix.vals[1][1];
-    xyz[5] = xyzMatrix.vals[2][1];
-    xyz[6] = xyzMatrix.vals[0][2];
-    xyz[7] = xyzMatrix.vals[1][2];
-    xyz[8] = xyzMatrix.vals[2][2];
-    env->ReleaseFloatArrayElements(xyzArray, xyz, 0);
-
-    skcms_TransferFunction transferParams;
-    if (!colorSpace->isNumericalTransferFn(&transferParams)) return JNI_FALSE;
-
-    jfloat* params = env->GetFloatArrayElements(paramsArray, NULL);
-    params[0] = transferParams.a;
-    params[1] = transferParams.b;
-    params[2] = transferParams.c;
-    params[3] = transferParams.d;
-    params[4] = transferParams.e;
-    params[5] = transferParams.f;
-    params[6] = transferParams.g;
-    env->ReleaseFloatArrayElements(paramsArray, params, 0);
-
-    return JNI_TRUE;
+    return GraphicsJNI::getColorSpace(env, colorSpace, bitmapHolder->info().colorType());
 }
 
 static void Bitmap_setColorSpace(JNIEnv* env, jobject, jlong bitmapHandle, jlong colorSpacePtr) {
@@ -1174,13 +1143,6 @@
     return createJavaGraphicBuffer(env, buffer);
 }
 
-static void Bitmap_copyColorSpace(JNIEnv* env, jobject, jlong srcBitmapPtr, jlong dstBitmapPtr) {
-    LocalScopedBitmap srcBitmapHandle(srcBitmapPtr);
-    LocalScopedBitmap dstBitmapHandle(dstBitmapPtr);
-
-    dstBitmapHandle->bitmap().setColorSpace(srcBitmapHandle->bitmap().info().refColorSpace());
-}
-
 static jboolean Bitmap_isImmutable(jlong bitmapHandle) {
     LocalScopedBitmap bitmapHolder(bitmapHandle);
     if (!bitmapHolder.valid()) return JNI_FALSE;
@@ -1215,7 +1177,6 @@
     {   "nativeErase",              "(JJJ)V", (void*)Bitmap_eraseLong },
     {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },
     {   "nativeConfig",             "(J)I", (void*)Bitmap_config },
-    {   "nativeIsConfigF16",        "(J)Z", (void*)Bitmap_isConfigF16 },
     {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },
     {   "nativeIsPremultiplied",    "(J)Z", (void*)Bitmap_isPremultiplied},
     {   "nativeSetHasAlpha",        "(JZZ)V", (void*)Bitmap_setHasAlpha},
@@ -1248,12 +1209,10 @@
         (void*) Bitmap_wrapHardwareBufferBitmap },
     {   "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;",
         (void*) Bitmap_createGraphicBufferHandle },
-    {   "nativeGetColorSpace",      "(J[F[F)Z", (void*)Bitmap_getColorSpace },
+    {   "nativeComputeColorSpace",  "(J)Landroid/graphics/ColorSpace;", (void*)Bitmap_computeColorSpace },
     {   "nativeSetColorSpace",      "(JJ)V", (void*)Bitmap_setColorSpace },
     {   "nativeIsSRGB",             "(J)Z", (void*)Bitmap_isSRGB },
     {   "nativeIsSRGBLinear",       "(J)Z", (void*)Bitmap_isSRGBLinear},
-    {   "nativeCopyColorSpace",     "(JJ)V",
-        (void*)Bitmap_copyColorSpace },
     {   "nativeSetImmutable",       "(J)V", (void*)Bitmap_setImmutable},
 
     // ------------ @CriticalNative ----------------
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 70e6604..4ba4540 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -307,7 +307,7 @@
         env->SetObjectField(options, gOptions_outConfigFieldID, config);
 
         env->SetObjectField(options, gOptions_outColorSpaceFieldID,
-                GraphicsJNI::getColorSpace(env, decodeColorSpace, decodeColorType));
+                GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType));
 
         if (onlyDecodeSize) {
             return nullptr;
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index d65f324..9c07e2d 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -215,7 +215,7 @@
         env->SetObjectField(options, gOptions_outConfigFieldID, config);
 
         env->SetObjectField(options, gOptions_outColorSpaceFieldID,
-                GraphicsJNI::getColorSpace(env, decodeColorSpace, decodeColorType));
+                GraphicsJNI::getColorSpace(env, decodeColorSpace.get(), decodeColorType));
     }
 
     // If we may have reused a bitmap, we need to indicate that the pixels have changed.
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 6570992..2987c5e 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -187,6 +187,8 @@
 
 static jclass gColorSpace_Named_class;
 static jfieldID gColorSpace_Named_sRGBFieldID;
+static jfieldID gColorSpace_Named_ExtendedSRGBFieldID;
+static jfieldID gColorSpace_Named_LinearSRGBFieldID;
 static jfieldID gColorSpace_Named_LinearExtendedSRGBFieldID;
 
 static jclass gTransferParameters_class;
@@ -412,67 +414,78 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-jobject GraphicsJNI::getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace,
+jobject GraphicsJNI::getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace,
         SkColorType decodeColorType) {
-    jobject colorSpace = nullptr;
-
-    // No need to match, we know what the output color space will be
-    if (decodeColorType == kRGBA_F16_SkColorType) {
-        jobject linearExtendedSRGB = env->GetStaticObjectField(
-                gColorSpace_Named_class, gColorSpace_Named_LinearExtendedSRGBFieldID);
-        colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
-                gColorSpace_getMethodID, linearExtendedSRGB);
-    } else {
-        // Same here, no need to match
-        if (decodeColorSpace->isSRGB()) {
-            jobject sRGB = env->GetStaticObjectField(
-                    gColorSpace_Named_class, gColorSpace_Named_sRGBFieldID);
-            colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
-                    gColorSpace_getMethodID, sRGB);
-        } else if (decodeColorSpace.get() != nullptr) {
-            // Try to match against known RGB color spaces using the CIE XYZ D50
-            // conversion matrix and numerical transfer function parameters
-            skcms_Matrix3x3 xyzMatrix;
-            LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix));
-
-            skcms_TransferFunction transferParams;
-            // We can only handle numerical transfer functions at the moment
-            LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams));
-
-            jobject params = env->NewObject(gTransferParameters_class,
-                    gTransferParameters_constructorMethodID,
-                    transferParams.a, transferParams.b, transferParams.c,
-                    transferParams.d, transferParams.e, transferParams.f,
-                    transferParams.g);
-
-            jfloatArray xyzArray = env->NewFloatArray(9);
-            jfloat xyz[9] = {
-                    xyzMatrix.vals[0][0],
-                    xyzMatrix.vals[1][0],
-                    xyzMatrix.vals[2][0],
-                    xyzMatrix.vals[0][1],
-                    xyzMatrix.vals[1][1],
-                    xyzMatrix.vals[2][1],
-                    xyzMatrix.vals[0][2],
-                    xyzMatrix.vals[1][2],
-                    xyzMatrix.vals[2][2]
-            };
-            env->SetFloatArrayRegion(xyzArray, 0, 9, xyz);
-
-            colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
-                    gColorSpace_matchMethodID, xyzArray, params);
-
-            if (colorSpace == nullptr) {
-                // We couldn't find an exact match, let's create a new color space
-                // instance with the 3x3 conversion matrix and transfer function
-                colorSpace = env->NewObject(gColorSpaceRGB_class,
-                        gColorSpaceRGB_constructorMethodID,
-                        env->NewStringUTF("Unknown"), xyzArray, params);
-            }
-
-            env->DeleteLocalRef(xyzArray);
-        }
+    if (!decodeColorSpace || decodeColorType == kAlpha_8_SkColorType) {
+        return nullptr;
     }
+
+    // Special checks for the common sRGB cases and their extended variants.
+    jobject namedCS = nullptr;
+    sk_sp<SkColorSpace> srgbLinear = SkColorSpace::MakeSRGBLinear();
+    if (decodeColorType == kRGBA_F16_SkColorType) {
+        // An F16 Bitmap will always report that it is EXTENDED if
+        // it matches a ColorSpace that has an EXTENDED variant.
+        if (decodeColorSpace->isSRGB()) {
+            namedCS = env->GetStaticObjectField(gColorSpace_Named_class,
+                                                gColorSpace_Named_ExtendedSRGBFieldID);
+        } else if (decodeColorSpace == srgbLinear.get()) {
+            namedCS = env->GetStaticObjectField(gColorSpace_Named_class,
+                                                gColorSpace_Named_LinearExtendedSRGBFieldID);
+        }
+    } else if (decodeColorSpace->isSRGB()) {
+        namedCS = env->GetStaticObjectField(gColorSpace_Named_class,
+                                            gColorSpace_Named_sRGBFieldID);
+    } else if (decodeColorSpace == srgbLinear.get()) {
+        namedCS = env->GetStaticObjectField(gColorSpace_Named_class,
+                                            gColorSpace_Named_LinearSRGBFieldID);
+    }
+
+    if (namedCS) {
+        return env->CallStaticObjectMethod(gColorSpace_class, gColorSpace_getMethodID, namedCS);
+    }
+
+    // Try to match against known RGB color spaces using the CIE XYZ D50
+    // conversion matrix and numerical transfer function parameters
+    skcms_Matrix3x3 xyzMatrix;
+    LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix));
+
+    skcms_TransferFunction transferParams;
+    // We can only handle numerical transfer functions at the moment
+    LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams));
+
+    jobject params = env->NewObject(gTransferParameters_class,
+            gTransferParameters_constructorMethodID,
+            transferParams.a, transferParams.b, transferParams.c,
+            transferParams.d, transferParams.e, transferParams.f,
+            transferParams.g);
+
+    jfloatArray xyzArray = env->NewFloatArray(9);
+    jfloat xyz[9] = {
+            xyzMatrix.vals[0][0],
+            xyzMatrix.vals[1][0],
+            xyzMatrix.vals[2][0],
+            xyzMatrix.vals[0][1],
+            xyzMatrix.vals[1][1],
+            xyzMatrix.vals[2][1],
+            xyzMatrix.vals[0][2],
+            xyzMatrix.vals[1][2],
+            xyzMatrix.vals[2][2]
+    };
+    env->SetFloatArrayRegion(xyzArray, 0, 9, xyz);
+
+    jobject colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
+            gColorSpace_matchMethodID, xyzArray, params);
+
+    if (colorSpace == nullptr) {
+        // We couldn't find an exact match, let's create a new color space
+        // instance with the 3x3 conversion matrix and transfer function
+        colorSpace = env->NewObject(gColorSpaceRGB_class,
+                gColorSpaceRGB_constructorMethodID,
+                env->NewStringUTF("Unknown"), xyzArray, params);
+    }
+
+    env->DeleteLocalRef(xyzArray);
     return colorSpace;
 }
 
@@ -658,6 +671,10 @@
             FindClassOrDie(env, "android/graphics/ColorSpace$Named"));
     gColorSpace_Named_sRGBFieldID = GetStaticFieldIDOrDie(env,
             gColorSpace_Named_class, "SRGB", "Landroid/graphics/ColorSpace$Named;");
+    gColorSpace_Named_ExtendedSRGBFieldID = GetStaticFieldIDOrDie(env,
+            gColorSpace_Named_class, "EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;");
+    gColorSpace_Named_LinearSRGBFieldID = GetStaticFieldIDOrDie(env,
+            gColorSpace_Named_class, "LINEAR_SRGB", "Landroid/graphics/ColorSpace$Named;");
     gColorSpace_Named_LinearExtendedSRGBFieldID = GetStaticFieldIDOrDie(env,
             gColorSpace_Named_class, "LINEAR_EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;");
 
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index dc0d022..f80651c 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -109,7 +109,13 @@
      */
     static sk_sp<SkColorSpace> getNativeColorSpace(jlong colorSpaceHandle);
 
-    static jobject getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace,
+    /**
+     * Return the android.graphics.ColorSpace Java object that corresponds to decodeColorSpace
+     * and decodeColorType.
+     *
+     * This may create a new object if none of the Named ColorSpaces match.
+     */
+    static jobject getColorSpace(JNIEnv* env, SkColorSpace* decodeColorSpace,
             SkColorType decodeColorType);
 
     /**
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 2d83ac3..9efcace 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -506,9 +506,9 @@
 
 static jobject ImageDecoder_nGetColorSpace(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
     auto* codec = reinterpret_cast<ImageDecoder*>(nativePtr)->mCodec.get();
-    auto colorType = codec->computeOutputColorType(codec->getInfo().colorType());
+    auto colorType = codec->computeOutputColorType(kN32_SkColorType);
     sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType);
-    return GraphicsJNI::getColorSpace(env, colorSpace, colorType);
+    return GraphicsJNI::getColorSpace(env, colorSpace.get(), colorType);
 }
 
 static const JNINativeMethod gImageDecoderMethods[] = {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 8c73630..2aa5cb4 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -33,7 +33,6 @@
 
 #include <private/EGL/cache.h>
 
-#include <utils/Looper.h>
 #include <utils/RefBase.h>
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
@@ -144,52 +143,22 @@
     uint32_t mRequestId;
 };
 
-class RenderingException : public MessageHandler {
+class FrameCompleteWrapper : public LightRefBase<FrameCompleteWrapper> {
 public:
-    RenderingException(JavaVM* vm, const std::string& message)
-            : mVm(vm)
-            , mMessage(message) {
-    }
-
-    virtual void handleMessage(const Message&) {
-        throwException(mVm, mMessage);
-    }
-
-    static void throwException(JavaVM* vm, const std::string& message) {
-        JNIEnv* env = getenv(vm);
-        jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
-    }
-
-private:
-    JavaVM* mVm;
-    std::string mMessage;
-};
-
-class FrameCompleteWrapper : public MessageHandler {
-public:
-    FrameCompleteWrapper(JNIEnv* env, jobject jobject) {
-        mLooper = Looper::getForThread();
-        LOG_ALWAYS_FATAL_IF(!mLooper.get(), "Must create runnable on a Looper thread!");
+    explicit FrameCompleteWrapper(JNIEnv* env, jobject jobject) {
         env->GetJavaVM(&mVm);
         mObject = env->NewGlobalRef(jobject);
         LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref");
     }
 
-    virtual ~FrameCompleteWrapper() {
+    ~FrameCompleteWrapper() {
         releaseObject();
     }
 
-    void postFrameComplete(int64_t frameNr) {
+    void onFrameComplete(int64_t frameNr) {
         if (mObject) {
-            mFrameNr = frameNr;
-            mLooper->sendMessage(this, 0);
-        }
-    }
-
-    virtual void handleMessage(const Message&) {
-        if (mObject) {
-            ATRACE_FORMAT("frameComplete %" PRId64, mFrameNr);
-            getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, mFrameNr);
+            ATRACE_FORMAT("frameComplete %" PRId64, frameNr);
+            getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, frameNr);
             releaseObject();
         }
     }
@@ -197,8 +166,6 @@
 private:
     JavaVM* mVm;
     jobject mObject;
-    sp<Looper> mLooper;
-    int64_t mFrameNr = -1;
 
     void releaseObject() {
         if (mObject) {
@@ -211,16 +178,14 @@
 class RootRenderNode : public RenderNode, ErrorHandler {
 public:
     explicit RootRenderNode(JNIEnv* env) : RenderNode() {
-        mLooper = Looper::getForThread();
-        LOG_ALWAYS_FATAL_IF(!mLooper.get(),
-                "Must create RootRenderNode on a thread with a looper!");
         env->GetJavaVM(&mVm);
     }
 
     virtual ~RootRenderNode() {}
 
     virtual void onError(const std::string& message) override {
-        mLooper->sendMessage(new RenderingException(mVm, message), 0);
+        JNIEnv* env = getenv(mVm);
+        jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
     }
 
     virtual void prepareTree(TreeInfo& info) override {
@@ -249,14 +214,6 @@
         info.errorHandler = nullptr;
     }
 
-    void sendMessage(const sp<MessageHandler>& handler) {
-        mLooper->sendMessage(handler, 0);
-    }
-
-    void sendMessageDelayed(const sp<MessageHandler>& handler, nsecs_t delayInMs) {
-        mLooper->sendMessageDelayed(ms2ns(delayInMs), handler, 0);
-    }
-
     void attachAnimatingNode(RenderNode* animatingNode) {
         mPendingAnimatingRenderNodes.push_back(animatingNode);
     }
@@ -404,7 +361,6 @@
     }
 
 private:
-    sp<Looper> mLooper;
     JavaVM* mVm;
     std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes;
     std::set< sp<PropertyValuesAnimatorSet> > mPendingVectorDrawableAnimators;
@@ -435,7 +391,9 @@
             // the onFinished callback will then be ignored.
             sp<FinishAndInvokeListener> message
                     = new FinishAndInvokeListener(anim);
-            sendMessageDelayed(message, remainingTimeInMs);
+            auto looper = Looper::getForThread();
+            LOG_ALWAYS_FATAL_IF(looper == nullptr, "Not on a looper thread?");
+            looper->sendMessageDelayed(ms2ns(remainingTimeInMs), message, 0);
             anim->clearOneShotListener();
         }
     }
@@ -463,7 +421,6 @@
     virtual void runRemainingAnimations(TreeInfo& info) {
         AnimationContext::runRemainingAnimations(info);
         mRootNode->runVectorDrawableAnimators(this, info);
-        postOnFinishedEvents();
     }
 
     virtual void pauseAnimators() override {
@@ -471,27 +428,16 @@
     }
 
     virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) {
-        OnFinishedEvent event(animator, listener);
-        mOnFinishedEvents.push_back(event);
+        listener->onAnimationFinished(animator);
     }
 
     virtual void destroy() {
         AnimationContext::destroy();
         mRootNode->detachAnimators();
-        postOnFinishedEvents();
     }
 
 private:
     sp<RootRenderNode> mRootNode;
-    std::vector<OnFinishedEvent> mOnFinishedEvents;
-
-    void postOnFinishedEvents() {
-        if (mOnFinishedEvents.size()) {
-            sp<InvokeAnimationListeners> message
-                    = new InvokeAnimationListeners(mOnFinishedEvents);
-            mRootNode->sendMessage(message);
-        }
-    }
 };
 
 class ContextFactoryImpl : public IContextFactory {
@@ -958,7 +904,7 @@
     } else {
         sp<FrameCompleteWrapper> wrapper = new FrameCompleteWrapper{env, callback};
         proxy->setFrameCompleteCallback([wrapper](int64_t frameNr) {
-            wrapper->postFrameComplete(frameNr);
+            wrapper->onFrameComplete(frameNr);
         });
     }
 }
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index a4167c1..516fa7b 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -58,6 +58,10 @@
         optional int64 duration_ms = 2;
     }
     repeated TxLevel tx = 4;
+
+    // Total rail charge consumed by the monitored rails by the controller. The value may
+    // always be 0 if the device doesn't support monitored rail calculations.
+    optional double monitored_rail_charge_mah = 5;
 }
 
 message SystemProto {
diff --git a/core/proto/android/server/connectivity/Android.bp b/core/proto/android/server/connectivity/Android.bp
new file mode 100644
index 0000000..c0ac2cb
--- /dev/null
+++ b/core/proto/android/server/connectivity/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library_static {
+    name: "datastallprotosnano",
+    proto: {
+        type: "nano",
+    },
+    srcs: [
+        "data_stall_event.proto",
+    ],
+    sdk_version: "system_current",
+    no_framework_libs: true,
+}
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_qs_night_display_on.xml b/core/res/res/drawable/ic_qs_night_display_on.xml
index 35907cc..a4755ee 100644
--- a/core/res/res/drawable/ic_qs_night_display_on.xml
+++ b/core/res/res/drawable/ic_qs_night_display_on.xml
@@ -1,5 +1,5 @@
 <!--
-    Copyright (C) 2017 The Android Open Source Project
+    Copyright (C) 2019 The Android Open Source Project
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -14,14 +14,13 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="64dp"
-    android:height="64dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <group
-        android:translateX="-1.0">
-        <path
-            android:pathData="M13,12c0,-3.57 2.2,-6.62 5.31,-7.87 0.89,-0.36 0.75,-1.69 -0.19,-1.9 -1.1,-0.24 -2.27,-0.3 -3.48,-0.14 -4.51,0.6 -8.12,4.31 -8.59,8.83C5.43,16.93 10.12,22 16,22c0.73,0 1.43,-0.08 2.12,-0.23 0.95,-0.21 1.1,-1.53 0.2,-1.9A8.471,8.471 0,0 1,13 12z"
-            android:fillColor="#FFF"/>
-    </group>
-</vector>
\ No newline at end of file
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M6.28,4.81c0,0.16,0.01,0.32,0.02,0.48c0.38,6.58,5.83,12.03,12.41,12.41c0.16,0.01,0.32,0.02,0.47,0.02 c-1.58,1.2-3.53,1.88-5.56,1.88c-0.46,0-0.93-0.03-1.4-0.1c-3.96-0.58-7.13-3.75-7.71-7.71C4.13,9.24,4.8,6.75,6.28,4.81 M8.27,0.6 c-0.08,0-0.17,0.02-0.25,0.07c-3.8,2.2-6.2,6.56-5.49,11.4c0.7,4.82,4.59,8.7,9.4,9.4c0.57,0.08,1.13,0.12,1.69,0.12 c4.15,0,7.78-2.26,9.72-5.62c0.2-0.35-0.07-0.76-0.44-0.76c-0.05,0-0.1,0.01-0.15,0.02c-1.03,0.31-2.12,0.48-3.25,0.48 c-0.22,0-0.44-0.01-0.67-0.02C13.23,15.38,8.62,10.77,8.29,5.17C8.21,3.81,8.38,2.49,8.75,1.26C8.86,0.91,8.59,0.6,8.27,0.6 L8.27,0.6z" />
+</vector>
+
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 3683bfd..10798ad 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -20,12 +20,10 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:maxWidth="@dimen/resolver_max_width"
     android:maxCollapsedHeight="288dp"
     android:maxCollapsedHeightSmall="56dp"
     android:id="@id/contentPanel">
 
-
     <RelativeLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -153,8 +151,9 @@
         android:background="?attr/colorBackgroundFloating">
 
         <LinearLayout
-            android:layout_width="match_parent"
+            android:layout_width="@dimen/chooser_preview_width"
             android:layout_height="wrap_content"
+            android:layout_gravity="center"
             android:orientation="horizontal"
             android:paddingLeft="@dimen/chooser_edge_margin_normal"
             android:paddingRight="@dimen/chooser_edge_margin_normal"
@@ -182,8 +181,9 @@
         <!-- Required sub-layout so we can get the nice rounded corners-->
         <!-- around this section -->
         <LinearLayout
-            android:layout_width="match_parent"
+            android:layout_width="@dimen/chooser_preview_width"
             android:layout_height="wrap_content"
+            android:layout_gravity="center"
             android:orientation="horizontal"
             android:layout_marginLeft="@dimen/chooser_edge_margin_thin"
             android:layout_marginRight="@dimen/chooser_edge_margin_thin"
@@ -224,8 +224,9 @@
         android:background="?attr/colorBackgroundFloating">
 
         <LinearLayout
-            android:layout_width="match_parent"
+            android:layout_width="@dimen/chooser_preview_width"
             android:layout_height="wrap_content"
+            android:layout_gravity="center"
             android:orientation="horizontal"
             android:paddingLeft="@dimen/chooser_edge_margin_normal"
             android:paddingRight="@dimen/chooser_edge_margin_normal"
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 351bd81..9e87a47 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -76,4 +76,6 @@
      <!-- Floating toolbar dimensions -->
      <dimen name="floating_toolbar_preferred_width">544dp</dimen>
 
+    <dimen name="chooser_preview_width">480dp</dimen>
+
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 46e14b4..224f54c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4349,14 +4349,18 @@
         <attr name="indeterminate" format="boolean" />
         <!-- Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). -->
         <attr name="indeterminateOnly" format="boolean" />
-        <!-- Drawable used for the indeterminate mode. -->
+        <!-- Drawable used for the indeterminate mode. One that implements Animatable offers more
+             control over the animation.-->
         <attr name="indeterminateDrawable" format="reference" />
         <!-- Drawable used for the progress mode. -->
         <attr name="progressDrawable" format="reference" />
-        <!-- Duration of the indeterminate animation. -->
+        <!-- Duration of the indeterminate animation. Only affects the indeterminate animation
+             if the indeterminate Drawable does not implement
+             android.graphics.drawable.Animatable. -->
         <attr name="indeterminateDuration" format="integer" min="1" />
-        <!-- Defines how the indeterminate mode should behave when the progress
-        reaches max. -->
+        <!-- Defines how the indeterminate mode should behave when the progress reaches max. Only
+             affects the indeterminate animation if the indeterminate Drawable does not implement
+             android.graphics.drawable.Animatable. -->
         <attr name="indeterminateBehavior">
             <!-- Progress starts over from 0. -->
             <enum name="repeat" value="1" />
@@ -4367,6 +4371,9 @@
         <attr name="maxWidth" />
         <attr name="minHeight" format="dimension" />
         <attr name="maxHeight" />
+        <!-- Sets the acceleration curve for the indeterminate animation. Defaults to a linear
+             interpolation. Only affects the indeterminate animation if the indeterminate Drawable
+             does not implement android.graphics.drawable.Animatable.-->
         <attr name="interpolator" format="reference" />
         <!-- Timeout between frames of animation in milliseconds.
              {@deprecated Not used by the framework}. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d14164f..8dfd5e84 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -46,6 +46,8 @@
         <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_microphone</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
@@ -55,8 +57,6 @@
         <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_microphone</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
     </string-array>
 
@@ -3252,7 +3252,7 @@
          skinny aspect ratio that is not expected to be widely used. -->
     <item name="config_pictureInPictureMinAspectRatio" format="float" type="dimen">0.41841004184</item>
 
-    <!-- The minimum aspect ratio (width/height) that is supported for picture-in-picture. Any
+    <!-- The maximum aspect ratio (width/height) that is supported for picture-in-picture. Any
          ratio larger than this is considered to wide and short to be usable. Currently 2.39:1. -->
     <item name="config_pictureInPictureMaxAspectRatio" format="float" type="dimen">2.39</item>
 
@@ -3273,6 +3273,11 @@
     -->
     <integer name="config_dockedStackDividerSnapMode">0</integer>
 
+    <!-- The maximum aspect ratio (longerSide/shorterSide) that is treated as close-to-square. If
+         config_forceDefaultOrientation is set to true, the rotation on a close-to-square display
+         will be fixed. -->
+    <item name="config_closeToSquareDisplayMaxAspectRatio" format="float" type="dimen">1.333</item>
+
     <!-- List of comma separated package names for which we the system will not show crash, ANR,
          etc. dialogs. -->
     <string translatable="false" name="config_appsNotReportingCrashes"></string>
@@ -3906,19 +3911,19 @@
 
     <!-- See DisplayWhiteBalanceController.
          A float array containing a list of ambient color temperatures, in Kelvin. This array,
-         together with config_displayWhiteBalanceDisplayTemperatureValues, is used to generate a
+         together with config_displayWhiteBalanceDisplayColorTemperatures, is used to generate a
          lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
          ambient color temperature readings to a target color temperature for the display.
          This table is optional. If used, this array must,
          1) Contain at least two entries
-         2) Be the same length as config_displayWhiteBalanceDisplayTemperatureValues. -->
-    <array name="config_displayWhiteBalanceAmbientTemperatureValues">
+         2) Be the same length as config_displayWhiteBalanceDisplayColorTemperatures. -->
+    <array name="config_displayWhiteBalanceAmbientColorTemperatures">
     </array>
 
     <!-- See DisplayWhiteBalanceController.
          An array containing a list of display color temperatures, in Kelvin. See
-         config_displayWhiteBalanceAmbientTemperatureValues for additional details.
+         config_displayWhiteBalanceAmbientColorTemperatures for additional details.
          The same restrictions apply to this array. -->
-    <array name="config_displayWhiteBalanceDisplayTemperatureValues">
+    <array name="config_displayWhiteBalanceDisplayColorTemperatures">
     </array>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 9f86f84..39cbd26 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -721,4 +721,5 @@
     <dimen name="chooser_edge_margin_thin">16dp</dimen>
     <dimen name="chooser_edge_margin_normal">24dp</dimen>
     <dimen name="chooser_preview_image_font_size">20sp</dimen>
+    <dimen name="chooser_preview_width">-1px</dimen>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0cdf388..5948f29 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3712,7 +3712,7 @@
     <string name="ext_media_browse_action">Explore</string>
 
     <!-- Notification action to transfer media [CHAR LIMIT=40] -->
-    <string name="ext_media_seamless_action">Seamless transfer</string>
+    <string name="ext_media_seamless_action">Switch output</string>
 
     <!-- Notification title when external media is missing [CHAR LIMIT=30] -->
     <string name="ext_media_missing_title"><xliff:g id="name" example="SD card">%s</xliff:g> missing</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a1bafbf..16aed90 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -403,6 +403,7 @@
   <java-symbol type="integer" name="config_defaultPictureInPictureGravity" />
   <java-symbol type="dimen" name="config_pictureInPictureMinAspectRatio" />
   <java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" />
+  <java-symbol type="dimen" name="config_closeToSquareDisplayMaxAspectRatio" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
@@ -2755,6 +2756,7 @@
   <java-symbol type="dimen" name="chooser_edge_margin_thin" />
   <java-symbol type="dimen" name="chooser_edge_margin_normal" />
   <java-symbol type="dimen" name="chooser_preview_image_font_size"/>
+  <java-symbol type="dimen" name="chooser_preview_width" />
   <java-symbol type="layout" name="chooser_grid" />
   <java-symbol type="layout" name="resolve_grid_item" />
   <java-symbol type="id" name="day_picker_view_pager" />
@@ -3651,7 +3653,7 @@
   <java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" />
   <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" />
   <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" />
-  <java-symbol type="array" name="config_displayWhiteBalanceAmbientTemperatureValues" />
-  <java-symbol type="array" name="config_displayWhiteBalanceDisplayTemperatureValues" />
+  <java-symbol type="array" name="config_displayWhiteBalanceAmbientColorTemperatures" />
+  <java-symbol type="array" name="config_displayWhiteBalanceDisplayColorTemperatures" />
   <java-symbol type="drawable" name="ic_action_open" />
 </resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 4d2f005..c57b609 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -621,6 +621,7 @@
                  Settings.Secure.DISABLED_PRINT_SERVICES,
                  Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
                  Settings.Secure.DISPLAY_DENSITY_FORCED,
+                 Settings.Secure.DOCKED_CLOCK_FACE,
                  Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
                  Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
                  Settings.Secure.ENABLED_INPUT_METHODS,  // Intentionally removed in P
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
index f325d89..a97c3fa 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureEventTest.java
@@ -15,6 +15,7 @@
  */
 package android.view.contentcapture;
 
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
@@ -174,7 +175,8 @@
         assertThat(event.getIds()).isNull();
         assertThat(event.getText()).isNull();
         assertThat(event.getViewNode()).isNull();
-        final ContentCaptureContext clientContext = event.getClientContext();
+        final ContentCaptureContext clientContext = event.getContentCaptureContext();
+        assertThat(clientContext).isNotNull();
         assertThat(clientContext.getAction()).isEqualTo("WHATEVER");
     }
 
@@ -205,9 +207,44 @@
         assertThat(event.getIds()).isNull();
         assertThat(event.getText()).isNull();
         assertThat(event.getViewNode()).isNull();
-        assertThat(event.getClientContext()).isNull();
+        assertThat(event.getContentCaptureContext()).isNull();
     }
 
+
+    @Test
+    public void testContextUpdated_directly() {
+        final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_CONTEXT_UPDATED)
+                .setClientContext(mClientContext);
+        assertThat(event).isNotNull();
+        assertContextUpdatedEvent(event);
+    }
+
+    @Test
+    public void testContextUpdated_throughParcel() {
+        final ContentCaptureEvent event = new ContentCaptureEvent("42", TYPE_CONTEXT_UPDATED)
+                .setClientContext(mClientContext);
+        assertThat(event).isNotNull();
+        final ContentCaptureEvent clone = cloneThroughParcel(event);
+        assertContextUpdatedEvent(clone);
+    }
+
+    private void assertContextUpdatedEvent(ContentCaptureEvent event) {
+        assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_UPDATED);
+        assertThat(event.getEventTime()).isAtLeast(MY_EPOCH);
+        assertThat(event.getSessionId()).isEqualTo("42");
+        assertThat(event.getParentSessionId()).isNull();
+        assertThat(event.getId()).isNull();
+        assertThat(event.getIds()).isNull();
+        assertThat(event.getText()).isNull();
+        assertThat(event.getViewNode()).isNull();
+        final ContentCaptureContext clientContext = event.getContentCaptureContext();
+        assertThat(clientContext).isNotNull();
+        assertThat(clientContext.getAction()).isEqualTo("WHATEVER");
+    }
+
+    // TODO(b/123036895): add test for all events type (right now we're just testing the 3 types
+    // that use logic to write to parcel
+
     private ContentCaptureEvent cloneThroughParcel(ContentCaptureEvent event) {
         Parcel parcel = Parcel.obtain();
 
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 34fdebf..b6717e1 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -160,5 +160,10 @@
         public void internalNotifyViewHierarchyEvent(boolean started) {
             throw new UnsupportedOperationException("should not have been called");
         }
+
+        @Override
+        public void updateContentCaptureContext(ContentCaptureContext context) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
     }
 }
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 18f0cae..8135671 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -891,8 +891,10 @@
             }
         }
 
+        ColorSpace cs = source.getColorSpace();
+
         if (m == null || m.isIdentity()) {
-            bitmap = createBitmap(neww, newh, newConfig, source.hasAlpha());
+            bitmap = createBitmap(null, neww, newh, newConfig, source.hasAlpha(), cs);
             paint = null;   // not needed
         } else {
             final boolean transformed = !m.rectStaysRect();
@@ -906,9 +908,14 @@
             if (transformed) {
                 if (transformedConfig != Config.ARGB_8888 && transformedConfig != Config.RGBA_F16) {
                     transformedConfig = Config.ARGB_8888;
+                    if (cs == null) {
+                        cs = ColorSpace.get(ColorSpace.Named.SRGB);
+                    }
                 }
             }
-            bitmap = createBitmap(neww, newh, transformedConfig, transformed || source.hasAlpha());
+
+            bitmap = createBitmap(null, neww, newh, transformedConfig,
+                    transformed || source.hasAlpha(), cs);
 
             paint = new Paint();
             paint.setFilterBitmap(filter);
@@ -917,8 +924,6 @@
             }
         }
 
-        nativeCopyColorSpace(source.mNativePtr, bitmap.mNativePtr);
-
         // The new bitmap was created from a known bitmap source so assume that
         // they use the same density
         bitmap.mDensity = source.mDensity;
@@ -1000,10 +1005,10 @@
      * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
      *                 mark the bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
-     * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16},
-     *                   {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the
-     *                   config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB}
-     *                   is assumed.
+     * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}
+     *                   and {@link ColorSpace.Named#SRGB sRGB} or
+     *                   {@link ColorSpace.Named#LINEAR_SRGB Linear sRGB} is provided then the
+     *                   corresponding extended range variant is assumed.
      *
      * @throws IllegalArgumentException if the width or height are <= 0, if
      *         Config is Config.HARDWARE (because hardware bitmaps are always
@@ -1055,10 +1060,10 @@
      * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
      *                 mark the bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
-     * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16},
-     *                   {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the
-     *                   config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB}
-     *                   is assumed.
+     * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}
+     *                   and {@link ColorSpace.Named#SRGB sRGB} or
+     *                   {@link ColorSpace.Named#LINEAR_SRGB Linear sRGB} is provided then the
+     *                   corresponding extended range variant is assumed.
      *
      * @throws IllegalArgumentException if the width or height are <= 0, if
      *         Config is Config.HARDWARE (because hardware bitmaps are always
@@ -1075,22 +1080,12 @@
         if (config == Config.HARDWARE) {
             throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE");
         }
-        if (colorSpace == null) {
+        if (colorSpace == null && config != Config.ALPHA_8) {
             throw new IllegalArgumentException("can't create bitmap without a color space");
         }
 
-        if (config != Config.ARGB_8888) {
-            if (config == Config.RGBA_F16) {
-                // FIXME: This should be LINEAR_EXTENDED_SRGB, but that would fail a CTS test. See
-                // b/120960866. SRGB matches the old (incorrect) behavior.
-                //colorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
-                colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
-            } else {
-                colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
-            }
-        }
         Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true,
-                colorSpace.getNativeInstance());
+                colorSpace == null ? 0 : colorSpace.getNativeInstance());
 
         if (display != null) {
             bm.mDensity = display.densityDpi;
@@ -1514,6 +1509,9 @@
      * Convenience method that returns the width of this bitmap divided
      * by the density scale factor.
      *
+     * Returns the bitmap's width multiplied by the ratio of the target density to the bitmap's
+     * source density
+     *
      * @param targetDensity The density of the target canvas of the bitmap.
      * @return The scaled width of this bitmap, according to the density scale factor.
      */
@@ -1525,6 +1523,9 @@
      * Convenience method that returns the height of this bitmap divided
      * by the density scale factor.
      *
+     * Returns the bitmap's height multiplied by the ratio of the target density to the bitmap's
+     * source density
+     *
      * @param targetDensity The density of the target canvas of the bitmap.
      * @return The scaled height of this bitmap, according to the density scale factor.
      */
@@ -1701,41 +1702,9 @@
     @Nullable
     public final ColorSpace getColorSpace() {
         checkRecycled("getColorSpace called on a recycled bitmap");
-        // Cache the color space retrieval since it can be fairly expensive
         if (mColorSpace == null) {
-            if (nativeIsConfigF16(mNativePtr)) {
-                // an F16 bitmaps is intended to always be linear extended, but due to
-                // inconsistencies in Bitmap.create() functions it is possible to have
-                // rendered into a bitmap in non-linear sRGB.
-                if (nativeIsSRGB(mNativePtr)) {
-                    mColorSpace = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB);
-                } else {
-                    mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
-                }
-            } else if (nativeIsSRGB(mNativePtr)) {
-                mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
-            } else if (nativeIsSRGBLinear(mNativePtr)) {
-                mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_SRGB);
-            } else {
-                float[] xyz = new float[9];
-                float[] params = new float[7];
-
-                boolean hasColorSpace = nativeGetColorSpace(mNativePtr, xyz, params);
-                if (hasColorSpace) {
-                    ColorSpace.Rgb.TransferParameters parameters =
-                            new ColorSpace.Rgb.TransferParameters(
-                                    params[0], params[1], params[2],
-                                    params[3], params[4], params[5], params[6]);
-                    ColorSpace cs = ColorSpace.match(xyz, parameters);
-                    if (cs != null) {
-                        mColorSpace = cs;
-                    } else {
-                        mColorSpace = new ColorSpace.Rgb("Unknown", xyz, parameters);
-                    }
-                }
-            }
+            mColorSpace = nativeComputeColorSpace(mNativePtr);
         }
-
         return mColorSpace;
     }
 
@@ -1749,6 +1718,9 @@
      *         components min/max values reduce the numerical range compared to the
      *         previously assigned color space.
      *
+     * @throws IllegalArgumentException If the {@code Config} (returned by {@link #getConfig()})
+     *         is {@link Config#ALPHA_8}.
+     *
      * @param colorSpace to assign to the bitmap
      */
     public void setColorSpace(@NonNull ColorSpace colorSpace) {
@@ -1756,29 +1728,47 @@
         if (colorSpace == null) {
             throw new IllegalArgumentException("The colorSpace cannot be set to null");
         }
-        if (getColorSpace() != null) {
-            if (mColorSpace.getComponentCount() != colorSpace.getComponentCount()) {
-                throw new IllegalArgumentException("The new ColorSpace must have the same "
-                        + "component count as the current ColorSpace");
-            }
-            for (int i = 0; i < mColorSpace.getComponentCount(); i++) {
-                if (mColorSpace.getMinValue(i) < colorSpace.getMinValue(i)) {
-                    throw new IllegalArgumentException("The new ColorSpace cannot increase the "
-                            + "minimum value for any of the components compared to the current "
-                            + "ColorSpace. To perform this type of conversion create a new Bitmap "
-                            + "in the desired ColorSpace and draw this Bitmap into it.");
-                }
-                if (mColorSpace.getMaxValue(i) > colorSpace.getMaxValue(i)) {
-                    throw new IllegalArgumentException("The new ColorSpace cannot decrease the "
-                            + "maximum value for any of the components compared to the current "
-                            + "ColorSpace/ To perform this type of conversion create a new Bitmap"
-                            + "in the desired ColorSpace and draw this Bitmap into it.");
-                }
-            }
+
+        if (getConfig() == Config.ALPHA_8) {
+            throw new IllegalArgumentException("Cannot set a ColorSpace on ALPHA_8");
         }
 
+        // Keep track of the old ColorSpace for comparison, and so we can reset it in case of an
+        // Exception.
+        final ColorSpace oldColorSpace = getColorSpace();
         nativeSetColorSpace(mNativePtr, colorSpace.getNativeInstance());
-        mColorSpace = colorSpace;
+
+        // This will update mColorSpace. It may not be the same as |colorSpace|, e.g. if we
+        // corrected it because the Bitmap is F16.
+        mColorSpace = null;
+        final ColorSpace newColorSpace = getColorSpace();
+
+        try {
+            if (oldColorSpace.getComponentCount() != newColorSpace.getComponentCount()) {
+                throw new IllegalArgumentException("The new ColorSpace must have the same "
+                        + "component count as the current ColorSpace");
+            } else {
+                for (int i = 0; i < oldColorSpace.getComponentCount(); i++) {
+                    if (oldColorSpace.getMinValue(i) < newColorSpace.getMinValue(i)) {
+                        throw new IllegalArgumentException("The new ColorSpace cannot increase the "
+                                + "minimum value for any of the components compared to the current "
+                                + "ColorSpace. To perform this type of conversion create a new "
+                                + "Bitmap in the desired ColorSpace and draw this Bitmap into it.");
+                    }
+                    if (oldColorSpace.getMaxValue(i) > newColorSpace.getMaxValue(i)) {
+                        throw new IllegalArgumentException("The new ColorSpace cannot decrease the "
+                                + "maximum value for any of the components compared to the current "
+                                + "ColorSpace/ To perform this type of conversion create a new "
+                                + "Bitmap in the desired ColorSpace and draw this Bitmap into it.");
+                    }
+                }
+            }
+        } catch (IllegalArgumentException e) {
+            // Undo the change to the ColorSpace.
+            mColorSpace = oldColorSpace;
+            nativeSetColorSpace(mNativePtr, mColorSpace.getNativeInstance());
+            throw e;
+        }
     }
 
     /**
@@ -2197,7 +2187,6 @@
     private static native void nativeErase(long nativeBitmap, long colorSpacePtr, long color);
     private static native int nativeRowBytes(long nativeBitmap);
     private static native int nativeConfig(long nativeBitmap);
-    private static native boolean nativeIsConfigF16(long nativeBitmap);
 
     private static native int nativeGetPixel(long nativeBitmap, int x, int y);
     private static native long nativeGetColor(long nativeBitmap, int x, int y);
@@ -2241,11 +2230,10 @@
     private static native Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
                                                                 long nativeColorSpace);
     private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap);
-    private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params);
+    private static native ColorSpace nativeComputeColorSpace(long nativePtr);
     private static native void nativeSetColorSpace(long nativePtr, long nativeColorSpace);
     private static native boolean nativeIsSRGB(long nativePtr);
     private static native boolean nativeIsSRGBLinear(long nativePtr);
-    private static native void nativeCopyColorSpace(long srcBitmap, long dstBitmap);
 
     private static native void nativeSetImmutable(long nativePtr);
 
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 7aff041..49c3a3b 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -151,12 +151,9 @@
          * the decoder will pick either the color space embedded in the image
          * or the color space best suited for the requested image configuration
          * (for instance {@link ColorSpace.Named#SRGB sRGB} for
-         * the {@link Bitmap.Config#ARGB_8888} configuration).</p>
-         *
-         * <p>{@link Bitmap.Config#RGBA_F16} always uses the
-         * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space).
-         * Bitmaps in other configurations without an embedded color space are
-         * assumed to be in the {@link ColorSpace.Named#SRGB sRGB} color space.</p>
+         * {@link Bitmap.Config#ARGB_8888} configuration and
+         * {@link ColorSpace.Named#EXTENDED_SRGB EXTENDED_SRGB} for
+         * {@link Bitmap.Config#RGBA_F16}).</p>
          *
          * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
          * currently supported. An <code>IllegalArgumentException</code> will
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index c9e4694..0d52338 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1475,7 +1475,7 @@
                 x -> absRcpResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
                 x -> absResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
                 -0.799f, 2.399f,
-                null, // FIXME: Use SRGB_TRANSFER_PARAMETERS
+                SRGB_TRANSFER_PARAMETERS,
                 Named.EXTENDED_SRGB.ordinal()
         );
         sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 7ec76d7..99d8c1b 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -20,7 +20,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.os.IBinder;
@@ -28,13 +27,16 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
+import android.util.TimeUtils;
 import android.view.FrameMetricsObserver;
 import android.view.IGraphicsStats;
 import android.view.IGraphicsStatsCallback;
 import android.view.NativeVectorDrawableAnimator;
+import android.view.PixelCopy;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.TextureLayer;
+import android.view.animation.AnimationUtils;
 
 import com.android.internal.util.VirtualRefBasePtr;
 
@@ -42,6 +44,7 @@
 import java.io.FileDescriptor;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
 
 import sun.misc.Cleaner;
 
@@ -50,13 +53,8 @@
  * from {@link RenderNode}'s to an output {@link android.view.Surface}. There can be as many
  * HardwareRenderer instances as desired.</p>
  *
- * <h3>Threading</h3>
- * <p>HardwareRenderer is not thread safe. An instance of a HardwareRenderer must only be
- * created & used from a single thread. It does not matter what thread is used, however
- * it must have a {@link android.os.Looper}. Multiple instances do not have to share the same
- * thread, although they can.</p>
- *
  * <h3>Resources & lifecycle</h3>
+ *
  * <p>All HardwareRenderer instances share a common render thread. The render thread contains
  * the GPU context & resources necessary to do GPU-accelerated rendering. As such, the first
  * HardwareRenderer created comes with the cost of also creating the associated GPU contexts,
@@ -64,6 +62,7 @@
  * is to have a HardwareRenderer instance for every active {@link Surface}. For example
  * when an Activity shows a Dialog the system internally will use 2 hardware renderers, both
  * of which may be drawing at the same time.</p>
+ *
  * <p>NOTE: Due to the shared, cooperative nature of the render thread it is critical that
  * any {@link Surface} used must have a prompt, reliable consuming side. System-provided
  * consumers such as {@link android.view.SurfaceView},
@@ -73,8 +72,6 @@
  * it is the app's responsibility to ensure that they consume updates promptly and rapidly.
  * Failure to do so will cause the render thread to stall on that surface, blocking all
  * HardwareRenderer instances.</p>
- *
- * @hide
  */
 public class HardwareRenderer {
     private static final String LOG_TAG = "HardwareRenderer";
@@ -89,18 +86,18 @@
      * The renderer is requesting a redraw. This can occur if there's an animation that's running
      * in the RenderNode tree and the hardware renderer is unable to self-animate.
      *
-     * If this is returned from syncAndDrawFrame the expectation is that syncAndDrawFrame
+     * <p>If this is returned from syncAndDraw the expectation is that syncAndDraw
      * will be called again on the next vsync signal.
      */
     public static final int SYNC_REDRAW_REQUESTED = 1 << 0;
 
     /**
      * The hardware renderer no longer has a valid {@link android.view.Surface} to render to.
-     * This can happen if {@link Surface#destroy()} was called. The user should no longer
-     * attempt to call syncAndDrawFrame until a new surface has been provided by calling
+     * This can happen if {@link Surface#release()} was called. The user should no longer
+     * attempt to call syncAndDraw until a new surface has been provided by calling
      * setSurface.
      *
-     * Spoiler: the reward is GPU-accelerated drawing, better find that Surface!
+     * <p>Spoiler: the reward is GPU-accelerated drawing, better find that Surface!
      */
     public static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1;
 
@@ -119,6 +116,7 @@
      */
     public static final int SYNC_FRAME_DROPPED = 1 << 3;
 
+    /** @hide */
     @IntDef(value = {
             SYNC_OK, SYNC_REDRAW_REQUESTED, SYNC_LOST_SURFACE_REWARD_IF_FOUND,
             SYNC_CONTEXT_IS_STOPPED, SYNC_FRAME_DROPPED})
@@ -153,7 +151,6 @@
     protected RenderNode mRootNode;
     private boolean mOpaque = true;
     private boolean mForceDark = false;
-    private FrameInfo mScratchInfo;
     private boolean mIsWideGamut = false;
 
     /**
@@ -175,14 +172,14 @@
      * Destroys the rendering context of this HardwareRenderer. This destroys the resources
      * associated with this renderer and releases the currently set {@link Surface}.
      *
-     * The renderer may be restored from this state by setting a new {@link Surface}, setting
+     * <p>The renderer may be restored from this state by setting a new {@link Surface}, setting
      * new rendering content with {@link #setContentRoot(RenderNode)}, and resuming
-     * rendering with {@link #syncAndDrawFrame(long)}.
+     * rendering by issuing a new {@link FrameRenderRequest}.
      *
-     * It is suggested to call this in response to callbacks such as
+     * <p>It is suggested to call this in response to callbacks such as
      * {@link android.view.SurfaceHolder.Callback#surfaceDestroyed(SurfaceHolder)}.
      *
-     * Note that if there are any outstanding frame commit callbacks they may end up never being
+     * <p>Note that if there are any outstanding frame commit callbacks they may never being
      * invoked if the frame was deferred to a later vsync.
      */
     public void destroy() {
@@ -204,14 +201,14 @@
      * Sets the center of the light source. The light source point controls the directionality
      * and shape of shadows rendered by RenderNode Z & elevation.
      *
-     * The platform's recommendation is to set lightX to 'displayWidth / 2f - windowLeft', set
+     * <p>The platform's recommendation is to set lightX to 'displayWidth / 2f - windowLeft', set
      * lightY to 0 - windowTop, lightZ set to 600dp, and lightRadius to 800dp.
      *
-     * The light source should be setup both as part of initial configuration, and whenever
+     * <p>The light source should be setup both as part of initial configuration, and whenever
      * the window moves to ensure the light source stays anchored in display space instead
      * of in window space.
      *
-     * This must be set at least once along with {@link #setLightSourceAlpha(float, float)}
+     * <p>This must be set at least once along with {@link #setLightSourceAlpha(float, float)}
      * before shadows will work.
      *
      * @param lightX      The X position of the light source
@@ -233,10 +230,10 @@
      * Configures the ambient & spot shadow alphas. This is the alpha used when the shadow
      * has max alpha, and ramps down from the values provided to zero.
      *
-     * These values are typically provided by the current theme, see
+     * <p>These values are typically provided by the current theme, see
      * {@link android.R.attr#spotShadowAlpha} and {@link android.R.attr#ambientShadowAlpha}.
      *
-     * This must be set at least once along with
+     * <p>This must be set at least once along with
      * {@link #setLightSourceGeometry(float, float, float, float)} before shadows will work.
      *
      * @param ambientShadowAlpha The alpha for the ambient shadow. If unsure, a reasonable default
@@ -254,8 +251,8 @@
     /**
      * Sets the content root to render. It is not necessary to call this whenever the content
      * recording changes. Any mutations to the RenderNode content, or any of the RenderNode's
-     * contained within the content node, will be applied whenever {@link #syncAndDrawFrame(long)}
-     * is called.
+     * contained within the content node, will be applied whenever a new {@link FrameRenderRequest}
+     * is issued via {@link #createRenderRequest()} and {@link FrameRenderRequest#syncAndDraw()}.
      *
      * @param content The content to set as the root RenderNode. If null the content root is removed
      *                and the renderer will draw nothing.
@@ -295,6 +292,126 @@
     }
 
     /**
+     * Sets the parameters that can be used to control a render request for a
+     * {@link HardwareRenderer}. This is not thread-safe and must not be held on to for longer
+     * than a single frame request.
+     */
+    public final class FrameRenderRequest {
+        private FrameInfo mFrameInfo = new FrameInfo();
+        private boolean mWaitForPresent;
+
+        private FrameRenderRequest() { }
+
+        private void reset() {
+            mWaitForPresent = false;
+            // Default to the animation time which, if choreographer is in play, will default to the
+            // current vsync time. Otherwise it will be 'now'.
+            mRenderRequest.setVsyncTime(
+                    AnimationUtils.currentAnimationTimeMillis() * TimeUtils.NANOS_PER_MS);
+        }
+
+        /** @hide */
+        public void setFrameInfo(FrameInfo info) {
+            System.arraycopy(info.frameInfo, 0, mFrameInfo.frameInfo, 0, info.frameInfo.length);
+        }
+
+        /**
+         * Sets the vsync time that represents the start point of this frame. Typically this
+         * comes from {@link android.view.Choreographer.FrameCallback}. Other compatible time
+         * sources include {@link System#nanoTime()}, however if the result is being displayed
+         * on-screen then using {@link android.view.Choreographer} is strongly recommended to
+         * ensure smooth animations.
+         *
+         * <p>If the clock source is not from a CLOCK_MONOTONIC source then any animations driven
+         * directly by RenderThread will not be synchronized properly with the current frame.
+         *
+         * @param vsyncTime The vsync timestamp for this frame. The timestamp is in nanoseconds
+         *                  and should come from a CLOCK_MONOTONIC source.
+         *
+         * @return this instance
+         */
+        public FrameRenderRequest setVsyncTime(long vsyncTime) {
+            mFrameInfo.setVsync(vsyncTime, vsyncTime);
+            mFrameInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS);
+            return this;
+        }
+
+        /**
+         * Adds a frame commit callback. This callback will be invoked when the current rendering
+         * content has been rendered into a frame and submitted to the swap chain. The frame may
+         * not currently be visible on the display when this is invoked, but it has been submitted.
+         * This callback is useful in combination with {@link PixelCopy} to capture the current
+         * rendered content of the UI reliably.
+         *
+         * @param executor The executor to run the callback on. It is strongly recommended that
+         *                 this executor post to a different thread, as the calling thread is
+         *                 highly sensitive to being blocked.
+         * @param frameCommitCallback The callback to invoke when the frame content has been drawn.
+         *                            Will be invoked on the given {@link Executor}.
+         *
+         * @return this instance
+         */
+        public FrameRenderRequest setFrameCommitCallback(@NonNull Executor executor,
+                @NonNull Runnable frameCommitCallback) {
+            setFrameCompleteCallback(frameNr -> executor.execute(frameCommitCallback));
+            return this;
+        }
+
+        /**
+         * Sets whether or not {@link #syncAndDraw()} should block until the frame has been
+         * presented. If this is true and {@link #syncAndDraw()} does not return
+         * {@link #SYNC_FRAME_DROPPED} or an error then when {@link #syncAndDraw()} has returned
+         * the frame has been submitted to the {@link Surface}. The default and typically
+         * recommended value is false, as blocking for present will prevent pipelining from
+         * happening, reducing overall throughput. This is useful for situations such as
+         * {@link SurfaceHolder.Callback2#surfaceRedrawNeeded(SurfaceHolder)} where it is desired
+         * to block until a frame has been presented to ensure first-frame consistency with
+         * other Surfaces.
+         *
+         * @param shouldWait If true the next call to {@link #syncAndDraw()} will block until
+         *                   completion.
+         * @return this instance
+         */
+        public FrameRenderRequest setWaitForPresent(boolean shouldWait) {
+            mWaitForPresent = shouldWait;
+            return this;
+        }
+
+        /**
+         * Syncs the RenderNode tree to the render thread and requests a frame to be drawn. This
+         * {@link FrameRenderRequest} instance should no longer be used after calling this method.
+         * The system internally may reuse instances of {@link FrameRenderRequest} to reduce
+         * allocation churn.
+         *
+         * @return The result of the sync operation. See {@link SyncAndDrawResult}.
+         */
+        @SyncAndDrawResult
+        public int syncAndDraw() {
+            int syncResult = syncAndDrawFrame(mFrameInfo);
+            if (mWaitForPresent && (syncResult & SYNC_FRAME_DROPPED) == 0) {
+                fence();
+            }
+            return syncResult;
+        }
+    }
+
+    private FrameRenderRequest mRenderRequest = new FrameRenderRequest();
+
+    /**
+     * Returns a {@link FrameRenderRequest} that can be used to render a new frame. This is used
+     * to synchronize the RenderNode content provided by {@link #setContentRoot(RenderNode)} with
+     * the RenderThread and then renders a single frame to the Surface set with
+     * {@link #setSurface(Surface)}.
+     *
+     * @return An instance of {@link FrameRenderRequest}. The instance may be reused for every
+     * frame, so the caller should not hold onto it for longer than a single render request.
+     */
+    public FrameRenderRequest createRenderRequest() {
+        mRenderRequest.reset();
+        return mRenderRequest;
+    }
+
+    /**
      * Syncs the RenderNode tree to the render thread and requests a frame to be drawn.
      *
      * @hide
@@ -305,54 +422,15 @@
     }
 
     /**
-     * Syncs the RenderNode tree to the render thread and requests a frame to be drawn.
-     *
-     * @param vsyncTime The vsync timestamp for this frame. Typically this comes from
-     *                  {@link android.view.Choreographer.FrameCallback}. Must be set and be valid
-     *                  as the renderer uses this time internally to drive animations.
-     * @return The result of the sync operation. See {@link SyncAndDrawResult}.
-     */
-    @SyncAndDrawResult
-    public int syncAndDrawFrame(long vsyncTime) {
-        if (mScratchInfo == null) {
-            mScratchInfo = new FrameInfo();
-        }
-        mScratchInfo.setVsync(vsyncTime, vsyncTime);
-        mScratchInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS);
-        return syncAndDrawFrame(mScratchInfo);
-    }
-
-    /**
-     * Syncs the RenderNode tree to the render thread and requests a frame to be drawn.
-     * frameCommitCallback callback will be invoked when the current rendering content has been
-     * rendered into a frame and submitted to the swap chain.
-     *
-     * @param vsyncTime           The vsync timestamp for this frame. Typically this comes from
-     *                            {@link android.view.Choreographer.FrameCallback}. Must be set and
-     *                            be valid as the renderer uses this time internally to drive
-     *                            animations.
-     * @param frameCommitCallback The callback to invoke when the frame content has been drawn.
-     *                            Will be invoked on the current {@link android.os.Looper} thread.
-     * @return The result of the sync operation. See {@link SyncAndDrawResult}.
-     */
-    @SyncAndDrawResult
-    public int syncAndDrawFrame(long vsyncTime,
-            @Nullable Runnable frameCommitCallback) {
-        if (frameCommitCallback != null) {
-            setFrameCompleteCallback(frameNr -> frameCommitCallback.run());
-        }
-        return syncAndDrawFrame(vsyncTime);
-    }
-
-    /**
      * Suspends any current rendering into the surface but do not do any destruction. This
      * is useful to temporarily suspend using the active Surface in order to do any Surface
      * mutations necessary.
      *
-     * Any subsequent draws will override the pause, resuming normal operation.
+     * <p>Any subsequent draws will override the pause, resuming normal operation.
      *
      * @return true if there was an outstanding render request, false otherwise. If this is true
-     * the caller should ensure that {@link #syncAndDrawFrame(long)} is called at the soonest
+     * the caller should ensure that {@link #createRenderRequest()}
+     * and {@link FrameRenderRequest#syncAndDraw()} is called at the soonest
      * possible time to resume normal operation.
      *
      * TODO Should this be exposed? ViewRootImpl needs it because it destroys the old
@@ -367,14 +445,14 @@
 
     /**
      * Hard stops rendering into the surface. If the renderer is stopped it will
-     * block any attempt to render. Calls to {@link #syncAndDrawFrame(long)} will still
-     * sync over the latest rendering content, however they will not render and instead
+     * block any attempt to render. Calls to {@link FrameRenderRequest#syncAndDraw()} will
+     * still sync over the latest rendering content, however they will not render and instead
      * {@link #SYNC_CONTEXT_IS_STOPPED} will be returned.
      *
-     * If false is passed then rendering will resume as normal. Any pending rendering requests
+     * <p>If false is passed then rendering will resume as normal. Any pending rendering requests
      * will produce a new frame at the next vsync signal.
      *
-     * This is useful in combination with lifecycle events such as {@link Activity#onStop()}
+     * <p>This is useful in combination with lifecycle events such as {@link Activity#onStop()}
      * and {@link Activity#onStart()}.
      *
      * @param stopped true to stop all rendering, false to resume
@@ -384,24 +462,26 @@
     }
 
     /**
-     * Destroys all hardware rendering resources associated with the current rendering content.
+     * Destroys all the display lists associated with the current rendering content.
      * This includes releasing a reference to the current content root RenderNode. It will
      * therefore be necessary to call {@link #setContentRoot(RenderNode)} in order to resume
-     * rendering after calling this.
+     * rendering after calling this, along with re-recording the display lists for the
+     * RenderNode tree.
      *
-     * It is recommended, but not necessary, to use this in combination with lifecycle events
+     * <p>It is recommended, but not necessary, to use this in combination with lifecycle events
      * such as {@link Activity#onStop()} and {@link Activity#onStart()} or in response to
      * {@link android.content.ComponentCallbacks2#onTrimMemory(int)} signals such as
      * {@link android.content.ComponentCallbacks2#TRIM_MEMORY_UI_HIDDEN}
      *
      * See also {@link #setStopped(boolean)}
      */
-    public void destroyHardwareResources() {
+    public void clearContent() {
         nDestroyHardwareResources(mNativeProxy);
     }
 
     /**
      * Whether or not the force-dark feature should be used for this renderer.
+     * @hide
      */
     public boolean setForceDark(boolean enable) {
         if (mForceDark != enable) {
@@ -415,20 +495,24 @@
     /**
      * Allocate buffers ahead of time to avoid allocation delays during rendering.
      *
-     * Typically a Surface will allocate buffers lazily. This is usually fine and reduces the
+     * <p>Typically a Surface will allocate buffers lazily. This is usually fine and reduces the
      * memory usage of Surfaces that render rarely or never hit triple buffering. However
      * for UI it can result in a slight bit of jank on first launch. This hint will
      * tell the HardwareRenderer that now is a good time to allocate the 3 buffers
      * necessary for typical rendering.
      *
-     * Must be called after a {@link Surface} has been set.
+     * <p>Must be called after a {@link Surface} has been set.
+     *
+     * TODO: Figure out if we even need/want this. Should HWUI just be doing this in response
+     * to setSurface anyway? Vulkan swapchain makes this murky, so delay making it public
+     * @hide
      */
     public void allocateBuffers() {
         nAllocateBuffers(mNativeProxy);
     }
 
     /**
-     * Notifies the hardware renderer that a call to {@link #syncAndDrawFrame(long)} will
+     * Notifies the hardware renderer that a call to {@link FrameRenderRequest#syncAndDraw()} will
      * be coming soon. This is used to help schedule when RenderThread-driven animations will
      * happen as the renderer wants to avoid producing more than one frame per vsync signal.
      */
@@ -439,7 +523,7 @@
     /**
      * Change the HardwareRenderer's opacity. Will take effect on the next frame produced.
      *
-     * If the renderer is set to opaque it is the app's responsibility to ensure that the
+     * <p>If the renderer is set to opaque it is the app's responsibility to ensure that the
      * content renders to every pixel of the Surface, otherwise corruption may result. Note that
      * this includes ensuring that the first draw of any given pixel does not attempt to blend
      * against the destination. If this is false then the hardware renderer will clear to
@@ -527,7 +611,7 @@
     }
 
     /**
-     * Prevents any further drawing until {@link #syncAndDrawFrame(long)} is called.
+     * Prevents any further drawing until {@link FrameRenderRequest#syncAndDraw()} is called.
      * This is a signal that the contents of the RenderNode tree are no longer safe to play back.
      * In practice this usually means that there are Functor pointers in the
      * display list that are no longer valid.
@@ -718,10 +802,8 @@
      * Interface for listening to picture captures
      * @hide
      */
-    @TestApi
     public interface PictureCapturedCallback {
         /** @hide */
-        @TestApi
         void onPictureCaptured(Picture picture);
     }
 
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 466a5fc..9b5e330 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -372,7 +372,7 @@
             }
             mResources = res;
             mInputStream = is;
-            mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE;
+            mInputDensity = inputDensity;
         }
 
         final Resources mResources;
@@ -1556,12 +1556,9 @@
      * decoder will pick either the color space embedded in the image or the
      * {@link ColorSpace} best suited for the requested image configuration
      * (for instance {@link ColorSpace.Named#SRGB sRGB} for the
-     * {@link Bitmap.Config#ARGB_8888} configuration).</p>
-     *
-     * <p>{@link Bitmap.Config#RGBA_F16} always uses the
-     * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space.
-     * Bitmaps in other configurations without an embedded color space are
-     * assumed to be in the {@link ColorSpace.Named#SRGB sRGB} color space.</p>
+     * {@link Bitmap.Config#ARGB_8888} configuration and
+     * {@link ColorSpace.Named#EXTENDED_SRGB EXTENDED_SRGB} for
+     * {@link Bitmap.Config#RGBA_F16}).</p>
      *
      * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
      * currently supported. An <code>IllegalArgumentException</code> will
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 789e38c..d7aee77 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -43,6 +43,7 @@
 import android.graphics.Rect;
 import android.graphics.RenderNode;
 import android.os.Build;
+import android.os.Handler;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.IntArray;
@@ -1241,6 +1242,7 @@
 
         // If the duration of an animation is more than 300 frames, we cap the sample size to 300.
         private static final int MAX_SAMPLE_POINTS = 300;
+        private Handler mHandler;
         private AnimatorListener mListener = null;
         private final LongArray mStartDelays = new LongArray();
         private PropertyValuesHolder.PropertyValues mTmpValues =
@@ -1671,6 +1673,9 @@
                                 .mRootName);
             }
             mStarted = true;
+            if (mHandler == null) {
+                mHandler = new Handler();
+            }
             nStart(mSetPtr, this, ++mLastListenerId);
             invalidateOwningView();
             if (mListener != null) {
@@ -1780,7 +1785,7 @@
         // onFinished: should be called from native
         @UnsupportedAppUsage
         private static void callOnFinished(VectorDrawableAnimatorRT set, int id) {
-            set.onAnimationEnd(id);
+            set.mHandler.post(() -> set.onAnimationEnd(id));
         }
 
         private void transferPendingActions(VectorDrawableAnimator animatorSet) {
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 76c5660..2ffda83 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -107,7 +107,7 @@
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
         mRenderThread.requireGlContext();
     } else {
-        mRenderThread.vulkanManager().initialize();
+        mRenderThread.requireVkContext();
     }
     if (!image.get()) {
         return CopyResult::UnknownError;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 15f53f2..87cffb5 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -120,7 +120,7 @@
 }
 
 DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
-    mVkManager.initialize();
+    mRenderThread.requireVkContext();
 
     return new DeferredLayerUpdater(mRenderThread.renderState());
 }
@@ -136,8 +136,9 @@
 
     setSurfaceColorProperties(colorMode);
     if (surface) {
+        mRenderThread.requireVkContext();
         mVkSurface = mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace,
-                                              mSurfaceColorType);
+                                              mSurfaceColorType, mRenderThread.getGrContext());
     }
 
     return mVkSurface != nullptr;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 3904ed2..fc63819 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -173,10 +173,10 @@
     initializeDisplayEventReceiver();
     mEglManager = new EglManager();
     mRenderState = new RenderState(*this);
-    mVkManager = new VulkanManager(*this);
+    mVkManager = new VulkanManager();
     mCacheManager = new CacheManager(mDisplayInfo);
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        mVkManager->initialize();
+        requireVkContext();
     }
 }
 
@@ -195,8 +195,7 @@
     LOG_ALWAYS_FATAL_IF(!glInterface.get());
 
     GrContextOptions options;
-    options.fPreferExternalImagesOverES3 = true;
-    options.fDisableDistanceFieldPaths = true;
+    initGrContextOptions(options);
     auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
     auto size = glesVersion ? strlen(glesVersion) : -1;
     cacheManager().configureContext(&options, glesVersion, size);
@@ -205,6 +204,25 @@
     setGrContext(grContext);
 }
 
+void RenderThread::requireVkContext() {
+    if (mVkManager->hasVkContext()) {
+        return;
+    }
+    mVkManager->initialize();
+    GrContextOptions options;
+    initGrContextOptions(options);
+    // TODO: get a string describing the SPIR-V compiler version and use it here
+    cacheManager().configureContext(&options, nullptr, 0);
+    sk_sp<GrContext> grContext = mVkManager->createContext(options);
+    LOG_ALWAYS_FATAL_IF(!grContext.get());
+    setGrContext(grContext);
+}
+
+void RenderThread::initGrContextOptions(GrContextOptions& options) {
+    options.fPreferExternalImagesOverES3 = true;
+    options.fDisableDistanceFieldPaths = true;
+}
+
 void RenderThread::destroyRenderingContext() {
     mFunctorManager.onContextDestroyed();
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index b182928..419e7c7 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -112,6 +112,7 @@
     void dumpGraphicsMemory(int fd);
 
     void requireGlContext();
+    void requireVkContext();
     void destroyRenderingContext();
 
     /**
@@ -122,6 +123,8 @@
      */
     static bool isCurrent();
 
+    static void initGrContextOptions(GrContextOptions& options);
+
 protected:
     virtual bool threadLoop() override;
 
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 90397fd..1e685ab 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -55,11 +55,7 @@
 #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F)
 #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F)
 
-VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {}
-
 void VulkanManager::destroy() {
-    mRenderThread.setGrContext(nullptr);
-
     // We don't need to explicitly free the command buffer since it automatically gets freed when we
     // delete the VkCommandPool below.
     mDummyCB = VK_NULL_HANDLE;
@@ -333,29 +329,10 @@
     LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion));
     LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0));
 
-    GrVkExtensions extensions;
-    this->setupDevice(extensions, mPhysicalDeviceFeatures2);
+    this->setupDevice(mExtensions, mPhysicalDeviceFeatures2);
 
     mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
 
-    auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
-        if (device != VK_NULL_HANDLE) {
-            return vkGetDeviceProcAddr(device, proc_name);
-        }
-        return vkGetInstanceProcAddr(instance, proc_name);
-    };
-
-    GrVkBackendContext backendContext;
-    backendContext.fInstance = mInstance;
-    backendContext.fPhysicalDevice = mPhysicalDevice;
-    backendContext.fDevice = mDevice;
-    backendContext.fQueue = mGraphicsQueue;
-    backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
-    backendContext.fMaxAPIVersion = mAPIVersion;
-    backendContext.fVkExtensions = &extensions;
-    backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
-    backendContext.fGetProc = std::move(getProc);
-
     // create the command pool for the command buffers
     if (VK_NULL_HANDLE == mCommandPool) {
         VkCommandPoolCreateInfo commandPoolInfo;
@@ -376,22 +353,35 @@
     }
     LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
 
-
     mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue);
 
-    GrContextOptions options;
-    options.fDisableDistanceFieldPaths = true;
-    // TODO: get a string describing the SPIR-V compiler version and use it here
-    mRenderThread.cacheManager().configureContext(&options, nullptr, 0);
-    sk_sp<GrContext> grContext(GrContext::MakeVulkan(backendContext, options));
-    LOG_ALWAYS_FATAL_IF(!grContext.get());
-    mRenderThread.setGrContext(grContext);
-
     if (Properties::enablePartialUpdates && Properties::useBufferAge) {
         mSwapBehavior = SwapBehavior::BufferAge;
     }
 }
 
+sk_sp<GrContext> VulkanManager::createContext(GrContextOptions options) {
+    auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
+        if (device != VK_NULL_HANDLE) {
+            return vkGetDeviceProcAddr(device, proc_name);
+        }
+        return vkGetInstanceProcAddr(instance, proc_name);
+    };
+
+    GrVkBackendContext backendContext;
+    backendContext.fInstance = mInstance;
+    backendContext.fPhysicalDevice = mPhysicalDevice;
+    backendContext.fDevice = mDevice;
+    backendContext.fQueue = mGraphicsQueue;
+    backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
+    backendContext.fMaxAPIVersion = mAPIVersion;
+    backendContext.fVkExtensions = &mExtensions;
+    backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
+    backendContext.fGetProc = std::move(getProc);
+
+    return GrContext::MakeVulkan(backendContext, options);
+}
+
 VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const {
     return VkFunctorInitParams{
             .instance = mInstance,
@@ -470,8 +460,9 @@
         ColorMode colorMode = surface->mColorMode;
         sk_sp<SkColorSpace> colorSpace = surface->mColorSpace;
         SkColorType colorType = surface->mColorType;
+        GrContext* grContext = surface->mGrContext;
         destroySurface(surface);
-        *surfaceOut = createSurface(window, colorMode, colorSpace, colorType);
+        *surfaceOut = createSurface(window, colorMode, colorSpace, colorType, grContext);
         surface = *surfaceOut;
         if (!surface) {
             return nullptr;
@@ -650,7 +641,7 @@
 
         VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
         imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(
-                mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin,
+                surface->mGrContext, backendRT, kTopLeft_GrSurfaceOrigin,
                 surface->mColorType, surface->mColorSpace, &props);
     }
 
@@ -880,15 +871,15 @@
 
 VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode,
                                             sk_sp<SkColorSpace> surfaceColorSpace,
-                                            SkColorType surfaceColorType) {
-    initialize();
-
+                                            SkColorType surfaceColorType,
+                                            GrContext* grContext) {
+    LOG_ALWAYS_FATAL_IF(!hasVkContext(), "Not initialized");
     if (!window) {
         return nullptr;
     }
 
     VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace,
-                                               surfaceColorType);
+                                               surfaceColorType, grContext);
 
     VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
     memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 1fe6c65..9763686 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -22,6 +22,8 @@
 #endif
 #include <vulkan/vulkan.h>
 
+#include <GrContextOptions.h>
+#include <vk/GrVkExtensions.h>
 #include <SkSurface.h>
 #include <ui/Fence.h>
 #include <utils/StrongPointer.h>
@@ -39,9 +41,9 @@
 class VulkanSurface {
 public:
     VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace,
-                  SkColorType colorType)
+                  SkColorType colorType, GrContext* grContext)
             : mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace),
-              mColorType(colorType) {}
+              mColorType(colorType), mGrContext(grContext) {}
 
     sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
 
@@ -93,6 +95,7 @@
     SkColorType mColorType;
     VkSurfaceTransformFlagBitsKHR mTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
     SkMatrix mPreTransform;
+    GrContext* mGrContext;
 };
 
 // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
@@ -100,6 +103,9 @@
 // windowing contexts. The VulkanManager must be initialized before use.
 class VulkanManager {
 public:
+    explicit VulkanManager() {}
+    ~VulkanManager() { destroy(); }
+
     // Sets up the vulkan context that is shared amonst all clients of the VulkanManager. This must
     // be call once before use of the VulkanManager. Multiple calls after the first will simiply
     // return.
@@ -112,7 +118,8 @@
     // VulkanSurface object which is returned.
     VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode,
                                  sk_sp<SkColorSpace> surfaceColorSpace,
-                                 SkColorType surfaceColorType);
+                                 SkColorType surfaceColorType,
+                                 GrContext* grContext);
 
     // Destroy the VulkanSurface and all associated vulkan objects.
     void destroySurface(VulkanSurface* surface);
@@ -143,12 +150,9 @@
     // Returned pointers are owned by VulkanManager.
     VkFunctorInitParams getVkFunctorInitParams() const;
 
+    sk_sp<GrContext> createContext(GrContextOptions options);
+
 private:
-    friend class RenderThread;
-
-    explicit VulkanManager(RenderThread& thread);
-    ~VulkanManager() { destroy(); }
-
     // Sets up the VkInstance and VkDevice objects. Also fills out the passed in
     // VkPhysicalDeviceFeatures struct.
     void setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&);
@@ -231,8 +235,6 @@
     VkPtr<PFN_vkWaitForFences> mWaitForFences;
     VkPtr<PFN_vkResetFences> mResetFences;
 
-    RenderThread& mRenderThread;
-
     VkInstance mInstance = VK_NULL_HANDLE;
     VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
     VkDevice mDevice = VK_NULL_HANDLE;
@@ -256,6 +258,7 @@
         BufferAge,
     };
     SwapBehavior mSwapBehavior = SwapBehavior::Discard;
+    GrVkExtensions mExtensions;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index a9f651d..e8ba15f 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -100,7 +100,7 @@
     // RenderState only valid once RenderThread is running, so queried here
     renderthread::RenderThread& renderThread = renderthread::RenderThread::getInstance();
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        renderThread.vulkanManager().initialize();
+        renderThread.requireVkContext();
     } else {
         renderThread.requireGlContext();
     }
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index d742cc3..733b866 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -115,10 +115,14 @@
 
     mLocked.pointerSprite.clear();
 
-    for (size_t i = 0; i < mLocked.spots.size(); i++) {
-        delete mLocked.spots.itemAt(i);
+    for (auto& it : mLocked.spotsByDisplay) {
+        const std::vector<Spot*>& spots = it.second;
+        size_t numSpots = spots.size();
+        for (size_t i = 0; i < numSpots; i++) {
+            delete spots[i];
+        }
     }
-    mLocked.spots.clear();
+    mLocked.spotsByDisplay.clear();
     mLocked.recycledSprites.clear();
 }
 
@@ -271,22 +275,30 @@
 }
 
 void PointerController::setSpots(const PointerCoords* spotCoords,
-        const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
+        const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId) {
 #if DEBUG_POINTER_UPDATES
     ALOGD("setSpots: idBits=%08x", spotIdBits.value);
     for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
         uint32_t id = idBits.firstMarkedBit();
         idBits.clearBit(id);
         const PointerCoords& c = spotCoords[spotIdToIndex[id]];
-        ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
+        ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id,
                 c.getAxisValue(AMOTION_EVENT_AXIS_X),
                 c.getAxisValue(AMOTION_EVENT_AXIS_Y),
-                c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+                c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+                displayId);
     }
 #endif
 
     AutoMutex _l(mLock);
 
+    std::vector<Spot*> newSpots;
+    std::map<int32_t, std::vector<Spot*>>::const_iterator iter =
+            mLocked.spotsByDisplay.find(displayId);
+    if (iter != mLocked.spotsByDisplay.end()) {
+        newSpots = iter->second;
+    }
+
     mSpriteController->openTransaction();
 
     // Add or move spots for fingers that are down.
@@ -298,17 +310,17 @@
         float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
         float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
 
-        Spot* spot = getSpotLocked(id);
+        Spot* spot = getSpot(id, newSpots);
         if (!spot) {
-            spot = createAndAddSpotLocked(id);
+            spot = createAndAddSpotLocked(id, newSpots);
         }
 
-        spot->updateSprite(&icon, x, y);
+        spot->updateSprite(&icon, x, y, displayId);
     }
 
     // Remove spots for fingers that went up.
-    for (size_t i = 0; i < mLocked.spots.size(); i++) {
-        Spot* spot = mLocked.spots.itemAt(i);
+    for (size_t i = 0; i < newSpots.size(); i++) {
+        Spot* spot = newSpots[i];
         if (spot->id != Spot::INVALID_ID
                 && !spotIdBits.hasBit(spot->id)) {
             fadeOutAndReleaseSpotLocked(spot);
@@ -316,6 +328,7 @@
     }
 
     mSpriteController->closeTransaction();
+    mLocked.spotsByDisplay[displayId] = newSpots;
 }
 
 void PointerController::clearSpots() {
@@ -539,21 +552,33 @@
     }
 
     // Animate spots that are fading out and being removed.
-    for (size_t i = 0; i < mLocked.spots.size();) {
-        Spot* spot = mLocked.spots.itemAt(i);
-        if (spot->id == Spot::INVALID_ID) {
-            spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
-            if (spot->alpha <= 0) {
-                mLocked.spots.removeAt(i);
-                releaseSpotLocked(spot);
-                continue;
-            } else {
-                spot->sprite->setAlpha(spot->alpha);
-                keepAnimating = true;
+    for(auto it = mLocked.spotsByDisplay.begin(); it != mLocked.spotsByDisplay.end();) {
+        std::vector<Spot*>& spots = it->second;
+        size_t numSpots = spots.size();
+        for (size_t i = 0; i < numSpots;) {
+            Spot* spot = spots[i];
+            if (spot->id == Spot::INVALID_ID) {
+                spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
+                if (spot->alpha <= 0) {
+                    spots.erase(spots.begin() + i);
+                    releaseSpotLocked(spot);
+                    numSpots--;
+                    continue;
+                } else {
+                    spot->sprite->setAlpha(spot->alpha);
+                    keepAnimating = true;
+                }
             }
+            ++i;
         }
-        ++i;
+
+        if (spots.size() == 0) {
+            it = mLocked.spotsByDisplay.erase(it);
+        } else {
+            ++it;
+        }
     }
+
     return keepAnimating;
 }
 
@@ -655,47 +680,49 @@
     mSpriteController->closeTransaction();
 }
 
-PointerController::Spot* PointerController::getSpotLocked(uint32_t id) {
-    for (size_t i = 0; i < mLocked.spots.size(); i++) {
-        Spot* spot = mLocked.spots.itemAt(i);
+PointerController::Spot* PointerController::getSpot(uint32_t id, const std::vector<Spot*>& spots) {
+    for (size_t i = 0; i < spots.size(); i++) {
+        Spot* spot = spots[i];
         if (spot->id == id) {
             return spot;
         }
     }
-    return NULL;
+
+    return nullptr;
 }
 
-PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id) {
+PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id,
+        std::vector<Spot*>& spots) {
     // Remove spots until we have fewer than MAX_SPOTS remaining.
-    while (mLocked.spots.size() >= MAX_SPOTS) {
-        Spot* spot = removeFirstFadingSpotLocked();
+    while (spots.size() >= MAX_SPOTS) {
+        Spot* spot = removeFirstFadingSpotLocked(spots);
         if (!spot) {
-            spot = mLocked.spots.itemAt(0);
-            mLocked.spots.removeAt(0);
+            spot = spots[0];
+            spots.erase(spots.begin());
         }
         releaseSpotLocked(spot);
     }
 
     // Obtain a sprite from the recycled pool.
     sp<Sprite> sprite;
-    if (! mLocked.recycledSprites.isEmpty()) {
-        sprite = mLocked.recycledSprites.top();
-        mLocked.recycledSprites.pop();
+    if (! mLocked.recycledSprites.empty()) {
+        sprite = mLocked.recycledSprites.back();
+        mLocked.recycledSprites.pop_back();
     } else {
         sprite = mSpriteController->createSprite();
     }
 
     // Return the new spot.
     Spot* spot = new Spot(id, sprite);
-    mLocked.spots.push(spot);
+    spots.push_back(spot);
     return spot;
 }
 
-PointerController::Spot* PointerController::removeFirstFadingSpotLocked() {
-    for (size_t i = 0; i < mLocked.spots.size(); i++) {
-        Spot* spot = mLocked.spots.itemAt(i);
+PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vector<Spot*>& spots) {
+    for (size_t i = 0; i < spots.size(); i++) {
+        Spot* spot = spots[i];
         if (spot->id == Spot::INVALID_ID) {
-            mLocked.spots.removeAt(i);
+            spots.erase(spots.begin() + i);
             return spot;
         }
     }
@@ -706,7 +733,7 @@
     spot->sprite->clearIcon();
 
     if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
-        mLocked.recycledSprites.push(spot->sprite);
+        mLocked.recycledSprites.push_back(spot->sprite);
     }
 
     delete spot;
@@ -720,9 +747,13 @@
 }
 
 void PointerController::fadeOutAndReleaseAllSpotsLocked() {
-    for (size_t i = 0; i < mLocked.spots.size(); i++) {
-        Spot* spot = mLocked.spots.itemAt(i);
-        fadeOutAndReleaseSpotLocked(spot);
+    for (auto& it : mLocked.spotsByDisplay) {
+        const std::vector<Spot*>& spots = it.second;
+        size_t numSpots = spots.size();
+        for (size_t i = 0; i < numSpots; i++) {
+            Spot* spot = spots[i];
+            fadeOutAndReleaseSpotLocked(spot);
+        }
     }
 }
 
@@ -743,12 +774,13 @@
 
 // --- PointerController::Spot ---
 
-void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y) {
+void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
+        int32_t displayId) {
     sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
     sprite->setAlpha(alpha);
     sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
     sprite->setPosition(x, y);
-
+    sprite->setDisplayId(displayId);
     this->x = x;
     this->y = y;
 
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index be05786..52305b8 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -103,7 +103,7 @@
 
     virtual void setPresentation(Presentation presentation);
     virtual void setSpots(const PointerCoords* spotCoords,
-            const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
+            const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId);
     virtual void clearSpots();
 
     void updatePointerIcon(int32_t iconId);
@@ -133,7 +133,7 @@
                 : id(id), sprite(sprite), alpha(1.0f), scale(1.0f),
                   x(0.0f), y(0.0f), lastIcon(NULL) { }
 
-        void updateSprite(const SpriteIcon* icon, float x, float y);
+        void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
 
     private:
         const SpriteIcon* lastIcon;
@@ -180,8 +180,8 @@
 
         int32_t buttonState;
 
-        Vector<Spot*> spots;
-        Vector<sp<Sprite> > recycledSprites;
+        std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay;
+        std::vector<sp<Sprite> > recycledSprites;
     } mLocked GUARDED_BY(mLock);
 
     bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
@@ -200,9 +200,9 @@
     void removeInactivityTimeoutLocked();
     void updatePointerLocked();
 
-    Spot* getSpotLocked(uint32_t id);
-    Spot* createAndAddSpotLocked(uint32_t id);
-    Spot* removeFirstFadingSpotLocked();
+    Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots);
+    Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots);
+    Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots);
     void releaseSpotLocked(Spot* spot);
     void fadeOutAndReleaseSpotLocked(Spot* spot);
     void fadeOutAndReleaseAllSpotsLocked();
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 0caa0c5..b3953fd 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -184,8 +184,7 @@
      * @return a new location request
      */
     public static LocationRequest create() {
-        LocationRequest request = new LocationRequest();
-        return request;
+        return new LocationRequest();
     }
 
     /** @hide */
@@ -230,12 +229,10 @@
                 quality = ACCURACY_FINE;
                 break;
             default: {
-                switch (criteria.getPowerRequirement()) {
-                    case Criteria.POWER_HIGH:
-                        quality = POWER_HIGH;
-                        break;
-                    default:
-                        quality = POWER_LOW;
+                if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) {
+                    quality = POWER_HIGH;
+                } else {
+                    quality = POWER_LOW;
                 }
             }
         }
@@ -288,7 +285,7 @@
      *
      * @param quality an accuracy or power constant
      * @return the same object, so that setters can be chained
-     * @throws InvalidArgumentException if the quality constant is not valid
+     * @throws IllegalArgumentException if the quality constant is not valid
      */
     public LocationRequest setQuality(int quality) {
         checkQuality(quality);
@@ -331,7 +328,7 @@
      *
      * @param millis desired interval in millisecond, inexact
      * @return the same object, so that setters can be chained
-     * @throws InvalidArgumentException if the interval is less than zero
+     * @throws IllegalArgumentException if the interval is less than zero
      */
     public LocationRequest setInterval(long millis) {
         checkInterval(millis);
@@ -433,7 +430,7 @@
      *
      * @param millis fastest interval for updates in milliseconds, exact
      * @return the same object, so that setters can be chained
-     * @throws InvalidArgumentException if the interval is less than zero
+     * @throws IllegalArgumentException if the interval is less than zero
      */
     public LocationRequest setFastestInterval(long millis) {
         checkInterval(millis);
@@ -528,7 +525,7 @@
      *
      * @param numUpdates the number of location updates requested
      * @return the same object, so that setters can be chained
-     * @throws InvalidArgumentException if numUpdates is 0 or less
+     * @throws IllegalArgumentException if numUpdates is 0 or less
      */
     public LocationRequest setNumUpdates(int numUpdates) {
         if (numUpdates <= 0) {
@@ -668,7 +665,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private static void checkProvider(String name) {
         if (name == null) {
-            throw new IllegalArgumentException("invalid provider: " + name);
+            throw new IllegalArgumentException("invalid provider: null");
         }
     }
 
@@ -758,9 +755,11 @@
         if (mNumUpdates != Integer.MAX_VALUE) {
             s.append(" num=").append(mNumUpdates);
         }
-        s.append(" lowPowerMode=").append(mLowPowerMode);
+        if (mLowPowerMode) {
+            s.append(" lowPowerMode");
+        }
         if (mLocationSettingsIgnored) {
-            s.append(" ignoreSettings");
+            s.append(" locationSettingsIgnored");
         }
         s.append(']');
         return s.toString();
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index a45c20d..af8123a 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -40,7 +40,7 @@
      * restrictions or any other restricting factors and always satisfy this request to the best of
      * their ability. This flag should only be used in event of an emergency.
      */
-    public boolean forceLocation = false;
+    public boolean locationSettingsIgnored = false;
 
     /**
      * Whether provider shall make stronger than normal tradeoffs to substantially restrict power
@@ -70,6 +70,7 @@
                     request.reportLocation = in.readInt() == 1;
                     request.interval = in.readLong();
                     request.lowPowerMode = in.readBoolean();
+                    request.locationSettingsIgnored = in.readBoolean();
                     int count = in.readInt();
                     for (int i = 0; i < count; i++) {
                         request.locationRequests.add(LocationRequest.CREATOR.createFromParcel(in));
@@ -93,6 +94,7 @@
         parcel.writeInt(reportLocation ? 1 : 0);
         parcel.writeLong(interval);
         parcel.writeBoolean(lowPowerMode);
+        parcel.writeBoolean(locationSettingsIgnored);
         parcel.writeInt(locationRequests.size());
         for (LocationRequest request : locationRequests) {
             request.writeToParcel(parcel, flags);
@@ -107,7 +109,12 @@
             s.append("ON");
             s.append(" interval=");
             TimeUtils.formatDuration(interval, s);
-            s.append(" lowPowerMode=" + lowPowerMode);
+            if (lowPowerMode) {
+                s.append(" lowPowerMode");
+            }
+            if (locationSettingsIgnored) {
+                s.append(" locationSettingsIgnored");
+            }
         } else {
             s.append("OFF");
         }
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 67d6496..dbb581f 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -31,6 +31,7 @@
     method public long getInterval();
     method public int getQuality();
     method public float getSmallestDisplacement();
+    method public boolean isLocationSettingsIgnored();
     field public static final int ACCURACY_BLOCK = 102; // 0x66
     field public static final int ACCURACY_CITY = 104; // 0x68
     field public static final int ACCURACY_FINE = 100; // 0x64
@@ -44,10 +45,10 @@
   }
 
   public final class ProviderRequestUnbundled {
-    method public boolean getForceLocation();
     method public long getInterval();
     method public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
     method public boolean getReportLocation();
+    method public boolean isLocationSettingsIgnored();
   }
 
 }
diff --git a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
index 41fd769..2511c39 100644
--- a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
@@ -121,6 +121,15 @@
         return delegate.getSmallestDisplacement();
     }
 
+    /**
+     * Returns true if location settings will be ignored in order to satisfy this request.
+     *
+     * @return true if location settings will be ignored in order to satisfy this request
+     */
+    public boolean isLocationSettingsIgnored() {
+        return delegate.isLocationSettingsIgnored();
+    }
+
     @Override
     public String toString() {
       return delegate.toString();
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index b825b58..febbf1b 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -46,15 +46,15 @@
         return mRequest.interval;
     }
 
-    public boolean getForceLocation() {
-        return mRequest.forceLocation;
+    public boolean isLocationSettingsIgnored() {
+        return mRequest.locationSettingsIgnored;
     }
 
     /**
      * Never null.
      */
     public List<LocationRequestUnbundled> getLocationRequests() {
-        List<LocationRequestUnbundled> result = new ArrayList<LocationRequestUnbundled>(
+        List<LocationRequestUnbundled> result = new ArrayList<>(
                 mRequest.locationRequests.size());
         for (LocationRequest r : mRequest.locationRequests) {
           result.add(new LocationRequestUnbundled(r));
diff --git a/media/apex/java/android/media/MediaController2.java b/media/apex/java/android/media/MediaController2.java
index 887b447..e85d997 100644
--- a/media/apex/java/android/media/MediaController2.java
+++ b/media/apex/java/android/media/MediaController2.java
@@ -425,7 +425,7 @@
         public void onDisconnected(@NonNull MediaController2 controller) {}
 
         /**
-         * Called when the playback of the session's playback activeness is changed.
+         * Called when the session's playback activeness is changed.
          *
          * @param controller the controller for this event
          * @param playbackActive {@code true} if the session's playback is active.
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index fb18c3b..e4d356b 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1180,13 +1180,8 @@
         }
 
         final File file = new File(path);
-        if (file.exists()) {
-            FileInputStream is = new FileInputStream(file);
-            FileDescriptor fd = is.getFD();
-            setDataSource(fd);
-            is.close();
-        } else {
-            throw new IOException("setDataSource failed.");
+        try (FileInputStream is = new FileInputStream(file)) {
+            setDataSource(is.getFD());
         }
     }
 
@@ -2868,15 +2863,9 @@
             throw new IllegalArgumentException(msg);
         }
 
-        File file = new File(path);
-        if (file.exists()) {
-            FileInputStream is = new FileInputStream(file);
-            FileDescriptor fd = is.getFD();
-            addTimedTextSource(fd, mimeType);
-            is.close();
-        } else {
-            // We do not support the case where the path is not a file.
-            throw new IOException(path);
+        final File file = new File(path);
+        try (FileInputStream is = new FileInputStream(file)) {
+            addTimedTextSource(is.getFD(), mimeType);
         }
     }
 
diff --git a/media/java/android/media/session/ControllerLink.java b/media/java/android/media/session/ControllerLink.java
index 64d283f..40c7166 100644
--- a/media/java/android/media/session/ControllerLink.java
+++ b/media/java/android/media/session/ControllerLink.java
@@ -493,6 +493,22 @@
     }
 
     /**
+     * Tell system that a controller requests changing the playback speed.
+     *
+     * @param packageName the package name of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param speed the playback speed
+     */
+    void setPlaybackSpeed(@NonNull String packageName, @NonNull ControllerCallbackLink caller,
+            float speed) {
+        try {
+            mISessionController.setPlaybackSpeed(packageName, caller, speed);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
      * Tell system that a controller sends a custom action.
      *
      * @param packageName the package name of the controller
@@ -759,6 +775,11 @@
                 @NonNull Rating rating) {
         }
 
+        /** Stub method for ISessionController.setPlaybackSpeed */
+        public void setPlaybackSpeed(@NonNull String packageName,
+                @NonNull ControllerCallbackLink caller, float speed) {
+        }
+
         /** Stub method for ISessionController.sendCustomAction */
         public void sendCustomAction(@NonNull String packageName,
                 @NonNull ControllerCallbackLink caller, @NonNull String action,
@@ -953,6 +974,12 @@
         }
 
         @Override
+        public void setPlaybackSpeed(String packageName, ControllerCallbackLink caller,
+                float speed) {
+            mControllerStub.setPlaybackSpeed(packageName, caller, speed);
+        }
+
+        @Override
         public void sendCustomAction(String packageName, ControllerCallbackLink caller,
                 String action, Bundle args) {
             mControllerStub.sendCustomAction(packageName, caller, action, args);
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 9b86bfc..cd33c04 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -60,6 +60,8 @@
             long pos);
     void notifyRate(String packageName, int pid, int uid, in ControllerCallbackLink caller,
             in Rating rating);
+    void notifySetPlaybackSpeed(String packageName, int pid, int uid,
+            in ControllerCallbackLink caller, float speed);
     void notifyCustomAction(String packageName, int pid, int uid, in ControllerCallbackLink caller,
             String action, in Bundle args);
 
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index e697c65..3e7b4fb 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -76,6 +76,7 @@
     void rewind(String packageName, in ControllerCallbackLink caller);
     void seekTo(String packageName, in ControllerCallbackLink caller, long pos);
     void rate(String packageName, in ControllerCallbackLink caller, in Rating rating);
+    void setPlaybackSpeed(String packageName, in ControllerCallbackLink caller, float speed);
     void sendCustomAction(String packageName, in ControllerCallbackLink caller,
             String action, in Bundle args);
     MediaMetadata getMetadata();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 6e2c8c5..9e4199c 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -865,6 +865,19 @@
         }
 
         /**
+         * Set the playback speed.
+         *
+         * @param speed The playback speed
+         */
+        public void setPlaybackSpeed(float speed) {
+            try {
+                mSessionBinder.setPlaybackSpeed(mContext.getPackageName(), mCbStub, speed);
+            } catch (RuntimeException e) {
+                Log.wtf(TAG, "Error calling setPlaybackSpeed.", e);
+            }
+        }
+
+        /**
          * Send a custom action back for the {@link MediaSession} to perform.
          *
          * @param customAction The action to perform.
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 1b9ebda..8ab893b 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -681,6 +681,19 @@
         }
 
         /**
+         * Override to handle the playback speed change.
+         * To update the new playback speed, create a new {@link PlaybackState} by using {@link
+         * PlaybackState.Builder#setState(int, long, float)}, and set it with
+         * {@link #setPlaybackState(PlaybackState)}.
+         *
+         * @param speed the playback speed
+         * @see #setPlaybackState(PlaybackState)
+         * @see PlaybackState.Builder#setState(int, long, float)
+         */
+        public void onSetPlaybackSpeed(float speed) {
+        }
+
+        /**
          * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be
          * performed.
          *
diff --git a/media/java/android/media/session/MediaSessionEngine.java b/media/java/android/media/session/MediaSessionEngine.java
index e19bdbc..266bf32 100644
--- a/media/java/android/media/session/MediaSessionEngine.java
+++ b/media/java/android/media/session/MediaSessionEngine.java
@@ -538,6 +538,10 @@
         postToCallback(caller, CallbackMessageHandler.MSG_RATE, rating, null);
     }
 
+    void dispatchSetPlaybackSpeed(RemoteUserInfo caller, float speed) {
+        postToCallback(caller, CallbackMessageHandler.MSG_SET_PLAYBACK_SPEED, speed, null);
+    }
+
     void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) {
         postToCallback(caller, CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
     }
@@ -871,6 +875,17 @@
         }
 
         /**
+         * Override to handle the playback speed change.
+         *
+         * @param speed the playback speed
+         */
+        public void onSetPlaybackSpeed(float speed) {
+            if (mCallback != null) {
+                mCallback.onSetPlaybackSpeed(speed);
+            }
+        }
+
+        /**
          * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be
          * performed.
          *
@@ -1092,10 +1107,11 @@
         private static final int MSG_REWIND = 17;
         private static final int MSG_SEEK_TO = 18;
         private static final int MSG_RATE = 19;
-        private static final int MSG_CUSTOM_ACTION = 20;
-        private static final int MSG_ADJUST_VOLUME = 21;
-        private static final int MSG_SET_VOLUME = 22;
-        private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23;
+        private static final int MSG_SET_PLAYBACK_SPEED = 20;
+        private static final int MSG_CUSTOM_ACTION = 21;
+        private static final int MSG_ADJUST_VOLUME = 22;
+        private static final int MSG_SET_VOLUME = 23;
+        private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 24;
 
         @SuppressWarnings("WeakerAccess") /* synthetic access */
         CallbackWrapper mCallbackWrapper;
@@ -1186,6 +1202,9 @@
                 case MSG_RATE:
                     mCallbackWrapper.onSetRating((Rating) obj);
                     break;
+                case MSG_SET_PLAYBACK_SPEED:
+                    mCallbackWrapper.onSetPlaybackSpeed((Float) obj);
+                    break;
                 case MSG_CUSTOM_ACTION:
                     mCallbackWrapper.onCustomAction((String) obj, msg.getData());
                     break;
diff --git a/media/java/android/media/session/SessionCallbackLink.java b/media/java/android/media/session/SessionCallbackLink.java
index f59a69d..f9fa45a 100644
--- a/media/java/android/media/session/SessionCallbackLink.java
+++ b/media/java/android/media/session/SessionCallbackLink.java
@@ -462,6 +462,25 @@
     }
 
     /**
+     * Notify session that a controller requests changing playback speed.
+     *
+     * @param packageName the package name of the controller
+     * @param pid the pid of the controller
+     * @param uid the uid of the controller
+     * @param caller the {@link ControllerCallbackLink} of the controller
+     * @param speed the playback speed
+     */
+    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+    public void notifySetPlaybackSpeed(@NonNull String packageName, int pid, int uid,
+            @NonNull ControllerCallbackLink caller, float speed) {
+        try {
+            mISessionCallback.notifySetPlaybackSpeed(packageName, pid, uid, caller, speed);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
      * Notify session that a controller sends a custom action.
      *
      * @param packageName the package name of the controller
@@ -871,6 +890,23 @@
             }
         }
 
+        @Override
+        public void notifySetPlaybackSpeed(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, float speed) {
+            ensureMediaControlPermission();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                MediaSessionEngine sessionImpl = mSessionImpl.get();
+                if (sessionImpl != null) {
+                    sessionImpl.dispatchSetPlaybackSpeed(
+                            createRemoteUserInfo(packageName, pid, uid), speed);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
         public void notifyCustomAction(String packageName, int pid, int uid,
                 ControllerCallbackLink caller, String action, Bundle args) {
             ensureMediaControlPermission();
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index ce627ce..a288d010 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -175,6 +175,7 @@
         webSettings.setSupportZoom(true);
         webSettings.setBuiltInZoomControls(true);
         webSettings.setDisplayZoomControls(false);
+        webSettings.setDomStorageEnabled(true);
         mWebViewClient = new MyWebViewClient();
         webview.setWebViewClient(mWebViewClient);
         webview.setWebChromeClient(new MyWebChromeClient());
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index f36b4aa..55c9361 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -106,6 +106,7 @@
         webSettings.setLoadWithOverviewMode(true);
         webSettings.setSupportZoom(true);
         webSettings.setBuiltInZoomControls(true);
+        webSettings.setDomStorageEnabled(true);
         mWebViewClient = new MyWebViewClient();
         mWebView.setWebViewClient(mWebViewClient);
         mWebView.setWebChromeClient(new MyWebChromeClient());
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index e4d3591..860ebfb 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -31,6 +31,7 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.NETWORK_STACK" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <application
         android:label="NetworkStack"
         android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java
index 9e59912..b1f6d24 100644
--- a/packages/NetworkStack/src/android/net/ip/IpClient.java
+++ b/packages/NetworkStack/src/android/net/ip/IpClient.java
@@ -46,6 +46,7 @@
 import android.net.util.InterfaceParams;
 import android.net.util.SharedLog;
 import android.os.ConditionVariable;
+import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -380,6 +381,13 @@
         public InterfaceParams getInterfaceParams(String ifname) {
             return InterfaceParams.getByName(ifname);
         }
+
+        /**
+         * Get a INetd connector.
+         */
+        public INetd getNetd(Context context) {
+            return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE));
+        }
     }
 
     public IpClient(Context context, String ifName, IIpClientCallbacks callback,
@@ -413,7 +421,7 @@
 
         // TODO: Consider creating, constructing, and passing in some kind of
         // InterfaceController.Dependencies class.
-        mNetd = mContext.getSystemService(INetd.class);
+        mNetd = deps.getNetd(mContext);
         mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog);
 
         mLinkObserver = new IpClientLinkObserver(
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index cedcb84..90db207 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -114,7 +114,8 @@
 
         NetworkStackConnector(Context context) {
             mContext = context;
-            mNetd = (INetd) context.getSystemService(Context.NETD_SERVICE);
+            mNetd = INetd.Stub.asInterface(
+                    (IBinder) context.getSystemService(Context.NETD_SERVICE));
             mObserverRegistry = new NetworkObserverRegistry();
             mCm = context.getSystemService(ConnectivityManager.class);
 
@@ -246,6 +247,12 @@
         }
 
         @Override
+        public void notifyCaptivePortalAppFinished(int response) {
+            checkNetworkStackCallingPermission();
+            mNm.notifyCaptivePortalAppFinished(response);
+        }
+
+        @Override
         public void forceReevaluation(int uid) {
             checkNetworkStackCallingPermission();
             mNm.forceReevaluation(uid);
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 2e72d82..ec4a479 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -39,9 +39,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.net.CaptivePortal;
 import android.net.ConnectivityManager;
-import android.net.ICaptivePortal;
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
 import android.net.LinkProperties;
@@ -67,15 +65,9 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.telephony.CellIdentityCdma;
-import android.telephony.CellIdentityGsm;
-import android.telephony.CellIdentityLte;
-import android.telephony.CellIdentityWcdma;
-import android.telephony.CellInfo;
-import android.telephony.CellInfoCdma;
-import android.telephony.CellInfoGsm;
-import android.telephony.CellInfoLte;
-import android.telephony.CellInfoWcdma;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -466,6 +458,13 @@
         sendMessage(CMD_LAUNCH_CAPTIVE_PORTAL_APP);
     }
 
+    /**
+     * Notify that the captive portal app was closed with the provided response code.
+     */
+    public void notifyCaptivePortalAppFinished(int response) {
+        sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
+    }
+
     @Override
     protected void log(String s) {
         if (DBG) Log.d(TAG + "/" + mNetwork.toString(), s);
@@ -677,29 +676,8 @@
                 case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
                     final Bundle appExtras = new Bundle();
                     // OneAddressPerFamilyNetwork is not parcelable across processes.
-                    appExtras.putParcelable(
-                            ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork));
-                    appExtras.putParcelable(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
-                            new CaptivePortal(new ICaptivePortal.Stub() {
-                                @Override
-                                public void appResponse(int response) {
-                                    if (response == APP_RETURN_WANTED_AS_IS) {
-                                        mContext.enforceCallingPermission(
-                                                PERMISSION_NETWORK_SETTINGS,
-                                                "CaptivePortal");
-                                    }
-                                    sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
-                                }
-
-                                @Override
-                                public void logEvent(int eventId, String packageName)
-                                        throws RemoteException {
-                                    mContext.enforceCallingPermission(
-                                            PERMISSION_NETWORK_SETTINGS,
-                                            "CaptivePortal");
-                                    mCallback.logCaptivePortalLoginEvent(eventId, packageName);
-                                }
-                            }));
+                    final Network network = new Network(mNetwork);
+                    appExtras.putParcelable(ConnectivityManager.EXTRA_NETWORK, network);
                     final CaptivePortalProbeResult probeRes = mLastPortalProbeResult;
                     appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
                     if (probeRes.probeSpec != null) {
@@ -708,7 +686,7 @@
                     }
                     appExtras.putString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
                             mCaptivePortalUserAgent);
-                    mCm.startCaptivePortalApp(appExtras);
+                    mCm.startCaptivePortalApp(network, appExtras);
                     return HANDLED;
                 default:
                     return NOT_HANDLED;
@@ -1312,6 +1290,7 @@
             urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC);
             urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
             urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
+            urlConnection.setRequestProperty("Connection", "close");
             urlConnection.setUseCaches(false);
             if (mCaptivePortalUserAgent != null) {
                 urlConnection.setRequestProperty("User-Agent", mCaptivePortalUserAgent);
@@ -1485,10 +1464,6 @@
      */
     private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal,
             long requestTimestampMs, long responseTimestampMs) {
-        if (!mWifiManager.isScanAlwaysAvailable()) {
-            return;
-        }
-
         if (!mSystemReady) {
             return;
         }
@@ -1496,6 +1471,10 @@
         Intent latencyBroadcast =
                 new Intent(NetworkMonitorUtils.ACTION_NETWORK_CONDITIONS_MEASURED);
         if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI)) {
+            if (!mWifiManager.isScanAlwaysAvailable()) {
+                return;
+            }
+
             WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
             if (currentWifiInfo != null) {
                 // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not
@@ -1515,39 +1494,21 @@
             }
             latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_WIFI);
         } else if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+            // TODO(b/123893112): Support multi-sim.
             latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_NETWORK_TYPE,
                     mTelephonyManager.getNetworkType());
-            List<CellInfo> info = mTelephonyManager.getAllCellInfo();
-            if (info == null) return;
-            int numRegisteredCellInfo = 0;
-            for (CellInfo cellInfo : info) {
-                if (cellInfo.isRegistered()) {
-                    numRegisteredCellInfo++;
-                    if (numRegisteredCellInfo > 1) {
-                        if (VDBG) {
-                            logw("more than one registered CellInfo."
-                                    + " Can't tell which is active.  Bailing.");
-                        }
-                        return;
-                    }
-                    if (cellInfo instanceof CellInfoCdma) {
-                        CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
-                        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
-                    } else if (cellInfo instanceof CellInfoGsm) {
-                        CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
-                        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
-                    } else if (cellInfo instanceof CellInfoLte) {
-                        CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity();
-                        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
-                    } else if (cellInfo instanceof CellInfoWcdma) {
-                        CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
-                        latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
-                    } else {
-                        if (VDBG) logw("Registered cellinfo is unrecognized");
-                        return;
-                    }
-                }
+            final ServiceState dataSs = mTelephonyManager.getServiceState();
+            if (dataSs == null) {
+                logw("failed to retrieve ServiceState");
+                return;
             }
+            // See if the data sub is registered for PS services on cell.
+            final NetworkRegistrationState nrs = dataSs.getNetworkRegistrationState(
+                    NetworkRegistrationState.DOMAIN_PS,
+                    AccessNetworkConstants.TransportType.WWAN);
+            latencyBroadcast.putExtra(
+                    NetworkMonitorUtils.EXTRA_CELL_ID,
+                    nrs == null ? null : nrs.getCellIdentity());
             latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_MOBILE);
         } else {
             return;
diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp
index 4a09b3e..5c7b514 100644
--- a/packages/NetworkStack/tests/Android.bp
+++ b/packages/NetworkStack/tests/Android.bp
@@ -18,6 +18,7 @@
     name: "NetworkStackTests",
     certificate: "platform",
     srcs: ["src/**/*.java"],
+    test_suites: ["device-tests"],
     resource_dirs: ["res"],
     static_libs: [
         "android-support-test",
diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
index a4a1000..af71ac5 100644
--- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
+++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
@@ -22,6 +22,7 @@
 import static android.system.OsConstants.ETH_P_IP;
 import static android.system.OsConstants.ETH_P_IPV6;
 import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_STREAM;
 
@@ -1017,6 +1018,7 @@
     private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4;
     private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8;
     private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12;
+    private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13;
     private static final byte[] IPV4_BROADCAST_ADDRESS =
             {(byte) 255, (byte) 255, (byte) 255, (byte) 255};
 
@@ -1568,7 +1570,7 @@
         // Verify IPv4 packet from another address is passed
         assertPass(program,
                 ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
-                        anotherDstPort, anotherSeqNum, anotherAckNum));
+                        anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
 
         // Remove IPv4 keepalive filter
         apfFilter.removeKeepalivePacketFilter(slot1);
@@ -1613,15 +1615,15 @@
             // dst: 10.0.0.5, port: 12345
             assertDrop(program,
                     ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
-                            dstPort, srcPort, ackNum, seqNum + 1));
+                            dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
             // Verify IPv4 non-keepalive ack packet from the same source address is passed
             assertPass(program,
                     ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
-                            dstPort, srcPort, ackNum + 100, seqNum));
+                            dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */));
             // Verify IPv4 packet from another address is passed
             assertPass(program,
                     ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
-                            anotherDstPort, anotherSeqNum, anotherAckNum));
+                            anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
 
             // Verify IPv6 keepalive ack packet is dropped
             // src: 2404:0:0:0:0:0:faf2, port: 54321
@@ -1650,13 +1652,13 @@
         // Verify IPv4, IPv6 packets are passed
         assertPass(program,
                 ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
-                        dstPort, srcPort, ackNum, seqNum + 1));
+                        dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
         assertPass(program,
                 ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
                         dstPort, srcPort, ackNum, seqNum + 1));
         assertPass(program,
                 ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort,
-                        dstPort, anotherSeqNum, anotherAckNum));
+                        dstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
         assertPass(program,
                 ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort,
                         dstPort, anotherSeqNum, anotherAckNum));
@@ -1664,28 +1666,30 @@
         apfFilter.shutdown();
     }
 
-    private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
-            int dport, int seq, int ack) {
-        ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+    private static byte[] ipv4Packet(byte[] sip, byte[] dip, int sport,
+            int dport, int seq, int ack, int dataLength) {
+        final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN;
+
+        ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]);
+
+        // ether type
         packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP);
+
+        // IPv4 header
         packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45);
+        packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
+        packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_TCP);
         put(packet, IPV4_SRC_ADDR_OFFSET, sip);
-        put(packet, IPV4_DEST_ADDR_OFFSET, tip);
+        put(packet, IPV4_DEST_ADDR_OFFSET, dip);
         packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport);
         packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport);
         packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq);
         packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack);
-        return packet.array();
-    }
 
-    private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
-            int dport, int seq, int ack, int dataLength) {
-        final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN;
-
-        ByteBuffer packet = ByteBuffer.wrap(ipv4Packet(sip, tip, sport, dport, seq, ack));
-        packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
-        // TCP header length 5, reserved 3 bits, NS=0
+        // TCP header length 5(20 bytes), reserved 3 bits, NS=0
         packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50);
+        // TCP flags: ACK set
+        packet.put(IPV4_TCP_HEADER_FLAG_OFFSET, (byte) 0x10);
         return packet.array();
     }
 
diff --git a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
index 7e57d1e..aaaff02 100644
--- a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
+++ b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
@@ -104,8 +104,8 @@
 
         when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm);
         when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm);
-        when(mContext.getSystemService(INetd.class)).thenReturn(mNetd);
         when(mContext.getResources()).thenReturn(mResources);
+        when(mDependencies.getNetd(any())).thenReturn(mNetd);
         when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
                 .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
 
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index b98b0f7..9a16bb7 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -16,8 +16,7 @@
 
 package com.android.server.connectivity;
 
-import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
-import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL;
+import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
 import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
 import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -41,8 +40,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.content.Intent;
-import android.net.CaptivePortal;
 import android.net.ConnectivityManager;
 import android.net.INetworkMonitorCallbacks;
 import android.net.InetAddresses;
@@ -54,10 +51,10 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.util.SharedLog;
 import android.net.wifi.WifiManager;
+import android.os.Bundle;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -487,19 +484,23 @@
         // Check that startCaptivePortalApp sends the expected intent.
         nm.launchCaptivePortalApp();
 
-        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext, timeout(HANDLER_TIMEOUT_MS).times(1))
-                .startActivityAsUser(intentCaptor.capture(), eq(UserHandle.CURRENT));
-        final Intent intent = intentCaptor.getValue();
-        assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, intent.getAction());
-        final Network network = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
-        assertEquals(TEST_NETID, network.netId);
+        final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+        final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class);
+        verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1))
+                .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture());
+        final Bundle bundle = bundleCaptor.getValue();
+        final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK);
+        assertEquals(TEST_NETID, bundleNetwork.netId);
+        // network is passed both in bundle and as parameter, as the bundle is opaque to the
+        // framework and only intended for the captive portal app, but the framework needs
+        // the network to identify the right NetworkMonitor.
+        assertEquals(TEST_NETID, networkCaptor.getValue().netId);
 
         // Have the app report that the captive portal is dismissed, and check that we revalidate.
         setStatus(mHttpsConnection, 204);
         setStatus(mHttpConnection, 204);
-        final CaptivePortal captivePortal = intent.getParcelableExtra(EXTRA_CAPTIVE_PORTAL);
-        captivePortal.reportCaptivePortalDismissed();
+
+        nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED);
         verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
                 .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
     }
diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
index 6d35550..b198f5a 100644
--- a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
+++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
@@ -52,7 +52,7 @@
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:singleLine="true"
-                  android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+                  android:textAppearance="?android:attr/textAppearanceListItem"
                   android:ellipsize="marquee"
                   android:fadingEdge="horizontal"/>
 
@@ -65,7 +65,7 @@
                       android:layout_width="0dp"
                       android:layout_height="wrap_content"
                       android:layout_weight="1"
-                      android:textAppearance="@android:style/TextAppearance.Material.Small"
+                      android:textAppearance="?android:attr/textAppearanceSmall"
                       android:textAlignment="viewStart"
                       android:textColor="?android:attr/textColorSecondary"/>
 
@@ -73,7 +73,7 @@
                       android:layout_width="0dp"
                       android:layout_height="wrap_content"
                       android:layout_weight="1"
-                      android:textAppearance="@android:style/TextAppearance.Material.Small"
+                      android:textAppearance="?android:attr/textAppearanceSmall"
                       android:textAlignment="viewEnd"
                       android:textColor="?android:attr/textColorSecondary"
                       android:maxLines="1"
diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
index 9604512..013d2d0 100644
--- a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
+++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml
@@ -23,7 +23,7 @@
     android:layout_marginEnd="16dp"
     android:gravity="center"
     android:clickable="true"
-    android:background="?android:attr/selectableItemBackground"
+    android:background="@*android:drawable/btn_borderless_material"
     android:orientation="vertical">
 
     <ImageView
diff --git a/packages/SettingsLib/res/drawable/ic_info_outline_24.xml b/packages/SettingsLib/res/drawable/ic_info_outline_24.xml
new file mode 100644
index 0000000..317e43b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_info_outline_24.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
+</vector>
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java
index a106846..2a12810 100644
--- a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java
@@ -56,7 +56,7 @@
     }
 
     private void init() {
-        setIcon(com.android.internal.R.drawable.ic_info_outline_24);
+        setIcon(R.drawable.ic_info_outline_24);
         setKey(KEY_FOOTER);
         setOrder(ORDER_FOOTER);
         setSelectable(false);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3778a9c..4a5388b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -126,6 +126,7 @@
     <uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" />
     <uses-permission android:name="android.permission.USE_FINGERPRINT" />
     <uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
+    <uses-permission android:name="android.permission.MANAGE_BIOMETRIC" />
     <uses-permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" />
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS" />
 
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index 51f6a4b..dc45b4b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -28,11 +28,20 @@
     android:clipToPadding="false"
     android:orientation="vertical"
     android:layout_centerHorizontal="true">
+    <TextView
+              android:id="@+id/title"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:paddingStart="64dp"
+              android:paddingEnd="64dp"
+              android:visibility="gone"
+              android:textColor="?attr/wallpaperTextColor"
+              android:theme="@style/TextAppearance.Keyguard"
+    />
     <view class="com.android.keyguard.KeyguardSliceView$Row"
               android:id="@+id/row"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
-              android:layout_marginTop="@dimen/subtitle_clock_padding"
               android:orientation="horizontal"
               android:gravity="center"
     />
diff --git a/packages/SystemUI/res-keyguard/layout/text_clock.xml b/packages/SystemUI/res-keyguard/layout/text_clock.xml
index b61ad9c..9f7ea0d 100644
--- a/packages/SystemUI/res-keyguard/layout/text_clock.xml
+++ b/packages/SystemUI/res-keyguard/layout/text_clock.xml
@@ -16,9 +16,10 @@
   -->
 <TextClock
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
+    android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_gravity="center_horizontal"
+    android:gravity="center_horizontal"
     android:letterSpacing="0.03"
     android:textColor="?attr/wallpaperTextColor"
     android:singleLine="true"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index e2ba23e..b6a41c1 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -42,18 +42,18 @@
     <dimen name="eca_overlap">-10dip</dimen>
 
     <!-- Slice header -->
-    <dimen name="widget_title_font_size">24dp</dimen>
-    <dimen name="widget_title_bottom_margin">14dp</dimen>
-    <dimen name="bottom_text_spacing_digital">0dp</dimen>
+    <dimen name="widget_title_font_size">22dp</dimen>
+    <dimen name="header_subtitle_padding">4dp</dimen>
+    <dimen name="header_icon_size">20dp</dimen>
     <!-- Slice subtitle -->
     <dimen name="widget_label_font_size">16dp</dimen>
     <!-- Clock without header -->
     <dimen name="widget_big_font_size">64dp</dimen>
+    <dimen name="bottom_text_spacing_digital">0dp</dimen>
     <!-- Clock with header -->
-    <dimen name="widget_small_clock_padding">-25dp</dimen>
-    <dimen name="widget_small_font_size">24dp</dimen>
-    <dimen name="widget_small_font_stroke">0.6dp</dimen>
+    <dimen name="widget_small_font_size">22dp</dimen>
     <dimen name="widget_vertical_padding">32dp</dimen>
+    <dimen name="widget_vertical_padding_clock">30dp</dimen>
     <!-- Subtitle paddings -->
     <dimen name="widget_horizontal_padding">8dp</dimen>
     <dimen name="widget_icon_size">16dp</dimen>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
index b673e4f..dd124b7 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml
@@ -14,12 +14,19 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0"
-    android:tint="?android:attr/colorControlNormal" >
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?android:attr/colorControlNormal">
+
     <path
-        android:pathData="M13.51,12l3.75,-3.74c0.41,-0.41 0.41,-1.07 0,-1.48l-4.47,-4.47 -0.03,-0.03a1.046,1.046 0,0 0,-1.76 0.76v6.44L6.95,5.43c-0.41,-0.41 -1.06,-0.41 -1.47,0s-0.41,1.06 0,1.47l5.09,5.1 -5.09,5.09c-0.41,0.41 -0.41,1.06 0,1.47s1.06,0.41 1.47,0L11,14.51v6.45a1.04,1.04 0,0 0,1.75 0.76l0.05,-0.05 4.46,-4.46c0.41,-0.41 0.41,-1.07 0,-1.48L13.51,12zM12.99,9.67v-4.3l2.15,2.15 -2.15,2.15zM12.99,18.62v-4.3l2.15,2.15 -2.15,2.15zM6.06,13.06c-0.59,0.59 -1.54,0.59 -2.12,0a1.49,1.49 0,0 1,0 -2.12,1.49 1.49,0 0,1 2.12,0c0.59,0.59 0.59,1.53 0,2.12zM20.06,10.94c0.59,0.59 0.59,1.54 0,2.12 -0.59,0.59 -1.54,0.59 -2.12,0a1.49,1.49 0,0 1,0 -2.12,1.49 1.49,0 0,1 2.12,0z"
-        android:fillColor="#FFFFFFFF" />
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M 5 10.5 C 5.82842712475 10.5 6.5 11.1715728753 6.5 12 C 6.5 12.8284271247 5.82842712475 13.5 5 13.5 C 4.17157287525 13.5 3.5 12.8284271247 3.5 12 C 3.5 11.1715728753 4.17157287525 10.5 5 10.5 Z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M 19 10.5 C 19.8284271247 10.5 20.5 11.1715728753 20.5 12 C 20.5 12.8284271247 19.8284271247 13.5 19 13.5 C 18.1715728753 13.5 17.5 12.8284271247 17.5 12 C 17.5 11.1715728753 18.1715728753 10.5 19 10.5 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
index 8cc6caa..220c63c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml
@@ -14,12 +14,13 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="64dp"
-    android:height="64dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0"
-    android:tint="?android:attr/colorControlNormal">
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?android:attr/colorControlNormal">
+
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M13.5,12l3.8,-3.7c0.4,-0.4 0.4,-1.1 0,-1.5l-4.5,-4.5c-0.4,-0.4 -1.1,-0.4 -1.5,0.1C11.1,2.5 11,2.8 11,3v6.4L6.9,5.4C6.5,5 5.9,5 5.5,5.4s-0.4,1.1 0,1.5l5.1,5.1l-5.1,5.1c-0.4,0.4 -0.4,1.1 0,1.5s1.1,0.4 1.5,0l4.1,-4V21c0,0.6 0.5,1 1,1c0.3,0 0.5,-0.1 0.7,-0.3l0.1,0l4.5,-4.5c0.4,-0.4 0.4,-1.1 0,-1.5L13.5,12zM13,9.7V5.4l2.1,2.2L13,9.7zM13,18.6v-4.3l2.1,2.2L13,18.6z"/>
+        android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane.xml b/packages/SystemUI/res/drawable/ic_signal_airplane.xml
index 0a4d752..f708ed9 100644
--- a/packages/SystemUI/res/drawable/ic_signal_airplane.xml
+++ b/packages/SystemUI/res/drawable/ic_signal_airplane.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2017 The Android Open Source Project
+     Copyright (C) 2019 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -15,16 +15,12 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="48dp"
-    android:height="48dp"
-    android:viewportWidth="20.5"
-    android:viewportHeight="20.5">
-    <group
-        android:translateX="1.75"
-        android:translateY="1.4">
-        <path
-            android:pathData="M16.01,9.87l-6.24,-3.9v-4.7C9.77,0.57 9.21,0 8.5,0S7.23,0.57 7.23,1.28v4.7L0.99,9.88c-0.37,0.23 -0.6,0.64 -0.6,1.08v0.41c0,0.29 0.29,0.5 0.55,0.41l6.27,-1.97v4.7l-1.37,1.02c-0.21,0.16 -0.34,0.41 -0.34,0.68v0.57c0,0.15 0.12,0.23 0.27,0.2 1.67,-0.47 1.12,-0.31 2.73,-0.78 1.03,0.3 1.7,0.49 2.72,0.78 0.15,0.03 0.27,-0.06 0.27,-0.2v-0.57c0,-0.27 -0.13,-0.52 -0.34,-0.68l-1.37,-1.02v-4.7l6.27,1.97c0.28,0.09 0.55,-0.12 0.55,-0.41v-0.41c0.01,-0.45 -0.23,-0.87 -0.59,-1.09z"
-            android:fillColor="#FFF"/>
-    </group>
-</vector>
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
 
+<path
+    android:fillColor="#FFFFFF"
+    android:pathData="M21,16v-2l-8-5V3.5C13,2.67,12.33,2,11.5,2S10,2.67,10,3.5V9l-8,5v2l8-2.5V19l-2,1.5V22l3.5-1l3.5,1v-1.5L13,19v-5.5L21,16z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/rounded_bg.xml b/packages/SystemUI/res/drawable/rounded_bg.xml
index c23a87f..3de67de 100644
--- a/packages/SystemUI/res/drawable/rounded_bg.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg.xml
@@ -3,8 +3,8 @@
        android:shape="rectangle">
     <solid android:color="?android:attr/colorPrimary" />
     <corners
-        android:bottomLeftRadius="@dimen/corner_size"
-        android:topLeftRadius="@dimen/corner_size"
+        android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+        android:topLeftRadius="?android:attr/dialogCornerRadius"
         android:bottomRightRadius="0dp"
         android:topRightRadius="0dp"
         />
diff --git a/packages/SystemUI/res/drawable/rounded_bg_bottom.xml b/packages/SystemUI/res/drawable/rounded_bg_bottom.xml
index b3bea63..7db59e9 100644
--- a/packages/SystemUI/res/drawable/rounded_bg_bottom.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg_bottom.xml
@@ -3,7 +3,7 @@
        android:shape="rectangle">
     <solid android:color="?android:attr/colorPrimaryDark" />
     <corners
-        android:bottomLeftRadius="@dimen/corner_size"
+        android:bottomLeftRadius="?android:attr/dialogCornerRadius"
         android:topLeftRadius="0dp"
         android:bottomRightRadius="0dp"
         android:topRightRadius="0dp"
diff --git a/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
index 622226f..382ca20 100644
--- a/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
@@ -3,9 +3,9 @@
        android:shape="rectangle">
     <solid android:color="?android:attr/panelColorBackground" />
     <corners
-        android:bottomLeftRadius="@dimen/corner_size"
+        android:bottomLeftRadius="?android:attr/dialogCornerRadius"
         android:topLeftRadius="0dp"
-        android:bottomRightRadius="@dimen/corner_size"
+        android:bottomRightRadius="?android:attr/dialogCornerRadius"
         android:topRightRadius="0dp"
         />
 </shape>
diff --git a/packages/SystemUI/res/drawable/rounded_bg_full.xml b/packages/SystemUI/res/drawable/rounded_bg_full.xml
index 03f18bb..e0d3f63 100644
--- a/packages/SystemUI/res/drawable/rounded_bg_full.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg_full.xml
@@ -3,9 +3,9 @@
        android:shape="rectangle">
     <solid android:color="?android:attr/colorBackgroundFloating" />
     <corners
-        android:bottomLeftRadius="@dimen/corner_size"
-        android:topLeftRadius="@dimen/corner_size"
-        android:bottomRightRadius="@dimen/corner_size"
-        android:topRightRadius="@dimen/corner_size"
+        android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+        android:topLeftRadius="?android:attr/dialogCornerRadius"
+        android:bottomRightRadius="?android:attr/dialogCornerRadius"
+        android:topRightRadius="?android:attr/dialogCornerRadius"
         />
 </shape>
diff --git a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
index a4b3c99..a62657d 100644
--- a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
+++ b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
@@ -17,9 +17,9 @@
        android:shape="rectangle">
     <solid android:color="?android:attr/colorPrimaryDark" />
     <corners
-        android:bottomLeftRadius="@dimen/corner_size"
+        android:bottomLeftRadius="?android:attr/dialogCornerRadius"
         android:topLeftRadius="0dp"
-        android:bottomRightRadius="@dimen/corner_size"
+        android:bottomRightRadius="?android:attr/dialogCornerRadius"
         android:topRightRadius="0dp"
         />
 </shape>
diff --git a/packages/SystemUI/res/layout-land/global_actions_grid.xml b/packages/SystemUI/res/layout-land/global_actions_grid.xml
new file mode 100644
index 0000000..911b661
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_actions_grid.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.systemui.globalactions.GlobalActionsGridLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/global_actions_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:clipToPadding="false"
+    android:theme="@style/qs_theme"
+    android:gravity="top|right"
+    android:clipChildren="false"
+>
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        android:gravity="top|right"
+        android:padding="0dp"
+        android:orientation="vertical"
+        android:layoutDirection="ltr"
+        android:layout_marginRight="@dimen/global_actions_grid_container_bottom_margin"
+    >
+        <!-- Grid of action items -->
+        <com.android.systemui.globalactions.ListGridLayout
+            android:id="@android:id/list"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layoutDirection="ltr"
+            android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+            android:translationZ="@dimen/global_actions_translate"
+            android:paddingLeft="@dimen/global_actions_grid_top_padding"
+            android:paddingRight="@dimen/global_actions_grid_bottom_padding"
+            android:paddingTop="@dimen/global_actions_grid_left_padding"
+            android:paddingBottom="@dimen/global_actions_grid_right_padding"
+            android:background="?android:attr/colorBackgroundFloating"
+        >
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:layoutDirection="ltr"
+                android:orientation="horizontal"
+            />
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:layoutDirection="ltr"
+                android:orientation="horizontal"
+            />
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:layoutDirection="ltr"
+                android:orientation="horizontal"
+            />
+        </com.android.systemui.globalactions.ListGridLayout>
+
+        <!-- For separated items-->
+        <LinearLayout
+            android:id="@+id/separated_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+            android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
+            android:paddingTop="@dimen/global_actions_grid_left_padding"
+            android:paddingLeft="@dimen/global_actions_grid_top_padding"
+            android:paddingBottom="@dimen/global_actions_grid_right_padding"
+            android:paddingRight="@dimen/global_actions_grid_bottom_padding"
+            android:orientation="horizontal"
+            android:layoutDirection="ltr"
+            android:background="?android:attr/colorBackgroundFloating"
+            android:translationZ="@dimen/global_actions_translate"
+        />
+
+    </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsGridLayout>
diff --git a/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml
new file mode 100644
index 0000000..669be1b
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/global_actions_grid_seascape.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.systemui.globalactions.GlobalActionsGridLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/global_actions_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:clipToPadding="false"
+    android:theme="@style/qs_theme"
+    android:gravity="top|left"
+    android:clipChildren="false"
+>
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        android:gravity="bottom|left"
+        android:padding="0dp"
+        android:orientation="vertical"
+        android:layout_marginLeft="@dimen/global_actions_grid_container_bottom_margin"
+    >
+        <!-- For separated items-->
+        <LinearLayout
+            android:id="@+id/separated_button"
+            android:layout_gravity="top|left"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+            android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
+            android:paddingTop="@dimen/global_actions_grid_left_padding"
+            android:paddingLeft="@dimen/global_actions_grid_top_padding"
+            android:paddingBottom="@dimen/global_actions_grid_right_padding"
+            android:paddingRight="@dimen/global_actions_grid_bottom_padding"
+            android:orientation="horizontal"
+            android:layoutDirection="rtl"
+            android:background="?android:attr/colorBackgroundFloating"
+            android:translationZ="@dimen/global_actions_translate"
+        />
+
+        <!-- Grid of action items -->
+        <com.android.systemui.globalactions.ListGridLayout
+            android:id="@android:id/list"
+            android:layout_gravity="bottom|left"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_marginTop="@dimen/global_actions_grid_side_margin"
+            android:translationZ="@dimen/global_actions_translate"
+            android:paddingLeft="@dimen/global_actions_grid_top_padding"
+            android:paddingRight="@dimen/global_actions_grid_bottom_padding"
+            android:paddingTop="@dimen/global_actions_grid_left_padding"
+            android:paddingBottom="@dimen/global_actions_grid_right_padding"
+            android:background="?android:attr/colorBackgroundFloating"
+        >
+            <LinearLayout
+                android:layout_gravity="bottom"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:layoutDirection="rtl"
+                android:orientation="horizontal"
+            />
+            <LinearLayout
+                android:layout_gravity="bottom"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:layoutDirection="rtl"
+                android:orientation="horizontal"
+            />
+            <LinearLayout
+                android:layout_gravity="bottom"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:layoutDirection="rtl"
+                android:orientation="horizontal"
+            />
+        </com.android.systemui.globalactions.ListGridLayout>
+    </LinearLayout>
+
+</com.android.systemui.globalactions.GlobalActionsGridLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_grid.xml b/packages/SystemUI/res/layout/global_actions_grid.xml
index e6f2376..1b56fa0 100644
--- a/packages/SystemUI/res/layout/global_actions_grid.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid.xml
@@ -12,10 +12,11 @@
 >
 
     <LinearLayout
-        android:layout_height="290dp"
-        android:layout_width="412dp"
-        android:gravity="bottom"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:gravity="bottom | right"
         android:padding="0dp"
+        android:layoutDirection="ltr"
         android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin"
     >
         <!-- For separated items-->
@@ -34,15 +35,11 @@
             android:translationZ="@dimen/global_actions_translate"
         />
 
-        <Space android:layout_width="match_parent" android:layout_height="2dp"
-               android:layout_weight="1" />
-
         <!-- Grid of action items -->
         <com.android.systemui.globalactions.ListGridLayout
             android:id="@android:id/list"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:gravity="right"
             android:orientation="horizontal"
             android:layoutDirection="rtl"
             android:layout_marginRight="@dimen/global_actions_grid_side_margin"
@@ -56,25 +53,19 @@
             <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_gravity="bottom|right"
                 android:visibility="gone"
-                android:gravity="bottom"
                 android:orientation="vertical"
             />
             <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_gravity="bottom|right"
                 android:visibility="gone"
-                android:gravity="bottom"
                 android:orientation="vertical"
             />
             <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_gravity="bottom|right"
                 android:visibility="gone"
-                android:gravity="bottom"
                 android:orientation="vertical"
             />
         </com.android.systemui.globalactions.ListGridLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_item.xml b/packages/SystemUI/res/layout/global_actions_grid_item.xml
index 0c11cd9..a893839 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_item.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml
@@ -47,6 +47,7 @@
         android:gravity="center"
         android:textSize="12sp"
         android:textAppearance="?android:attr/textAppearanceSmall"
+        android:singleLine="true"
     />
 
     <TextView
@@ -57,5 +58,6 @@
         android:gravity="center"
         android:textColor="?android:attr/textColorTertiary"
         android:textAppearance="?android:attr/textAppearanceSmall"
+        android:singleLine="true"
     />
 </LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7d009b5..01595f0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1582,19 +1582,19 @@
     <!-- Notification Inline controls: continue receiving notifications prompt, channel level -->
     <string name="inline_keep_showing">Keep showing these notifications?</string>
 
-    <!-- Notification inline controls: block notifications button -->
+    <!-- Notification inline controls: block notifications button [CHAR_LIMIT=25] -->
     <string name="inline_stop_button">Stop notifications</string>
 
     <!-- Notification inline controls: button to deliver notifications silently from this channel [CHAR_LIMIT=35] -->
     <string name="inline_deliver_silently_button">Deliver Silently</string>
 
-    <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=35] -->
+    <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=20] -->
     <string name="inline_block_button">Block</string>
 
-    <!-- Notification inline controls: keep getting notifications button -->
+    <!-- Notification inline controls: keep getting notifications button [CHAR_LIMIT=25] -->
     <string name="inline_keep_button">Keep showing</string>
 
-    <!-- Notification inline controls: minimize notifications button -->
+    <!-- Notification inline controls: minimize notifications button [CHAR_LIMIT=20] -->
     <string name="inline_minimize_button">Minimize</string>
 
     <!-- Notification inline controls: button to show notifications silently, without alerting the user [CHAR_LIMIT=35] -->
@@ -2323,7 +2323,7 @@
     <!-- Action for accepting the Ongoing privacy dialog [CHAR LIMIT=10]-->
     <string name="ongoing_privacy_dialog_ok">Got it</string>
 
-    <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]-->
+    <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=23]-->
     <string name="ongoing_privacy_dialog_open_settings">Privacy settings</string>
 
     <!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 822920e..8de84bf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -1,10 +1,19 @@
 package com.android.keyguard;
 
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.graphics.Paint;
 import android.graphics.Paint.Style;
+import android.transition.ChangeBounds;
+import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.transition.TransitionValues;
 import android.util.AttributeSet;
+import android.util.MathUtils;
+import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -16,6 +25,7 @@
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.clock.ClockManager;
 import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -28,6 +38,7 @@
  */
 public class KeyguardClockSwitch extends RelativeLayout {
 
+    private final Transition mTransition;
     /**
      * Optional/alternative clock injected via plugin.
      */
@@ -53,6 +64,10 @@
      * Maintain state so that a newly connected plugin can be initialized.
      */
     private float mDarkAmount;
+    /**
+     * If the Keyguard Slice has a header (big center-aligned text.)
+     */
+    private boolean mShowingHeader;
     private boolean mSupportsDarkText;
     private int[] mColorPalette;
 
@@ -98,6 +113,7 @@
 
     public KeyguardClockSwitch(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mTransition = new ClockBoundsTransition();
     }
 
     /**
@@ -286,6 +302,26 @@
         }
     }
 
+    /**
+     * Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock
+     * in these cases.
+     */
+    public void setKeyguardShowingHeader(boolean hasHeader) {
+        if (mShowingHeader == hasHeader || hasCustomClock()) {
+            return;
+        }
+        mShowingHeader = hasHeader;
+
+        TransitionManager.beginDelayedTransition((ViewGroup) mClockView.getParent(), mTransition);
+        int fontSize = mContext.getResources().getDimensionPixelSize(mShowingHeader
+                ? R.dimen.widget_small_font_size : R.dimen.widget_big_font_size);
+        int paddingBottom = mContext.getResources().getDimensionPixelSize(mShowingHeader
+                ? R.dimen.widget_vertical_padding_clock : R.dimen.header_subtitle_padding);
+        mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
+        mClockView.setPadding(mClockView.getPaddingLeft(), mClockView.getPaddingTop(),
+                mClockView.getPaddingRight(), paddingBottom);
+    }
+
     @VisibleForTesting (otherwise = VisibleForTesting.NONE)
     ClockManager.ClockChangedListener getClockChangedListener() {
         return mClockChangedListener;
@@ -295,4 +331,54 @@
     StatusBarStateController.StateListener getStateListener() {
         return mStateListener;
     }
+
+    /**
+     * Special layout transition that scales the clock view as its bounds change, to make it look
+     * like the text is shrinking.
+     */
+    private class ClockBoundsTransition extends ChangeBounds {
+
+        ClockBoundsTransition() {
+            setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2);
+            setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        }
+
+        @Override
+        public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+                TransitionValues endValues) {
+            Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
+            if (animator == null || startValues.view != mClockView) {
+                return animator;
+            }
+
+            ValueAnimator boundsAnimator = null;
+            if (animator instanceof AnimatorSet) {
+                Animator first = ((AnimatorSet) animator).getChildAnimations().get(0);
+                if (first instanceof ValueAnimator) {
+                    boundsAnimator = (ValueAnimator) first;
+                }
+            } else if (animator instanceof ValueAnimator) {
+                boundsAnimator = (ValueAnimator) animator;
+            }
+
+            if (boundsAnimator != null) {
+                float bigFontSize = mContext.getResources()
+                        .getDimensionPixelSize(R.dimen.widget_big_font_size);
+                float smallFontSize = mContext.getResources()
+                        .getDimensionPixelSize(R.dimen.widget_small_font_size);
+                float startScale = mShowingHeader
+                        ? bigFontSize / smallFontSize : smallFontSize / bigFontSize;
+                boundsAnimator.addUpdateListener(animation -> {
+                    float scale = MathUtils.lerp(startScale, 1f /* stop */,
+                            animation.getAnimatedFraction());
+                    mClockView.setPivotX(mClockView.getWidth() / 2);
+                    mClockView.setPivotY(0);
+                    mClockView.setScaleX(scale);
+                    mClockView.setScaleY(scale);
+                });
+            }
+
+            return animator;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index bac7844..2040a76 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -37,10 +37,10 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.animation.Animation;
 import android.widget.Button;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.Observer;
@@ -58,6 +58,7 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
+import com.android.systemui.R;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.tuner.TunerService;
@@ -78,6 +79,8 @@
 
     private final HashMap<View, PendingIntent> mClickActions;
     private Uri mKeyguardSliceUri;
+    @VisibleForTesting
+    TextView mTitle;
     private Row mRow;
     private int mTextColor;
     private float mDarkAmount = 0;
@@ -91,6 +94,8 @@
     private Runnable mContentChangeListener;
     private Slice mSlice;
     private boolean mHasHeader;
+    private final int mRowWithHeaderPadding;
+    private final int mRowPadding;
 
     public KeyguardSliceView(Context context) {
         this(context, null, 0);
@@ -107,6 +112,9 @@
         tunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI);
 
         mClickActions = new HashMap<>();
+        mRowPadding = context.getResources().getDimensionPixelSize(R.dimen.subtitle_clock_padding);
+        mRowWithHeaderPadding = context.getResources()
+                .getDimensionPixelSize(R.dimen.header_subtitle_padding);
 
         LayoutTransition transition = new LayoutTransition();
         transition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2);
@@ -117,13 +125,13 @@
         transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.FAST_OUT_SLOW_IN);
         transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
         transition.setAnimateParentHierarchy(false);
-        transition.addTransitionListener(new SliceViewTransitionListener());
         setLayoutTransition(transition);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mTitle = findViewById(R.id.title);
         mRow = findViewById(R.id.row);
         mTextColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
         mIconSize = (int) mContext.getResources().getDimension(R.dimen.widget_icon_size);
@@ -160,6 +168,7 @@
     private void showSlice() {
         Trace.beginSection("KeyguardSliceView#showSlice");
         if (mSlice == null) {
+            mTitle.setVisibility(GONE);
             mRow.setVisibility(GONE);
             mHasHeader = false;
             if (mContentChangeListener != null) {
@@ -170,8 +179,7 @@
 
         ListContent lc = new ListContent(getContext(), mSlice);
         SliceContent headerContent = lc.getHeader();
-        mHasHeader = headerContent != null
-                && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
+        mHasHeader = headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
         List<SliceContent> subItems = new ArrayList<>();
         for (int i = 0; i < lc.getRowItems().size(); i++) {
             SliceContent subItem = lc.getRowItems().get(i);
@@ -181,12 +189,26 @@
                 subItems.add(subItem);
             }
         }
+        if (!mHasHeader) {
+            mTitle.setVisibility(GONE);
+        } else {
+            mTitle.setVisibility(VISIBLE);
+
+            RowContent header = lc.getHeader();
+            SliceItem mainTitle = header.getTitleItem();
+            CharSequence title = mainTitle != null ? mainTitle.getText() : null;
+            mTitle.setText(title);
+        }
 
         mClickActions.clear();
         final int subItemsCount = subItems.size();
         final int blendedColor = getTextColor();
         final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it
         mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE);
+        LinearLayout.LayoutParams layoutParams = (LayoutParams) mRow.getLayoutParams();
+        layoutParams.topMargin = mHasHeader ? mRowWithHeaderPadding : mRowPadding;
+        mRow.setLayoutParams(layoutParams);
+
         for (int i = startIndex; i < subItemsCount; i++) {
             RowContent rc = (RowContent) subItems.get(i);
             SliceItem item = rc.getSliceItem();
@@ -250,6 +272,7 @@
 
     private void updateTextColors() {
         final int blendedColor = getTextColor();
+        mTitle.setTextColor(blendedColor);
         int childCount = mRow.getChildCount();
         for (int i = 0; i < childCount; i++) {
             View v = mRow.getChildAt(i);
@@ -294,7 +317,10 @@
         setupUri(newValue);
     }
 
-    private void setupUri(String uriString) {
+    /**
+     * Sets the slice provider Uri.
+     */
+    public void setupUri(String uriString) {
         if (uriString == null) {
             uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI;
         }
@@ -512,29 +538,4 @@
             }
         }
     }
-
-    private class SliceViewTransitionListener implements LayoutTransition.TransitionListener {
-        @Override
-        public void startTransition(LayoutTransition transition, ViewGroup container, View view,
-                int transitionType) {
-            switch (transitionType) {
-                case  LayoutTransition.APPEARING:
-                    int translation = getResources().getDimensionPixelSize(
-                            R.dimen.pulsing_notification_appear_translation);
-                    view.setTranslationY(translation);
-                    view.animate()
-                            .translationY(0)
-                            .setDuration(DEFAULT_ANIM_DURATION)
-                            .setInterpolator(Interpolators.ALPHA_IN)
-                            .start();
-                    break;
-            }
-        }
-
-        @Override
-        public void endTransition(LayoutTransition transition, ViewGroup container, View view,
-                int transitionType) {
-
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index bb549ad..b0670fd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -16,14 +16,11 @@
 
 package com.android.keyguard;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Color;
-import android.graphics.Paint;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -37,15 +34,12 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.widget.GridLayout;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import androidx.core.graphics.ColorUtils;
 
 import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.ViewClippingUtil;
 import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import com.google.android.collect.Sets;
@@ -54,14 +48,13 @@
 import java.util.TimeZone;
 
 public class KeyguardStatusView extends GridLayout implements
-        ConfigurationController.ConfigurationListener, View.OnLayoutChangeListener {
+        ConfigurationController.ConfigurationListener {
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
     private static final String TAG = "KeyguardStatusView";
     private static final int MARQUEE_DELAY_MS = 2000;
 
     private final LockPatternUtils mLockPatternUtils;
     private final IActivityManager mIActivityManager;
-    private final float mSmallClockScale;
 
     private TextView mLogoutView;
     private KeyguardClockSwitch mClockView;
@@ -74,8 +67,6 @@
     private boolean mPulsing;
     private float mDarkAmount = 0;
     private int mTextColor;
-    private int mLastLayoutHeight;
-    private int mSmallClockPadding;
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
 
@@ -135,8 +126,6 @@
         mIActivityManager = ActivityManager.getService();
         mLockPatternUtils = new LockPatternUtils(getContext());
         mHandler = new Handler(Looper.myLooper());
-        mSmallClockScale = getResources().getDimension(R.dimen.widget_small_font_size)
-                / getResources().getDimension(R.dimen.widget_big_font_size);
         onDensityOrFontScaleChanged();
     }
 
@@ -189,9 +178,6 @@
         mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice);
         mTextColor = mClockView.getCurrentTextColor();
 
-        int clockStroke = getResources().getDimensionPixelSize(R.dimen.widget_small_font_stroke);
-        mClockView.getPaint().setStrokeWidth(clockStroke);
-        mClockView.addOnLayoutChangeListener(this);
         mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged);
         onSliceContentChanged();
 
@@ -207,72 +193,20 @@
      * Moves clock, adjusting margins when slice content changes.
      */
     private void onSliceContentChanged() {
-        LinearLayout.LayoutParams layoutParams =
-                (LinearLayout.LayoutParams) mClockView.getLayoutParams();
-        layoutParams.bottomMargin = mKeyguardSlice.hasHeader() ? mSmallClockPadding : 0;
-        mClockView.setLayoutParams(layoutParams);
-    }
-
-    /**
-     * Animate clock when necessary.
-     */
-    @Override
-    public void onLayoutChange(View view, int left, int top, int right, int bottom,
-            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-        boolean smallClock = mKeyguardSlice.hasHeader();
-        int heightOffset = smallClock ? 0 : getHeight() - mLastLayoutHeight;
-        long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION;
-        long delay = smallClock ? 0 : duration / 4;
-
-        boolean shouldAnimate = mKeyguardSlice.getLayoutTransition() != null
-                && mKeyguardSlice.getLayoutTransition().isRunning();
-        if (view == mClockView) {
-            float clockScale = smallClock ? mSmallClockScale : 1;
-            Paint.Style style = smallClock ? Paint.Style.FILL_AND_STROKE : Paint.Style.FILL;
-            mClockView.animate().cancel();
-            if (shouldAnimate) {
-                mClockView.setY(oldTop + heightOffset);
-                mClockView.animate()
-                        .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                        .setDuration(duration)
-                        .setListener(new ClipChildrenAnimationListener())
-                        .setStartDelay(delay)
-                        .y(top)
-                        .scaleX(clockScale)
-                        .scaleY(clockScale)
-                        .withEndAction(() -> {
-                            mClockView.setStyle(style);
-                            mClockView.invalidate();
-                        })
-                        .start();
-            } else {
-                mClockView.setY(top);
-                mClockView.setScaleX(clockScale);
-                mClockView.setScaleY(clockScale);
-                mClockView.setStyle(style);
-                mClockView.invalidate();
-            }
-        }
+        mClockView.setKeyguardShowingHeader(mKeyguardSlice.hasHeader());
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        mClockView.setPivotX(mClockView.getWidth() / 2);
-        mClockView.setPivotY(0);
-        mLastLayoutHeight = getHeight();
         layoutOwnerInfo();
     }
 
     @Override
     public void onDensityOrFontScaleChanged() {
-        mSmallClockPadding = getResources()
-                .getDimensionPixelSize(R.dimen.widget_small_clock_padding);
         if (mClockView != null) {
             mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
                     getResources().getDimensionPixelSize(R.dimen.widget_big_font_size));
-            mClockView.getPaint().setStrokeWidth(
-                    getResources().getDimensionPixelSize(R.dimen.widget_small_font_stroke));
         }
         if (mOwnerInfo != null) {
             mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX,
@@ -461,24 +395,4 @@
             Log.e(TAG, "Failed to logout user", re);
         }
     }
-
-    private class ClipChildrenAnimationListener extends AnimatorListenerAdapter implements
-            ViewClippingUtil.ClippingParameters {
-
-        ClipChildrenAnimationListener() {
-            ViewClippingUtil.setClippingDeactivated(mClockView, true /* deactivated */,
-                    this /* clippingParams */);
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            ViewClippingUtil.setClippingDeactivated(mClockView, false /* deactivated */,
-                    this /* clippingParams */);
-        }
-
-        @Override
-        public boolean shouldFinish(View view) {
-            return view == getParent();
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 9598142..078108d 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -15,6 +15,7 @@
  */
 package com.android.keyguard.clock;
 
+import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
@@ -25,16 +26,18 @@
 import android.provider.Settings;
 import android.view.LayoutInflater;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.keyguard.R;
+import com.android.systemui.dock.DockManager;
+import com.android.systemui.dock.DockManager.DockEventListener;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.ExtensionController.Extension;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
 import java.util.function.Consumer;
-import java.util.function.Supplier;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -45,7 +48,6 @@
 @Singleton
 public final class ClockManager {
 
-    private final LayoutInflater mLayoutInflater;
     private final ContentResolver mContentResolver;
 
     private final List<ClockInfo> mClockInfos = new ArrayList<>();
@@ -62,7 +64,6 @@
                     }
                 }
             };
-
     private final ExtensionController mExtensionController;
     /**
      * Used to select between plugin or default implementations of ClockPlugin interface.
@@ -72,13 +73,35 @@
      * Consumer that accepts the a new ClockPlugin implementation when the Extension reloads.
      */
     private final Consumer<ClockPlugin> mClockPluginConsumer = this::setClockPlugin;
+    /**
+     * Supplier of default ClockPlugin implementation.
+     */
+    private final DefaultClockSupplier mDefaultClockSupplier;
+    /**
+     * Observe changes to dock state to know when to switch the clock face.
+     */
+    private final DockEventListener mDockEventListener =
+            new DockEventListener() {
+                @Override
+                public void onEvent(int event) {
+                    final boolean isDocked = (event == DockManager.STATE_DOCKED
+                            || event == DockManager.STATE_DOCKED_HIDE);
+                    mDefaultClockSupplier.setDocked(isDocked);
+                    if (mClockExtension != null) {
+                        mClockExtension.reload();
+                    }
+                }
+            };
+    @Nullable
+    private final DockManager mDockManager;
 
     private final List<ClockChangedListener> mListeners = new ArrayList<>();
 
     @Inject
-    public ClockManager(Context context, ExtensionController extensionController) {
+    public ClockManager(Context context, ExtensionController extensionController,
+            @Nullable DockManager dockManager) {
         mExtensionController = extensionController;
-        mLayoutInflater = LayoutInflater.from(context);
+        mDockManager = dockManager;
         mContentResolver = context.getContentResolver();
 
         Resources res = context.getResources();
@@ -110,6 +133,9 @@
                 .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.type_thumbnail))
                 .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.type_preview))
                 .build());
+
+        mDefaultClockSupplier = new DefaultClockSupplier(new SettingsWrapper(mContentResolver),
+                LayoutInflater.from(context));
     }
 
     /**
@@ -154,41 +180,32 @@
         mContentResolver.registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
                 false, mContentObserver);
+        mContentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE),
+                false, mContentObserver);
+        if (mDockManager != null) {
+            mDockManager.addListener(mDockEventListener);
+        }
         mClockExtension = mExtensionController.newExtension(ClockPlugin.class)
             .withPlugin(ClockPlugin.class)
             .withCallback(mClockPluginConsumer)
-            // Using withDefault even though this isn't the default as a workaround.
-            // ExtensionBuilder doesn't provide the ability to supply a ClockPlugin
-            // instance based off of the value of a setting. Since multiple "default"
-            // can be provided, using a supplier that changes the settings value.
-            // A null return will cause Extension#reload to look at the next "default"
-            // supplier.
-            .withDefault(
-                    new SettingsGattedSupplier(
-                        mContentResolver,
-                        Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
-                        BubbleClockController.class.getName(),
-                            () -> BubbleClockController.build(mLayoutInflater)))
-            .withDefault(
-                    new SettingsGattedSupplier(
-                        mContentResolver,
-                        Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
-                        StretchAnalogClockController.class.getName(),
-                            () -> StretchAnalogClockController.build(mLayoutInflater)))
-            .withDefault(
-                    new SettingsGattedSupplier(
-                        mContentResolver,
-                        Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
-                        TypeClockController.class.getName(),
-                            () -> TypeClockController.build(mLayoutInflater)))
+            .withDefault(mDefaultClockSupplier)
             .build();
     }
 
     private void unregister() {
         mContentResolver.unregisterContentObserver(mContentObserver);
+        if (mDockManager != null) {
+            mDockManager.removeListener(mDockEventListener);
+        }
         mClockExtension.destroy();
     }
 
+    @VisibleForTesting
+    boolean isDocked() {
+        return mDefaultClockSupplier.isDocked();
+    }
+
     /**
      * Listener for events that should cause the custom clock face to change.
      */
@@ -200,44 +217,4 @@
          */
         void onClockChanged(ClockPlugin clock);
     }
-
-    /**
-     * Supplier that only gets an instance when a settings value matches expected value.
-     */
-    private static class SettingsGattedSupplier implements Supplier<ClockPlugin> {
-
-        private final ContentResolver mContentResolver;
-        private final String mKey;
-        private final String mValue;
-        private final Supplier<ClockPlugin> mSupplier;
-
-        /**
-         * Constructs a supplier that changes secure setting key against value.
-         *
-         * @param contentResolver Used to look up settings value.
-         * @param key Settings key.
-         * @param value If the setting matches this values that get supplies a ClockPlugin
-         *        instance.
-         * @param supplier Supplier of ClockPlugin instance, only used if the setting
-         *        matches value.
-         */
-        SettingsGattedSupplier(ContentResolver contentResolver, String key, String value,
-                Supplier<ClockPlugin> supplier) {
-            mContentResolver = contentResolver;
-            mKey = key;
-            mValue = value;
-            mSupplier = supplier;
-        }
-
-        /**
-         * Returns null if the settings value doesn't match the expected value.
-         *
-         * A null return causes Extension#reload to skip this supplier and move to the next.
-         */
-        @Override
-        public ClockPlugin get() {
-            final String currentValue = Settings.Secure.getString(mContentResolver, mKey);
-            return Objects.equals(currentValue, mValue) ? mSupplier.get() : null;
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockSupplier.java b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockSupplier.java
new file mode 100644
index 0000000..7fdd235
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockSupplier.java
@@ -0,0 +1,101 @@
+/*
+ * 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.keyguard.clock;
+
+import android.util.ArrayMap;
+import android.view.LayoutInflater;
+
+import com.android.systemui.plugins.ClockPlugin;
+
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ * Supplier that only gets an instance when a settings value matches expected value.
+ */
+public class DefaultClockSupplier implements Supplier<ClockPlugin> {
+
+    private final SettingsWrapper mSettingsWrapper;
+    /**
+     * Map from expected value stored in settings to supplier of custom clock face.
+     */
+    private final Map<String, Supplier<ClockPlugin>> mClocks = new ArrayMap<>();
+    /**
+     * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face
+     * to show.
+     */
+    private boolean mIsDocked;
+
+    /**
+     * Constructs a supplier that changes secure setting key against value.
+     *
+     * @param settingsWrapper Wrapper around settings used to look up the custom clock face.
+     * @param layoutInflater Provided to clocks as dependency to inflate clock views.
+     */
+    public DefaultClockSupplier(SettingsWrapper settingsWrapper, LayoutInflater layoutInflater) {
+        mSettingsWrapper = settingsWrapper;
+
+        mClocks.put(BubbleClockController.class.getName(),
+                () -> BubbleClockController.build(layoutInflater));
+        mClocks.put(StretchAnalogClockController.class.getName(),
+                () -> StretchAnalogClockController.build(layoutInflater));
+        mClocks.put(TypeClockController.class.getName(),
+                () -> TypeClockController.build(layoutInflater));
+    }
+
+    /**
+     * Sets the dock state.
+     *
+     * @param isDocked True when docked, false otherwise.
+     */
+    public void setDocked(boolean isDocked) {
+        mIsDocked = isDocked;
+    }
+
+    boolean isDocked() {
+        return mIsDocked;
+    }
+
+    /**
+     * Get the custom clock face based on values in settings.
+     *
+     * @return Custom clock face, null if the settings value doesn't match a custom clock.
+     */
+    @Override
+    public ClockPlugin get() {
+        ClockPlugin plugin = null;
+        if (mIsDocked) {
+            final String name = mSettingsWrapper.getDockedClockFace();
+            if (name != null) {
+                Supplier<ClockPlugin> supplier = mClocks.get(name);
+                if (supplier != null) {
+                    plugin = supplier.get();
+                    if (plugin != null) {
+                        return plugin;
+                    }
+                }
+            }
+        }
+        final String name = mSettingsWrapper.getLockScreenCustomClockFace();
+        if (name != null) {
+            Supplier<ClockPlugin> supplier = mClocks.get(name);
+            if (supplier != null) {
+                plugin = supplier.get();
+            }
+        }
+        return plugin;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java
new file mode 100644
index 0000000..58e1155
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java
@@ -0,0 +1,48 @@
+/*
+ * 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.keyguard.clock;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+
+/**
+ * Wrapper around Settings used for testing.
+ */
+public class SettingsWrapper {
+
+    private static final String CUSTOM_CLOCK_FACE = Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE;
+    private static final String DOCKED_CLOCK_FACE = Settings.Secure.DOCKED_CLOCK_FACE;
+
+    private ContentResolver mContentResolver;
+
+    public SettingsWrapper(ContentResolver contentResolver) {
+        mContentResolver = contentResolver;
+    }
+
+    /**
+     * Gets the value stored in settings for the custom clock face.
+     */
+    public String getLockScreenCustomClockFace() {
+        return Settings.Secure.getString(mContentResolver, CUSTOM_CLOCK_FACE);
+    }
+
+    /**
+     * Gets the value stored in settings for the clock face to use when docked.
+     */
+    public String getDockedClockFace() {
+        return Settings.Secure.getString(mContentResolver, DOCKED_CLOCK_FACE);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
index ce9c637..3c6f081 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
@@ -21,11 +21,13 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.PowerNotificationWarnings;
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
@@ -194,6 +196,12 @@
     /**
      */
     @Binds
+    public abstract StatusBarStateController provideStatusBarStateController(
+            StatusBarStateControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
     public abstract StatusBarIconController provideStatusBarIconController(
             StatusBarIconControllerImpl controllerImpl);
 
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
index e28aa9d..2a1d066 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
@@ -23,7 +23,6 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.provider.Settings;
 import android.util.AttributeSet;
 import android.view.Gravity;
@@ -59,7 +58,6 @@
     private int mEndPoint;
     private boolean mEdgeBleed;
     private boolean mRoundedDivider;
-    private int mRotation = ROTATION_NONE;
     private boolean mRotatedBackground;
     private boolean mSwapOrientation = true;
 
@@ -89,7 +87,7 @@
     }
 
     @Override
-    public ViewGroup getParentView(boolean separated, int index) {
+    public ViewGroup getParentView(boolean separated, int index, boolean reverse) {
         if (separated) {
             return getSeparatedView();
         } else {
@@ -174,7 +172,6 @@
                 mSeparatedView.setBackground(mSeparatedViewBackground);
                 updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
                 mOldHeight = mList.getMeasuredHeight();
-                updateRotation();
             } else {
                 return;
             }
@@ -188,25 +185,13 @@
         post(() -> updatePosition());
     }
 
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        updateRotation();
-    }
-
     public void setSwapOrientation(boolean swapOrientation) {
         mSwapOrientation = swapOrientation;
     }
 
-    private void updateRotation() {
-        int rotation = RotationUtils.getRotation(getContext());
-        if (rotation != mRotation) {
-            rotate(mRotation, rotation);
-            mRotation = rotation;
-        }
-    }
-
-    private void rotate(int from, int to) {
+    @Override
+    protected void rotate(int from, int to) {
+        super.rotate(from, to);
         if (from != ROTATION_NONE && to != ROTATION_NONE) {
             // Rather than handling this confusing case, just do 2 rotations.
             rotate(from, ROTATION_NONE);
diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
index 85265f4..00ff518 100644
--- a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
@@ -17,11 +17,14 @@
 package com.android.systemui;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
+import com.android.systemui.util.leak.RotationUtils;
+
 /**
  * Layout class representing the Global Actions menu which appears when the power button is held.
  */
@@ -32,8 +35,12 @@
     protected int mExpectedSeparatedItemCount;
     protected int mExpectedListItemCount;
 
+    protected int mRotation;
+    protected RotationListener mRotationListener;
+
     public MultiListLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mRotation = RotationUtils.getRotation(context);
     }
 
     protected abstract ViewGroup getSeparatedView();
@@ -50,10 +57,12 @@
      * @param separated Whether or not this index refers to a position in the separated or list
      *                  container.
      * @param index The index of the item within the container.
+     * @param reverse If the MultiListLayout contains sub-lists within the list container, reverse
+     *                the order that they are filled.
      * @return The parent ViewGroup which will be used to contain the specified item
      * after it has been added to the layout.
      */
-    public abstract ViewGroup getParentView(boolean separated, int index);
+    public abstract ViewGroup getParentView(boolean separated, int index, boolean reverse);
 
     /**
      * Sets the divided view, which may have a differently-colored background.
@@ -111,6 +120,26 @@
         setFocusable(true);
     }
 
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        int newRotation = RotationUtils.getRotation(mContext);
+        if (newRotation != mRotation) {
+            rotate(mRotation, newRotation);
+            mRotation = newRotation;
+        }
+    }
+
+    protected void rotate(int from, int to) {
+        if (mRotationListener != null) {
+            mRotationListener.onRotate(from, to);
+        }
+    }
+
+    public void setRotationListener(RotationListener listener) {
+        mRotationListener = listener;
+    }
+
     /**
      * Retrieve the MultiListLayout associated with the given view.
      */
@@ -121,4 +150,8 @@
         }
         return null;
     }
+
+    interface RotationListener {
+        void onRotate(int from, int to);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 755d6fc..6d583df 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -31,6 +31,7 @@
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.dock.DockManager;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -41,7 +42,6 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
 import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationData;
@@ -153,15 +153,6 @@
         return new VolumeDialogComponent(systemUi, context);
     }
 
-    /**
-     * Provides status bar state controller implementation
-     */
-    @Singleton
-    @Provides
-    public StatusBarStateController provideStatusBarStateController(Context context) {
-        return new StatusBarStateControllerImpl();
-    }
-
     @Singleton
     @Provides
     public NotificationData.KeyguardEnvironment provideKeyguardEnvironment(Context context) {
@@ -228,6 +219,16 @@
         return SysUiServiceProvider.getComponent(context, StatusBar.class);
     }
 
+    /**
+     * Provides DockManager.
+     */
+    @Singleton
+    @Provides
+    @Nullable
+    public DockManager providesDockManager(Context context) {
+        return SysUiServiceProvider.getComponent(context, DockManager.class);
+    }
+
     @Module
     protected static class ContextHolder {
         private Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 038f491..83398cf 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -122,23 +122,9 @@
                     }
 
                     @Override
-                    public void onTranscriptionUpdate(String transcription) {
+                    public void onSetUiHints(Bundle hints) {
                         if (VERBOSE) {
-                            Log.v(TAG, "Transcription Updated: \"" + transcription + "\"");
-                        }
-                    }
-
-                    @Override
-                    public void onTranscriptionComplete(boolean immediate) {
-                        if (VERBOSE) {
-                            Log.v(TAG, "Transcription complete (immediate=" + immediate + ")");
-                        }
-                    }
-
-                    @Override
-                    public void onVoiceStateChange(int state) {
-                        if (VERBOSE) {
-                            Log.v(TAG, "Voice state is now " + state);
+                            Log.v(TAG, "UI hints received");
                         }
                     }
                 });
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
new file mode 100644
index 0000000..4778434
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -0,0 +1,55 @@
+/*
+ * 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.bubbles;
+
+
+import android.view.LayoutInflater;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * Encapsulates the data and UI elements of a bubble.
+ */
+class Bubble {
+
+    public BubbleView iconView;
+    public BubbleExpandedView expandedView;
+    public String key;
+    public NotificationEntry entry;
+
+    Bubble(NotificationEntry e, LayoutInflater inflater, BubbleStackView stackView,
+            BubbleExpandedView.OnBubbleBlockedListener listener) {
+        entry = e;
+        key = entry.key;
+
+        iconView = (BubbleView) inflater.inflate(
+                R.layout.bubble_view, stackView, false /* attachToRoot */);
+        iconView.setNotif(entry);
+
+        expandedView = (BubbleExpandedView) inflater.inflate(
+                R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
+        expandedView.setEntry(entry, stackView);
+
+        expandedView.setOnBlockedListener(listener);
+    }
+
+    public void setEntry(NotificationEntry entry) {
+        key = entry.key;
+        iconView.update(entry);
+        // TODO: should also update the expanded view here (e.g. height change)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 41bc1b2..e62c77d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,10 +16,6 @@
 
 package com.android.systemui.bubbles;
 
-import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
-import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
-import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
-import static android.util.StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -33,21 +29,14 @@
 import android.app.IActivityTaskManager;
 import android.app.INotificationManager;
 import android.app.Notification;
-import android.app.PendingIntent;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
-import android.util.Log;
-import android.util.StatsLog;
 import android.view.Display;
-import android.view.LayoutInflater;
 import android.view.ViewGroup;
-import android.view.WindowManager;
 import android.widget.FrameLayout;
 
 import androidx.annotation.MainThread;
@@ -63,13 +52,10 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -105,11 +91,9 @@
     private final BubbleTaskStackListener mTaskStackListener;
     private BubbleStateChangeListener mStateChangeListener;
     private BubbleExpandListener mExpandListener;
-    private LayoutInflater mInflater;
 
-    private final Map<String, BubbleView> mBubbles = new HashMap<>();
+    private BubbleData mBubbleData;
     private BubbleStackView mStackView;
-    private final Point mDisplaySize;
 
     // Bubbles get added to the status bar view
     private final StatusBarWindowController mStatusBarWindowController;
@@ -166,12 +150,9 @@
     }
 
     @Inject
-    public BubbleController(Context context, StatusBarWindowController statusBarWindowController) {
+    public BubbleController(Context context, StatusBarWindowController statusBarWindowController,
+            BubbleData data) {
         mContext = context;
-        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        mDisplaySize = new Point();
-        wm.getDefaultDisplay().getSize(mDisplaySize);
-        mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
         mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
         mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
@@ -190,6 +171,8 @@
         mActivityTaskManager = ActivityTaskManager.getService();
         mTaskStackListener = new BubbleTaskStackListener();
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+
+        mBubbleData = data;
     }
 
     /**
@@ -219,8 +202,11 @@
      * screen (e.g. if on AOD).
      */
     public boolean hasBubbles() {
-        for (BubbleView bv : mBubbles.values()) {
-            if (!bv.getEntry().isBubbleDismissed()) {
+        if (mStackView == null) {
+            return false;
+        }
+        for (Bubble bubble : mBubbleData.getBubbles()) {
+            if (!bubble.entry.isBubbleDismissed()) {
                 return true;
             }
         }
@@ -250,10 +236,6 @@
         if (mStackView == null) {
             return;
         }
-        Set<String> keys = mBubbles.keySet();
-        for (String key: keys) {
-            mBubbles.get(key).getEntry().setBubbleDismissed(true);
-        }
         mStackView.stackDismissed();
 
         updateVisibility();
@@ -267,13 +249,12 @@
      * @param updatePosition whether this update should promote the bubble to the top of the stack.
      */
     public void updateBubble(NotificationEntry notif, boolean updatePosition) {
-        if (mBubbles.containsKey(notif.key)) {
+        if (mStackView != null && mBubbleData.getBubble(notif.key) != null) {
             // It's an update
-            BubbleView bubble = mBubbles.get(notif.key);
-            mStackView.updateBubble(bubble, notif, updatePosition);
+            mStackView.updateBubble(notif, updatePosition);
         } else {
             if (mStackView == null) {
-                mStackView = new BubbleStackView(mContext);
+                mStackView = new BubbleStackView(mContext, mBubbleData);
                 ViewGroup sbv = mStatusBarWindowController.getStatusBarView();
                 // XXX: Bug when you expand the shade on top of expanded bubble, there is no scrim
                 // between bubble and the shade
@@ -286,15 +267,7 @@
                 mStackView.setOnBlockedListener(this);
             }
             // It's new
-            BubbleView bubble = (BubbleView) mInflater.inflate(
-                    R.layout.bubble_view, mStackView, false /* attachToRoot */);
-            bubble.setNotif(notif);
-            PendingIntent bubbleIntent = getValidBubbleIntent(notif);
-            if (bubbleIntent != null) {
-                bubble.setBubbleIntent(bubbleIntent);
-            }
-            mBubbles.put(bubble.getKey(), bubble);
-            mStackView.addBubble(bubble);
+            mStackView.addBubble(notif);
         }
         updateVisibility();
     }
@@ -306,25 +279,18 @@
      */
     @MainThread
     void removeBubble(String key) {
-        BubbleView bv = mBubbles.remove(key);
-        if (mStackView != null && bv != null) {
-            mStackView.removeBubble(bv);
-            bv.destroyActivityView(mStackView);
+        if (mStackView != null) {
+            mStackView.removeBubble(key);
         }
-
-        NotificationEntry entry = bv != null ? bv.getEntry() : null;
-        if (entry != null) {
-            entry.setBubbleDismissed(true);
-            mNotificationEntryManager.updateNotifications();
-        }
+        mNotificationEntryManager.updateNotifications();
         updateVisibility();
     }
 
     @Override
     public void onBubbleBlocked(NotificationEntry entry) {
-        Object[] bubbles = mBubbles.values().toArray();
+        Object[] bubbles = mBubbleData.getBubbles().toArray();
         for (int i = 0; i < bubbles.length; i++) {
-            NotificationEntry e = ((BubbleView) bubbles[i]).getEntry();
+            NotificationEntry e = ((Bubble) bubbles[i]).entry;
             boolean samePackage = entry.notification.getPackageName().equals(
                     e.notification.getPackageName());
             if (samePackage) {
@@ -349,8 +315,7 @@
         }
 
         @Override
-        public void onEntryInflated(NotificationEntry entry,
-                @NotificationInflater.InflationFlag int inflatedFlags) {
+        public void onEntryInflated(NotificationEntry entry, @InflationFlag int inflatedFlags) {
             if (!areBubblesEnabled(mContext)) {
                 return;
             }
@@ -368,10 +333,8 @@
                     && alertAgain(entry, entry.notification.getNotification())) {
                 entry.setShowInShadeWhenBubble(true);
                 entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed
-                if (mBubbles.containsKey(entry.key)) {
-                    mBubbles.get(entry.key).updateDotVisibility();
-                }
                 updateBubble(entry, true /* updatePosition */);
+                mStackView.updateDotVisibility(entry.key);
             }
         }
 
@@ -383,8 +346,8 @@
                 return;
             }
             entry.setShowInShadeWhenBubble(false);
-            if (mBubbles.containsKey(entry.key)) {
-                mBubbles.get(entry.key).updateDotVisibility();
+            if (mStackView != null) {
+                mStackView.updateDotVisibility(entry.key);
             }
             if (!removedByUser) {
                 // This was a cancel so we should remove the bubble
@@ -441,65 +404,6 @@
         return mStackView;
     }
 
-    @Nullable
-    private PendingIntent getValidBubbleIntent(NotificationEntry notif) {
-        Notification notification = notif.notification.getNotification();
-        String packageName = notif.notification.getPackageName();
-        Notification.BubbleMetadata data = notif.getBubbleMetadata();
-        if (data != null && canLaunchInActivityView(data.getIntent(),
-                true /* enable logging for bubbles */, packageName)) {
-            return data.getIntent();
-        }
-        if (shouldUseContentIntent(mContext)
-                && canLaunchInActivityView(notification.contentIntent,
-                false /* disable logging for notifications */, packageName)) {
-            Log.d(TAG, "[addBubble " + notif.key
-                    + "]: No appOverlayIntent, using contentIntent.");
-            return notification.contentIntent;
-        }
-        Log.d(TAG, "[addBubble " + notif.key + "]: No supported intent for ActivityView.");
-        return null;
-    }
-
-    /**
-     * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
-     *
-     * @param intent the pending intent of the bubble.
-     * @param enableLogging whether bubble developer error should be logged.
-     * @param packageName the notification package name for this bubble.
-     * @return
-     */
-    private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging,
-                                            String packageName) {
-        if (intent == null) {
-            return false;
-        }
-        ActivityInfo info =
-                intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
-        if (info == null) {
-            if (enableLogging) {
-                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
-                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
-            }
-            return false;
-        }
-        if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
-            if (enableLogging) {
-                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
-                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
-            }
-            return false;
-        }
-        if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
-            if (enableLogging) {
-                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
-                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
-            }
-            return false;
-        }
-        return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
-    }
-
     /**
      * Whether the notification has been developer configured to bubble and is allowed by the user.
      */
@@ -620,7 +524,7 @@
                 ENABLE_AUTO_BUBBLE_ALL, 0) != 0;
     }
 
-    private static boolean shouldUseContentIntent(Context context) {
+    static boolean shouldUseContentIntent(Context context) {
         return Settings.Secure.getInt(context.getContentResolver(),
                 ENABLE_BUBBLE_CONTENT_INTENT, 0) != 0;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
new file mode 100644
index 0000000..5002f5c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -0,0 +1,70 @@
+/*
+ * 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.bubbles;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.Collection;
+import java.util.HashMap;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Keeps track of active bubbles.
+ */
+@Singleton
+class BubbleData {
+
+    private HashMap<String, Bubble> mBubbles = new HashMap<>();
+
+    @Inject
+    BubbleData() {}
+
+    /**
+     * The set of bubbles.
+     */
+    public Collection<Bubble> getBubbles() {
+        return mBubbles.values();
+    }
+
+    @Nullable
+    public Bubble getBubble(String key) {
+        return mBubbles.get(key);
+    }
+
+    public void addBubble(Bubble b) {
+        mBubbles.put(b.key, b);
+    }
+
+    @Nullable
+    public Bubble removeBubble(String key) {
+        return mBubbles.remove(key);
+    }
+
+    public void updateBubble(String key, NotificationEntry newEntry) {
+        Bubble oldBubble = mBubbles.get(key);
+        if (oldBubble != null) {
+            oldBubble.setEntry(newEntry);
+        }
+    }
+
+    public void clear() {
+        mBubbles.clear();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 976a766..492eadd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,19 +16,28 @@
 
 package com.android.systemui.bubbles;
 
+import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
+import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
+import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
+import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
+
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
 import android.annotation.Nullable;
+import android.app.ActivityView;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
+import android.graphics.Insets;
+import android.graphics.Point;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.os.RemoteException;
@@ -39,16 +48,21 @@
 import android.util.Log;
 import android.util.StatsLog;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.recents.TriangleShape;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 
 /**
  * Container for the expanded bubble view, handles rendering the caret and header of the view.
@@ -68,8 +82,15 @@
     // Permission view
     private View mPermissionView;
 
-    // The view that is being displayed for the expanded state
-    private View mExpandedView;
+    // Views for expanded state
+    private ExpandableNotificationRow mNotifRow;
+    private ActivityView mActivityView;
+
+    private boolean mActivityViewReady = false;
+    private PendingIntent mBubbleIntent;
+
+    private int mBubbleHeight;
+    private int mDefaultHeight;
 
     private NotificationEntry mEntry;
     private PackageManager mPm;
@@ -77,11 +98,40 @@
     private Drawable mAppIcon;
 
     private INotificationManager mNotificationManagerService;
+    private BubbleController mBubbleController = Dependency.get(BubbleController.class);
 
-    // Need reference to let it know to collapse when new task is launched
     private BubbleStackView mStackView;
 
-    private OnBubbleBlockedListener mOnBubbleBlockedListener;
+    private BubbleExpandedView.OnBubbleBlockedListener mOnBubbleBlockedListener;
+
+    private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
+        @Override
+        public void onActivityViewReady(ActivityView view) {
+            if (!mActivityViewReady) {
+                mActivityViewReady = true;
+                mActivityView.startActivity(mBubbleIntent);
+            }
+        }
+
+        @Override
+        public void onActivityViewDestroyed(ActivityView view) {
+            mActivityViewReady = false;
+        }
+
+        /**
+         * This is only called for tasks on this ActivityView, which is also set to
+         * single-task mode -- meaning never more than one task on this display. If a task
+         * is being removed, it's the top Activity finishing and this bubble should
+         * be removed or collapsed.
+         */
+        @Override
+        public void onTaskRemovalStarted(int taskId) {
+            if (mEntry != null) {
+                // Must post because this is called from a binder thread.
+                post(() -> mBubbleController.removeBubble(mEntry.key));
+            }
+        }
+    };
 
     public BubbleExpandedView(Context context) {
         this(context, null);
@@ -99,6 +149,8 @@
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         mPm = context.getPackageManager();
+        mDefaultHeight = getResources().getDimensionPixelSize(
+                R.dimen.bubble_expanded_default_height);
         try {
             mNotificationManagerService = INotificationManager.Stub.asInterface(
                     ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE));
@@ -152,6 +204,28 @@
         mPermissionView = findViewById(R.id.permission_layout);
         findViewById(R.id.no_bubbles_button).setOnClickListener(this);
         findViewById(R.id.yes_bubbles_button).setOnClickListener(this);
+
+        mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
+                true /* singleTaskInstance */);
+        addView(mActivityView);
+
+        mActivityView.setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
+            ActivityView activityView = (ActivityView) view;
+            // Here we assume that the position of the ActivityView on the screen
+            // remains regardless of IME status. When we move ActivityView, the
+            // forwardedInsets should be computed not against the current location
+            // and size, but against the post-moved location and size.
+            Point displaySize = new Point();
+            view.getContext().getDisplay().getSize(displaySize);
+            int[] windowLocation = view.getLocationOnScreen();
+            final int windowBottom = windowLocation[1] + view.getHeight();
+            final int keyboardHeight = insets.getSystemWindowInsetBottom()
+                    - insets.getStableInsetBottom();
+            final int insetsBottom = Math.max(0,
+                    windowBottom + keyboardHeight - displaySize.y);
+            activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
+            return view.onApplyWindowInsets(insets);
+        });
     }
 
     /**
@@ -189,6 +263,14 @@
         }
         updateHeaderView();
         updatePermissionView();
+        updateExpandedView();
+    }
+
+    /**
+     * Lets activity view know it should be shown / populated.
+     */
+    public void populateActivityView() {
+        mActivityView.setCallback(mStateCallback);
     }
 
     private void updateHeaderView() {
@@ -225,6 +307,34 @@
         }
     }
 
+    private void updateExpandedView() {
+        mBubbleIntent = getBubbleIntent(mEntry);
+        if (mBubbleIntent != null) {
+            if (mNotifRow != null) {
+                // Clear out the row if we had it previously
+                removeView(mNotifRow);
+                mNotifRow = null;
+            }
+            Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
+            mBubbleHeight = data != null && data.getDesiredHeight() > 0
+                    ? data.getDesiredHeight()
+                    : mDefaultHeight;
+            // XXX: enforce max / min height
+            LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
+            lp.height = mBubbleHeight;
+            mActivityView.setLayoutParams(lp);
+            mActivityView.setVisibility(VISIBLE);
+        } else {
+            // Hide activity view if we had it previously
+            mActivityView.setVisibility(GONE);
+
+            // Use notification view
+            mNotifRow = mEntry.getRow();
+            addView(mNotifRow);
+        }
+        updateView();
+    }
+
     @Override
     public void onClick(View view) {
         if (mEntry == null) {
@@ -279,36 +389,87 @@
     }
 
     /**
+     * Update appearance of the expanded view being displayed.
+     */
+    public void updateView() {
+        if (usingActivityView()
+                && mActivityView.getVisibility() == VISIBLE
+                && mActivityView.isAttachedToWindow()) {
+            mActivityView.onLocationChanged();
+        } else if (mNotifRow != null) {
+            applyRowState(mNotifRow);
+        }
+    }
+
+    /**
      * Set the x position that the tip of the triangle should point to.
      */
-    public void setPointerPosition(int x) {
+    public void setPointerPosition(float x) {
         // Adjust for the pointer size
-        x -= (mPointerView.getWidth() / 2);
+        x -= (mPointerView.getWidth() / 2f);
         mPointerView.setTranslationX(x);
     }
 
     /**
-     * Set the view to display for the expanded state. Passing null will clear the view.
+     * Removes and releases an ActivityView if one was previously created for this bubble.
      */
-    public void setExpandedView(View view) {
-        if (mExpandedView == view) {
+    public void destroyActivityView(ViewGroup tmpParent) {
+        if (mActivityView == null) {
             return;
         }
-        if (mExpandedView != null) {
-            removeView(mExpandedView);
+        if (!mActivityViewReady) {
+            // release not needed, never initialized?
+            mActivityView = null;
+            return;
         }
-        mExpandedView = view;
-        if (mExpandedView != null) {
-            addView(mExpandedView);
+        // HACK: release() will crash if the view is not attached.
+        if (!isAttachedToWindow()) {
+            mActivityView.setVisibility(View.GONE);
+            tmpParent.addView(this);
         }
+
+        mActivityView.release();
+        ((ViewGroup) getParent()).removeView(this);
+        mActivityView = null;
+        mActivityViewReady = false;
     }
 
-    /**
-     * @return the view containing the expanded content, can be null.
-     */
-    @Nullable
-    public View getExpandedView() {
-        return mExpandedView;
+    private boolean usingActivityView() {
+        return mBubbleIntent != null;
+    }
+
+    private void applyRowState(ExpandableNotificationRow view) {
+        view.reset();
+        view.setHeadsUp(false);
+        view.resetTranslation();
+        view.setOnKeyguard(false);
+        view.setOnAmbient(false);
+        view.setClipBottomAmount(0);
+        view.setClipTopAmount(0);
+        view.setContentTransformationAmount(0, false);
+        view.setIconsVisible(true);
+
+        // TODO - Need to reset this (and others) when view goes back in shade, leave for now
+        // view.setTopRoundness(1, false);
+        // view.setBottomRoundness(1, false);
+
+        ExpandableViewState viewState = view.getViewState();
+        viewState = viewState == null ? new ExpandableViewState() : viewState;
+        viewState.height = view.getIntrinsicHeight();
+        viewState.gone = false;
+        viewState.hidden = false;
+        viewState.dimmed = false;
+        viewState.dark = false;
+        viewState.alpha = 1f;
+        viewState.notGoneIndex = -1;
+        viewState.xTranslation = 0;
+        viewState.yTranslation = 0;
+        viewState.zTranslation = 0;
+        viewState.scaleX = 1;
+        viewState.scaleY = 1;
+        viewState.inShelf = true;
+        viewState.headsUpIsVisible = false;
+        viewState.applyToView(view);
     }
 
     private Intent getSettingsIntent(String packageName, final int appUid) {
@@ -320,6 +481,61 @@
         return intent;
     }
 
+    @Nullable
+    private PendingIntent getBubbleIntent(NotificationEntry entry) {
+        Notification notif = entry.notification.getNotification();
+        String packageName = entry.notification.getPackageName();
+        Notification.BubbleMetadata data = notif.getBubbleMetadata();
+        if (data != null && canLaunchInActivityView(data.getIntent(), true /* enableLogging */,
+                packageName)) {
+            return data.getIntent();
+        } else if (BubbleController.shouldUseContentIntent(mContext)
+                && canLaunchInActivityView(notif.contentIntent, false /* enableLogging */,
+                packageName)) {
+            return notif.contentIntent;
+        }
+        return null;
+    }
+
+    /**
+     * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
+     *
+     * @param intent the pending intent of the bubble.
+     * @param enableLogging whether bubble developer error should be logged.
+     * @param packageName the notification package name for this bubble.
+     * @return
+     */
+    private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging,
+            String packageName) {
+        if (intent == null) {
+            return false;
+        }
+        ActivityInfo info =
+                intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
+        if (info == null) {
+            if (enableLogging) {
+                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
+            }
+            return false;
+        }
+        if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
+            if (enableLogging) {
+                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
+            }
+            return false;
+        }
+        if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
+            if (enableLogging) {
+                StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
+                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
+            }
+            return false;
+        }
+        return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
+    }
+
     /**
      * Listener that is notified when a bubble is blocked.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 2b344f6..ed9b38b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -19,7 +19,6 @@
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
-import android.app.ActivityView;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Outline;
@@ -33,13 +32,11 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
 
 import androidx.annotation.MainThread;
 import androidx.annotation.Nullable;
@@ -54,8 +51,6 @@
 import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
 import com.android.systemui.bubbles.animation.StackAnimationController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
@@ -63,7 +58,7 @@
 /**
  * Renders bubbles in a stack and handles animating expanded and collapsed states.
  */
-public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.FloatingView {
+public class BubbleStackView extends FrameLayout {
     private static final String TAG = "BubbleStackView";
 
     /**
@@ -90,27 +85,33 @@
 
     private final SpringAnimation mExpandedViewXAnim;
     private final SpringAnimation mExpandedViewYAnim;
+    private final BubbleData mBubbleData;
 
     private PhysicsAnimationLayout mBubbleContainer;
     private StackAnimationController mStackAnimationController;
     private ExpandedAnimationController mExpandedAnimationController;
 
-    private BubbleExpandedView mExpandedViewContainer;
+    private FrameLayout mExpandedViewContainer;
+
 
     private int mBubbleSize;
     private int mBubblePadding;
     private int mExpandedAnimateXDistance;
     private int mExpandedAnimateYDistance;
+    private int mStatusBarHeight;
 
+    private Bubble mExpandedBubble;
     private boolean mIsExpanded;
-    private int mExpandedBubbleHeight;
+
     private BubbleTouchHandler mTouchHandler;
-    private BubbleView mExpandedBubble;
     private BubbleController.BubbleExpandListener mExpandListener;
+    private BubbleExpandedView.OnBubbleBlockedListener mBlockedListener;
 
     private boolean mViewUpdatedRequested = false;
     private boolean mIsAnimating = false;
 
+    private LayoutInflater mInflater;
+
     // Used for determining view / touch intersection
     int[] mTempLoc = new int[2];
     RectF mTempRect = new RectF();
@@ -140,11 +141,14 @@
         }
     };
 
-    public BubbleStackView(Context context) {
+    public BubbleStackView(Context context, BubbleData data) {
         super(context);
 
-        mTouchHandler = new BubbleTouchHandler(context);
+        mBubbleData = data;
+        mInflater = LayoutInflater.from(context);
+        mTouchHandler = new BubbleTouchHandler(context, this);
         setOnTouchListener(mTouchHandler);
+        mInflater = LayoutInflater.from(context);
 
         Resources res = getResources();
         mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
@@ -153,8 +157,9 @@
                 res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_x_distance);
         mExpandedAnimateYDistance =
                 res.getDimensionPixelSize(R.dimen.bubble_expanded_animate_y_distance);
+        mStatusBarHeight =
+                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
 
-        mExpandedBubbleHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
         mDisplaySize = new Point();
         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         wm.getDefaultDisplay().getSize(mDisplaySize);
@@ -174,9 +179,7 @@
         mBubbleContainer.setClipChildren(false);
         addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
 
-        mExpandedViewContainer = (BubbleExpandedView)
-                LayoutInflater.from(context).inflate(R.layout.bubble_expanded_view,
-                        this /* parent */, false /* attachToRoot */);
+        mExpandedViewContainer = new FrameLayout(context);
         mExpandedViewContainer.setElevation(elevation);
         mExpandedViewContainer.setPadding(padding, padding, padding, padding);
         mExpandedViewContainer.setClipChildren(false);
@@ -197,6 +200,8 @@
                         .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
 
         setClipChildren(false);
+
+        mBubbleContainer.bringToFront();
     }
 
     @Override
@@ -218,6 +223,17 @@
     }
 
     /**
+     * Updates the visibility of the 'dot' indicating an update on the bubble.
+     * @param key the {@link NotificationEntry#key} associated with the bubble.
+     */
+    public void updateDotVisibility(String key) {
+        Bubble b = mBubbleData.getBubble(key);
+        if (b != null) {
+            b.iconView.updateDotVisibility();
+        }
+    }
+
+    /**
      * Sets the listener to notify when the bubble stack is expanded.
      */
     public void setExpandListener(BubbleController.BubbleExpandListener listener) {
@@ -228,7 +244,10 @@
      * Sets the listener to notify when a bubble is blocked.
      */
     public void setOnBlockedListener(BubbleExpandedView.OnBubbleBlockedListener listener) {
-        mExpandedViewContainer.setOnBlockedListener(listener);
+        mBlockedListener = listener;
+        for (Bubble b : mBubbleData.getBubbles()) {
+            b.expandedView.setOnBlockedListener(mBlockedListener);
+        }
     }
 
     /**
@@ -241,19 +260,29 @@
     /**
      * The {@link BubbleView} that is expanded, null if one does not exist.
      */
-    public BubbleView getExpandedBubble() {
+    BubbleView getExpandedBubbleView() {
+        return mExpandedBubble != null ? mExpandedBubble.iconView : null;
+    }
+
+    /**
+     * The {@link Bubble} that is expanded, null if one does not exist.
+     */
+    Bubble getExpandedBubble() {
         return mExpandedBubble;
     }
 
     /**
      * Sets the bubble that should be expanded and expands if needed.
+     *
+     * @param key the {@link NotificationEntry#key} associated with the bubble to expand.
      */
-    public void setExpandedBubble(BubbleView bubbleToExpand) {
+    void setExpandedBubble(String key) {
+        Bubble bubbleToExpand = mBubbleData.getBubble(key);
         if (mIsExpanded && !bubbleToExpand.equals(mExpandedBubble)) {
             // Previously expanded, notify that this bubble is no longer expanded
-            notifyExpansionChanged(mExpandedBubble, false /* expanded */);
+            notifyExpansionChanged(mExpandedBubble.entry, false /* expanded */);
         }
-        BubbleView prevBubble = mExpandedBubble;
+        Bubble prevBubble = mExpandedBubble;
         mExpandedBubble = bubbleToExpand;
         if (!mIsExpanded) {
             // If we weren't previously expanded we should animate open.
@@ -268,8 +297,8 @@
             logBubbleEvent(prevBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
             logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
         }
-        mExpandedBubble.getEntry().setShowInShadeWhenBubble(false);
-        notifyExpansionChanged(mExpandedBubble, true /* expanded */);
+        mExpandedBubble.entry.setShowInShadeWhenBubble(false);
+        notifyExpansionChanged(mExpandedBubble.entry, true /* expanded */);
     }
 
     /**
@@ -280,7 +309,7 @@
         for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
             BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
             if (entry.equals(bv.getEntry())) {
-                setExpandedBubble(bv);
+                setExpandedBubble(entry.key);
             }
         }
     }
@@ -288,48 +317,67 @@
     /**
      * Adds a bubble to the top of the stack.
      *
-     * @param bubbleView the view to add to the stack.
+     * @param entry the notification to add to the stack of bubbles.
      */
-    public void addBubble(BubbleView bubbleView) {
-        mBubbleContainer.addView(bubbleView, 0,
+    public void addBubble(NotificationEntry entry) {
+        Bubble b = new Bubble(entry, mInflater, this /* stackView */, mBlockedListener);
+        mBubbleData.addBubble(b);
+
+        mBubbleContainer.addView(b.iconView, 0,
                 new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-        ViewClippingUtil.setClippingDeactivated(bubbleView, true, mClippingParameters);
+        ViewClippingUtil.setClippingDeactivated(b.iconView, true, mClippingParameters);
+
         requestUpdate();
-        logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
+        logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
     }
 
     /**
      * Remove a bubble from the stack.
      */
-    public void removeBubble(BubbleView bubbleView) {
-        int removedIndex = mBubbleContainer.indexOfChild(bubbleView);
-        mBubbleContainer.removeView(bubbleView);
+    public void removeBubble(String key) {
+        Bubble b = mBubbleData.removeBubble(key);
+        if (b == null) {
+            return;
+        }
+        b.entry.setBubbleDismissed(true);
+
+        // Remove it from the views
+        int removedIndex = mBubbleContainer.indexOfChild(b.iconView);
+        b.expandedView.destroyActivityView(this /* tmpParent */);
+        mBubbleContainer.removeView(b.iconView);
+
         int bubbleCount = mBubbleContainer.getChildCount();
         if (bubbleCount == 0) {
             // If no bubbles remain, collapse the entire stack.
             collapseStack();
             return;
-        } else if (bubbleView.equals(mExpandedBubble)) {
+        } else if (b.equals(mExpandedBubble)) {
             // Was the current bubble just removed?
             // If we have other bubbles and are expanded go to the next one or previous
             // if the bubble removed was last
             int nextIndex = bubbleCount > removedIndex ? removedIndex : bubbleCount - 1;
             BubbleView expandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex);
             if (mIsExpanded) {
-                setExpandedBubble(expandedBubble);
+                setExpandedBubble(expandedBubble.getKey());
             } else {
                 mExpandedBubble = null;
             }
         }
-        logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+        logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
     }
 
     /**
      * Dismiss the stack of bubbles.
      */
     public void stackDismissed() {
+        for (Bubble bubble : mBubbleData.getBubbles()) {
+            bubble.entry.setBubbleDismissed(true);
+            bubble.expandedView.destroyActivityView(this /* tmpParent */);
+        }
+        mBubbleData.clear();
         collapseStack();
         mBubbleContainer.removeAllViews();
+        mExpandedViewContainer.removeAllViews();
         logBubbleEvent(null /* no bubble associated with bubble stack dismiss */,
                 StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED);
     }
@@ -337,25 +385,26 @@
     /**
      * Updates a bubble in the stack.
      *
-     * @param bubbleView the view to update in the stack.
-     * @param entry the entry to update it with.
+     * @param entry the entry to update in the stack.
      * @param updatePosition whether this bubble should be moved to top of the stack.
      */
-    public void updateBubble(BubbleView bubbleView, NotificationEntry entry,
-            boolean updatePosition) {
-        bubbleView.update(entry);
+    public void updateBubble(NotificationEntry entry, boolean updatePosition) {
+        Bubble b = mBubbleData.getBubble(entry.key);
+        b.iconView.update(entry);
+        // TODO: should also update the expanded view here (e.g. height change)
+
         if (updatePosition && !mIsExpanded) {
             // If alerting it gets promoted to top of the stack.
-            if (mBubbleContainer.indexOfChild(bubbleView) != 0) {
-                mBubbleContainer.moveViewTo(bubbleView, 0);
+            if (mBubbleContainer.indexOfChild(b.iconView) != 0) {
+                mBubbleContainer.moveViewTo(b.iconView, 0);
             }
             requestUpdate();
         }
-        if (mIsExpanded && bubbleView.equals(mExpandedBubble)) {
+        if (mIsExpanded && entry.equals(mExpandedBubble.entry)) {
             entry.setShowInShadeWhenBubble(false);
             requestUpdate();
         }
-        logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
+        logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
     }
 
     /**
@@ -393,7 +442,7 @@
         if (mIsExpanded) {
             // TODO: Save opened bubble & move it to top of stack
             animateExpansion(false /* shouldExpand */);
-            notifyExpansionChanged(mExpandedBubble, mIsExpanded);
+            notifyExpansionChanged(mExpandedBubble.entry, mIsExpanded);
             logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
         }
     }
@@ -412,8 +461,8 @@
     @MainThread
     public void expandStack() {
         if (!mIsExpanded) {
-            mExpandedBubble = getTopBubble();
-            setExpandedBubble(mExpandedBubble);
+            String expandedBubbleKey = getBubbleAt(0).getKey();
+            setExpandedBubble(expandedBubbleKey);
             logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
         }
     }
@@ -438,9 +487,10 @@
             if (shouldExpand) {
                 mBubbleContainer.setController(mExpandedAnimationController);
                 mExpandedAnimationController.expandFromStack(
-                                mStackAnimationController.getStackPosition(), () -> {
-                                updatePointerPosition();
-                                updateAfter.run();
+                        mStackAnimationController.getStackPosition(),
+                        () -> {
+                            updatePointerPosition();
+                            updateAfter.run();
                         });
             } else {
                 mBubbleContainer.cancelAllAnimations();
@@ -459,7 +509,8 @@
             final float yStart = Math.min(
                     mStackAnimationController.getStackPosition().y,
                     mExpandedAnimateYDistance);
-            final float yDest = getStatusBarHeight() + mExpandedBubble.getHeight() + mBubblePadding;
+            final float yDest = getStatusBarHeight()
+                    + mExpandedBubble.iconView.getHeight() + mBubblePadding;
 
             if (shouldExpand) {
                 mExpandedViewContainer.setTranslationX(xStart);
@@ -484,17 +535,12 @@
                 + mBubbleContainer.getPaddingStart();
     }
 
-    private void notifyExpansionChanged(BubbleView bubbleView, boolean expanded) {
+    private void notifyExpansionChanged(NotificationEntry entry, boolean expanded) {
         if (mExpandListener != null) {
-            NotificationEntry entry = bubbleView != null ? bubbleView.getEntry() : null;
             mExpandListener.onBubbleExpandChanged(expanded, entry != null ? entry.key : null);
         }
     }
 
-    private BubbleView getTopBubble() {
-        return getBubbleAt(0);
-    }
-
     /** Return the BubbleView at the given index from the bubble container. */
     public BubbleView getBubbleAt(int i) {
         return mBubbleContainer.getChildCount() > i
@@ -502,55 +548,49 @@
                 : null;
     }
 
-    @Override
-    public void setPosition(float x, float y) {
-        mStackAnimationController.moveFirstBubbleWithStackFollowing(x, y);
-    }
-
-    @Override
-    public void setPositionX(float x) {
-        // Unsupported, use setPosition(x, y).
-    }
-
-    @Override
-    public void setPositionY(float y) {
-        // Unsupported, use setPosition(x, y).
-    }
-
-    @Override
-    public PointF getPosition() {
+    public PointF getStackPosition() {
         return mStackAnimationController.getStackPosition();
     }
 
     /** Called when a drag operation on an individual bubble has started. */
-    public void onBubbleDragStart(BubbleView bubble) {
-        // TODO: Save position and snap back if not dismissed.
+    public void onBubbleDragStart(View bubble) {
+        mExpandedAnimationController.prepareForBubbleDrag(bubble);
     }
 
     /** Called with the coordinates to which an individual bubble has been dragged. */
-    public void onBubbleDragged(BubbleView bubble, float x, float y) {
-        bubble.setTranslationX(x);
-        bubble.setTranslationY(y);
+    public void onBubbleDragged(View bubble, float x, float y) {
+        if (!mIsExpanded || mIsAnimating) {
+            return;
+        }
+
+        mExpandedAnimationController.dragBubbleOut(bubble, x, y);
     }
 
     /** Called when a drag operation on an individual bubble has finished. */
-    public void onBubbleDragFinish(BubbleView bubble, float x, float y, float velX, float velY) {
-        // TODO: Add fling to bottom to dismiss.
+    public void onBubbleDragFinish(
+            View bubble, float x, float y, float velX, float velY, boolean dismissed) {
+        if (!mIsExpanded || mIsAnimating) {
+            return;
+        }
+
+        if (dismissed) {
+            mExpandedAnimationController.prepareForDismissalWithVelocity(bubble, velX, velY);
+        } else {
+            mExpandedAnimationController.snapBubbleBack(bubble, velX, velY);
+        }
     }
 
     void onDragStart() {
-        if (mIsExpanded) {
+        if (mIsExpanded || mIsAnimating) {
             return;
         }
 
         mStackAnimationController.cancelStackPositionAnimations();
         mBubbleContainer.setController(mStackAnimationController);
-        mIsAnimating = false;
     }
 
     void onDragged(float x, float y) {
-        // TODO: We can drag if animating - just need to reroute inflight anims to drag point.
-        if (mIsExpanded) {
+        if (mIsExpanded || mIsAnimating) {
             return;
         }
 
@@ -639,7 +679,7 @@
         if (getRootWindowInsets() != null) {
             WindowInsets insets = getRootWindowInsets();
             return Math.max(
-                    insets.getSystemWindowInsetTop(),
+                    mStatusBarHeight,
                     insets.getDisplayCutout() != null
                             ? insets.getDisplayCutout().getSafeInsetTop()
                             : 0);
@@ -665,31 +705,11 @@
     }
 
     private void updateExpandedBubble() {
-        if (mExpandedBubble == null) {
-            return;
-        }
-
-        mExpandedViewContainer.setEntry(mExpandedBubble.getEntry(), this);
-        if (mExpandedBubble.hasAppOverlayIntent()) {
-            // Bubble with activity view expanded state
-            ActivityView expandedView = mExpandedBubble.getActivityView();
-            // XXX: gets added to linear layout
-            expandedView.setLayoutParams(new LinearLayout.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT, mExpandedBubbleHeight));
-
-            mExpandedViewContainer.setExpandedView(expandedView);
-        } else {
-            // Bubble with notification view expanded state
-            ExpandableNotificationRow row = mExpandedBubble.getRowView();
-            if (row.getParent() != null) {
-                // Row might still be in the shade when we expand
-                ((ViewGroup) row.getParent()).removeView(row);
-            }
-            if (mIsExpanded) {
-                mExpandedViewContainer.setExpandedView(row);
-            } else {
-                mExpandedViewContainer.setExpandedView(null);
-            }
+        mExpandedViewContainer.removeAllViews();
+        if (mExpandedBubble != null && mIsExpanded) {
+            mExpandedViewContainer.addView(mExpandedBubble.expandedView);
+            mExpandedBubble.expandedView.populateActivityView();
+            mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
         }
     }
 
@@ -697,18 +717,10 @@
         Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
 
         mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
-        if (!mIsExpanded) {
-            mExpandedViewContainer.setExpandedView(null);
-        } else {
-            View expandedView = mExpandedViewContainer.getExpandedView();
-            if (expandedView instanceof ActivityView) {
-                if (expandedView.isAttachedToWindow()) {
-                    ((ActivityView) expandedView).onLocationChanged();
-                }
-            } else {
-                applyRowState(mExpandedBubble.getRowView());
-            }
+        if (mIsExpanded) {
+            mExpandedBubble.expandedView.updateView();
         }
+
         int bubbsCount = mBubbleContainer.getChildCount();
         for (int i = 0; i < bubbsCount; i++) {
             BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
@@ -730,46 +742,12 @@
 
     private void updatePointerPosition() {
         if (mExpandedBubble != null) {
-            float pointerPosition = mExpandedBubble.getPosition().x
-                    + (mExpandedBubble.getWidth() / 2f);
-            mExpandedViewContainer.setPointerPosition((int) pointerPosition);
+            float pointerPosition = mExpandedBubble.iconView.getTranslationX()
+                    + (mExpandedBubble.iconView.getWidth() / 2f);
+            mExpandedBubble.expandedView.setPointerPosition((int) pointerPosition);
         }
     }
 
-    private void applyRowState(ExpandableNotificationRow view) {
-        view.reset();
-        view.setHeadsUp(false);
-        view.resetTranslation();
-        view.setOnKeyguard(false);
-        view.setOnAmbient(false);
-        view.setClipBottomAmount(0);
-        view.setClipTopAmount(0);
-        view.setContentTransformationAmount(0, false);
-        view.setIconsVisible(true);
-
-        // TODO - Need to reset this (and others) when view goes back in shade, leave for now
-        // view.setTopRoundness(1, false);
-        // view.setBottomRoundness(1, false);
-
-        ExpandableViewState viewState = view.getViewState();
-        viewState = viewState == null ? new ExpandableViewState() : viewState;
-        viewState.height = view.getIntrinsicHeight();
-        viewState.gone = false;
-        viewState.hidden = false;
-        viewState.dimmed = false;
-        viewState.dark = false;
-        viewState.alpha = 1f;
-        viewState.notGoneIndex = -1;
-        viewState.xTranslation = 0;
-        viewState.yTranslation = 0;
-        viewState.zTranslation = 0;
-        viewState.scaleX = 1;
-        viewState.scaleY = 1;
-        viewState.inShelf = true;
-        viewState.headsUpIsVisible = false;
-        viewState.applyToView(view);
-    }
-
     /**
      * @return the number of bubbles in the stack view.
      */
@@ -780,19 +758,19 @@
     /**
      * Finds the bubble index within the stack.
      *
-     * @param bubbleView the view of the bubble.
+     * @param bubble the bubble to look up.
      * @return the index of the bubble view within the bubble stack. The range of the position
      * is between 0 and the bubble count minus 1.
      */
-    public int getBubbleIndex(BubbleView bubbleView) {
-        return mBubbleContainer.indexOfChild(bubbleView);
+    int getBubbleIndex(Bubble bubble) {
+        return mBubbleContainer.indexOfChild(bubble.iconView);
     }
 
     /**
      * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places.
      */
     public float getNormalizedXPosition() {
-        return new BigDecimal(getPosition().x / mDisplaySize.x)
+        return new BigDecimal(getStackPosition().x / mDisplaySize.x)
                 .setScale(4, RoundingMode.CEILING.HALF_UP)
                 .floatValue();
     }
@@ -801,7 +779,7 @@
      * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places.
      */
     public float getNormalizedYPosition() {
-        return new BigDecimal(getPosition().y / mDisplaySize.y)
+        return new BigDecimal(getStackPosition().y / mDisplaySize.y)
                 .setScale(4, RoundingMode.CEILING.HALF_UP)
                 .floatValue();
     }
@@ -813,7 +791,7 @@
      *               the user interaction is not specific to one bubble.
      * @param action the user interaction enum.
      */
-    private void logBubbleEvent(@Nullable BubbleView bubble, int action) {
+    private void logBubbleEvent(@Nullable Bubble bubble, int action) {
         if (bubble == null) {
             StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
                     null /* package name */,
@@ -825,7 +803,7 @@
                     getNormalizedXPosition(),
                     getNormalizedYPosition());
         } else {
-            StatusBarNotification notification = bubble.getEntry().notification;
+            StatusBarNotification notification = bubble.entry.notification;
             StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
                     notification.getPackageName(),
                     notification.getNotification().getChannelId(),
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 22cd2fc..165eb1d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -34,17 +34,16 @@
  * dismissing, and flings.
  */
 class BubbleTouchHandler implements View.OnTouchListener {
+    /** Velocity required to dismiss a bubble without dragging it into the dismiss target. */
+    private static final float DISMISS_MIN_VELOCITY = 4000f;
+
+    private final PointF mTouchDown = new PointF();
+    private final PointF mViewPositionOnTouchDown = new PointF();
+    private final BubbleStackView mStack;
 
     private BubbleController mController = Dependency.get(BubbleController.class);
     private PipDismissViewController mDismissViewController;
 
-    // The position of the bubble on down event
-    private float mBubbleDownPosX;
-    private float mBubbleDownPosY;
-    // The touch position on down event
-    private float mDownX = -1;
-    private float mDownY = -1;
-
     private boolean mMovedEnough;
     private int mTouchSlopSquared;
     private VelocityTracker mVelocityTracker;
@@ -58,65 +57,42 @@
         }
     };
 
-    // Bubble being dragged from the row of bubbles when the stack is expanded
-    private BubbleView mBubbleDraggingOut;
+    /** View that was initially touched, when we received the first ACTION_DOWN event. */
+    private View mTouchedView;
 
-    /**
-     * Views movable by this touch handler should implement this interface.
-     */
-    public interface FloatingView {
-
-        /**
-         * Sets the position of the view.
-         */
-        void setPosition(float x, float y);
-
-        /**
-         * Sets the x position of the view.
-         */
-        void setPositionX(float x);
-
-        /**
-         * Sets the y position of the view.
-         */
-        void setPositionY(float y);
-
-        /**
-         * @return the position of the view.
-         */
-        PointF getPosition();
-    }
-
-    public BubbleTouchHandler(Context context) {
+    BubbleTouchHandler(Context context, BubbleStackView stackView) {
         final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mTouchSlopSquared = touchSlop * touchSlop;
         mDismissViewController = new PipDismissViewController(context);
+        mStack = stackView;
     }
 
     @Override
     public boolean onTouch(View v, MotionEvent event) {
-        int action = event.getActionMasked();
+        final int action = event.getActionMasked();
 
-        BubbleStackView stack = (BubbleStackView) v;
-        View targetView = mBubbleDraggingOut != null
-                ? mBubbleDraggingOut
-                : stack.getTargetView(event);
-        boolean isFloating = targetView instanceof FloatingView;
-        if (!isFloating || targetView == null || action == MotionEvent.ACTION_OUTSIDE) {
-            stack.collapseStack();
+        // If we aren't currently in the process of touching a view, figure out what we're touching.
+        // It'll be the stack, an individual bubble, or nothing.
+        if (mTouchedView == null) {
+            mTouchedView = mStack.getTargetView(event);
+        }
+
+        // If this is an ACTION_OUTSIDE event, or the stack reported that we aren't touching
+        // anything, collapse the stack.
+        if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) {
+            mStack.collapseStack();
             cleanUpDismissTarget();
-            resetTouches();
+            mTouchedView = null;
             return false;
         }
 
-        FloatingView floatingView = (FloatingView) targetView;
-        boolean isBubbleStack = floatingView instanceof BubbleStackView;
+        final boolean isStack = mStack.equals(mTouchedView);
+        final float rawX = event.getRawX();
+        final float rawY = event.getRawY();
 
-        PointF startPos = floatingView.getPosition();
-        float rawX = event.getRawX();
-        float rawY = event.getRawY();
-        float x = mBubbleDownPosX + rawX - mDownX;
-        float y = mBubbleDownPosY + rawY - mDownY;
+        // The coordinates of the touch event, in terms of the touched view's position.
+        final float viewX = mViewPositionOnTouchDown.x + rawX - mTouchDown.x;
+        final float viewY = mViewPositionOnTouchDown.y + rawY - mTouchDown.y;
         switch (action) {
             case MotionEvent.ACTION_DOWN:
                 trackMovement(event);
@@ -124,87 +100,83 @@
                 mDismissViewController.createDismissTarget();
                 mHandler.postDelayed(mShowDismissAffordance, SHOW_TARGET_DELAY);
 
-                mBubbleDownPosX = startPos.x;
-                mBubbleDownPosY = startPos.y;
-                mDownX = rawX;
-                mDownY = rawY;
-                mMovedEnough = false;
+                mTouchDown.set(rawX, rawY);
 
-                if (isBubbleStack) {
-                    stack.onDragStart();
+                if (isStack) {
+                    mViewPositionOnTouchDown.set(mStack.getStackPosition());
+                    mStack.onDragStart();
                 } else {
-                    stack.onBubbleDragStart((BubbleView) floatingView);
+                    mViewPositionOnTouchDown.set(
+                            mTouchedView.getTranslationX(), mTouchedView.getTranslationY());
+                    mStack.onBubbleDragStart(mTouchedView);
                 }
 
                 break;
-
             case MotionEvent.ACTION_MOVE:
                 trackMovement(event);
+                final float deltaX = rawX - mTouchDown.x;
+                final float deltaY = rawY - mTouchDown.y;
 
-                if (mBubbleDownPosX == -1 || mDownX == -1) {
-                    mBubbleDownPosX = startPos.x;
-                    mBubbleDownPosY = startPos.y;
-                    mDownX = rawX;
-                    mDownY = rawY;
-                }
-                final float deltaX = rawX - mDownX;
-                final float deltaY = rawY - mDownY;
                 if ((deltaX * deltaX) + (deltaY * deltaY) > mTouchSlopSquared && !mMovedEnough) {
                     mMovedEnough = true;
                 }
 
                 if (mMovedEnough) {
-                    if (floatingView instanceof BubbleView) {
-                        mBubbleDraggingOut = ((BubbleView) floatingView);
-                        stack.onBubbleDragged(mBubbleDraggingOut, x, y);
+                    if (isStack) {
+                        mStack.onDragged(viewX, viewY);
                     } else {
-                        stack.onDragged(x, y);
+                        mStack.onBubbleDragged(mTouchedView, viewX, viewY);
                     }
                 }
+
                 // TODO - when we're in the target stick to it / animate in some way?
                 mInDismissTarget = mDismissViewController.updateTarget(
-                        isBubbleStack ? stack.getBubbleAt(0) : (View) floatingView);
+                        isStack ? mStack.getBubbleAt(0) : mTouchedView);
                 break;
 
             case MotionEvent.ACTION_CANCEL:
-                resetTouches();
+                mTouchedView = null;
                 cleanUpDismissTarget();
                 break;
 
             case MotionEvent.ACTION_UP:
                 trackMovement(event);
-                if (mInDismissTarget) {
-                    if (isBubbleStack) {
-                        mController.dismissStack();
-                    } else {
-                        mController.removeBubble(((BubbleView) floatingView).getKey());
-                    }
+                if (mInDismissTarget && isStack) {
+                    mController.dismissStack();
                 } else if (mMovedEnough) {
                     mVelocityTracker.computeCurrentVelocity(1000);
                     final float velX = mVelocityTracker.getXVelocity();
                     final float velY = mVelocityTracker.getYVelocity();
-                    if (isBubbleStack) {
-                        stack.onDragFinish(x, y, velX, velY);
+                    if (isStack) {
+                        mStack.onDragFinish(viewX, viewY, velX, velY);
                     } else {
-                        stack.onBubbleDragFinish(mBubbleDraggingOut, x, y, velX, velY);
+                        final boolean dismissed = mInDismissTarget || velY > DISMISS_MIN_VELOCITY;
+                        mStack.onBubbleDragFinish(
+                                mTouchedView, viewX, viewY, velX, velY, /* dismissed */ dismissed);
+                        if (dismissed) {
+                            mController.removeBubble(((BubbleView) mTouchedView).getKey());
+                        }
                     }
-                } else if (floatingView.equals(stack.getExpandedBubble())) {
-                    stack.collapseStack();
-                } else if (isBubbleStack) {
-                    if (stack.isExpanded()) {
-                        stack.collapseStack();
+                } else if (mTouchedView.equals(mStack.getExpandedBubbleView())) {
+                    mStack.collapseStack();
+                } else if (isStack) {
+                    if (mStack.isExpanded()) {
+                        mStack.collapseStack();
                     } else {
-                        stack.expandStack();
+                        mStack.expandStack();
                     }
                 } else {
-                    stack.setExpandedBubble((BubbleView) floatingView);
+                    mStack.setExpandedBubble(((BubbleView) mTouchedView).getKey());
                 }
+
                 cleanUpDismissTarget();
                 mVelocityTracker.recycle();
                 mVelocityTracker = null;
-                resetTouches();
+                mTouchedView = null;
+                mMovedEnough = false;
                 break;
         }
+
         return true;
     }
 
@@ -216,16 +188,6 @@
         mDismissViewController.destroyDismissTarget();
     }
 
-    /**
-     * Resets anything we care about after a gesture is complete.
-     */
-    private void resetTouches() {
-        mDownX = -1;
-        mDownY = -1;
-        mBubbleDownPosX = -1;
-        mBubbleDownPosY = -1;
-        mBubbleDraggingOut = null;
-    }
 
     private void trackMovement(MotionEvent event) {
         if (mVelocityTracker == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 74ddc8f..b409a31 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -17,30 +17,19 @@
 package com.android.systemui.bubbles;
 
 import android.annotation.Nullable;
-import android.app.ActivityView;
 import android.app.Notification;
-import android.app.PendingIntent;
 import android.content.Context;
 import android.graphics.Color;
-import android.graphics.Insets;
-import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.graphics.drawable.InsetDrawable;
 import android.graphics.drawable.LayerDrawable;
 import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.internal.graphics.ColorUtils;
-import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -49,7 +38,7 @@
 /**
  * A floating object on the screen that can post message updates.
  */
-public class BubbleView extends FrameLayout implements BubbleTouchHandler.FloatingView {
+public class BubbleView extends FrameLayout {
     private static final String TAG = "BubbleView";
 
     // Same value as Launcher3 badge code
@@ -62,11 +51,6 @@
     private int mIconInset;
 
     private NotificationEntry mEntry;
-    private PendingIntent mAppOverlayIntent;
-    private BubbleController mBubbleController;
-    private ActivityView mActivityView;
-    private boolean mActivityViewReady;
-    private boolean mActivityViewStarted;
 
     public BubbleView(Context context) {
         this(context, null);
@@ -86,7 +70,6 @@
         // XXX: can this padding just be on the view and we look it up?
         mPadding = getResources().getDimensionPixelSize(R.dimen.bubble_view_padding);
         mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset);
-        mBubbleController = Dependency.get(BubbleController.class);
     }
 
     @Override
@@ -168,7 +151,6 @@
         updateViews();
     }
 
-
     /**
      * @return the {@link ExpandableNotificationRow} view to display notification content when the
      * bubble is expanded.
@@ -234,123 +216,4 @@
         // XXX: should we pull from the drawable, app icon, notif tint?
         return ColorUtils.blendARGB(defaultTint, Color.WHITE, WHITE_SCRIM_ALPHA);
     }
-
-    /**
-     * @return a view used to display app overlay content when expanded.
-     */
-    public ActivityView getActivityView() {
-        if (mActivityView == null) {
-            mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
-                    true /* singleTaskInstance */);
-            Log.d(TAG, "[getActivityView] created: " + mActivityView);
-            mActivityView.setCallback(new ActivityView.StateCallback() {
-                @Override
-                public void onActivityViewReady(ActivityView view) {
-                    mActivityViewReady = true;
-                    mActivityView.startActivity(mAppOverlayIntent);
-                }
-
-                @Override
-                public void onActivityViewDestroyed(ActivityView view) {
-                    mActivityViewReady = false;
-                }
-
-                /**
-                 * This is only called for tasks on this ActivityView, which is also set to
-                 * single-task mode -- meaning never more than one task on this display. If a task
-                 * is being removed, it's the top Activity finishing and this bubble should
-                 * be removed or collapsed.
-                 */
-                @Override
-                public void onTaskRemovalStarted(int taskId) {
-                    if (mEntry != null) {
-                        // Must post because this is called from a binder thread.
-                        post(() -> mBubbleController.removeBubble(mEntry.key));
-                    }
-                }
-            });
-            mActivityView.setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
-                ActivityView activityView = (ActivityView) view;
-                // Here we assume that the position of the ActivityView on the screen
-                // remains regardless of IME status. When we move ActivityView, the
-                // forwardedInsets should be computed not against the current location
-                // and size, but against the post-moved location and size.
-                Point displaySize = new Point();
-                view.getContext().getDisplay().getSize(displaySize);
-                int[] windowLocation = view.getLocationOnScreen();
-                final int windowBottom = windowLocation[1] + view.getHeight();
-                final int keyboardHeight = insets.getSystemWindowInsetBottom()
-                        - insets.getStableInsetBottom();
-                final int insetsBottom = Math.max(0,
-                        windowBottom + keyboardHeight - displaySize.y);
-                activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
-                return view.onApplyWindowInsets(insets);
-            });
-
-        }
-        return mActivityView;
-    }
-
-    /**
-     * Removes and releases an ActivityView if one was previously created for this bubble.
-     */
-    public void destroyActivityView(ViewGroup tmpParent) {
-        if (mActivityView == null) {
-            return;
-        }
-        if (!mActivityViewReady) {
-            // release not needed, never initialized?
-            mActivityView = null;
-            return;
-        }
-        // HACK: release() will crash if the view is not attached.
-        if (!mActivityView.isAttachedToWindow()) {
-            mActivityView.setVisibility(View.GONE);
-            tmpParent.addView(mActivityView, new LinearLayout.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT,
-                    ViewGroup.LayoutParams.MATCH_PARENT));
-        }
-
-        mActivityView.release();
-
-        ((ViewGroup) mActivityView.getParent()).removeView(mActivityView);
-        mActivityView = null;
-    }
-
-    @Override
-    public void setPosition(float x, float y) {
-        setPositionX(x);
-        setPositionY(y);
-    }
-
-    @Override
-    public void setPositionX(float x) {
-        setTranslationX(x);
-    }
-
-    @Override
-    public void setPositionY(float y) {
-        setTranslationY(y);
-    }
-
-    @Override
-    public PointF getPosition() {
-        return new PointF(getTranslationX(), getTranslationY());
-    }
-
-    /**
-     * @return whether an ActivityView should be used to display the content of this Bubble
-     */
-    public boolean hasAppOverlayIntent() {
-        return mAppOverlayIntent != null;
-    }
-
-    public PendingIntent getAppOverlayIntent() {
-        return mAppOverlayIntent;
-
-    }
-
-    public void setBubbleIntent(PendingIntent intent) {
-        mAppOverlayIntent = intent;
-    }
 }
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 1644064..9fd26b8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bubbles.animation;
 
+import android.content.res.Resources;
 import android.graphics.PointF;
 import android.view.View;
 import android.view.WindowInsets;
@@ -43,6 +44,9 @@
      */
     private static final int ANIMATE_TRANSLATION_FACTOR = 4;
 
+    /** How much to scale down bubbles when they're animating in/out. */
+    private static final float ANIMATE_SCALE_PERCENT = 0.5f;
+
     /**
      * The stack position from which the bubbles were expanded. Saved in {@link #expandFromStack}
      * and used to return to stack form in {@link #collapseBackToStack}.
@@ -55,16 +59,35 @@
     private float mBubblePaddingPx;
     /** Size of each bubble. */
     private float mBubbleSizePx;
+    /** Height of the status bar. */
+    private float mStatusBarHeight;
+
+    /**
+     * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause
+     * the rest of the bubbles to animate to fill the gap.
+     */
+    private boolean mBubbleDraggedOutEnough = false;
+
+    /** The bubble currently being dragged out of the row (to potentially be dismissed). */
+    private View mBubbleDraggingOut;
+
+    /**
+     * Drag velocities for the dragging-out bubble when the drag finished. These are used by
+     * {@link #onChildRemoved} to animate out the bubble while respecting touch velocity.
+     */
+    private float mBubbleDraggingOutVelX;
+    private float mBubbleDraggingOutVelY;
 
     @Override
     protected void setLayout(PhysicsAnimationLayout layout) {
         super.setLayout(layout);
-        mStackOffsetPx = layout.getResources().getDimensionPixelSize(
-                R.dimen.bubble_stack_offset);
-        mBubblePaddingPx = layout.getResources().getDimensionPixelSize(
-                R.dimen.bubble_padding);
-        mBubbleSizePx = layout.getResources().getDimensionPixelSize(
-                R.dimen.individual_bubble_size);
+
+        final Resources res = layout.getResources();
+        mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
+        mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding);
+        mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
+        mStatusBarHeight =
+                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
     }
 
     /**
@@ -98,12 +121,93 @@
         runAfterTranslationsEnd(after);
     }
 
+    /** Prepares the given bubble to be dragged out. */
+    public void prepareForBubbleDrag(View bubble) {
+        mLayout.cancelAnimationsOnView(bubble);
+
+        mBubbleDraggingOut = bubble;
+        mBubbleDraggingOut.setTranslationZ(Short.MAX_VALUE);
+    }
+
+    /**
+     * Drags an individual bubble to the given coordinates. Bubbles to the right will animate to
+     * take its place once it's dragged out of the row of bubbles, and animate out of the way if the
+     * bubble is dragged back into the row.
+     */
+    public void dragBubbleOut(View bubbleView, float x, float y) {
+        bubbleView.setTranslationX(x);
+        bubbleView.setTranslationY(y);
+
+        final boolean draggedOutEnough =
+                y > getExpandedY() + mBubbleSizePx || y < getExpandedY() - mBubbleSizePx;
+        if (draggedOutEnough != mBubbleDraggedOutEnough) {
+            animateStackByBubbleWidthsStartingFrom(
+                    /* numBubbleWidths */ draggedOutEnough ? -1 : 0,
+                    /* startIndex */ mLayout.indexOfChild(bubbleView) + 1);
+            mBubbleDraggedOutEnough = draggedOutEnough;
+        }
+    }
+
+    /**
+     * Snaps a bubble back to its position within the bubble row, and animates the rest of the
+     * bubbles to accommodate it if it was previously dragged out past the threshold.
+     */
+    public void snapBubbleBack(View bubbleView, float velX, float velY) {
+        final int index = mLayout.indexOfChild(bubbleView);
+
+        // Snap the bubble back, respecting its current velocity.
+        mLayout.animateValueForChildAtIndex(
+                DynamicAnimation.TRANSLATION_X, index, getXForChildAtIndex(index), velX);
+        mLayout.animateValueForChildAtIndex(
+                DynamicAnimation.TRANSLATION_Y, index, getExpandedY(), velY);
+        mLayout.setEndListenerForProperties(
+                mLayout.new OneTimeMultiplePropertyEndListener() {
+                    @Override
+                    void onAllAnimationsForPropertiesEnd() {
+                        // Reset Z translation once the bubble is done snapping back.
+                        bubbleView.setTranslationZ(0f);
+                    }
+                },
+                DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+
+        animateStackByBubbleWidthsStartingFrom(
+                /* numBubbleWidths */ 0, /* startIndex */ index + 1);
+
+        mBubbleDraggingOut = null;
+        mBubbleDraggedOutEnough = false;
+    }
+
+    /**
+     * Sets configuration variables so that when the given bubble is removed, the animations are
+     * started with the given velocities.
+     */
+    public void prepareForDismissalWithVelocity(View bubbleView, float velX, float velY) {
+        mBubbleDraggingOut = bubbleView;
+        mBubbleDraggingOutVelX = velX;
+        mBubbleDraggingOutVelY = velY;
+        mBubbleDraggedOutEnough = false;
+    }
+
+    /**
+     * Animates the bubbles, starting at the given index, to the left or right by the given number
+     * of bubble widths. Passing zero for numBubbleWidths will animate the bubbles to their normal
+     * positions.
+     */
+    private void animateStackByBubbleWidthsStartingFrom(int numBubbleWidths, int startIndex) {
+        for (int i = startIndex; i < mLayout.getChildCount(); i++) {
+            mLayout.animateValueForChildAtIndex(
+                    DynamicAnimation.TRANSLATION_X,
+                    i,
+                    getXForChildAtIndex(i + numBubbleWidths));
+        }
+    }
+
     /** The Y value of the row of expanded bubbles. */
     private float getExpandedY() {
         final WindowInsets insets = mLayout.getRootWindowInsets();
         if (insets != null) {
             return mBubblePaddingPx + Math.max(
-                    insets.getSystemWindowInsetTop(),
+                    mStatusBarHeight,
                     insets.getDisplayCutout() != null
                             ? insets.getDisplayCutout().getSafeInsetTop()
                             : 0);
@@ -161,12 +265,7 @@
         child.setTranslationX(getXForChildAtIndex(index));
         child.setTranslationY(getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR);
         mLayout.animateValueForChild(DynamicAnimation.TRANSLATION_Y, child, getExpandedY());
-
-        // Animate the remaining bubbles to the correct X position.
-        for (int i = index + 1; i < mLayout.getChildCount(); i++) {
-            mLayout.animateValueForChildAtIndex(
-                    DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i));
-        }
+        animateBubblesAfterIndexToCorrectX(index);
     }
 
     @Override
@@ -175,16 +274,36 @@
         // TODO: Reverse this when bubbles are at the bottom.
         mLayout.animateValueForChild(
                 DynamicAnimation.ALPHA, child, 0f, finishRemoval);
-        mLayout.animateValueForChild(
-                DynamicAnimation.TRANSLATION_Y,
-                child,
-                getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR);
 
-        // Animate the remaining bubbles to the correct X position.
-        for (int i = index; i < mLayout.getChildCount(); i++) {
-            mLayout.animateValueForChildAtIndex(
-                    DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i));
+        // If we're removing the dragged-out bubble, that means it got dismissed.
+        if (child.equals(mBubbleDraggingOut)) {
+            // Throw it to the bottom of the screen, towards the center horizontally.
+            mLayout.animateValueForChild(
+                    DynamicAnimation.TRANSLATION_X,
+                    child,
+                    mLayout.getWidth() / 2f - mBubbleSizePx / 2f,
+                    mBubbleDraggingOutVelX);
+            mLayout.animateValueForChild(
+                    DynamicAnimation.TRANSLATION_Y,
+                    child,
+                    mLayout.getHeight() + mBubbleSizePx,
+                    mBubbleDraggingOutVelY);
+
+            // Scale it down a bit so it looks like it's disappearing.
+            mLayout.animateValueForChild(DynamicAnimation.SCALE_X, child, ANIMATE_SCALE_PERCENT);
+            mLayout.animateValueForChild(DynamicAnimation.SCALE_Y, child, ANIMATE_SCALE_PERCENT);
+
+            mBubbleDraggingOut = null;
+        } else {
+            // If we're removing some random bubble just throw it off the top.
+            mLayout.animateValueForChild(
+                    DynamicAnimation.TRANSLATION_Y,
+                    child,
+                    getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR);
         }
+
+        // Animate all the other bubbles to their new positions sans this bubble.
+        animateBubblesAfterIndexToCorrectX(index);
     }
 
     @Override
@@ -203,6 +322,23 @@
                 () -> super.setChildVisibility(child, index, visibility));
     }
 
+    /**
+     * Animates the bubbles after the given index to the X position they should be in according to
+     * {@link #getXForChildAtIndex}.
+     */
+    private void animateBubblesAfterIndexToCorrectX(int start) {
+        for (int i = start; i < mLayout.getChildCount(); i++) {
+            final View bubble = mLayout.getChildAt(i);
+
+            // Don't animate the dragging out bubble, or it'll jump around while being dragged. It
+            // will be snapped to the correct X value after the drag (if it's not dismissed).
+            if (!bubble.equals(mBubbleDraggingOut)) {
+                mLayout.animateValueForChild(
+                        DynamicAnimation.TRANSLATION_X, bubble, getXForChildAtIndex(i));
+            }
+        }
+    }
+
     /** Returns the appropriate X translation value for a bubble at the given index. */
     private float getXForChildAtIndex(int index) {
         return mBubblePaddingPx + (mBubbleSizePx + mBubblePaddingPx) * index;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
index a4ddbf7..dfdcfc9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
@@ -187,6 +187,20 @@
     }
 
     /**
+     * Sets an end listener that will be called whenever any of the given properties' animations
+     * end. For example, setting a listener for TRANSLATION_X and TRANSLATION_Y will result in that
+     * listener being called twice - once when all TRANSLATION_X animations end, and again when all
+     * TRANSLATION_Y animations end.
+     */
+    public void setEndListenerForProperties(
+            DynamicAnimation.OnAnimationEndListener endListener,
+            DynamicAnimation.ViewProperty... properties) {
+        for (DynamicAnimation.ViewProperty property : properties) {
+            setEndListenerForProperty(endListener, property);
+        }
+    }
+
+    /**
      * Removes the end listener that would have been called when all child animations for a given
      * property stopped running.
      */
@@ -197,7 +211,6 @@
     @Override
     public void addView(View child, int index, ViewGroup.LayoutParams params) {
         super.addView(child, index, params);
-        setChildrenVisibility();
 
         // Set up animations for the new view, if the controller is set. If it isn't set, we'll be
         // setting up animations for all children when setController is called.
@@ -208,6 +221,8 @@
 
             mController.onChildAdded(child, index);
         }
+
+        setChildrenVisibility();
     }
 
     @Override
@@ -294,6 +309,13 @@
         }
     }
 
+    /** Cancels all of the physics animations running on the given view. */
+    public void cancelAnimationsOnView(View view) {
+        for (DynamicAnimation.ViewProperty property : mController.getAnimatedProperties()) {
+            getAnimationFromView(property, view).cancel();
+        }
+    }
+
     /**
      * Animates the property of the given child view, then runs the callback provided when the
      * animation ends.
@@ -318,6 +340,11 @@
                 });
             }
 
+            // Set the start velocity if it's something other than the not-set value.
+            if (startVel != Float.MAX_VALUE) {
+                animation.setStartVelocity(startVel);
+            }
+
             animation.animateToFinalPosition(value);
         }
     }
@@ -337,6 +364,14 @@
         animateValueForChild(property, view, value, Float.MAX_VALUE, /* after */ null);
     }
 
+    protected void animateValueForChild(
+            DynamicAnimation.ViewProperty property,
+            View view,
+            float value,
+            float startVel) {
+        animateValueForChild(property, view, value, startVel, /* after */ null);
+    }
+
     /**
      * Animates the property of the child at the given index to the given value, then runs the
      * callback provided when the animation ends.
@@ -414,7 +449,13 @@
      */
     private SpringAnimation getAnimationAtIndex(
             DynamicAnimation.ViewProperty property, int index) {
-        return (SpringAnimation) getChildAt(index).getTag(getTagIdForProperty(property));
+        return getAnimationFromView(property, getChildAt(index));
+    }
+
+    /** Retrieves the animation of the given property from the view via the view tag system. */
+    private SpringAnimation getAnimationFromView(
+            DynamicAnimation.ViewProperty property, View view) {
+        return (SpringAnimation) view.getTag(getTagIdForProperty(property));
     }
 
     /** Sets up SpringAnimations of the given property for each child view in the layout. */
@@ -528,4 +569,33 @@
             }
         }
     }
+
+    /**
+     * One time end listener that waits for every animation on every given property to finish. At
+     * that point, it calls {@link #onAllAnimationsForPropertiesEnd} and then removes itself as an
+     * end listener from each property.
+     */
+    public abstract class OneTimeMultiplePropertyEndListener
+            implements DynamicAnimation.OnAnimationEndListener {
+        final DynamicAnimation.ViewProperty[] mViewProperties;
+
+        OneTimeMultiplePropertyEndListener(DynamicAnimation.ViewProperty... properties) {
+            mViewProperties = properties;
+        }
+
+        @Override
+        public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value,
+                float velocity) {
+            if (!arePropertiesAnimating(mViewProperties)) {
+                onAllAnimationsForPropertiesEnd();
+
+                for (DynamicAnimation.ViewProperty property : mViewProperties) {
+                    removeEndListenerForProperty(property);
+                }
+            }
+        }
+
+        /** Called when every animation for every property has finished. */
+        abstract void onAllAnimationsForPropertiesEnd();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 0c089a75..7dfb21c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -16,15 +16,12 @@
 
 package com.android.systemui.bubbles.animation;
 
-import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.util.Log;
 import android.view.View;
 import android.view.WindowInsets;
-import android.view.WindowManager;
 
 import androidx.dynamicanimation.animation.DynamicAnimation;
 import androidx.dynamicanimation.animation.FlingAnimation;
@@ -88,9 +85,8 @@
     private int mBubbleOffscreen;
     /** How far down the screen the stack starts, when there is no pre-existing location. */
     private int mStackStartingVerticalOffset;
-
-    private Point mDisplaySize;
-    private RectF mAllowableStackPositionRegion;
+    /** Height of the status bar. */
+    private float mStatusBarHeight;
 
     @Override
     protected void setLayout(PhysicsAnimationLayout layout) {
@@ -103,11 +99,8 @@
         mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen);
         mStackStartingVerticalOffset =
                 res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y);
-
-        mDisplaySize = new Point();
-        WindowManager wm =
-                (WindowManager) layout.getContext().getSystemService(Context.WINDOW_SERVICE);
-        wm.getDefaultDisplay().getSize(mDisplaySize);
+        mStatusBarHeight =
+                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
     }
 
     /**
@@ -203,10 +196,9 @@
      */
     public RectF getAllowableStackPositionRegion() {
         final WindowInsets insets = mLayout.getRootWindowInsets();
-        mAllowableStackPositionRegion = new RectF();
-
+        final RectF allowableRegion = new RectF();
         if (insets != null) {
-            mAllowableStackPositionRegion.left =
+            allowableRegion.left =
                     -mBubbleOffscreen
                             - mBubblePadding
                             + Math.max(
@@ -214,7 +206,7 @@
                             insets.getDisplayCutout() != null
                                     ? insets.getDisplayCutout().getSafeInsetLeft()
                                     : 0);
-            mAllowableStackPositionRegion.right =
+            allowableRegion.right =
                     mLayout.getWidth()
                             - mIndividualBubbleSize
                             + mBubbleOffscreen
@@ -222,17 +214,17 @@
                             - Math.max(
                             insets.getSystemWindowInsetRight(),
                             insets.getDisplayCutout() != null
-                                ? insets.getDisplayCutout().getSafeInsetRight()
-                                : 0);
+                                    ? insets.getDisplayCutout().getSafeInsetRight()
+                                    : 0);
 
-            mAllowableStackPositionRegion.top =
+            allowableRegion.top =
                     mBubblePadding
                             + Math.max(
-                            insets.getSystemWindowInsetTop(),
+                            mStatusBarHeight,
                             insets.getDisplayCutout() != null
-                                ? insets.getDisplayCutout().getSafeInsetTop()
-                                : 0);
-            mAllowableStackPositionRegion.bottom =
+                                    ? insets.getDisplayCutout().getSafeInsetTop()
+                                    : 0);
+            allowableRegion.bottom =
                     mLayout.getHeight()
                             - mIndividualBubbleSize
                             - mBubblePadding
@@ -243,7 +235,7 @@
                                     : 0);
         }
 
-        return mAllowableStackPositionRegion;
+        return allowableRegion;
     }
 
     @Override
@@ -287,31 +279,14 @@
 
     @Override
     void onChildAdded(View child, int index) {
-        // If this is the first child added, position the stack in its starting position.
         if (mLayout.getChildCount() == 1) {
-            moveStackToStartPosition();
-        }
-
-        if (mLayout.indexOfChild(child) == 0) {
-            child.setTranslationY(mStackPosition.y);
-
-            // Pop in the new bubble.
-            child.setScaleX(ANIMATE_IN_STARTING_SCALE);
-            child.setScaleY(ANIMATE_IN_STARTING_SCALE);
-            mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_X, 0, 1f);
-            mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_Y, 0, 1f);
-
-            // Fade in the new bubble.
-            child.setAlpha(0);
-            mLayout.animateValueForChildAtIndex(DynamicAnimation.ALPHA, 0, 1f);
-
-            // Start the new bubble 4x the normal offset distance in the opposite direction. We'll
-            // animate in from this position. Since the animations are chained, when the new bubble
-            // flies in from the side, it will push the other ones out of the way.
-            float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
-            child.setTranslationX(mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset);
-            mLayout.animateValueForChildAtIndex(
-                    DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x);
+            // If this is the first child added, position the stack in its starting position before
+            // animating in.
+            moveStackToStartPosition(() -> animateInBubble(child));
+        } else if (mLayout.indexOfChild(child) == 0) {
+            // Otherwise, animate the bubble in if it's the newest bubble. If we're adding a bubble
+            // to the back of the stack, it'll be largely invisible so don't bother animating it in.
+            animateInBubble(child);
         }
     }
 
@@ -334,10 +309,14 @@
     }
 
     /** Moves the stack, without any animation, to the starting position. */
-    private void moveStackToStartPosition() {
-        mLayout.post(() -> setStackPosition(
-                getAllowableStackPositionRegion().right,
-                getAllowableStackPositionRegion().top + mStackStartingVerticalOffset));
+    private void moveStackToStartPosition(Runnable after) {
+        // Post to ensure that the layout's width and height have been calculated.
+        mLayout.post(() -> {
+            setStackPosition(
+                    getAllowableStackPositionRegion().right,
+                    getAllowableStackPositionRegion().top + mStackStartingVerticalOffset);
+            after.run();
+        });
     }
 
     /**
@@ -379,6 +358,29 @@
         }
     }
 
+    /** Animates in the given bubble. */
+    private void animateInBubble(View child) {
+        child.setTranslationY(mStackPosition.y);
+
+        // Pop in the new bubble.
+        child.setScaleX(ANIMATE_IN_STARTING_SCALE);
+        child.setScaleY(ANIMATE_IN_STARTING_SCALE);
+        mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_X, 0, 1f);
+        mLayout.animateValueForChildAtIndex(DynamicAnimation.SCALE_Y, 0, 1f);
+
+        // Fade in the new bubble.
+        child.setAlpha(0);
+        mLayout.animateValueForChildAtIndex(DynamicAnimation.ALPHA, 0, 1f);
+
+        // Start the new bubble 4x the normal offset distance in the opposite direction. We'll
+        // animate in from this position. Since the animations are chained, when the new bubble
+        // flies in from the side, it will push the other ones out of the way.
+        float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
+        child.setTranslationX(mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset);
+        mLayout.animateValueForChildAtIndex(
+                DynamicAnimation.TRANSLATION_X, 0, mStackPosition.x);
+    }
+
     /**
      * Springs the first bubble to the given final position, with the rest of the stack 'following'.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
index 4a2e06c..f5cf149 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
@@ -25,9 +25,4 @@
      * Invoked every time a minute is elapsed in doze mode
      */
     void dozeTimeTick();
-
-    /**
-     * When view is double tapped in doze mode.
-     */
-    void onDozeDoubleTap();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 5efdc2f..2d1dba6 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -131,7 +131,7 @@
                 new PluginSensor(
                         new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
                         Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
-                        mConfig.wakeScreenGestureAvailable(),
+                        mConfig.wakeScreenGestureAvailable() && alwaysOn,
                         DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
                         false /* reports touch coordinates */,
                         false /* touchscreen */),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 873fbc2..e207cb1 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -161,7 +161,8 @@
                 } else {
                     mDozeHost.extendPulse();
                 }
-            }, sensorPerformedProxCheck || mDockManager.isDocked(), pulseReason);
+            }, sensorPerformedProxCheck
+                    || (mDockManager != null && mDockManager.isDocked()), pulseReason);
         }
 
         if (isPickup) {
@@ -224,7 +225,9 @@
             case INITIALIZED:
                 mBroadcastReceiver.register(mContext);
                 mDozeHost.addCallback(mHostCallback);
-                mDockManager.addListener(mDockEventListener);
+                if (mDockManager != null) {
+                    mDockManager.addListener(mDockEventListener);
+                }
                 checkTriggersAtInit();
                 break;
             case DOZE:
@@ -250,7 +253,9 @@
             case FINISH:
                 mBroadcastReceiver.unregister(mContext);
                 mDozeHost.removeCallback(mHostCallback);
-                mDockManager.removeListener(mDockEventListener);
+                if (mDockManager != null) {
+                    mDockManager.removeListener(mDockEventListener);
+                }
                 mDozeSensors.setListening(false);
                 mDozeSensors.setProxListening(false);
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index f5ac0d3..7c9b286 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -33,6 +33,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.UserInfo;
+import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.graphics.Point;
 import android.graphics.drawable.Drawable;
@@ -91,6 +92,7 @@
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.EmergencyDialerConstants;
+import com.android.systemui.util.leak.RotationUtils;
 import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
 
 import java.util.ArrayList;
@@ -159,6 +161,8 @@
     private final ScreenshotHelper mScreenshotHelper;
     private final ScreenRecordHelper mScreenRecordHelper;
 
+    private int mLastRotation;
+
     /**
      * @param context everything needs a context :(
      */
@@ -201,6 +205,8 @@
         mScreenshotHelper = new ScreenshotHelper(context);
         mScreenRecordHelper = new ScreenRecordHelper(context);
 
+        mLastRotation = RotationUtils.getRotation(mContext);
+
         Dependency.get(ConfigurationController.class).addCallback(this);
     }
 
@@ -426,6 +432,15 @@
         mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
     }
 
+    @Override
+    public void onConfigChanged(Configuration newConfig) {
+        int rotation = RotationUtils.getRotation(mContext);
+        if (rotation != mLastRotation) {
+            mDialog.onRotate();
+        }
+        mLastRotation = rotation;
+    }
+
     public void destroy() {
         Dependency.get(ConfigurationController.class).removeCallback(this);
     }
@@ -1091,7 +1106,7 @@
         }
 
         protected int getActionLayoutId(Context context) {
-            if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED)) {
+            if (isGridEnabled(context)) {
                 return com.android.systemui.R.layout.global_actions_grid_item;
             }
             return com.android.systemui.R.layout.global_actions_item;
@@ -1465,7 +1480,7 @@
 
         private final Context mContext;
         private final MyAdapter mAdapter;
-        private final MultiListLayout mGlobalActionsLayout;
+        private MultiListLayout mGlobalActionsLayout;
         private final OnClickListener mClickListener;
         private final OnItemLongClickListener mLongClickListener;
         private final GradientDrawable mGradientDrawable;
@@ -1505,8 +1520,13 @@
             window.setBackgroundDrawable(mGradientDrawable);
             window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
 
+            initializeLayout();
 
-            setContentView(getGlobalActionsLayoutId(context));
+            setTitle(R.string.global_actions);
+        }
+
+        private void initializeLayout() {
+            setContentView(getGlobalActionsLayoutId(mContext));
             mGlobalActionsLayout = (MultiListLayout)
                     findViewById(com.android.systemui.R.id.global_actions_view);
             mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss());
@@ -1520,11 +1540,20 @@
                     return true;
                 }
             });
-            setTitle(R.string.global_actions);
+        }
+
+        public void onRotate() {
+            if (mShowing && isGridEnabled(mContext)) {
+                initializeLayout();
+                updateList();
+            }
         }
 
         private int getGlobalActionsLayoutId(Context context) {
-            if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED)) {
+            if (isGridEnabled(context)) {
+                if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) {
+                    return com.android.systemui.R.layout.global_actions_grid_seascape;
+                }
                 return com.android.systemui.R.layout.global_actions_grid;
             }
             return com.android.systemui.R.layout.global_actions_wrapped;
@@ -1543,10 +1572,20 @@
                 int separatedIndex = separatedActions.indexOf(action);
                 ViewGroup parent;
                 if (separatedIndex != -1) {
-                    parent = mGlobalActionsLayout.getParentView(true, separatedIndex);
+                    parent = mGlobalActionsLayout.getParentView(true, separatedIndex, false);
                 } else {
+                    boolean reverse = false;
+
+                    // If we're using the grid layout and we're in seascape, reverse the order
+                    // of sublists to make sure they render in the correct positions,
+                    // since we can't reverse vertical LinearLayouts through the layout xml.
+
+                    if (isGridEnabled(mContext) && RotationUtils.getRotation(mContext)
+                            == RotationUtils.ROTATION_SEASCAPE) {
+                        reverse = true;
+                    }
                     int listIndex = listActions.indexOf(action);
-                    parent = mGlobalActionsLayout.getParentView(false, listIndex);
+                    parent = mGlobalActionsLayout.getParentView(false, listIndex, reverse);
                 }
                 View v = mAdapter.getView(i, null, parent);
                 final int pos = i;
@@ -1665,4 +1704,11 @@
             mKeyguardShowing = keyguardShowing;
         }
     }
+
+    /**
+     * Determines whether or not the Global Actions Dialog should use the newer grid-style layout.
+     */
+    public static boolean isGridEnabled(Context context) {
+        return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.GLOBAL_ACTIONS_GRID_ENABLED);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
index 0e49b5f..1d04277 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java
@@ -83,11 +83,11 @@
     }
 
     @Override
-    public ViewGroup getParentView(boolean separated, int index) {
+    public ViewGroup getParentView(boolean separated, int index, boolean reverseOrder) {
         if (separated) {
             return getSeparatedView();
         } else {
-            return getListView().getParentView(index);
+            return getListView().getParentView(index, reverseOrder);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java
index 3775515..d5dcd74 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/ListGridLayout.java
@@ -28,16 +28,13 @@
  *
  * * Try to maintain a 'square' grid (equal number of columns and rows) based on the expected item
  *   count.
+ * * Determine the position and parent of any item by its index and the total item count.
  * * Display and hide sub-lists as needed, depending on the expected item count.
- * * Favor bias toward having more rows or columns depending on the orientation of the device
- *   (TODO(123344999): Implement this, currently always favors adding more rows.)
- * * Change the orientation (horizontal vs. vertical) of the container and sub-lists to act as rows
- *   or columns depending on the orientation of the device.
- *   (TODO(123344999): Implement this, currently always columns.)
  *
  * While we could implement this behavior with a GridLayout, it would take significantly more
  * time and effort, and would require more substantial refactoring of the existing code in
- * GlobalActionsDialog, since it would require manipulation of the child items themselves.
+ * GlobalActionsDialog, since it would require manipulation of layout properties on the child items
+ * themselves.
  *
  */
 
@@ -65,14 +62,25 @@
     /**
      * Get the parent view associated with the item which should be placed at the given position.
      */
-    public ViewGroup getParentView(int index) {
-        ViewGroup firstParent = (ViewGroup) getChildAt(0);
+    public ViewGroup getParentView(int index, boolean reverseSublists) {
         if (mRows == 0) {
-            return firstParent;
+            return null;
         }
+        int column = getParentViewIndex(index, reverseSublists);
+        return (ViewGroup) getChildAt(column);
+    }
+
+    private int reverseSublistIndex(int index) {
+        return getChildCount() - (index + 1);
+    }
+
+    private int getParentViewIndex(int index, boolean reverseSublists) {
         int column = (int) Math.floor(index / mRows);
-        ViewGroup parent = (ViewGroup) getChildAt(column);
-        return parent != null ? parent : firstParent;
+        int columnCount = getChildCount();
+        if (reverseSublists) {
+            column = reverseSublistIndex(column);
+        }
+        return column;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 684175c..85d975f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -25,7 +25,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.icu.text.DateFormat;
 import android.icu.text.DisplayContext;
@@ -35,10 +37,13 @@
 import android.os.Trace;
 import android.provider.Settings;
 import android.service.notification.ZenModeConfig;
-import android.text.Spannable;
 import android.text.SpannableStringBuilder;
+import android.text.Spanned;
 import android.text.TextUtils;
+import android.text.style.DynamicDrawableSpan;
+import android.text.style.ImageSpan;
 import android.text.style.StyleSpan;
+import android.util.MathUtils;
 
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
@@ -72,6 +77,8 @@
 
     private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD);
     public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
+    private static final String KEYGUARD_HEADER_URI =
+            "content://com.android.systemui.keyguard/header";
     public static final String KEYGUARD_DATE_URI = "content://com.android.systemui.keyguard/date";
     public static final String KEYGUARD_NEXT_ALARM_URI =
             "content://com.android.systemui.keyguard/alarm";
@@ -90,6 +97,7 @@
     private static KeyguardSliceProvider sInstance;
 
     protected final Uri mSliceUri;
+    protected final Uri mHeaderUri;
     protected final Uri mDateUri;
     protected final Uri mAlarmUri;
     protected final Uri mDndUri;
@@ -163,6 +171,7 @@
     KeyguardSliceProvider(Handler handler) {
         mHandler = handler;
         mSliceUri = Uri.parse(KEYGUARD_SLICE_URI);
+        mHeaderUri = Uri.parse(KEYGUARD_HEADER_URI);
         mDateUri = Uri.parse(KEYGUARD_DATE_URI);
         mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI);
         mDndUri = Uri.parse(KEYGUARD_DND_URI);
@@ -213,26 +222,32 @@
     protected void addMediaLocked(ListBuilder listBuilder) {
         if (mMediaMetaData != null) {
             SpannableStringBuilder builder = new SpannableStringBuilder();
+
+            Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
+            if (notificationIcon != null) {
+                Drawable drawable = notificationIcon.loadDrawable(getContext());
+                Rect mediaBounds = new Rect(0 /* left */, 0 /* top */,
+                        drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+                int iconHeaderSize = getContext().getResources()
+                        .getDimensionPixelSize(R.dimen.header_icon_size);
+                MathUtils.fitRect(mediaBounds, iconHeaderSize);
+                drawable.setBounds(mediaBounds);
+                builder.append("# ");
+                builder.setSpan(new ImageSpan(drawable, DynamicDrawableSpan.ALIGN_CENTER),
+                        0 /* start */, 1 /* end */, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+            }
+
             CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE);
             if (TextUtils.isEmpty(title)) {
                 title = getContext().getResources().getString(R.string.music_controls_no_title);
             }
             builder.append(title);
-            builder.setSpan(BOLD_STYLE, 0, title.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+            listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(builder));
 
             CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST);
             if (!TextUtils.isEmpty(album)) {
-                builder.append("  ").append(album);
+                listBuilder.addRow(new RowBuilder(mMediaUri).setTitle(album));
             }
-
-            RowBuilder mediaBuilder = new RowBuilder(mMediaUri).setTitle(builder);
-            Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
-            if (notificationIcon != null) {
-                IconCompat icon = IconCompat.createFromIcon(notificationIcon);
-                mediaBuilder.addEndItem(icon, ListBuilder.ICON_IMAGE);
-            }
-
-            listBuilder.addRow(mediaBuilder);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index f7ca51d..a6e48f8 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -62,4 +62,6 @@
             context.packageManager.getApplicationLabel(it) as String
         } ?: packageName
     }
+
+    override fun toString() = "PrivacyApplication(packageName=$packageName, uid=$uid)"
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index faeacf7..625eacd 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -26,16 +26,26 @@
 import android.os.UserHandle
 import android.os.UserManager
 import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.Dependency
+import com.android.systemui.Dependency.BG_HANDLER_NAME
+import com.android.systemui.Dependency.MAIN_HANDLER_NAME
+import com.android.systemui.R
 import com.android.systemui.appops.AppOpItem
 import com.android.systemui.appops.AppOpsController
-import com.android.systemui.R
+import com.android.systemui.Dumpable
+import java.io.FileDescriptor
+import java.io.PrintWriter
 import java.lang.ref.WeakReference
 import javax.inject.Inject
+import javax.inject.Named
 import javax.inject.Singleton
 
 @Singleton
-class PrivacyItemController @Inject constructor(private val context: Context) {
+class PrivacyItemController @Inject constructor(
+        val context: Context,
+        private val appOpsController: AppOpsController,
+        @Named(MAIN_HANDLER_NAME) private val uiHandler: Handler,
+        @Named(BG_HANDLER_NAME) private val bgHandler: Handler
+) : Dumpable {
 
     companion object {
         val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
@@ -48,16 +58,13 @@
         const val TAG = "PrivacyItemController"
         const val SYSTEM_UID = 1000
     }
-    private var privacyList = emptyList<PrivacyItem>()
 
-    @Suppress("DEPRECATION")
-    private val appOpsController = Dependency.get(AppOpsController::class.java)
+    @VisibleForTesting
+    internal var privacyList = emptyList<PrivacyItem>()
+        get() = field.toList() // Provides a shallow copy of the list
+
     private val userManager = context.getSystemService(UserManager::class.java)
     private var currentUserIds = emptyList<Int>()
-    @Suppress("DEPRECATION")
-    private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER))
-    @Suppress("DEPRECATION")
-    private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
     private var listening = false
     val systemApp =
             PrivacyApplication(context.getString(R.string.device_services), SYSTEM_UID, context)
@@ -188,4 +195,22 @@
             callback?.privacyChanged(list)
         }
     }
+
+    override fun dump(fd: FileDescriptor?, pw: PrintWriter?, args: Array<out String>?) {
+        pw?.println("PrivacyItemController state:")
+        pw?.println("  Listening: $listening")
+        pw?.println("  Current user ids: $currentUserIds")
+        pw?.println("  Privacy Items:")
+        privacyList.forEach {
+            pw?.print("    ")
+            pw?.println(it.toString())
+        }
+        pw?.println("  Callbacks:")
+        callbacks.forEach {
+            it.get()?.let {
+                pw?.print("    ")
+                pw?.println(it.toString())
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 2f19630..a6af82a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -28,7 +28,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 
 import java.util.stream.Stream;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index a3beb96..57d0588 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
 
 import android.annotation.NonNull;
 import android.content.Context;
@@ -26,7 +26,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
index 4944732..52b8cc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
@@ -49,7 +49,7 @@
 
         context.display.getSize(mTmpSize)
         val renderScript = RenderScript.create(context)
-        val rect = Rect(0, 0,artwork.width, artwork.height)
+        val rect = Rect(0, 0, artwork.width, artwork.height)
         MathUtils.fitRect(rect, Math.max(mTmpSize.x / DOWNSAMPLE, mTmpSize.y / DOWNSAMPLE))
         val inBitmap = Bitmap.createScaledBitmap(artwork, rect.width(), rect.height(),
                 true /* filter */)
@@ -67,6 +67,7 @@
         input.destroy()
         output.destroy()
         inBitmap.recycle()
+        blur.destroy()
 
         val canvas = Canvas(outBitmap)
         canvas.drawColor(ColorUtils.setAlphaComponent(color, COLOR_ALPHA))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 01b0bb1..110d515 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -348,15 +348,13 @@
             if (notGoneIndex == 0) {
                 StatusBarIconView icon = row.getEntry().expandedIcon;
                 NotificationIconContainer.IconState iconState = getIconState(icon);
+                // The icon state might be null in rare cases where the notification is actually
+                // added to the layout, but not to the shelf. An example are replied messages, since
+                // they don't show up on AOD
                 if (iconState != null && iconState.clampedAppearAmount == 1.0f) {
                     // only if the first icon is fully in the shelf we want to clip to it!
                     backgroundTop = (int) (row.getTranslationY() - getTranslationY());
                     firstElementRoundness = row.getCurrentTopRoundness();
-                } else if (iconState == null) {
-                    Log.wtf(TAG, "iconState is null. ExpandedIcon: " + row.getEntry().expandedIcon
-                            + (row.getEntry().expandedIcon != null
-                            ? "\n icon parent: " + row.getEntry().expandedIcon.getParent() : "")
-                            + " \n number of notifications: " + mHostLayout.getChildCount() );
                 }
             }
             if (row.isFirstInSection() && previousRow != null && previousRow.isLastInSection()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 662cf51..ee5ac7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -24,6 +24,7 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -61,7 +62,7 @@
     protected final NotificationLockscreenUserManager mLockscreenUserManager;
     protected final NotificationGroupManager mGroupManager;
     protected final VisualStabilityManager mVisualStabilityManager;
-    private final StatusBarStateControllerImpl mStatusBarStateController;
+    private final SysuiStatusBarStateController mStatusBarStateController;
     private final NotificationEntryManager mEntryManager;
 
     // Lazy
@@ -82,13 +83,13 @@
             NotificationLockscreenUserManager notificationLockscreenUserManager,
             NotificationGroupManager groupManager,
             VisualStabilityManager visualStabilityManager,
-            StatusBarStateControllerImpl statusBarStateController,
+            StatusBarStateController statusBarStateController,
             NotificationEntryManager notificationEntryManager,
             Lazy<ShadeController> shadeController) {
         mLockscreenUserManager = notificationLockscreenUserManager;
         mGroupManager = groupManager;
         mVisualStabilityManager = visualStabilityManager;
-        mStatusBarStateController = statusBarStateController;
+        mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
         mEntryManager = notificationEntryManager;
         mShadeController = shadeController;
         Resources res = context.getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index 5605f3d..f6d3cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -17,8 +17,8 @@
 package com.android.systemui.statusbar.notification;
 
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.app.Notification;
 import android.service.notification.StatusBarNotification;
@@ -30,7 +30,7 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -102,8 +102,7 @@
      * @param entry         entry to add
      * @param inflatedFlags flags representing content views that were inflated
      */
-    private void showAlertingView(NotificationEntry entry,
-            @NotificationInflater.InflationFlag int inflatedFlags) {
+    private void showAlertingView(NotificationEntry entry, @InflationFlag int inflatedFlags) {
         if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
             // Possible for shouldHeadsUp to change between the inflation starting and ending.
             // If it does and we no longer need to heads up, we should free the view.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index 1ed671f..a5a6d87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -20,7 +20,7 @@
 
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 
 /**
  * Listener interface for changes sent by NotificationEntryManager.
@@ -65,8 +65,7 @@
     /**
      * Called when a notification's views are inflated for the first time.
      */
-    default void onEntryInflated(NotificationEntry entry,
-            @NotificationInflater.InflationFlag int inflatedFlags) {
+    default void onEntryInflated(NotificationEntry entry, @InflationFlag int inflatedFlags) {
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 56922be..3fbc641 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -36,8 +36,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.leak.LeakDetector;
@@ -56,7 +56,7 @@
  */
 public class NotificationEntryManager implements
         Dumpable,
-        NotificationInflater.InflationCallback,
+        NotificationContentInflater.InflationCallback,
         NotificationUpdateHandler,
         VisualStabilityManager.Callback {
     private static final String TAG = "NotificationEntryMgr";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
index f1bb0d7..6f5baf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
@@ -19,8 +19,8 @@
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -42,8 +42,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -78,7 +78,7 @@
     private NotificationPresenter mPresenter;
     private NotificationListContainer mListContainer;
     private HeadsUpManager mHeadsUpManager;
-    private NotificationInflater.InflationCallback mInflationCallback;
+    private NotificationContentInflater.InflationCallback mInflationCallback;
     private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
     private BindRowCallback mBindRowCallback;
     private NotificationClicker mNotificationClicker;
@@ -105,7 +105,7 @@
     public void setUpWithPresenter(NotificationPresenter presenter,
             NotificationListContainer listContainer,
             HeadsUpManager headsUpManager,
-            NotificationInflater.InflationCallback inflationCallback,
+            NotificationContentInflater.InflationCallback inflationCallback,
             BindRowCallback bindRowCallback) {
         mPresenter = presenter;
         mListContainer = listContainer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index f6d4ce22..3bf4d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -54,8 +54,8 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -515,7 +515,7 @@
         if (row != null) row.resetUserExpansion();
     }
 
-    public void freeContentViewWhenSafe(@NotificationInflater.InflationFlag int inflationFlag) {
+    public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
         if (row != null) row.freeContentViewWhenSafe(inflationFlag);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 8f7778b..2b643d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -17,13 +17,13 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationCallback;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -89,7 +89,7 @@
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationMediaTemplateViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -134,7 +134,7 @@
     }
 
     private LayoutListener mLayoutListener;
-    private final NotificationInflater mNotificationInflater;
+    private final NotificationContentInflater mNotificationInflater;
     private int mIconTransformContentShift;
     private int mIconTransformContentShiftNoIcon;
     private int mMaxHeadsUpHeightBeforeN;
@@ -1213,6 +1213,7 @@
             l.initView();
             l.reInflateViews();
         }
+        mStatusBarNotification.clearPackageContext();
         mNotificationInflater.clearCachesAndReInflate();
         onNotificationUpdated();
     }
@@ -1615,7 +1616,7 @@
     }
 
     @VisibleForTesting
-    public NotificationInflater getNotificationInflater() {
+    public NotificationContentInflater getNotificationInflater() {
         return mNotificationInflater;
     }
 
@@ -1630,7 +1631,7 @@
     public ExpandableNotificationRow(Context context, AttributeSet attrs) {
         super(context, attrs);
         mFalsingManager = FalsingManager.getInstance(context);
-        mNotificationInflater = new NotificationInflater(this);
+        mNotificationInflater = new NotificationContentInflater(this);
         mMenuRow = new NotificationMenuRow(mContext);
         mImageResolver = new NotificationInlineImageResolver(context,
                 new NotificationInlineImageCache());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 42ebfce..b34907d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -55,9 +55,9 @@
 /**
  * A utility that inflates the right kind of contentView based on the state
  */
-public class NotificationInflater {
+public class NotificationContentInflater {
 
-    public static final String TAG = "NotificationInflater";
+    public static final String TAG = "NotifContentInflater";
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true,
@@ -127,7 +127,7 @@
     private boolean mRedactAmbient;
     private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
 
-    public NotificationInflater(ExpandableNotificationRow row) {
+    public NotificationContentInflater(ExpandableNotificationRow row) {
         mRow = row;
     }
 
@@ -232,8 +232,7 @@
      * will reinflate it.
      *
      * @param reInflateFlags flags which views should be inflated. Should be a subset of
-     *                       {@link NotificationInflater#mInflationFlags} as only those will be
-     *                       inflated/reinflated.
+     *                       {@link #mInflationFlags} as only those will be inflated/reinflated.
      */
     private void inflateNotificationViews(@InflationFlag int reInflateFlags) {
         if (mRow.isRemoved()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
index a5411ec..6eb376b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
@@ -52,7 +52,7 @@
     @Override
     public void preload(Uri uri) {
         PreloadImageTask newTask = new PreloadImageTask(mResolver);
-        newTask.executeOnExecutor(NotificationInflater.EXECUTOR, uri);
+        newTask.executeOnExecutor(NotificationContentInflater.EXECUTOR, uri);
         mCache.put(uri, newTask);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 4c06ff6..3808702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -26,12 +26,15 @@
 import android.graphics.Paint;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
+import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.TransformableView;
 import com.android.systemui.statusbar.notification.TransformState;
@@ -108,6 +111,11 @@
             return false;
         }
 
+        // Apps targeting Q should fix their dark mode bugs.
+        if (mRow.getEntry().targetSdk >= Build.VERSION_CODES.Q) {
+            return false;
+        }
+
         int background = getBackgroundColor(view);
         if (background == Color.TRANSPARENT) {
             background = defaultBackgroundColor;
@@ -138,17 +146,19 @@
         }
     }
 
-    private boolean childrenNeedInversion(@ColorInt int parentBackground, ViewGroup viewGroup) {
+    @VisibleForTesting
+    boolean childrenNeedInversion(@ColorInt int parentBackground, ViewGroup viewGroup) {
         if (viewGroup == null) {
             return false;
         }
 
+        int backgroundColor = getBackgroundColor(viewGroup);
+        if (Color.alpha(backgroundColor) != 255) {
+            backgroundColor = ContrastColorUtil.compositeColors(backgroundColor, parentBackground);
+            backgroundColor = ColorUtils.setAlphaComponent(backgroundColor, 255);
+        }
         for (int i = 0; i < viewGroup.getChildCount(); i++) {
             View child = viewGroup.getChildAt(i);
-            int backgroundColor = getBackgroundColor(viewGroup);
-            if (backgroundColor == Color.TRANSPARENT) {
-                backgroundColor = parentBackground;
-            }
             if (child instanceof TextView) {
                 int foreground = ((TextView) child).getCurrentTextColor();
                 if (ColorUtils.calculateContrast(foreground, backgroundColor) < 3) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 6410860..195d02d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -191,7 +191,7 @@
     }
 
     protected int adjustDisableFlags(int state) {
-        if (!mStatusBarComponent.isLaunchTransitionFadingAway()
+        if (!mKeyguardMonitor.isLaunchTransitionFadingAway()
                 && !mKeyguardMonitor.isKeyguardFadingAway()
                 && shouldHideNotificationIcons()) {
             state |= DISABLE_NOTIFICATION_ICONS;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
index dae4da7..af442d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
@@ -16,9 +16,17 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.animation.ObjectAnimator;
 import android.annotation.NonNull;
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.util.FloatProperty;
+import android.util.MathUtils;
+import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.WindowManager;
 
@@ -27,6 +35,71 @@
 public class NavigationBarEdgePanel extends View {
     private static final String TAG = "NavigationBarEdgePanel";
 
+    // TODO: read from resources once drawing is finalized.
+    private static final boolean SHOW_PROTECTION_STROKE = true;
+    private static final int PROTECTION_COLOR = 0xffc0c0c0;
+    private static final int STROKE_COLOR = 0xffe5e5e5;
+    private static final int PROTECTION_WIDTH_PX = 4;
+    private static final int BASE_EXTENT = 32;
+    private static final int ARROW_HEIGHT_DP = 32;
+    private static final int POINT_EXTENT_DP = 8;
+    private static final int ARROW_THICKNESS_DP = 4;
+    private static final float TRACK_LENGTH_MULTIPLIER = 1.5f;
+    private static final float START_POINTING_RATIO = 0.3f;
+    private static final float POINTEDNESS_BEFORE_SNAP_RATIO = 0.4f;
+    private static final int ANIM_DURATION_MS = 150;
+
+    private final Paint mPaint = new Paint();
+    private final Paint mProtectionPaint = new Paint();
+
+    private final ObjectAnimator mEndAnimator;
+    private final ObjectAnimator mLegAnimator;
+
+    private final float mDensity;
+    private final float mBaseExtent;
+    private final float mPointExtent;
+    private final float mHeight;
+    private final float mStrokeThickness;
+    private final boolean mIsLeftPanel;
+
+    private float mStartY;
+    private float mStartX;
+
+    private boolean mGestureDetected;
+    private boolean mArrowsPointLeft;
+    private float mGestureLength;
+    private float mLegProgress;
+    private float mDragProgress;
+
+    // How much the "legs" of the back arrow have proceeded from being a line to an arrow.
+    private static final FloatProperty<NavigationBarEdgePanel> LEG_PROGRESS =
+            new FloatProperty<NavigationBarEdgePanel>("legProgress") {
+        @Override
+        public void setValue(NavigationBarEdgePanel object, float value) {
+            object.setLegProgress(value);
+        }
+
+        @Override
+        public Float get(NavigationBarEdgePanel object) {
+            return object.getLegProgress();
+        }
+    };
+
+    // How far across the view the arrow should be drawn.
+    private static final FloatProperty<NavigationBarEdgePanel> DRAG_PROGRESS =
+            new FloatProperty<NavigationBarEdgePanel>("dragProgress") {
+
+                @Override
+                public void setValue(NavigationBarEdgePanel object, float value) {
+                    object.setDragProgress(value);
+                }
+
+                @Override
+                public Float get(NavigationBarEdgePanel object) {
+                    return object.getDragProgress();
+                }
+            };
+
     public static NavigationBarEdgePanel create(@NonNull Context context, int width, int height,
             int gravity) {
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
@@ -40,13 +113,43 @@
         lp.setTitle(TAG + context.getDisplayId());
         lp.accessibilityTitle = context.getString(R.string.nav_bar_edge_panel);
         lp.windowAnimations = 0;
-        NavigationBarEdgePanel panel = new NavigationBarEdgePanel(context);
+        NavigationBarEdgePanel panel = new NavigationBarEdgePanel(
+                context, (gravity & Gravity.LEFT) != 0);
         panel.setLayoutParams(lp);
         return panel;
     }
 
-    private NavigationBarEdgePanel(Context context) {
+    private NavigationBarEdgePanel(Context context, boolean isLeftPanel) {
         super(context);
+
+        mEndAnimator = ObjectAnimator.ofFloat(this, DRAG_PROGRESS, 1f);
+        mEndAnimator.setAutoCancel(true);
+        mEndAnimator.setDuration(ANIM_DURATION_MS);
+
+        mLegAnimator = ObjectAnimator.ofFloat(this, LEG_PROGRESS, 1f);
+        mLegAnimator.setAutoCancel(true);
+        mLegAnimator.setDuration(ANIM_DURATION_MS);
+
+        mDensity = context.getResources().getDisplayMetrics().density;
+
+        mBaseExtent = dp(BASE_EXTENT);
+        mHeight = dp(ARROW_HEIGHT_DP);
+        mPointExtent = dp(POINT_EXTENT_DP);
+        mStrokeThickness = dp(ARROW_THICKNESS_DP);
+
+        mPaint.setStrokeWidth(mStrokeThickness);
+        mPaint.setStrokeCap(Paint.Cap.ROUND);
+        mPaint.setColor(STROKE_COLOR);
+        mPaint.setAntiAlias(true);
+
+        mProtectionPaint.setStrokeWidth(mStrokeThickness + PROTECTION_WIDTH_PX);
+        mProtectionPaint.setStrokeCap(Paint.Cap.ROUND);
+        mProtectionPaint.setColor(PROTECTION_COLOR);
+        mProtectionPaint.setAntiAlias(true);
+
+        // Both panels arrow point the same way
+        mArrowsPointLeft = getLayoutDirection() == LAYOUT_DIRECTION_LTR;
+        mIsLeftPanel = isLeftPanel;
     }
 
     public void setWindowFlag(int flags, boolean enable) {
@@ -62,6 +165,58 @@
         updateLayout(lp);
     }
 
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN : {
+                show(event.getX(), event.getY());
+                break;
+            }
+            case MotionEvent.ACTION_MOVE: {
+                handleNewSwipePoint(event.getX());
+                break;
+            }
+            // Fall through
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL: {
+                hide();
+                break;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        float edgeOffset = mBaseExtent * mDragProgress - mStrokeThickness;
+        float animatedOffset = mPointExtent * mLegProgress;
+        canvas.save();
+        canvas.translate(
+                mIsLeftPanel ? edgeOffset : getWidth() - edgeOffset,
+                mStartY - mHeight * 0.5f);
+
+        float outsideX = mArrowsPointLeft ? animatedOffset : 0;
+        float middleX = mArrowsPointLeft ? 0 : animatedOffset;
+
+        if (SHOW_PROTECTION_STROKE) {
+            canvas.drawLine(outsideX, 0, middleX, mHeight * 0.5f, mProtectionPaint);
+            canvas.drawLine(middleX, mHeight * 0.5f, outsideX, mHeight, mProtectionPaint);
+        }
+
+        canvas.drawLine(outsideX, 0, middleX, mHeight * 0.5f, mPaint);
+        canvas.drawLine(middleX, mHeight * 0.5f, outsideX, mHeight, mPaint);
+        canvas.restore();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        // TODO: read the gesture length from the nav controller.
+        mGestureLength = getWidth();
+    }
+
     public void setDimensions(int width, int height) {
         final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
         if (lp.width != width || lp.height != height) {
@@ -71,8 +226,72 @@
         }
     }
 
+    private void setLegProgress(float progress) {
+        mLegProgress = progress;
+        invalidate();
+    }
+
+    private float getLegProgress() {
+        return mLegProgress;
+    }
+
+    private void setDragProgress(float dragProgress) {
+        mDragProgress = dragProgress;
+        invalidate();
+    }
+
+    private float getDragProgress() {
+        return mDragProgress;
+    }
+
+    private void hide() {
+        animate().alpha(0f).setDuration(ANIM_DURATION_MS);
+    }
+
+    private void show(float x, float y) {
+        mEndAnimator.cancel();
+        mLegAnimator.cancel();
+        setLegProgress(0f);
+        setDragProgress(0f);
+        setAlpha(1f);
+
+        float halfHeight = mHeight * 0.5f;
+        mStartY = MathUtils.constrain(y, halfHeight, getHeight() - halfHeight);
+        mStartX = x;
+    }
+
+    private void handleNewSwipePoint(float x) {
+        float dist = MathUtils.abs(x - mStartX);
+
+        setDragProgress(MathUtils.constrainedMap(
+                0, 1.0f,
+                0, mGestureLength * TRACK_LENGTH_MULTIPLIER,
+                dist));
+
+        if (dist < mGestureLength) {
+            setLegProgress(MathUtils.constrainedMap(
+                    0f, POINTEDNESS_BEFORE_SNAP_RATIO,
+                    mGestureLength * START_POINTING_RATIO, mGestureLength,
+                    dist));
+
+            mGestureDetected = false;
+        } else {
+            if (!mGestureDetected) {
+                performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
+                mGestureDetected = true;
+
+                mLegAnimator.setFloatValues(1f);
+                mLegAnimator.start();
+            }
+        }
+    }
+
     private void updateLayout(WindowManager.LayoutParams lp) {
         WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
         wm.updateViewLayout(this, lp);
     }
+
+    private float dp(float dp) {
+        return mDensity * dp;
+    }
 }
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 ebd4204..8152206 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -1227,10 +1227,11 @@
                     .getSystemService(Context.WINDOW_SERVICE);
             int width = mPrototypeController.getEdgeSensitivityWidth();
             int height = mPrototypeController.getEdgeSensitivityHeight();
+            // Explicitly left and right, not start and end as this is device relative.
             mLeftEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
-                    Gravity.START | Gravity.BOTTOM);
+                    Gravity.LEFT | Gravity.BOTTOM);
             mRightEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
-                    Gravity.END | Gravity.BOTTOM);
+                    Gravity.RIGHT | Gravity.BOTTOM);
             mLeftEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
             mRightEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
             wm.addView(mLeftEdgePanel, mLeftEdgePanel.getLayoutParams());
@@ -1252,15 +1253,12 @@
             mButtonDispatchers.valueAt(i).onDestroy();
         }
 
-        if (mPrototypeController.isEnabled()) {
-            WindowManager wm = (WindowManager) getContext()
-                    .getSystemService(Context.WINDOW_SERVICE);
-            if (mLeftEdgePanel != null) {
-                wm.removeView(mLeftEdgePanel);
-            }
-            if (mRightEdgePanel != null) {
-                wm.removeView(mRightEdgePanel);
-            }
+        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+        if (mLeftEdgePanel != null) {
+            wm.removeView(mLeftEdgePanel);
+        }
+        if (mRightEdgePanel != null) {
+            wm.removeView(mRightEdgePanel);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index b613e8e..4dbd854 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -34,8 +34,8 @@
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.AsyncInflationTask;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 2799191..e0c5e59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -92,6 +92,8 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.NotificationChannels;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.List;
 import java.util.Locale;
 
@@ -793,6 +795,15 @@
         boolean showMicrophone = false;
         boolean showLocation = false;
         for (PrivacyItem item : items) {
+            if (item == null /* b/124234367 */) {
+                if (DEBUG) {
+                    Log.e(TAG, "updatePrivacyItems - null item found");
+                    StringWriter out = new StringWriter();
+                    mPrivacyItemController.dump(null, new PrintWriter(out), null);
+                    Log.e(TAG, out.toString());
+                }
+                continue;
+            }
             switch (item.getPrivacyType()) {
                 case TYPE_CAMERA:
                     showCamera = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 6b6998d..30d5b65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -117,7 +117,6 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
 import android.widget.DateTimeView;
-import android.widget.ImageView;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
@@ -189,7 +188,6 @@
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
@@ -461,9 +459,6 @@
     protected boolean mDozing;
     private boolean mDozingRequested;
 
-    protected BackDropView mBackdrop;
-    protected ImageView mBackdropFront, mBackdropBack;
-
     private NotificationMediaManager mMediaManager;
     protected NotificationLockscreenUserManager mLockscreenUserManager;
     protected NotificationRemoteInputManager mRemoteInputManager;
@@ -493,7 +488,6 @@
     };
 
     private Runnable mLaunchTransitionEndRunnable;
-    protected boolean mLaunchTransitionFadingAway;
     private NotificationEntry mDraggedDownEntry;
     private boolean mLaunchCameraOnScreenTurningOn;
     private boolean mLaunchCameraOnFinishedGoingToSleep;
@@ -653,7 +647,7 @@
 
         mColorExtractor.addOnColorsChangedListener(this);
         mStatusBarStateController.addCallback(this,
-                StatusBarStateControllerImpl.RANK_STATUS_BAR);
+                SysuiStatusBarStateController.RANK_STATUS_BAR);
 
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mDreamManager = IDreamManager.Stub.asInterface(
@@ -933,11 +927,9 @@
                 mHeadsUpManager, mNotificationIconAreaController, mScrimController);
         mDozeScrimController = new DozeScrimController(DozeParameters.getInstance(context));
 
-        mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop);
-        mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front);
-        mBackdropBack = mBackdrop.findViewById(R.id.backdrop_back);
-        mMediaManager.setup(mBackdrop, mBackdropFront, mBackdropBack,
-                mScrimController, mLockscreenWallpaper);
+        BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop);
+        mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
+                backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);
 
         // Other icons
         mVolumeComponent = getComponent(VolumeComponent.class);
@@ -1593,10 +1585,6 @@
         return mPulsing;
     }
 
-    public boolean isLaunchTransitionFadingAway() {
-        return mLaunchTransitionFadingAway;
-    }
-
     public boolean hideStatusBarIconsWhenExpanded() {
         return mNotificationPanel.hideStatusBarIconsWhenExpanded();
     }
@@ -1893,6 +1881,8 @@
 
             mStatusBarWindow.cancelExpandHelper();
             mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
+        } else {
+            mBubbleController.collapseStack();
         }
     }
 
@@ -2533,6 +2523,9 @@
                 if (mRemoteInputManager.getController() != null) {
                     mRemoteInputManager.getController().closeRemoteInputs();
                 }
+                if (mBubbleController.isStackExpanded()) {
+                    mBubbleController.collapseStack();
+                }
                 if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
                     int flags = CommandQueue.FLAG_EXCLUDE_NONE;
                     String reason = intent.getStringExtra("reason");
@@ -2546,6 +2539,9 @@
                 if (mStatusBarWindowController != null) {
                     mStatusBarWindowController.setNotTouchable(false);
                 }
+                if (mBubbleController.isStackExpanded()) {
+                    mBubbleController.collapseStack();
+                }
                 finishBarAnimations();
                 resetUserExpandedStates();
             }
@@ -2967,7 +2963,7 @@
 
     public void showKeyguardImpl() {
         mIsKeyguard = true;
-        if (mLaunchTransitionFadingAway) {
+        if (mKeyguardMonitor.isLaunchTransitionFadingAway()) {
             mNotificationPanel.animate().cancel();
             onLaunchTransitionFadingEnded();
         }
@@ -2999,7 +2995,7 @@
         mNotificationPanel.onAffordanceLaunchEnded();
         releaseGestureWakeLock();
         runLaunchTransitionEndRunnable();
-        mLaunchTransitionFadingAway = false;
+        mKeyguardMonitor.setLaunchTransitionFadingAway(false);
         mPresenter.updateMediaMetaData(true /* metaDataChanged */, true);
     }
 
@@ -3025,7 +3021,6 @@
         mLaunchTransitionEndRunnable = endRunnable;
         Runnable hideRunnable = () -> {
             mKeyguardMonitor.setLaunchTransitionFadingAway(true);
-            mLaunchTransitionFadingAway = true;
             if (beforeFading != null) {
                 beforeFading.run();
             }
@@ -4004,8 +3999,7 @@
                 float viewY = screenY - mTmpInt2[1];
                 if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth()
                         && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) {
-                    if (mAmbientIndicationContainer instanceof DozeReceiver)
-                    ((DozeReceiver) mAmbientIndicationContainer).onDozeDoubleTap();
+                    dispatchTap(mAmbientIndicationContainer, viewX, viewY);
                 }
             }
         }
@@ -4020,6 +4014,12 @@
             mScrimController.setAodFrontScrimAlpha(scrimOpacity);
         }
 
+        private void dispatchTap(View view, float x, float y) {
+            long now = SystemClock.elapsedRealtime();
+            dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN);
+            dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_UP);
+        }
+
         private void dispatchTouchEvent(View view, float x, float y, long now, int action) {
             MotionEvent ev = MotionEvent.obtain(now, now, action, x, y, 0 /* meta */);
             view.dispatchTouchEvent(ev);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index fd3f680..0461057 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -31,7 +31,7 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index e73c70b..efb4ff0 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -68,6 +68,13 @@
             </intent-filter>
         </receiver>
 
+        <activity android:name="com.android.systemui.bubbles.BubblesTestActivity"
+            android:allowEmbedded="true"
+            android:documentLaunchMode="always"
+            android:excludeFromRecents="true"
+            android:exported="false"
+            android:resizeableActivity="true" />
+
         <provider
             android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
             tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index 77895c9..190ce75 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -75,6 +75,17 @@
     }
 
     @Test
+    public void hasHeader_readsSliceData() {
+        ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
+        mKeyguardSliceView.onChanged(builder.build());
+        Assert.assertFalse("View should not have a header", mKeyguardSliceView.hasHeader());
+
+        builder.setHeader(new ListBuilder.HeaderBuilder().setTitle("header title!"));
+        mKeyguardSliceView.onChanged(builder.build());
+        Assert.assertTrue("View should have a header", mKeyguardSliceView.hasHeader());
+    }
+
+    @Test
     public void refresh_replacesSliceContentAndNotifiesListener() {
         AtomicBoolean notified = new AtomicBoolean();
         mKeyguardSliceView.setContentChangeListener(()-> notified.set(true));
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
new file mode 100644
index 0000000..f813ac6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.LeakCheck;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dock.DockManager;
+import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.utils.leaks.FakeExtensionController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class ClockManagerTest extends SysuiTestCase {
+
+    private ClockManager mClockManager;
+    private LeakCheck mLeakCheck;
+    private FakeExtensionController mFakeExtensionController;
+    private DockManagerFake mFakeDockManager;
+    @Mock ClockManager.ClockChangedListener mMockListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mLeakCheck = new LeakCheck();
+        mFakeExtensionController = new FakeExtensionController(mLeakCheck);
+        mFakeDockManager = new DockManagerFake();
+        mClockManager = new ClockManager(getContext(), mFakeExtensionController,
+                mFakeDockManager);
+        mClockManager.addOnClockChangedListener(mMockListener);
+    }
+
+    @After
+    public void tearDown() {
+        mClockManager.removeOnClockChangedListener(mMockListener);
+    }
+
+    @Test
+    public void dockEvent() {
+        mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED);
+        assertThat(mClockManager.isDocked()).isTrue();
+    }
+
+    @Test
+    public void undockEvent() {
+        mFakeDockManager.setDockEvent(DockManager.STATE_NONE);
+        assertThat(mClockManager.isDocked()).isFalse();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/DefaultClockSupplierTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/DefaultClockSupplierTest.java
new file mode 100644
index 0000000..1a3b198
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/DefaultClockSupplierTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ClockPlugin;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class DefaultClockSupplierTest extends SysuiTestCase {
+
+    private static final String BUBBLE_CLOCK = BubbleClockController.class.getName();
+    private static final Class<?> BUBBLE_CLOCK_CLASS = BubbleClockController.class;
+
+    private DefaultClockSupplier mDefaultClockSupplier;
+    @Mock SettingsWrapper mMockSettingsWrapper;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mDefaultClockSupplier = new DefaultClockSupplier(mMockSettingsWrapper,
+                LayoutInflater.from(getContext()));
+    }
+
+    @Test
+    public void get_default() {
+        // GIVEN that settings doesn't contain any values
+        when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(null);
+        when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(null);
+        // WHEN get is called
+        ClockPlugin plugin = mDefaultClockSupplier.get();
+        // THEN the result is null, indicated the default clock face should be used.
+        assertThat(plugin).isNull();
+    }
+
+    @Test
+    public void get_customClock() {
+        // GIVEN that settings is set to the bubble clock face
+        when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK);
+        // WHEN get is called
+        ClockPlugin plugin = mDefaultClockSupplier.get();
+        // THEN the plugin is the bubble clock face.
+        assertThat(plugin).isInstanceOf(BUBBLE_CLOCK_CLASS);
+    }
+
+    @Test
+    public void get_badSettingsValue() {
+        // GIVEN that settings contains a value that doesn't correspond to a
+        // custom clock face.
+        when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn("bad value");
+        // WHEN get is called
+        ClockPlugin plugin = mDefaultClockSupplier.get();
+        // THEN the result is null.
+        assertThat(plugin).isNull();
+    }
+
+    @Test
+    public void get_dockedDefault() {
+        // GIVEN docked
+        mDefaultClockSupplier.setDocked(true);
+        // WHEN get is called
+        ClockPlugin plugin = mDefaultClockSupplier.get();
+        // THEN the result is null, indicating the default clock face.
+        assertThat(plugin).isNull();
+    }
+
+    @Test
+    public void get_dockedCustomClock() {
+        // GIVEN docked and settings is set to the bubble clock face
+        mDefaultClockSupplier.setDocked(true);
+        when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(BUBBLE_CLOCK);
+        // WHEN get is called
+        ClockPlugin plugin = mDefaultClockSupplier.get();
+        // THEN the plugin is the bubble clock face.
+        assertThat(plugin).isInstanceOf(BUBBLE_CLOCK_CLASS);
+    }
+
+    @Test
+    public void get_badDockedSettingsValue() {
+        // GIVEN docked and settings contains a value that doesn't correspond to
+        // an available clock face.
+        mDefaultClockSupplier.setDocked(true);
+        when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value");
+        // WHEN get is called
+        ClockPlugin plugin = mDefaultClockSupplier.get();
+        // THEN the result is null.
+        assertThat(plugin).isNull();
+    }
+
+    @Test
+    public void get_badDockedSettingsFallback() {
+        // GIVEN docked and settings contains a value that doesn't correspond to
+        // an available clock face, but locked screen settings is set to bubble
+        // clock.
+        mDefaultClockSupplier.setDocked(true);
+        when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value");
+        when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK);
+        // WHEN get is called
+        ClockPlugin plugin = mDefaultClockSupplier.get();
+        // THEN the plugin is the bubble clock face.
+        assertThat(plugin).isInstanceOf(BUBBLE_CLOCK_CLASS);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 2742577..ca72602 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -82,6 +82,8 @@
     @Mock
     private BubbleController.BubbleExpandListener mBubbleExpandListener;
 
+    private BubbleData mBubbleData;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -104,7 +106,9 @@
         when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel);
         when(mNotificationData.getChannel(mNoChannelRow.getEntry().key)).thenReturn(null);
 
-        mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController);
+        mBubbleData = new BubbleData();
+        mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController,
+                mBubbleData);
         mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
@@ -207,12 +211,12 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
 
         // Last added is the one that is expanded
-        assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
         assertFalse(mRow2.getEntry().showInShadeWhenBubble());
 
         // Switch which bubble is expanded
         stackView.setExpandedBubble(mRow.getEntry());
-        assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry());
         assertFalse(mRow.getEntry().showInShadeWhenBubble());
 
         // collapse for previous bubble
@@ -262,19 +266,19 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
 
         // Last added is the one that is expanded
-        assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
         assertFalse(mRow2.getEntry().showInShadeWhenBubble());
 
         // Dismiss currently expanded
-        mBubbleController.removeBubble(stackView.getExpandedBubble().getKey());
+        mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey());
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key);
 
         // Make sure next bubble is selected
-        assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
 
         // Dismiss that one
-        mBubbleController.removeBubble(stackView.getExpandedBubble().getKey());
+        mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey());
 
         // Make sure state changes and collapse happens
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
@@ -297,8 +301,8 @@
     static class TestableBubbleController extends BubbleController {
 
         TestableBubbleController(Context context,
-                StatusBarWindowController statusBarWindowController) {
-            super(context, statusBarWindowController);
+                StatusBarWindowController statusBarWindowController, BubbleData data) {
+            super(context, statusBarWindowController, data);
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
new file mode 100644
index 0000000..ea472da
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubblesTestActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.systemui.R;
+
+/**
+ * Referenced by NotificationTestHelper#makeBubbleMetadata
+ */
+public class BubblesTestActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
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 c0aac7e..3bd582f 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
@@ -22,6 +22,8 @@
 import android.graphics.PointF;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.widget.FrameLayout;
 
 import androidx.dynamicanimation.animation.DynamicAnimation;
 
@@ -63,15 +65,13 @@
     public void testExpansionAndCollapse() throws InterruptedException {
         Runnable afterExpand = Mockito.mock(Runnable.class);
         mExpandedController.expandFromStack(mExpansionPoint, afterExpand);
-
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
 
-        testExpanded();
+        testBubblesInCorrectExpandedPositions();
         Mockito.verify(afterExpand).run();
 
         Runnable afterCollapse = Mockito.mock(Runnable.class);
         mExpandedController.collapseBackToStack(afterCollapse);
-
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
 
         testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y, -1);
@@ -79,17 +79,70 @@
     }
 
     @Test
-    public void testOnChildRemoved() throws InterruptedException {
-        Runnable afterExpand = Mockito.mock(Runnable.class);
-        mExpandedController.expandFromStack(mExpansionPoint, afterExpand);
+    public void testOnChildAdded() throws InterruptedException {
+        expand();
+
+        // Add another new view and wait for its animation.
+        final View newView = new FrameLayout(getContext());
+        mLayout.addView(newView, 0);
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
-        testExpanded();
+
+        testBubblesInCorrectExpandedPositions();
+    }
+
+    @Test
+    public void testOnChildRemoved() throws InterruptedException {
+        expand();
 
         // Remove some views and see if the remaining child views still pass the expansion test.
         mLayout.removeView(mViews.get(0));
         mLayout.removeView(mViews.get(3));
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
-        testExpanded();
+        testBubblesInCorrectExpandedPositions();
+    }
+
+    @Test
+    public void testBubbleDraggedNotDismissedSnapsBack() throws InterruptedException {
+        expand();
+
+        final View draggedBubble = mViews.get(0);
+        mExpandedController.prepareForBubbleDrag(draggedBubble);
+        mExpandedController.dragBubbleOut(draggedBubble, 500f, 500f);
+
+        assertEquals(500f, draggedBubble.getTranslationX(), 1f);
+        assertEquals(500f, draggedBubble.getTranslationY(), 1f);
+
+        // Snap it back and make sure it made it back correctly.
+        mExpandedController.snapBubbleBack(draggedBubble, 0f, 0f);
+        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+        testBubblesInCorrectExpandedPositions();
+    }
+
+    @Test
+    public void testBubbleDismissed() throws InterruptedException {
+        expand();
+
+        final View draggedBubble = mViews.get(0);
+        mExpandedController.prepareForBubbleDrag(draggedBubble);
+        mExpandedController.dragBubbleOut(draggedBubble, 500f, 500f);
+
+        assertEquals(500f, draggedBubble.getTranslationX(), 1f);
+        assertEquals(500f, draggedBubble.getTranslationY(), 1f);
+
+        // Snap it back and make sure it made it back correctly.
+        mExpandedController.prepareForDismissalWithVelocity(draggedBubble, 0f, 0f);
+        mLayout.removeView(draggedBubble);
+        waitForLayoutMessageQueue();
+        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+
+        assertEquals(-1, mLayout.indexOfChild(draggedBubble));
+        testBubblesInCorrectExpandedPositions();
+    }
+
+    /** Expand the stack and wait for animations to finish. */
+    private void expand() throws InterruptedException {
+        mExpandedController.expandFromStack(mExpansionPoint, Mockito.mock(Runnable.class));
+        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
     }
 
     /** Check that children are in the correct positions for being stacked. */
@@ -108,7 +161,7 @@
     }
 
     /** Check that children are in the correct positions for being expanded. */
-    private void testExpanded() {
+    private void testBubblesInCorrectExpandedPositions() {
         // Check all the visible bubbles to see if they're in the right place.
         for (int i = 0; i < Math.min(mLayout.getChildCount(), mMaxRenderedBubbles); i++) {
             assertEquals(mBubblePadding + (i * (mBubbleSize + mBubblePadding)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
index 31e44d7..d94b669 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
@@ -155,6 +155,11 @@
         }
 
         @Override
+        public void cancelAnimationsOnView(View view) {
+            mMainThreadHandler.post(() -> super.cancelAnimationsOnView(view));
+        }
+
+        @Override
         protected void animateValueForChildAtIndex(DynamicAnimation.ViewProperty property,
                 int index, float value, float startVel, Runnable after) {
             mMainThreadHandler.post(() ->
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index 98bf3c27..bb384dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager
 import android.app.AppOpsManager
+import android.content.Context
 import android.content.Intent
 import android.content.pm.UserInfo
 import android.os.Handler
@@ -28,11 +29,18 @@
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import com.android.systemui.Dependency
+import com.android.systemui.Dependency.BG_HANDLER
+import com.android.systemui.Dependency.MAIN_HANDLER
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.appops.AppOpItem
 import com.android.systemui.appops.AppOpsController
+import org.hamcrest.Matchers.hasItem
+import org.hamcrest.Matchers.not
+import org.hamcrest.Matchers.nullValue
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThat
+import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -81,15 +89,20 @@
 
     private lateinit var testableLooper: TestableLooper
     private lateinit var privacyItemController: PrivacyItemController
+    private lateinit var handler: Handler
+
+    fun PrivacyItemController(context: Context) =
+            PrivacyItemController(context, appOpsController, handler, handler)
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
         testableLooper = TestableLooper.get(this)
+        handler = Handler(testableLooper.looper)
 
         appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
-        mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
-        mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper))
+        mDependency.injectTestDependency(Dependency.BG_HANDLER, handler)
+        mDependency.injectTestDependency(Dependency.MAIN_HANDLER, handler)
         mContext.addMockSystemService(UserManager::class.java, userManager)
         mContext.getOrCreateTestableResources().addOverride(R.string.device_services,
                 DEVICE_SERVICES_STRING)
@@ -232,4 +245,26 @@
         verify(callback, never()).privacyChanged(anyList())
         verify(otherCallback).privacyChanged(anyList())
     }
+
+    @Test
+    fun testListShouldNotHaveNull() {
+        doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0),
+                        AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
+                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+        privacyItemController.addCallback(callback)
+        testableLooper.processAllMessages()
+
+        verify(callback).privacyChanged(capture(argCaptor))
+        assertEquals(1, argCaptor.value.size)
+        assertThat(argCaptor.value, not(hasItem(nullValue())))
+    }
+
+    @Test
+    fun testListShouldBeCopy() {
+        val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA,
+                PrivacyApplication("", TEST_UID, mContext)))
+        privacyItemController.privacyList = list
+        assertEquals(list, privacyItemController.privacyList)
+        assertTrue(list !== privacyItemController.privacyList)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 660f853..7b1253a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -17,7 +17,7 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_CONTRACTED;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 0b24c21..3c919a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -36,10 +36,11 @@
 import android.widget.RemoteViews;
 
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubblesTestActivity;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
-import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflaterTest;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -278,7 +279,7 @@
         entry.channel.setBlockableSystem(true);
         row.setEntry(entry);
         row.getNotificationInflater().addInflationFlags(extraInflationFlags);
-        NotificationInflaterTest.runThenWaitForInflation(
+        NotificationContentInflaterTest.runThenWaitForInflation(
                 () -> row.inflateViews(),
                 row.getNotificationInflater());
 
@@ -290,7 +291,8 @@
     }
 
     private Notification.BubbleMetadata makeBubbleMetadata() {
-        PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Intent target = new Intent(mContext, BubblesTestActivity.class);
+        PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, 0);
         return new Notification.BubbleMetadata.Builder()
                 .setIntent(bubbleIntent)
                 .setTitle("bubble title")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 79bc0a3..04e7cab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -68,8 +68,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -143,7 +143,7 @@
 
         @Override
         public void onAsyncInflationFinished(NotificationEntry entry,
-                @NotificationInflater.InflationFlag int inflatedFlags) {
+                @InflationFlag int inflatedFlags) {
             super.onAsyncInflationFinished(entry, inflatedFlags);
 
             mCountDownLatch.countDown();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 6d35539..f6fb416 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -18,9 +18,9 @@
 
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
similarity index 85%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 648df3c..dfaa76a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -16,11 +16,11 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_EXPANDED;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_EXPANDED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -49,6 +49,7 @@
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationCallback;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -64,9 +65,9 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
-public class NotificationInflaterTest extends SysuiTestCase {
+public class NotificationContentInflaterTest extends SysuiTestCase {
 
-    private NotificationInflater mNotificationInflater;
+    private NotificationContentInflater mNotificationInflater;
     private Notification.Builder mBuilder;
     private ExpandableNotificationRow mRow;
 
@@ -80,8 +81,8 @@
         ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(
                 mBuilder.build());
         mRow = spy(row);
-        mNotificationInflater = new NotificationInflater(mRow);
-        mNotificationInflater.setInflationCallback(new NotificationInflater.InflationCallback() {
+        mNotificationInflater = new NotificationContentInflater(mRow);
+        mNotificationInflater.setInflationCallback(new InflationCallback() {
             @Override
             public void handleInflationException(StatusBarNotification notification,
                     Exception e) {
@@ -89,7 +90,7 @@
 
             @Override
             public void onAsyncInflationFinished(NotificationEntry entry,
-                    @NotificationInflater.InflationFlag int inflatedFlags) {
+                    @NotificationContentInflater.InflationFlag int inflatedFlags) {
             }
         });
     }
@@ -158,14 +159,14 @@
     @Test
     @Ignore
     public void testInflationIsRetriedIfAsyncFails() throws Exception {
-        NotificationInflater.InflationProgress result =
-                new NotificationInflater.InflationProgress();
+        NotificationContentInflater.InflationProgress result =
+                new NotificationContentInflater.InflationProgress();
         result.packageContext = mContext;
         CountDownLatch countDownLatch = new CountDownLatch(1);
-        NotificationInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0,
+        NotificationContentInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0,
                 new ArrayMap() /* cachedContentViews */, mRow, false /* redactAmbient */,
                 true /* isNewView */, (v, p, r) -> true,
-                new NotificationInflater.InflationCallback() {
+                new InflationCallback() {
                     @Override
                     public void handleInflationException(StatusBarNotification notification,
                             Exception e) {
@@ -175,11 +176,11 @@
 
                     @Override
                     public void onAsyncInflationFinished(NotificationEntry entry,
-                            @NotificationInflater.InflationFlag int inflatedFlags) {
+                            @NotificationContentInflater.InflationFlag int inflatedFlags) {
                         countDownLatch.countDown();
                     }
                 }, mRow.getPrivateLayout(), null, null, new HashMap<>(),
-                new NotificationInflater.ApplyCallback() {
+                new NotificationContentInflater.ApplyCallback() {
                     @Override
                     public void setResultView(View v) {
                     }
@@ -199,8 +200,8 @@
         mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_PUBLIC, true);
         mNotificationInflater.updateNeedsRedaction(true);
 
-        NotificationInflater.AsyncInflationTask asyncInflationTask =
-                (NotificationInflater.AsyncInflationTask) mRow.getEntry().getRunningTask();
+        NotificationContentInflater.AsyncInflationTask asyncInflationTask =
+                (NotificationContentInflater.AsyncInflationTask) mRow.getEntry().getRunningTask();
         assertEquals(FLAG_CONTENT_VIEW_AMBIENT | FLAG_CONTENT_VIEW_PUBLIC,
                 asyncInflationTask.getReInflateFlags());
         asyncInflationTask.abort();
@@ -217,8 +218,8 @@
         mNotificationInflater.setIsChildInGroup(true);
 
         InflationTask runningTask = mRow.getEntry().getRunningTask();
-        NotificationInflater.AsyncInflationTask asyncInflationTask =
-                (NotificationInflater.AsyncInflationTask) runningTask;
+        NotificationContentInflater.AsyncInflationTask asyncInflationTask =
+                (NotificationContentInflater.AsyncInflationTask) runningTask;
         assertEquals("Successive inflations don't inherit the previous flags!",
                 FLAG_CONTENT_VIEW_ALL, asyncInflationTask.getReInflateFlags());
         runningTask.abort();
@@ -233,19 +234,19 @@
                 R.layout.custom_view_dark));
         RemoteViews decoratedMediaView = mBuilder.createContentView();
         Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!",
-                NotificationInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
+                NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
     }
 
     public static void runThenWaitForInflation(Runnable block,
-            NotificationInflater inflater) throws Exception {
+            NotificationContentInflater inflater) throws Exception {
         runThenWaitForInflation(block, false /* expectingException */, inflater);
     }
 
     private static void runThenWaitForInflation(Runnable block, boolean expectingException,
-            NotificationInflater inflater) throws Exception {
+            NotificationContentInflater inflater) throws Exception {
         CountDownLatch countDownLatch = new CountDownLatch(1);
         final ExceptionHolder exceptionHolder = new ExceptionHolder();
-        inflater.setInflationCallback(new NotificationInflater.InflationCallback() {
+        inflater.setInflationCallback(new InflationCallback() {
             @Override
             public void handleInflationException(StatusBarNotification notification,
                     Exception e) {
@@ -257,7 +258,7 @@
 
             @Override
             public void onAsyncInflationFinished(NotificationEntry entry,
-                    @NotificationInflater.InflationFlag int inflatedFlags) {
+                    @NotificationContentInflater.InflationFlag int inflatedFlags) {
                 if (expectingException) {
                     exceptionHolder.setException(new RuntimeException(
                             "Inflation finished even though there should be an error"));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
similarity index 63%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
index 24aa772..637b30c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row.wrapper;
+
+import static org.mockito.Mockito.mock;
 
 import android.content.Context;
 import android.support.test.filters.SmallTest;
@@ -22,13 +24,15 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.util.Assert;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -37,12 +41,26 @@
 @RunWithLooper
 public class NotificationViewWrapperTest extends SysuiTestCase {
 
-    @Test
-    public void constructor_doesntUseViewContext() throws Exception {
+    private View mView;
+    private ExpandableNotificationRow mRow;
+    private TestableNotificationViewWrapper mNotificationViewWrapper;
+
+    @Before
+    public void setup() throws Exception {
         Assert.sMainLooper = TestableLooper.get(this).getLooper();
-        new TestableNotificationViewWrapper(mContext,
-                new View(mContext),
-                new NotificationTestHelper(getContext()).createRow());
+        mView = mock(View.class);
+        mRow = new NotificationTestHelper(getContext()).createRow();
+        mNotificationViewWrapper = new TestableNotificationViewWrapper(mContext, mView, mRow);
+    }
+
+    @Test
+    public void childrenNeedInversion_doesntCrash_whenOpacity() {
+        LinearLayout viewGroup = new LinearLayout(mContext);
+        TextView textView = new TextView(mContext);
+        textView.setTextColor(0xcc000000);
+        viewGroup.addView(textView);
+
+        mNotificationViewWrapper.childrenNeedInversion(0xcc000000, viewGroup);
     }
 
     static class TestableNotificationViewWrapper extends NotificationViewWrapper {
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 9b863a9..61f63d3 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -47,7 +47,7 @@
 
     private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName();
 
-    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
+    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
 
     RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
             int userId, RemoteAugmentedAutofillServiceCallbacks callbacks,
@@ -106,6 +106,12 @@
                 activityComponent, focusedId, focusedValue));
     }
 
+    @Override
+    public String toString() {
+        return "RemoteAugmentedAutofillService["
+                + ComponentName.flattenToShortString(getComponentName()) + "]";
+    }
+
     /**
      * Called by {@link Session} when it's time to destroy all augmented autofill requests.
      */
@@ -181,11 +187,13 @@
 
         @Override
         protected void onTimeout(RemoteAugmentedAutofillService remoteService) {
-            Slog.wtf(TAG, "timed out: " + this);
+            // TODO(b/122858578): must update the logged AUTOFILL_AUGMENTED_REQUEST with the
+            // timeout
+            Slog.w(TAG, "PendingAutofillRequest timed out (" + TIMEOUT_REMOTE_REQUEST_MILLIS
+                    + "ms) for " + remoteService);
             // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks
             finish();
         }
-
     }
 
     public interface RemoteAugmentedAutofillServiceCallbacks
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 17e9b35..00cb6d3 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -47,6 +47,8 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
+import com.android.server.backup.utils.FileUtils;
+import com.android.server.backup.utils.RandomAccessFileUtils;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -89,6 +91,12 @@
      */
     private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated";
 
+    /**
+     * Name of file for non-system users that remembers whether backup was explicitly activated or
+     * deactivated with a call to setBackupServiceActive.
+     */
+    private static final String REMEMBER_ACTIVATED_FILENAME_PREFIX = "backup-remember-activated";
+
     // Product-level suppression of backup/restore.
     private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
 
@@ -134,11 +142,17 @@
     }
 
     /** Stored in the system user's directory and the file is indexed by the user it refers to. */
-    protected File getActivatedFileForNonSystemUser(int userId) {
-        return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
-                BACKUP_ACTIVATED_FILENAME + "-" + userId);
+    protected File getRememberActivatedFileForNonSystemUser(int userId) {
+        return FileUtils.createNewFile(UserBackupManagerFiles.getStateFileInSystemDir(
+                REMEMBER_ACTIVATED_FILENAME_PREFIX, userId));
     }
 
+    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+    protected File getActivatedFileForNonSystemUser(int userId) {
+        return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId);
+    }
+
+    // TODO (b/124359804) move to util method in FileUtils
     private void createFile(File file) throws IOException {
         if (file.exists()) {
             return;
@@ -150,6 +164,7 @@
         }
     }
 
+    // TODO (b/124359804) move to util method in FileUtils
     private void deleteFile(File file) {
         if (!file.exists()) {
             return;
@@ -312,6 +327,19 @@
     public void setBackupServiceActive(int userId, boolean makeActive) {
         enforcePermissionsOnUser(userId);
 
+        // In Q, backup is OFF by default for non-system users. In the future, we will change that
+        // to ON unless backup was explicitly deactivated with a (permissioned) call to
+        // setBackupServiceActive.
+        // Therefore, remember this for use in the future. Basically the default in the future will
+        // be: rememberFile.exists() ? rememberFile.value() : ON
+        // Note that this has to be done right after the permission checks and before any other
+        // action since we need to remember that a permissioned call was made irrespective of
+        // whether the call changes the state or not.
+        if (userId != UserHandle.USER_SYSTEM) {
+            RandomAccessFileUtils.writeBoolean(getRememberActivatedFileForNonSystemUser(userId),
+                    makeActive);
+        }
+
         if (mGlobalDisable) {
             Slog.i(TAG, "Backup service not supported");
             return;
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
index aabd41a..4638ac6 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
@@ -48,4 +48,9 @@
         // is a staging dir, we dont need to copy below dir to new system user dir
         return new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
     }
+
+    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+    static File getStateFileInSystemDir(String prefix, int userId) {
+        return new File(getBaseStateDir(UserHandle.USER_SYSTEM), prefix + "-" + userId);
+    }
 }
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index b2afbc3..d4ac731 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -130,6 +130,7 @@
 import com.android.server.backup.utils.AppBackupUtils;
 import com.android.server.backup.utils.BackupManagerMonitorUtils;
 import com.android.server.backup.utils.BackupObserverUtils;
+import com.android.server.backup.utils.FileUtils;
 import com.android.server.backup.utils.SparseArrayUtils;
 
 import com.google.android.collect.Sets;
@@ -2319,6 +2320,7 @@
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
                 "setAncestralSerialNumber");
         Slog.v(TAG, "Setting ancestral work profile id to " + ancestralSerialNumber);
+        // TODO (b/124359804)
         try (RandomAccessFile af = getAncestralSerialNumberFile()) {
             af.writeLong(ancestralSerialNumber);
         } catch (IOException e) {
@@ -2331,6 +2333,7 @@
      * {@link #setAncestralSerialNumber(long)}. Will return {@code -1} if not set.
      */
     public long getAncestralSerialNumber() {
+        // TODO (b/124359804)
         try (RandomAccessFile af = getAncestralSerialNumberFile()) {
             return af.readLong();
         } catch (IOException e) {
@@ -2344,13 +2347,7 @@
             mAncestralSerialNumberFile = new File(
                 UserBackupManagerFiles.getBaseStateDir(getUserId()),
                 SERIAL_ID_FILE);
-            if (!mAncestralSerialNumberFile.exists()) {
-                try {
-                    mAncestralSerialNumberFile.createNewFile();
-                } catch (IOException e) {
-                    Slog.w(TAG, "serial number mapping file creation failed", e);
-                }
-            }
+            FileUtils.createNewFile(mAncestralSerialNumberFile);
         }
         return new RandomAccessFile(mAncestralSerialNumberFile, "rwd");
     }
diff --git a/services/backup/java/com/android/server/backup/utils/FileUtils.java b/services/backup/java/com/android/server/backup/utils/FileUtils.java
new file mode 100644
index 0000000..00686cb
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/utils/FileUtils.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.utils;
+
+import static com.android.server.backup.BackupManagerService.TAG;
+
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+
+/** Utility methods useful for working with backup related files. */
+public final class FileUtils {
+    /**
+     * Ensure that the file exists in the file system. If an IOException is thrown, it is ignored.
+     * This method is useful to avoid code duplication of the "try-catch-ignore exception" block.
+     */
+    public static File createNewFile(File file) {
+        try {
+            file.createNewFile();
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to create file:" + file.getAbsolutePath(), e);
+        }
+        return file;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java b/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java
new file mode 100644
index 0000000..abf906a
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.utils;
+
+import static com.android.server.backup.BackupManagerService.TAG;
+
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/** Utility methods useful for working with backup related RandomAccessFiles. */
+public final class RandomAccessFileUtils {
+    private static RandomAccessFile getRandomAccessFile(File file) throws FileNotFoundException {
+        return new RandomAccessFile(file, "rwd");
+    }
+
+    /** Write a boolean to a File by wrapping it using a RandomAccessFile. */
+    public static void writeBoolean(File file, boolean b) {
+        try (RandomAccessFile af = getRandomAccessFile(file)) {
+            af.writeBoolean(b);
+        } catch (IOException e) {
+            Slog.w(TAG, "Error writing file:" + file.getAbsolutePath(), e);
+        }
+    }
+
+    /** Read a boolean from a File by wrapping it using a RandomAccessFile. */
+    public static boolean readBoolean(File file, boolean def) {
+        try (RandomAccessFile af = getRandomAccessFile(file)) {
+            return af.readBoolean();
+        } catch (IOException e) {
+            Slog.w(TAG, "Error reading file:" + file.getAbsolutePath(), e);
+        }
+        return def;
+    }
+}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index 3c52e17..4ed5c3d 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -37,6 +37,9 @@
     final IBinder mActivityToken;
     private final ContentCapturePerUserService mService;
     private final RemoteContentCaptureService mRemoteService;
+
+    // NOTE: this is the "internal" context (like package and taskId), not the explicit content
+    // set by apps - those are only send to the ContentCaptureService.
     private final ContentCaptureContext mContentCaptureContext;
 
     /**
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d1cd072..915c131 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -57,8 +57,10 @@
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
+import android.net.CaptivePortal;
 import android.net.ConnectionInfo;
 import android.net.ConnectivityManager;
+import android.net.ICaptivePortal;
 import android.net.IConnectivityManager;
 import android.net.IIpConnectivityMetrics;
 import android.net.INetd;
@@ -86,6 +88,7 @@
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
+import android.net.NetworkStackClient;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.NetworkWatchlistManager;
@@ -917,7 +920,8 @@
 
         mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
 
-        //set up the listener for user state for creating user VPNs
+        // Set up the listener for user state for creating user VPNs.
+        // Should run on mHandler to avoid any races.
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_STARTED);
         intentFilter.addAction(Intent.ACTION_USER_STOPPED);
@@ -925,7 +929,11 @@
         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiverAsUser(
-                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+                mIntentReceiver,
+                UserHandle.ALL,
+                intentFilter,
+                null /* broadcastPermission */,
+                mHandler);
         mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
                 new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
 
@@ -936,7 +944,11 @@
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
         mContext.registerReceiverAsUser(
-                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+                mIntentReceiver,
+                UserHandle.ALL,
+                intentFilter,
+                null /* broadcastPermission */,
+                mHandler);
 
         try {
             mNMS.registerObserver(mTethering);
@@ -2690,11 +2702,6 @@
                     EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE,
                     mNai.network.netId));
         }
-
-        @Override
-        public void logCaptivePortalLoginEvent(int eventId, String packageName) {
-            new MetricsLogger().action(eventId, packageName);
-        }
     }
 
     private boolean networkRequiresValidation(NetworkAgentInfo nai) {
@@ -2842,6 +2849,8 @@
         if (DBG) {
             log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
         }
+        // Clear all notifications of this network.
+        mNotifier.clearNotification(nai.network.netId);
         // A network agent has disconnected.
         // TODO - if we move the logic to the network agent (have them disconnect
         // because they lost all their requests or because their score isn't good)
@@ -3247,22 +3256,63 @@
     /**
      * NetworkStack endpoint to start the captive portal app. The NetworkStack needs to use this
      * endpoint as it does not have INTERACT_ACROSS_USERS_FULL itself.
+     * @param network Network on which the captive portal was detected.
      * @param appExtras Bundle to use as intent extras for the captive portal application.
      *                  Must be treated as opaque to avoid preventing the captive portal app to
      *                  update its arguments.
      */
     @Override
-    public void startCaptivePortalAppInternal(Bundle appExtras) {
+    public void startCaptivePortalAppInternal(Network network, Bundle appExtras) {
         mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
 
         final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
         appIntent.putExtras(appExtras);
+        appIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
+                new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
         appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
 
         Binder.withCleanCallingIdentity(() ->
                 mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
     }
 
+    private class CaptivePortalImpl extends ICaptivePortal.Stub {
+        private final Network mNetwork;
+
+        private CaptivePortalImpl(Network network) {
+            mNetwork = network;
+        }
+
+        @Override
+        public void appResponse(final int response) throws RemoteException {
+            if (response == CaptivePortal.APP_RETURN_WANTED_AS_IS) {
+                enforceSettingsPermission();
+            }
+
+            // getNetworkAgentInfoForNetwork is thread-safe
+            final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(mNetwork);
+            if (nai == null) return;
+
+            // nai.networkMonitor() is thread-safe
+            final INetworkMonitor nm = nai.networkMonitor();
+            if (nm == null) return;
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                nm.notifyCaptivePortalAppFinished(response);
+            } finally {
+                // Not using Binder.withCleanCallingIdentity() to keep the checked RemoteException
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void logEvent(int eventId, String packageName) {
+            enforceSettingsPermission();
+
+            new MetricsLogger().action(eventId, packageName);
+        }
+    }
+
     public boolean avoidBadWifi() {
         return mMultinetworkPolicyTracker.getAvoidBadWifi();
     }
@@ -4097,17 +4147,27 @@
      * handler thread through their agent, this is asynchronous. When the capabilities objects
      * are computed they will be up-to-date as they are computed synchronously from here and
      * this is running on the ConnectivityService thread.
-     * TODO : Fix this and call updateCapabilities inline to remove out-of-order events.
      */
     private void updateAllVpnsCapabilities() {
+        Network defaultNetwork = getNetwork(getDefaultNetwork());
         synchronized (mVpns) {
             for (int i = 0; i < mVpns.size(); i++) {
                 final Vpn vpn = mVpns.valueAt(i);
-                vpn.updateCapabilities();
+                NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+                updateVpnCapabilities(vpn, nc);
             }
         }
     }
 
+    private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) {
+        ensureRunningOnConnectivityServiceThread();
+        NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId());
+        if (vpnNai == null || nc == null) {
+            return;
+        }
+        updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc);
+    }
+
     @Override
     public boolean updateLockdownVpn() {
         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
@@ -4448,22 +4508,28 @@
 
     private void onUserAdded(int userId) {
         mPermissionMonitor.onUserAdded(userId);
+        Network defaultNetwork = getNetwork(getDefaultNetwork());
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
                 Vpn vpn = mVpns.valueAt(i);
                 vpn.onUserAdded(userId);
+                NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+                updateVpnCapabilities(vpn, nc);
             }
         }
     }
 
     private void onUserRemoved(int userId) {
         mPermissionMonitor.onUserRemoved(userId);
+        Network defaultNetwork = getNetwork(getDefaultNetwork());
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
                 Vpn vpn = mVpns.valueAt(i);
                 vpn.onUserRemoved(userId);
+                NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+                updateVpnCapabilities(vpn, nc);
             }
         }
     }
@@ -4532,6 +4598,7 @@
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            ensureRunningOnConnectivityServiceThread();
             final String action = intent.getAction();
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
             final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
@@ -5041,6 +5108,19 @@
         return getNetworkForRequest(mDefaultRequest.requestId);
     }
 
+    @Nullable
+    private Network getNetwork(@Nullable NetworkAgentInfo nai) {
+        return nai != null ? nai.network : null;
+    }
+
+    private void ensureRunningOnConnectivityServiceThread() {
+        if (mHandler.getLooper().getThread() != Thread.currentThread()) {
+            throw new IllegalStateException(
+                    "Not running on ConnectivityService thread: "
+                            + Thread.currentThread().getName());
+        }
+    }
+
     private boolean isDefaultNetwork(NetworkAgentInfo nai) {
         return nai == getDefaultNetwork();
     }
@@ -5097,7 +5177,7 @@
         if (DBG) log("registerNetworkAgent " + nai);
         final long token = Binder.clearCallingIdentity();
         try {
-            mContext.getSystemService(NetworkStack.class).makeNetworkMonitor(
+            getNetworkStack().makeNetworkMonitor(
                     toStableParcelable(nai.network), name, new NetworkMonitorCallbacks(nai));
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -5109,6 +5189,11 @@
         return nai.network.netId;
     }
 
+    @VisibleForTesting
+    protected NetworkStackClient getNetworkStack() {
+        return NetworkStackClient.getInstance();
+    }
+
     private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
         nai.onNetworkMonitorCreated(networkMonitor);
         if (VDBG) log("Got NetworkAgent Messenger");
@@ -5667,6 +5752,8 @@
         updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
         mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
         notifyIfacesChangedForNetworkStats();
+        // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
+        updateAllVpnsCapabilities();
     }
 
     private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
@@ -6106,6 +6193,10 @@
             // doing.
             updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
 
+            if (networkAgent.isVPN()) {
+                updateAllVpnsCapabilities();
+            }
+
             // Consider network even though it is not yet validated.
             final long now = SystemClock.elapsedRealtime();
             rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now);
@@ -6367,7 +6458,11 @@
             success = mVpns.get(user).setUnderlyingNetworks(networks);
         }
         if (success) {
-            mHandler.post(() -> notifyIfacesChangedForNetworkStats());
+            mHandler.post(() -> {
+                // Update VPN's capabilities based on updated underlying network set.
+                updateAllVpnsCapabilities();
+                notifyIfacesChangedForNetworkStats();
+            });
         }
         return success;
     }
diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java
index ebd4c55..eb59152 100644
--- a/services/core/java/com/android/server/ExtconUEventObserver.java
+++ b/services/core/java/com/android/server/ExtconUEventObserver.java
@@ -49,7 +49,7 @@
     private static final String TAG = "ExtconUEventObserver";
     private static final boolean LOG = false;
     private static final String SELINUX_POLICIES_NEED_TO_BE_CHANGED =
-            "This probably mean the selinux policies need to be changed.";
+            "This probably means the selinux policies need to be changed.";
 
     private final Map<String, ExtconInfo> mExtconInfos = new ArrayMap<>();
 
@@ -159,6 +159,15 @@
     /** Does the {@link /sys/class/extcon} directory exist */
     public static boolean extconExists() {
         File extconDir = new File("/sys/class/extcon");
-        return extconDir.exists() && extconDir.isDirectory();
+        boolean retVal = extconDir.exists() && extconDir.isDirectory();
+        // TODO(b/124364409): return the correct value after selinux policy is updated.
+        if (retVal) {
+            Slog.w(TAG, extconDir + " exists " + extconDir.exists() + " isDir "
+                    + extconDir.isDirectory()
+                    + " but reporting it does not exist until selinux policies are updated."
+                    + " see b/124364409"
+            );
+        }
+        return false;
     }
 }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 3479e18..9d92ea2b 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1645,36 +1645,6 @@
         }
     }
 
-    private abstract static class LinkedListenerBase implements IBinder.DeathRecipient {
-        protected final CallerIdentity mCallerIdentity;
-        protected final String mListenerName;
-
-        private LinkedListenerBase(@NonNull CallerIdentity callerIdentity,
-                @NonNull String listenerName) {
-            mCallerIdentity = callerIdentity;
-            mListenerName = listenerName;
-        }
-    }
-
-    private static class LinkedListener<TListener> extends LinkedListenerBase {
-        private final TListener mListener;
-        private final Consumer<TListener> mBinderDeathCallback;
-
-        private LinkedListener(@NonNull TListener listener, String listenerName,
-                @NonNull CallerIdentity callerIdentity,
-                @NonNull Consumer<TListener> binderDeathCallback) {
-            super(callerIdentity, listenerName);
-            mListener = listener;
-            mBinderDeathCallback = binderDeathCallback;
-        }
-
-        @Override
-        public void binderDied() {
-            if (D) Log.d(TAG, "Remote " + mListenerName + " died.");
-            mBinderDeathCallback.accept(mListener);
-        }
-    }
-
     @Override
     public void removeGnssBatchingCallback() {
         synchronized (mLock) {
@@ -2069,7 +2039,7 @@
                 }
                 if (!provider.isUseableLocked()) {
                     if (isSettingsExemptLocked(record)) {
-                        providerRequest.forceLocation = true;
+                        providerRequest.locationSettingsIgnored = true;
                         providerRequest.lowPowerMode = false;
                     } else {
                         continue;
@@ -2079,8 +2049,9 @@
                 LocationRequest locationRequest = record.mRealRequest;
                 long interval = locationRequest.getInterval();
 
+
                 // if we're forcing location, don't apply any throttling
-                if (!providerRequest.forceLocation && !isThrottlingExemptLocked(
+                if (!providerRequest.locationSettingsIgnored && !isThrottlingExemptLocked(
                         record.mReceiver.mCallerIdentity)) {
                     if (!record.mIsForegroundUid) {
                         interval = Math.max(interval, backgroundThrottleInterval);
@@ -2710,77 +2681,85 @@
 
     @Override
     public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName) {
-        if (!hasGnssPermissions(packageName) || mGnssStatusProvider == null) {
-            return false;
-        }
-
-        CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
-                Binder.getCallingPid(), packageName);
-        LinkedListener<IGnssStatusListener> linkedListener = new LinkedListener<>(listener,
-                "GnssStatusListener", callerIdentity, this::unregisterGnssStatusCallback);
-        IBinder binder = listener.asBinder();
-        synchronized (mLock) {
-            if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) {
-                return false;
-            }
-
-            mGnssStatusListeners.put(binder, linkedListener);
-            long identity = Binder.clearCallingIdentity();
-            try {
-                if (isThrottlingExemptLocked(callerIdentity)
-                        || isImportanceForeground(
-                        mActivityManager.getPackageImportance(packageName))) {
-                    mGnssStatusProvider.addListener(listener, callerIdentity);
-                }
-                return true;
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
+        return addGnssDataListener(listener, packageName, "GnssStatusListener",
+                mGnssStatusProvider, mGnssStatusListeners,
+                this::unregisterGnssStatusCallback);
     }
 
     @Override
     public void unregisterGnssStatusCallback(IGnssStatusListener listener) {
-        if (mGnssStatusProvider == null) {
-            return;
-        }
-
-        IBinder binder = listener.asBinder();
-        synchronized (mLock) {
-            LinkedListener<IGnssStatusListener> linkedListener =
-                    mGnssStatusListeners.remove(binder);
-            if (linkedListener == null) {
-                return;
-            }
-            unlinkFromListenerDeathNotificationLocked(binder, linkedListener);
-            mGnssStatusProvider.removeListener(listener);
-        }
+        removeGnssDataListener(listener, mGnssStatusProvider, mGnssStatusListeners);
     }
 
     @Override
     public boolean addGnssMeasurementsListener(
             IGnssMeasurementsListener listener, String packageName) {
-        if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
+        return addGnssDataListener(listener, packageName, "GnssMeasurementsListener",
+                mGnssMeasurementsProvider, mGnssMeasurementsListeners,
+                this::removeGnssMeasurementsListener);
+    }
+
+    @Override
+    public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
+        removeGnssDataListener(listener, mGnssMeasurementsProvider, mGnssMeasurementsListeners);
+    }
+
+    private abstract static class LinkedListenerBase implements IBinder.DeathRecipient {
+        protected final CallerIdentity mCallerIdentity;
+        protected final String mListenerName;
+
+        private LinkedListenerBase(@NonNull CallerIdentity callerIdentity,
+                @NonNull String listenerName) {
+            mCallerIdentity = callerIdentity;
+            mListenerName = listenerName;
+        }
+    }
+
+    private static class LinkedListener<TListener> extends LinkedListenerBase {
+        private final TListener mListener;
+        private final Consumer<TListener> mBinderDeathCallback;
+
+        private LinkedListener(@NonNull TListener listener, String listenerName,
+                @NonNull CallerIdentity callerIdentity,
+                @NonNull Consumer<TListener> binderDeathCallback) {
+            super(callerIdentity, listenerName);
+            mListener = listener;
+            mBinderDeathCallback = binderDeathCallback;
+        }
+
+        @Override
+        public void binderDied() {
+            if (D) Log.d(TAG, "Remote " + mListenerName + " died.");
+            mBinderDeathCallback.accept(mListener);
+        }
+    }
+
+    private <TListener extends IInterface> boolean addGnssDataListener(
+            TListener listener, String packageName, String listenerName,
+            RemoteListenerHelper<TListener> gnssDataProvider,
+            ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners,
+            Consumer<TListener> binderDeathCallback) {
+        if (!hasGnssPermissions(packageName) || gnssDataProvider == null) {
             return false;
         }
 
         CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
                 Binder.getCallingPid(), packageName);
-        LinkedListener<IGnssMeasurementsListener> linkedListener = new LinkedListener<>(listener,
-                "GnssMeasurementsListener", callerIdentity, this::removeGnssMeasurementsListener);
+        LinkedListener<TListener> linkedListener = new LinkedListener<>(listener,
+                listenerName, callerIdentity, binderDeathCallback);
         IBinder binder = listener.asBinder();
         synchronized (mLock) {
             if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) {
                 return false;
             }
 
-            mGnssMeasurementsListeners.put(binder, linkedListener);
+            gnssDataListeners.put(binder, linkedListener);
             long identity = Binder.clearCallingIdentity();
             try {
                 if (isThrottlingExemptLocked(callerIdentity)
                         || isImportanceForeground(
                         mActivityManager.getPackageImportance(packageName))) {
-                    mGnssMeasurementsProvider.addListener(listener, callerIdentity);
+                    gnssDataProvider.addListener(listener, callerIdentity);
                 }
                 return true;
             } finally {
@@ -2789,25 +2768,24 @@
         }
     }
 
-    @Override
-    public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
-        if (mGnssMeasurementsProvider == null) {
+    private <TListener extends IInterface> void removeGnssDataListener(
+            TListener listener, RemoteListenerHelper<TListener> gnssDataProvider,
+            ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners) {
+        if (gnssDataProvider == null) {
             return;
         }
 
         IBinder binder = listener.asBinder();
         synchronized (mLock) {
-            LinkedListener<IGnssMeasurementsListener> linkedListener =
-                    mGnssMeasurementsListeners.remove(binder);
+            LinkedListener<TListener> linkedListener = gnssDataListeners.remove(binder);
             if (linkedListener == null) {
                 return;
             }
             unlinkFromListenerDeathNotificationLocked(binder, linkedListener);
-            mGnssMeasurementsProvider.removeListener(listener);
+            gnssDataProvider.removeListener(listener);
         }
     }
 
-
     private boolean linkToListenerDeathNotificationLocked(IBinder binder,
             LinkedListenerBase linkedListener) {
         try {
@@ -2863,52 +2841,15 @@
     @Override
     public boolean addGnssNavigationMessageListener(
             IGnssNavigationMessageListener listener, String packageName) {
-        if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) {
-            return false;
-        }
-
-        CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
-                Binder.getCallingPid(), packageName);
-        LinkedListener<IGnssNavigationMessageListener> linkedListener =
-                new LinkedListener<>(listener, "GnssNavigationMessageListener", callerIdentity,
-                        this::removeGnssNavigationMessageListener);
-        IBinder binder = listener.asBinder();
-        synchronized (mLock) {
-            if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) {
-                return false;
-            }
-
-            mGnssNavigationMessageListeners.put(binder, linkedListener);
-            long identity = Binder.clearCallingIdentity();
-            try {
-                if (isThrottlingExemptLocked(callerIdentity)
-                        || isImportanceForeground(
-                        mActivityManager.getPackageImportance(packageName))) {
-                    mGnssNavigationMessageProvider.addListener(listener, callerIdentity);
-                }
-                return true;
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
+        return addGnssDataListener(listener, packageName, "GnssNavigationMessageListener",
+                mGnssNavigationMessageProvider, mGnssNavigationMessageListeners,
+                this::removeGnssNavigationMessageListener);
     }
 
     @Override
     public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
-        if (mGnssNavigationMessageProvider == null) {
-            return;
-        }
-
-        IBinder binder = listener.asBinder();
-        synchronized (mLock) {
-            LinkedListener<IGnssNavigationMessageListener> linkedListener =
-                    mGnssNavigationMessageListeners.remove(binder);
-            if (linkedListener == null) {
-                return;
-            }
-            unlinkFromListenerDeathNotificationLocked(binder, linkedListener);
-            mGnssNavigationMessageProvider.removeListener(listener);
-        }
+        removeGnssDataListener(listener, mGnssNavigationMessageProvider,
+                mGnssNavigationMessageListeners);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index f505b76..dc394d0 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -20,18 +20,18 @@
 import static android.Manifest.permission.NETWORK_SETTINGS;
 import static android.Manifest.permission.NETWORK_STACK;
 import static android.Manifest.permission.SHUTDOWN;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_BLACKLIST;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_NONE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
+import static android.net.INetd.FIREWALL_WHITELIST;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
-import static android.net.NetworkPolicyManager.FIREWALL_TYPE_BLACKLIST;
-import static android.net.NetworkPolicyManager.FIREWALL_TYPE_WHITELIST;
 import static android.net.NetworkStats.SET_DEFAULT;
 import static android.net.NetworkStats.STATS_PER_UID;
 import static android.net.NetworkStats.TAG_ALL;
@@ -1946,7 +1946,7 @@
 
         int numUids = 0;
         if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName);
-        if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) {
+        if (getFirewallType(chain) == FIREWALL_WHITELIST) {
             // Close all sockets on all non-system UIDs...
             ranges = new UidRange[] {
                 // TODO: is there a better way of finding all existing users? If so, we could
@@ -1958,7 +1958,7 @@
                 final SparseIntArray rules = getUidFirewallRulesLR(chain);
                 exemptUids = new int[rules.size()];
                 for (int i = 0; i < exemptUids.length; i++) {
-                    if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_ALLOW) {
+                    if (rules.valueAt(i) == FIREWALL_RULE_ALLOW) {
                         exemptUids[numUids] = rules.keyAt(i);
                         numUids++;
                     }
@@ -1980,7 +1980,7 @@
                 final SparseIntArray rules = getUidFirewallRulesLR(chain);
                 ranges = new UidRange[rules.size()];
                 for (int i = 0; i < ranges.length; i++) {
-                    if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_DENY) {
+                    if (rules.valueAt(i) == FIREWALL_RULE_DENY) {
                         int uid = rules.keyAt(i);
                         ranges[numUids] = new UidRange(uid, uid);
                         numUids++;
@@ -2052,13 +2052,13 @@
     private int getFirewallType(int chain) {
         switch (chain) {
             case FIREWALL_CHAIN_STANDBY:
-                return FIREWALL_TYPE_BLACKLIST;
+                return FIREWALL_BLACKLIST;
             case FIREWALL_CHAIN_DOZABLE:
-                return FIREWALL_TYPE_WHITELIST;
+                return FIREWALL_WHITELIST;
             case FIREWALL_CHAIN_POWERSAVE:
-                return FIREWALL_TYPE_WHITELIST;
+                return FIREWALL_WHITELIST;
             default:
-                return isFirewallEnabled() ? FIREWALL_TYPE_WHITELIST : FIREWALL_TYPE_BLACKLIST;
+                return isFirewallEnabled() ? FIREWALL_WHITELIST : FIREWALL_BLACKLIST;
         }
     }
 
@@ -2160,14 +2160,14 @@
 
     private @NonNull String getFirewallRuleName(int chain, int rule) {
         String ruleName;
-        if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) {
-            if (rule == NetworkPolicyManager.FIREWALL_RULE_ALLOW) {
+        if (getFirewallType(chain) == FIREWALL_WHITELIST) {
+            if (rule == FIREWALL_RULE_ALLOW) {
                 ruleName = "allow";
             } else {
                 ruleName = "deny";
             }
         } else { // Blacklist mode
-            if (rule == NetworkPolicyManager.FIREWALL_RULE_DENY) {
+            if (rule == FIREWALL_RULE_DENY) {
                 ruleName = "deny";
             } else {
                 ruleName = "allow";
@@ -2194,7 +2194,7 @@
 
     private int getFirewallRuleType(int chain, int rule) {
         if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
-            return getFirewallType(chain) == FIREWALL_TYPE_WHITELIST
+            return getFirewallType(chain) == FIREWALL_WHITELIST
                     ? INetd.FIREWALL_RULE_DENY : INetd.FIREWALL_RULE_ALLOW;
         }
         return rule;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 2f1510e..8120976 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -26,6 +26,7 @@
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -246,7 +247,10 @@
     private PreciseDataConnectionState mPreciseDataConnectionState =
                 new PreciseDataConnectionState();
 
-    static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK =
+    // Nothing here yet, but putting it here in case we want to add more in the future.
+    static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = 0;
+
+    static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK =
             PhoneStateListener.LISTEN_CELL_LOCATION
                     | PhoneStateListener.LISTEN_CELL_INFO;
 
@@ -637,8 +641,14 @@
                     if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
                         try {
                             if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
-                            r.callback.onServiceStateChanged(
-                                    new ServiceState(mServiceState[phoneId]));
+                            ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
+                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                r.callback.onServiceStateChanged(rawSs);
+                            } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(false));
+                            } else {
+                                r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(true));
+                            }
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -673,7 +683,7 @@
                         try {
                             if (DBG_LOC) log("listen: mCellLocation = "
                                     + mCellLocation[phoneId]);
-                            if (checkLocationAccess(r)) {
+                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                                 r.callback.onCellLocationChanged(
                                         new Bundle(mCellLocation[phoneId]));
                             }
@@ -722,7 +732,7 @@
                         try {
                             if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
                                     + mCellInfo.get(phoneId));
-                            if (checkLocationAccess(r)) {
+                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                                 r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
                             }
                         } catch (RemoteException ex) {
@@ -1009,13 +1019,22 @@
                     }
                     if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) &&
                             idMatch(r.subId, subId, phoneId)) {
+
                         try {
+                            ServiceState stateToSend;
+                            if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                stateToSend = new ServiceState(state);
+                            } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                stateToSend = state.sanitizeLocationInfo(false);
+                            } else {
+                                stateToSend = state.sanitizeLocationInfo(true);
+                            }
                             if (DBG) {
                                 log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
                                         + " subId=" + subId + " phoneId=" + phoneId
                                         + " state=" + state);
                             }
-                            r.callback.onServiceStateChanged(new ServiceState(state));
+                            r.callback.onServiceStateChanged(stateToSend);
                         } catch (RemoteException ex) {
                             mRemoveList.add(r.binder);
                         }
@@ -1198,7 +1217,7 @@
                 for (Record r : mRecords) {
                     if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
                             idMatch(r.subId, subId, phoneId) &&
-                            checkLocationAccess(r)) {
+                            checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r);
@@ -1500,7 +1519,7 @@
                 for (Record r : mRecords) {
                     if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
                             idMatch(r.subId, subId, phoneId) &&
-                            checkLocationAccess(r)) {
+                            checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifyCellLocation: cellLocation=" + cellLocation
@@ -2108,12 +2127,35 @@
 
     private boolean checkListenerPermission(
             int events, int subId, String callingPackage, String message) {
+        LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
+                new LocationAccessPolicy.LocationPermissionQuery.Builder()
+                .setCallingPackage(callingPackage)
+                .setMethod(message + " events: " + events)
+                .setCallingPid(Binder.getCallingPid())
+                .setCallingUid(Binder.getCallingUid());
+
+        boolean shouldCheckLocationPermissions = false;
         if ((events & ENFORCE_COARSE_LOCATION_PERMISSION_MASK) != 0) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
-            if (mAppOps.noteOp(AppOpsManager.OP_COARSE_LOCATION, Binder.getCallingUid(),
-                    callingPackage) != AppOpsManager.MODE_ALLOWED) {
-                return false;
+            locationQueryBuilder.setMinSdkVersionForCoarse(0);
+            shouldCheckLocationPermissions = true;
+        }
+
+        if ((events & ENFORCE_FINE_LOCATION_PERMISSION_MASK) != 0) {
+            // Everything that requires fine location started in Q. So far...
+            locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q);
+            shouldCheckLocationPermissions = true;
+        }
+
+        if (shouldCheckLocationPermissions) {
+            LocationAccessPolicy.LocationPermissionResult result =
+                    LocationAccessPolicy.checkLocationPermission(
+                            mContext, locationQueryBuilder.build());
+            switch (result) {
+                case DENIED_HARD:
+                    throw new SecurityException("Unable to listen for events " + events + " due to "
+                            + "insufficient location permissions.");
+                case DENIED_SOFT:
+                    return false;
             }
         }
 
@@ -2228,15 +2270,38 @@
         }
     }
 
-    private boolean checkLocationAccess(Record r) {
-        long token = Binder.clearCallingIdentity();
-        try {
-            return LocationAccessPolicy.canAccessCellLocation(mContext,
-                    r.callingPackage, r.callerUid, r.callerPid,
-                    /*throwOnDeniedPermission*/ false);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+    private boolean checkFineLocationAccess(Record r, int minSdk) {
+        LocationAccessPolicy.LocationPermissionQuery query =
+                new LocationAccessPolicy.LocationPermissionQuery.Builder()
+                        .setCallingPackage(r.callingPackage)
+                        .setCallingPid(r.callerPid)
+                        .setCallingUid(r.callerUid)
+                        .setMethod("TelephonyRegistry push")
+                        .setMinSdkVersionForFine(minSdk)
+                        .build();
+
+        return Binder.withCleanCallingIdentity(() -> {
+            LocationAccessPolicy.LocationPermissionResult locationResult =
+                    LocationAccessPolicy.checkLocationPermission(mContext, query);
+            return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+        });
+    }
+
+    private boolean checkCoarseLocationAccess(Record r, int minSdk) {
+        LocationAccessPolicy.LocationPermissionQuery query =
+                new LocationAccessPolicy.LocationPermissionQuery.Builder()
+                        .setCallingPackage(r.callingPackage)
+                        .setCallingPid(r.callerPid)
+                        .setCallingUid(r.callerUid)
+                        .setMethod("TelephonyRegistry push")
+                        .setMinSdkVersionForCoarse(minSdk)
+                        .build();
+
+        return Binder.withCleanCallingIdentity(() -> {
+            LocationAccessPolicy.LocationPermissionResult locationResult =
+                    LocationAccessPolicy.checkLocationPermission(mContext, query);
+            return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+        });
     }
 
     private void checkPossibleMissNotify(Record r, int phoneId) {
@@ -2286,7 +2351,7 @@
                     log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
                             + mCellInfo.get(phoneId));
                 }
-                if (checkLocationAccess(r)) {
+                if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                     r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
                 }
             } catch (RemoteException ex) {
@@ -2336,7 +2401,7 @@
             try {
                 if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = "
                         + mCellLocation[phoneId]);
-                if (checkLocationAccess(r)) {
+                if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                     r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
                 }
             } catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c4a9db6..2f20572 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18100,8 +18100,10 @@
                     if (!queue.isIdle()) {
                         final String msg = "Waiting for queue " + queue + " to become idle...";
                         pw.println(msg);
+                        pw.println(queue.describeState());
                         pw.flush();
                         Slog.v(TAG, msg);
+                        queue.cancelDeferrals();
                         idle = false;
                     }
                 }
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 24543b7..236797b 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -477,6 +477,10 @@
                 mStats.updateRpmStatsLocked();
             }
 
+            if ((updateFlags & UPDATE_RAIL) != 0) {
+                mStats.updateRailStatsLocked();
+            }
+
             if (bluetoothInfo != null) {
                 if (bluetoothInfo.isValid()) {
                     mStats.updateBluetoothStateLocked(bluetoothInfo);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0890032..4d5cb8c 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -59,6 +59,7 @@
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.PowerProfile;
+import com.android.internal.os.RailStats;
 import com.android.internal.os.RpmStats;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.ParseUtils;
@@ -84,7 +85,8 @@
  */
 public final class BatteryStatsService extends IBatteryStats.Stub
         implements PowerManagerInternal.LowPowerModeListener,
-        BatteryStatsImpl.PlatformIdleStateCallback {
+        BatteryStatsImpl.PlatformIdleStateCallback,
+        BatteryStatsImpl.RailEnergyDataCallback {
     static final String TAG = "BatteryStatsService";
     static final boolean DBG = false;
 
@@ -98,6 +100,7 @@
     private native void getLowPowerStats(RpmStats rpmStats);
     private native int getPlatformLowPowerStats(ByteBuffer outBuffer);
     private native int getSubsystemLowPowerStats(ByteBuffer outBuffer);
+    private native void getRailEnergyPowerStats(RailStats railStats);
     private CharsetDecoder mDecoderStat = StandardCharsets.UTF_8
                     .newDecoder()
                     .onMalformedInput(CodingErrorAction.REPLACE)
@@ -121,6 +124,16 @@
     }
 
     @Override
+    public void fillRailDataStats(RailStats railStats) {
+        if (DBG) Slog.d(TAG, "begin getRailEnergyPowerStats");
+        try {
+            getRailEnergyPowerStats(railStats);
+        } finally {
+            if (DBG) Slog.d(TAG, "end getRailEnergyPowerStats");
+        }
+    }
+
+    @Override
     public String getPlatformLowPowerStats() {
         if (DBG) Slog.d(TAG, "begin getPlatformLowPowerStats");
         try {
@@ -177,7 +190,8 @@
                 return (umi != null) ? umi.getUserIds() : null;
             }
         };
-        mStats = new BatteryStatsImpl(systemDir, handler, this, mUserManagerUserInfoProvider);
+        mStats = new BatteryStatsImpl(systemDir, handler, this,
+                this, mUserManagerUserInfoProvider);
         mWorker = new BatteryExternalStatsWorker(context, mStats);
         mStats.setExternalStatsSyncLocked(mWorker);
         mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
@@ -1460,7 +1474,8 @@
                                 in.unmarshall(raw, 0, raw.length);
                                 in.setDataPosition(0);
                                 BatteryStatsImpl checkinStats = new BatteryStatsImpl(
-                                        null, mStats.mHandler, null, mUserManagerUserInfoProvider);
+                                        null, mStats.mHandler, null, null,
+                                        mUserManagerUserInfoProvider);
                                 checkinStats.readSummaryFromParcel(in);
                                 in.recycle();
                                 checkinStats.dumpProtoLocked(
@@ -1498,7 +1513,8 @@
                                 in.unmarshall(raw, 0, raw.length);
                                 in.setDataPosition(0);
                                 BatteryStatsImpl checkinStats = new BatteryStatsImpl(
-                                        null, mStats.mHandler, null, mUserManagerUserInfoProvider);
+                                        null, mStats.mHandler, null, null,
+                                        mUserManagerUserInfoProvider);
                                 checkinStats.readSummaryFromParcel(in);
                                 in.recycle();
                                 checkinStats.dumpCheckinLocked(mContext, pw, apps, flags,
diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java
index 6371cd3..0b38ef9 100644
--- a/services/core/java/com/android/server/am/BroadcastDispatcher.java
+++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java
@@ -65,6 +65,14 @@
             broadcasts.add(br);
         }
 
+        int size() {
+            return broadcasts.size();
+        }
+
+        boolean isEmpty() {
+            return broadcasts.isEmpty();
+        }
+
         void writeToProto(ProtoOutputStream proto, long fieldId) {
             for (BroadcastRecord br : broadcasts) {
                 br.writeToProto(proto, fieldId);
@@ -252,22 +260,48 @@
         synchronized (mLock) {
             return mCurrentBroadcast == null
                     && mOrderedBroadcasts.isEmpty()
-                    && mDeferredBroadcasts.isEmpty()
-                    && mAlarmBroadcasts.isEmpty();
+                    && isDeferralsListEmpty(mDeferredBroadcasts)
+                    && isDeferralsListEmpty(mAlarmBroadcasts);
         }
     }
 
-    /**
-     * Not quite the traditional size() measurement; includes any in-process but
-     * not yet retired active outbound broadcast.
-     */
-    public int totalUndelivered() {
-        synchronized (mLock) {
-            return mAlarmBroadcasts.size()
-                    + mDeferredBroadcasts.size()
-                    + mOrderedBroadcasts.size()
-                    + (mCurrentBroadcast == null ? 0 : 1);
+    private static int pendingInDeferralsList(ArrayList<Deferrals> list) {
+        int pending = 0;
+        final int numEntries = list.size();
+        for (int i = 0; i < numEntries; i++) {
+            pending += list.get(i).size();
         }
+        return pending;
+    }
+
+    private static boolean isDeferralsListEmpty(ArrayList<Deferrals> list) {
+        return pendingInDeferralsList(list) == 0;
+    }
+
+    /**
+     * Strictly for logging, describe the currently pending contents in a human-
+     * readable way
+     */
+    public String describeStateLocked() {
+        final StringBuilder sb = new StringBuilder(128);
+        if (mCurrentBroadcast != null) {
+            sb.append("1 in flight, ");
+        }
+        sb.append(mOrderedBroadcasts.size());
+        sb.append(" ordered");
+        int n = pendingInDeferralsList(mAlarmBroadcasts);
+        if (n > 0) {
+            sb.append(", ");
+            sb.append(n);
+            sb.append(" deferrals in alarm recipients");
+        }
+        n = pendingInDeferralsList(mDeferredBroadcasts);
+        if (n > 0) {
+            sb.append(", ");
+            sb.append(n);
+            sb.append(" deferred");
+        }
+        return sb.toString();
     }
 
     // ----------------------------------
@@ -579,6 +613,26 @@
         }
     }
 
+    /**
+     * Cancel all current deferrals; that is, make all currently-deferred broadcasts
+     * immediately deliverable.  Used by the wait-for-broadcast-idle mechanism.
+     */
+    public void cancelDeferrals() {
+        synchronized (mLock) {
+            zeroDeferralTimes(mAlarmBroadcasts);
+            zeroDeferralTimes(mDeferredBroadcasts);
+        }
+    }
+
+    private static void zeroDeferralTimes(ArrayList<Deferrals> list) {
+        final int num = list.size();
+        for (int i = 0; i < num; i++) {
+            Deferrals d = list.get(i);
+            // Safe to do this in-place because it won't break ordering
+            d.deferUntil = d.deferredBy = 0;
+        }
+    }
+
     // ----------------------------------
 
     /**
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index f0b137a..d9ea1da 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -898,6 +898,11 @@
         for (int i = perms.length-1; i >= 0; i--) {
             try {
                 PermissionInfo pi = pm.getPermissionInfo(perms[i], "android", 0);
+                if (pi == null) {
+                    // a required permission that no package has actually
+                    // defined cannot be signature-required.
+                    return false;
+                }
                 if ((pi.protectionLevel & (PermissionInfo.PROTECTION_MASK_BASE
                         | PermissionInfo.PROTECTION_FLAG_PRIVILEGED))
                         != PermissionInfo.PROTECTION_SIGNATURE) {
@@ -923,8 +928,8 @@
 
         if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
                 + mQueueName + "]: "
-                + mParallelBroadcasts.size() + " parallel broadcasts, "
-                + mDispatcher.totalUndelivered() + " ordered broadcasts");
+                + mParallelBroadcasts.size() + " parallel broadcasts; "
+                + mDispatcher.describeStateLocked());
 
         mService.updateCpuStats();
 
@@ -1822,11 +1827,24 @@
                 record.intent == null ? "" : record.intent.getAction());
     }
 
-    final boolean isIdle() {
+    boolean isIdle() {
         return mParallelBroadcasts.isEmpty() && mDispatcher.isEmpty()
                 && (mPendingBroadcast == null);
     }
 
+    // Used by wait-for-broadcast-idle : fast-forward all current deferrals to
+    // be immediately deliverable.
+    void cancelDeferrals() {
+        mDispatcher.cancelDeferrals();
+    }
+
+    String describeState() {
+        synchronized (mService) {
+            return mParallelBroadcasts.size() + " parallel; "
+                    + mDispatcher.describeStateLocked();
+        }
+    }
+
     void writeToProto(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
         proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 93a71e5..4e03b72 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1692,7 +1692,8 @@
                         (app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
                                 app.curAdj == ProcessList.HOME_APP_ADJ)) {
                     mAppCompact.compactAppSome(app);
-                } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
+                } else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
+                                || app.setAdj > ProcessList.CACHED_APP_MAX_ADJ)
                         && app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
                         && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
                     mAppCompact.compactAppFull(app);
diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java
index 0b6a432..bbe4ed1 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingService.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingService.java
@@ -177,13 +177,12 @@
      * Handle boot phase PHASE_ACTIVITY_MANAGER_READY.
      */
     private void onPhaseActivityManagerReady() {
+        // RoleManager doesn't tell us about upgrade, so we still need to listen for app upgrades.
+        // (app uninstall/disable will be notified by RoleManager.)
         final IntentFilter packageFilter = new IntentFilter();
         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         packageFilter.addDataScheme("package");
 
-        packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         mContext.registerReceiverAsUser(mPackageUserMonitor, UserHandle.ALL,
                 packageFilter, null, mHandler);
 
@@ -256,14 +255,6 @@
                         handlePackageAddedReplacing(packageName, userId);
                     }
                     break;
-                case Intent.ACTION_PACKAGE_REMOVED:
-                    if (!replacing) {
-                        handlePackageRemoved(packageName, userId);
-                    }
-                    break;
-                case Intent.ACTION_PACKAGE_CHANGED:
-                    handlePackageChanged(packageName, userId);
-                    break;
             }
         }
     };
@@ -371,31 +362,6 @@
         }
     }
 
-    private void handlePackageRemoved(String packageName, int userId) {
-        if (DEBUG) {
-            Slog.d(TAG, "handlePackageRemoved: u" + userId + " " + packageName);
-        }
-        synchronized (mLock) {
-            final AppServiceFinder finder = findFinderLocked(userId, packageName);
-            if (finder != null) {
-                unbindServicesLocked(userId, finder, "package uninstall");
-            }
-        }
-    }
-
-    private void handlePackageChanged(String packageName, int userId) {
-        if (DEBUG) {
-            Slog.d(TAG, "handlePackageChanged: u" + userId + " " + packageName);
-        }
-        synchronized (mLock) {
-            final AppServiceFinder finder = findFinderLocked(userId, packageName);
-            if (finder != null) {
-                unbindServicesLocked(userId, finder, "package changed");
-                bindServicesLocked(userId, finder, "package changed");
-            }
-        }
-    }
-
     private void rebindAllLocked(String reason) {
         for (int i = 0; i < mRunningUsers.size(); i++) {
             if (!mRunningUsers.valueAt(i)) {
diff --git a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
index 4c5f1a1..753d3b0 100644
--- a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
+++ b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
@@ -16,14 +16,10 @@
 
 package com.android.server.appbinding.finders;
 
-import static android.provider.Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL;
-
 import android.Manifest.permission;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
+import android.app.role.OnRoleHoldersChangedListener;
+import android.app.role.RoleManager;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ServiceInfo;
 import android.os.Handler;
 import android.os.IBinder;
@@ -35,7 +31,8 @@
 import android.util.Slog;
 
 import com.android.internal.R;
-import com.android.internal.telephony.SmsApplication;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.CollectionUtils;
 import com.android.server.appbinding.AppBindingConstants;
 
 import java.util.function.BiConsumer;
@@ -45,10 +42,15 @@
  */
 public class CarrierMessagingClientServiceFinder
         extends AppServiceFinder<CarrierMessagingClientService, ICarrierMessagingClientService> {
+
+    private final RoleManager mRoleManager;
+
     public CarrierMessagingClientServiceFinder(Context context,
             BiConsumer<AppServiceFinder, Integer> listener,
             Handler callbackHandler) {
         super(context, listener, callbackHandler);
+
+        mRoleManager = context.getSystemService(RoleManager.class);
     }
 
     @Override
@@ -84,9 +86,8 @@
 
     @Override
     public String getTargetPackage(int userId) {
-        final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser(
-                mContext, /* updateIfNeeded= */ true, userId);
-        String ret = cn == null ? null : cn.getPackageName();
+        final String ret = CollectionUtils.firstOrNull(mRoleManager.getRoleHoldersAsUser(
+                RoleManager.ROLE_SMS, UserHandle.of(userId)));
 
         if (DEBUG) {
             Slog.d(TAG, "getTargetPackage()=" + ret);
@@ -97,9 +98,8 @@
 
     @Override
     public void startMonitoring() {
-        final IntentFilter filter = new IntentFilter(ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
-        mContext.registerReceiverAsUser(mSmsAppChangedWatcher, UserHandle.ALL, filter,
-                /* permission= */ null, mHandler);
+        mRoleManager.addOnRoleHoldersChangedListenerAsUser(
+                mContext.getMainExecutor(), mRoleHolderChangedListener, UserHandle.ALL);
     }
 
     @Override
@@ -118,12 +118,11 @@
         return constants.SMS_APP_BIND_FLAGS;
     }
 
-    private final BroadcastReceiver mSmsAppChangedWatcher = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL.equals(intent.getAction())) {
-                mListener.accept(CarrierMessagingClientServiceFinder.this, getSendingUserId());
-            }
+    private final OnRoleHoldersChangedListener mRoleHolderChangedListener = (role, user) -> {
+        if (RoleManager.ROLE_SMS.equals(role)) {
+            BackgroundThread.getHandler().post(() -> {
+                mListener.accept(CarrierMessagingClientServiceFinder.this, user.getIdentifier());
+            });
         }
     };
 }
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 9a1d7bf..f293328 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -201,11 +201,6 @@
                             StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
                                     error);
                         }
-
-                        @Override
-                        public IBinder asBinder() {
-                            return null;
-                        }
                     });
                 } catch (RemoteException e) {
                     Slog.e(LOG_TAG, "Cannot call into the AttentionService");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a6643d4..fbabc82 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3217,6 +3217,21 @@
         if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
             return;
         }
+
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MODIFY_PHONE_STATE)
+                != PackageManager.PERMISSION_GRANTED) {
+            synchronized (mSetModeDeathHandlers) {
+                for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+                    if (h.getMode() == AudioSystem.MODE_IN_CALL) {
+                        Log.w(TAG, "getMode is call, Permission Denial: setSpeakerphoneOn from pid="
+                                + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+                        return;
+                    }
+                }
+            }
+        }
+
         // for logging only
         final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
                 .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index bd4acdb..7d4ac59 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -34,7 +34,7 @@
     private long mOpId;
 
     public abstract int handleFailedAttempt();
-    public abstract void resetFailedAttempts();
+    public void resetFailedAttempts() {}
 
     public static final int LOCKOUT_NONE = 0;
     public static final int LOCKOUT_TIMED = 1;
@@ -42,6 +42,11 @@
 
     private final boolean mRequireConfirmation;
 
+    // We need to track this state since it's possible for applications to request for
+    // authentication while the device is already locked out. In that case, the client is created
+    // but not started yet. The user shouldn't receive the error haptics in this case.
+    private boolean mStarted;
+
     /**
      * This method is called when authentication starts.
      */
@@ -53,6 +58,11 @@
      */
     public abstract void onStop();
 
+    /**
+     * @return true if the framework should handle lockout.
+     */
+    public abstract boolean shouldFrameworkHandleLockout();
+
     public AuthenticationClient(Context context, Metrics metrics,
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
@@ -91,6 +101,23 @@
     }
 
     @Override
+    public boolean onError(long deviceId, int error, int vendorCode) {
+        if (!shouldFrameworkHandleLockout()) {
+            switch (error) {
+                case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
+                case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
+                    if (mStarted) {
+                        vibrateError();
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+        return super.onError(deviceId, error, vendorCode);
+    }
+
+    @Override
     public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
             boolean authenticated, ArrayList<Byte> token) {
         super.logOnAuthenticated(authenticated, mRequireConfirmation, getTargetUserId(),
@@ -113,7 +140,9 @@
                     vibrateSuccess();
                 }
                 result = true;
-                resetFailedAttempts();
+                if (shouldFrameworkHandleLockout()) {
+                    resetFailedAttempts();
+                }
                 onStop();
 
                 final byte[] byteToken = new byte[token.size()];
@@ -147,9 +176,10 @@
                 if (listener != null) {
                     vibrateError();
                 }
+
                 // Allow system-defined limit of number of attempts before giving up
                 final int lockoutMode = handleFailedAttempt();
-                if (lockoutMode != LOCKOUT_NONE) {
+                if (lockoutMode != LOCKOUT_NONE && shouldFrameworkHandleLockout()) {
                     Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode("
                             + lockoutMode + ")");
                     stop(false);
@@ -170,7 +200,7 @@
                         }
                     }
                 }
-                result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
+                result = lockoutMode != LOCKOUT_NONE; // in a lockout mode
             }
         } catch (RemoteException e) {
             Slog.e(getLogTag(), "Remote exception", e);
@@ -184,6 +214,7 @@
      */
     @Override
     public int start() {
+        mStarted = true;
         onStart();
         try {
             final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
@@ -209,6 +240,8 @@
             return 0;
         }
 
+        mStarted = false;
+
         onStop();
 
         try {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index bca84f7..ddd416e 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -273,16 +273,13 @@
          */
         private static final int STATE_AUTH_STARTED = 2;
         /**
-         * Authentication is paused, waiting for the user to press "try again" button. Since the
-         * try again button requires us to cancel authentication, this represents the state where
-         * ERROR_CANCELED is not received yet.
+         * Authentication is paused, waiting for the user to press "try again" button. Only
+         * passive modalities such as Face or Iris should have this state. Note that for passive
+         * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
+         * fingerprint.
          */
         private static final int STATE_AUTH_PAUSED = 3;
         /**
-         * Same as above, except the ERROR_CANCELED has been received.
-         */
-        private static final int STATE_AUTH_PAUSED_CANCELED = 4;
-        /**
          * Authentication is successful, but we're waiting for the user to press "confirm" button.
          */
         private static final int STATE_AUTH_PENDING_CONFIRM = 5;
@@ -457,11 +454,6 @@
                         // Pause authentication. onBiometricAuthenticated(false) causes the
                         // dialog to show a "try again" button for passive modalities.
                         mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
-                        // Cancel authentication. Skip the token/package check since we are
-                        // cancelling from system server. The interface is permission protected so
-                        // this is fine.
-                        cancelInternal(null /* token */, null /* package */,
-                                false /* fromClient */);
                     }
 
                     mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
@@ -507,24 +499,15 @@
                                     }
                                 }, BiometricPrompt.HIDE_DIALOG_DELAY);
                             }
-                        } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
-                                || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED) {
-                            if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
-                                    && error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
-                                // Skip the first ERROR_CANCELED message when this happens, since
-                                // "try again" requires us to cancel authentication but keep
-                                // the prompt showing.
-                                mCurrentAuthSession.mState = STATE_AUTH_PAUSED_CANCELED;
-                            } else {
-                                // In the "try again" state, we should forward canceled errors to
-                                // the client and and clean up.
-                                mCurrentAuthSession.mClientReceiver.onError(error, message);
-                                mStatusBarService.onBiometricError(message);
-                                mActivityTaskManager.unregisterTaskStackListener(
-                                        mTaskStackListener);
-                                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
-                                mCurrentAuthSession = null;
-                            }
+                        } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
+                            // In the "try again" state, we should forward canceled errors to
+                            // the client and and clean up.
+                            mCurrentAuthSession.mClientReceiver.onError(error, message);
+                            mStatusBarService.onBiometricError(message);
+                            mActivityTaskManager.unregisterTaskStackListener(
+                                    mTaskStackListener);
+                            mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+                            mCurrentAuthSession = null;
                         } else {
                             Slog.e(TAG, "Impossible session error state: "
                                     + mCurrentAuthSession.mState);
@@ -705,8 +688,7 @@
 
             if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
                 final boolean continuing = mCurrentAuthSession != null &&
-                        (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
-                                || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED);
+                        (mCurrentAuthSession.mState == STATE_AUTH_PAUSED);
 
                 mCurrentAuthSession = mPendingAuthSession;
                 mPendingAuthSession = null;
@@ -1029,7 +1011,7 @@
         }
 
         @Override // Binder call
-        public void resetTimeout(byte[] token) {
+        public void resetLockout(byte[] token) {
             checkInternalPermission();
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -1037,7 +1019,7 @@
                     mFingerprintService.resetTimeout(token);
                 }
                 if (mFaceService != null) {
-                    mFaceService.resetTimeout(token);
+                    mFaceService.resetLockout(token);
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote exception", e);
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 9e0f2fc..92a8d93 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -20,17 +20,12 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
-import android.app.AlarmManager;
 import android.app.AppOpsManager;
 import android.app.IActivityTaskManager;
-import android.app.PendingIntent;
 import android.app.SynchronousUserSwitchObserver;
 import android.app.TaskStackListener;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricAuthenticator;
@@ -54,14 +49,11 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
 import android.util.StatsLog;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.server.SystemService;
-import com.android.server.biometrics.fingerprint.FingerprintService;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -80,38 +72,36 @@
 
     protected static final boolean DEBUG = true;
 
+    private static final boolean CLEANUP_UNKNOWN_TEMPLATES = true;
     private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
     private static final int MSG_USER_SWITCHING = 10;
-    private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000;
     private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
 
     private final Context mContext;
     private final String mKeyguardPackage;
-    private final SparseBooleanArray mTimedLockoutCleared;
-    private final SparseIntArray mFailedAttempts;
     private final IActivityTaskManager mActivityTaskManager;
-    private final AlarmManager mAlarmManager;
     private final PowerManager mPowerManager;
     private final UserManager mUserManager;
     private final MetricsLogger mMetricsLogger;
     private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
     private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
-    private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
     private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
 
     protected final IStatusBarService mStatusBarService;
     protected final Map<Integer, Long> mAuthenticatorIds =
             Collections.synchronizedMap(new HashMap<>());
-    protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
-            new ResetFailedAttemptsForUserRunnable();
     protected final AppOpsManager mAppOps;
     protected final H mHandler = new H();
 
+    private final IBinder mToken = new Binder(); // Used for internal enumeration
+    private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
+
     private IBiometricService mBiometricService;
     private ClientMonitor mCurrentClient;
     private ClientMonitor mPendingClient;
     private PerformanceStats mPerformanceStats;
     protected int mCurrentUserId = UserHandle.USER_NULL;
+    protected long mHalDeviceId;
     // Tracks if the current authentication makes use of CryptoObjects.
     protected boolean mIsCrypto;
     // Normal authentications are tracked by mPerformanceMap.
@@ -135,23 +125,16 @@
     protected abstract String getTag();
 
     /**
+     * @return wrapper for the HAL
+     */
+    protected abstract DaemonWrapper getDaemonWrapper();
+
+    /**
      * @return the biometric utilities for a specific implementation.
      */
     protected abstract BiometricUtils getBiometricUtils();
 
     /**
-     * @return the number of failed attempts after which the user will be temporarily locked out
-     *         from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
-     */
-    protected abstract int getFailedAttemptsLockoutTimed();
-
-    /**
-     * @return the number of failed attempts after which the user will be permanently locked out
-     *         from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
-     */
-    protected abstract int getFailedAttemptsLockoutPermanent();
-
-    /**
      * @return the metrics constants for a biometric implementation.
      */
     protected abstract Metrics getMetrics();
@@ -186,13 +169,6 @@
     protected abstract long getHalDeviceId();
 
     /**
-     * This method is called when the user switches. Implementations should probably notify the
-     * HAL.
-     * @param userId
-     */
-    protected abstract void handleUserSwitching(int userId);
-
-    /**
      * @param userId
      * @return Returns true if the user has any enrolled biometrics.
      */
@@ -215,6 +191,9 @@
      */
     protected abstract boolean checkAppOps(int uid, String opPackageName);
 
+    protected abstract List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(
+            int userId);
+
     /**
      * Notifies clients of any change in the biometric state (active / idle). This is mainly for
      * Fingerprint navigation gestures.
@@ -224,6 +203,11 @@
 
     protected abstract int statsModality();
 
+    /**
+     * @return one of the AuthenticationClient LOCKOUT constants
+     */
+    protected abstract int getLockoutMode();
+
     protected abstract class AuthenticationClientImpl extends AuthenticationClient {
 
         // Used to check if the public API that was invoked was from FingerprintManager. Only
@@ -271,21 +255,12 @@
         }
 
         @Override
-        public void resetFailedAttempts() {
-            resetFailedAttemptsForUser(true /* clearAttemptCounter */,
-                    ActivityManager.getCurrentUser());
-        }
-
-        @Override
         public void notifyUserActivity() {
             userActivity();
         }
 
         @Override
         public int handleFailedAttempt() {
-            final int currentUser = ActivityManager.getCurrentUser();
-            mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
-            mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
             final int lockoutMode = getLockoutMode();
             if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
                 mPerformanceStats.permanentLockout++;
@@ -295,7 +270,6 @@
 
             // Failing multiple times will continue to push out the lockout time
             if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
-                scheduleLockoutResetForUser(currentUser);
                 return lockoutMode;
             }
             return AuthenticationClient.LOCKOUT_NONE;
@@ -319,40 +293,106 @@
         }
     }
 
-    protected abstract class RemovalClientImpl extends RemovalClient {
-        private boolean mShouldNotify;
-
-        public RemovalClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
-                IBinder token, ServiceListener listener, int fingerId, int groupId, int userId,
+    /**
+     * An internal class to help clean up unknown templates in HAL and Framework
+     */
+    private final class InternalRemovalClient extends RemovalClient {
+        InternalRemovalClient(Context context,
+                DaemonWrapper daemon, long halDeviceId, IBinder token,
+                ServiceListener listener, int templateId, int groupId, int userId,
                 boolean restricted, String owner) {
-            super(context, getMetrics(), daemon, halDeviceId, token, listener, fingerId, groupId,
+            super(context, getMetrics(), daemon, halDeviceId, token, listener, templateId, groupId,
                     userId, restricted, owner, getBiometricUtils());
         }
 
-        public void setShouldNotifyUserActivity(boolean shouldNotify) {
-            mShouldNotify = shouldNotify;
-        }
-
         @Override
-        public void notifyUserActivity() {
-            if (mShouldNotify) {
-                userActivity();
-            }
+        protected int statsModality() {
+            return BiometricServiceBase.this.statsModality();
         }
     }
 
-    protected abstract class EnumerateClientImpl extends EnumerateClient {
+    /**
+     * Internal class to help clean up unknown templates in the HAL and Framework
+     */
+    private final class InternalEnumerateClient extends EnumerateClient {
 
-        public EnumerateClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
-                IBinder token, ServiceListener listener, int groupId, int userId,
-                boolean restricted, String owner) {
+        private BiometricUtils mUtils;
+        // List of templates that are known to the Framework. Remove from this list when enumerate
+        // returns a template that contains a match.
+        private List<? extends BiometricAuthenticator.Identifier> mEnrolledList;
+        // List of templates to remove from the HAL
+        private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();
+
+        InternalEnumerateClient(Context context,
+                DaemonWrapper daemon, long halDeviceId, IBinder token,
+                ServiceListener listener, int groupId, int userId, boolean restricted,
+                String owner, List<? extends BiometricAuthenticator.Identifier> enrolledList,
+                BiometricUtils utils) {
             super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId,
                     restricted, owner);
+            mEnrolledList = enrolledList;
+            mUtils = utils;
+        }
+
+        private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) {
+            if (identifier == null) {
+                return;
+            }
+            Slog.v(getTag(), "handleEnumeratedTemplate: " + identifier.getBiometricId());
+            boolean matched = false;
+            for (int i = 0; i < mEnrolledList.size(); i++) {
+                if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) {
+                    mEnrolledList.remove(i);
+                    matched = true;
+                    break;
+                }
+            }
+
+            // TemplateId 0 means no templates in HAL
+            if (!matched && identifier.getBiometricId() != 0) {
+                mUnknownHALTemplates.add(identifier);
+            }
+            Slog.v(getTag(), "Matched: " + matched);
+        }
+
+        private void doTemplateCleanup() {
+            if (mEnrolledList == null) {
+                return;
+            }
+
+            // At this point, mEnrolledList only contains templates known to the framework and
+            // not the HAL.
+            for (int i = 0; i < mEnrolledList.size(); i++) {
+                BiometricAuthenticator.Identifier identifier = mEnrolledList.get(i);
+                Slog.e(getTag(), "doTemplateCleanup(): Removing dangling template from framework: "
+                        + identifier.getBiometricId() + " "
+                        + identifier.getName());
+                mUtils.removeBiometricForUser(getContext(),
+                        getTargetUserId(), identifier.getBiometricId());
+                StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                        statsModality(),
+                        BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK);
+            }
+            mEnrolledList.clear();
+        }
+
+        public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() {
+            return mUnknownHALTemplates;
         }
 
         @Override
-        public void notifyUserActivity() {
-            userActivity();
+        public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+                int remaining) {
+            handleEnumeratedTemplate(identifier);
+            if (remaining == 0) {
+                doTemplateCleanup();
+            }
+            return remaining == 0;
+        }
+
+        @Override
+        protected int statsModality() {
+            return BiometricServiceBase.this.statsModality();
         }
     }
 
@@ -429,13 +469,14 @@
      * subclasses.
      */
     protected interface DaemonWrapper {
-        int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. see errno.h.
+        int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h.
         int authenticate(long operationId, int groupId) throws RemoteException;
         int cancel() throws RemoteException;
         int remove(int groupId, int biometricId) throws RemoteException;
         int enumerate() throws RemoteException;
-        int enroll(byte[] cryptoToken, int groupId, int timeout,
+        int enroll(byte[] token, int groupId, int timeout,
                 ArrayList<Integer> disabledFeatures) throws RemoteException;
+        void resetLockout(byte[] token) throws RemoteException;
     }
 
     /**
@@ -506,24 +547,7 @@
         }
     }
 
-    private final class LockoutReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Slog.v(getTag(), "Resetting lockout: " + intent.getAction());
-            if (getLockoutResetIntent().equals(intent.getAction())) {
-                final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
-                resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
-            }
-        }
-    }
 
-    private final class ResetFailedAttemptsForUserRunnable implements Runnable {
-        @Override
-        public void run() {
-            resetFailedAttemptsForUser(true /* clearAttemptCounter */,
-                    ActivityManager.getCurrentUser());
-        }
-    }
 
     private final class LockoutResetMonitor implements IBinder.DeathRecipient {
         private static final long WAKELOCK_TIMEOUT_MS = 2000;
@@ -583,6 +607,19 @@
     }
 
     /**
+     * Container for enumerated templates. Used to keep track when cleaning up unknown
+     * templates.
+     */
+    private final class UserTemplate {
+        final BiometricAuthenticator.Identifier mIdentifier;
+        final int mUserId;
+        UserTemplate(BiometricAuthenticator.Identifier identifier, int userId) {
+            this.mIdentifier = identifier;
+            this.mUserId = userId;
+        }
+    }
+
+    /**
      * Initializes the system service.
      * <p>
      * Subclasses must define a single argument constructor that accepts the context
@@ -599,16 +636,11 @@
         mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
                 com.android.internal.R.string.config_keyguardComponent)).getPackageName();
         mAppOps = context.getSystemService(AppOpsManager.class);
-        mTimedLockoutCleared = new SparseBooleanArray();
-        mFailedAttempts = new SparseIntArray();
         mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
                 Context.ACTIVITY_TASK_SERVICE)).getService();
         mPowerManager = mContext.getSystemService(PowerManager.class);
-        mAlarmManager = mContext.getSystemService(AlarmManager.class);
         mUserManager = UserManager.get(mContext);
         mMetricsLogger = new MetricsLogger();
-        mContext.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
-                getLockoutBroadcastPermission(), null /* handler */);
     }
 
     @Override
@@ -688,6 +720,11 @@
         if (DEBUG) Slog.v(getTag(), "handleError(client="
                 + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
 
+        if (client instanceof InternalRemovalClient
+                || client instanceof InternalEnumerateClient) {
+            clearEnumerateState();
+        }
+
         if (client != null && client.onError(deviceId, error, vendorCode)) {
             removeClient(client);
         }
@@ -721,6 +758,39 @@
                 updateActiveGroup(userId, null);
             }
         }
+
+        if (client instanceof InternalRemovalClient && !mUnknownHALTemplates.isEmpty()) {
+            startCleanupUnknownHALTemplates();
+        } else if (client instanceof InternalRemovalClient) {
+            clearEnumerateState();
+        }
+    }
+
+    protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) {
+        ClientMonitor client = getCurrentClient();
+
+        client.onEnumerationResult(identifier, remaining);
+
+        // All templates in the HAL for this user were enumerated
+        if (remaining == 0) {
+            if (client instanceof InternalEnumerateClient) {
+                List<BiometricAuthenticator.Identifier> unknownHALTemplates =
+                        ((InternalEnumerateClient) client).getUnknownHALTemplates();
+
+                if (!unknownHALTemplates.isEmpty()) {
+                    Slog.w(getTag(), "Adding " + unknownHALTemplates.size()
+                            + " templates for deletion");
+                }
+                for (int i = 0; i < unknownHALTemplates.size(); i++) {
+                    mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplates.get(i),
+                            client.getTargetUserId()));
+                }
+                removeClient(client);
+                startCleanupUnknownHALTemplates();
+            } else {
+                removeClient(client);
+            }
+        }
     }
 
     /**
@@ -832,13 +902,13 @@
         });
     }
 
-    protected void removeInternal(RemovalClientImpl client) {
+    protected void removeInternal(RemovalClient client) {
         mHandler.post(() -> {
             startClient(client, true /* initiatedByClient */);
         });
     }
 
-    protected void enumerateInternal(EnumerateClientImpl client) {
+    protected void enumerateInternal(EnumerateClient client) {
         mHandler.post(() -> {
             startClient(client, true /* initiatedByClient */);
         });
@@ -919,19 +989,6 @@
         return mKeyguardPackage.equals(clientPackage);
     }
 
-    protected int getLockoutMode() {
-        final int currentUser = ActivityManager.getCurrentUser();
-        final int failedAttempts = mFailedAttempts.get(currentUser, 0);
-        if (failedAttempts >= getFailedAttemptsLockoutPermanent()) {
-            return AuthenticationClient.LOCKOUT_PERMANENT;
-        } else if (failedAttempts > 0 &&
-                mTimedLockoutCleared.get(currentUser, false) == false
-                && (failedAttempts % getFailedAttemptsLockoutTimed() == 0)) {
-            return AuthenticationClient.LOCKOUT_TIMED;
-        }
-        return AuthenticationClient.LOCKOUT_NONE;
-    }
-
     private boolean isForegroundActivity(int uid, int pid) {
         try {
             List<ActivityManager.RunningAppProcessInfo> procs =
@@ -965,10 +1022,10 @@
                     currentClient.getOwnerString());
             // This check only matters for FingerprintService, since enumerate may call back
             // multiple times.
-            if (currentClient instanceof FingerprintService.EnumerateClientImpl ||
-                    currentClient instanceof FingerprintService.RemovalClientImpl) {
+            if (currentClient instanceof InternalEnumerateClient
+                    || currentClient instanceof InternalRemovalClient) {
                 // This condition means we're currently running internal diagnostics to
-                // remove extra fingerprints in the hardware and/or the software
+                // remove extra templates in the hardware and/or the software
                 // TODO: design an escape hatch in case client never finishes
                 if (newClient != null) {
                     Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
@@ -1124,16 +1181,75 @@
         return mAuthenticatorIds.getOrDefault(userId, 0L);
     }
 
-    private void scheduleLockoutResetForUser(int userId) {
-        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
-                getLockoutResetIntentForUser(userId));
+    /**
+     * This method should be called upon connection to the daemon, and when user switches.
+     * @param userId
+     */
+    protected void doTemplateCleanupForUser(int userId) {
+        if (CLEANUP_UNKNOWN_TEMPLATES) {
+            enumerateUser(userId);
+        }
     }
 
-    private PendingIntent getLockoutResetIntentForUser(int userId) {
-        return PendingIntent.getBroadcast(mContext, userId,
-                new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId),
-                PendingIntent.FLAG_UPDATE_CURRENT);
+    private void clearEnumerateState() {
+        if (DEBUG) Slog.v(getTag(), "clearEnumerateState()");
+        mUnknownHALTemplates.clear();
+    }
+
+    /**
+     * Remove unknown templates from HAL
+     */
+    private void startCleanupUnknownHALTemplates() {
+        if (!mUnknownHALTemplates.isEmpty()) {
+            UserTemplate template = mUnknownHALTemplates.get(0);
+            mUnknownHALTemplates.remove(template);
+            boolean restricted = !hasPermission(getManageBiometricPermission());
+            InternalRemovalClient client = new InternalRemovalClient(getContext(),
+                    getDaemonWrapper(), mHalDeviceId, mToken, null /* listener */,
+                    template.mIdentifier.getBiometricId(), 0 /* groupId */, template.mUserId,
+                    restricted, getContext().getPackageName());
+            removeInternal(client);
+            StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                    statsModality(),
+                    BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
+        } else {
+            clearEnumerateState();
+        }
+    }
+
+    private void enumerateUser(int userId) {
+        if (DEBUG) Slog.v(getTag(), "Enumerating user(" + userId + ")");
+
+        final boolean restricted = !hasPermission(getManageBiometricPermission());
+        final List<? extends BiometricAuthenticator.Identifier> enrolledList =
+                getEnrolledTemplates(userId);
+
+        InternalEnumerateClient client = new InternalEnumerateClient(getContext(),
+                getDaemonWrapper(), mHalDeviceId, mToken, null /* serviceListener */, userId,
+                userId, restricted, getContext().getOpPackageName(), enrolledList,
+                getBiometricUtils());
+        enumerateInternal(client);
+    }
+
+    /**
+     * This method is called when the user switches. Implementations should probably notify the
+     * HAL.
+     */
+    protected void handleUserSwitching(int userId) {
+        if (getCurrentClient() instanceof InternalRemovalClient
+                || getCurrentClient() instanceof InternalEnumerateClient) {
+            Slog.w(getTag(), "User switched while performing cleanup");
+            removeClient(getCurrentClient());
+            clearEnumerateState();
+        }
+        updateActiveGroup(userId, null);
+        doTemplateCleanupForUser(userId);
+    }
+
+    protected void notifyLockoutResetMonitors() {
+        for (int i = 0; i < mLockoutMonitors.size(); i++) {
+            mLockoutMonitors.get(i).sendLockoutReset();
+        }
     }
 
     private void userActivity() {
@@ -1169,25 +1285,6 @@
         return userId;
     }
 
-    // Attempt counter should only be cleared when Keyguard goes away or when
-    // a biometric is successfully authenticated.
-    private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
-        if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
-            Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter);
-        }
-        if (clearAttemptCounter) {
-            mFailedAttempts.put(userId, 0);
-        }
-        mTimedLockoutCleared.put(userId, true);
-        // If we're asked to reset failed attempts externally (i.e. from Keyguard),
-        // the alarm might still be pending; remove it.
-        cancelLockoutResetForUser(userId);
-        notifyLockoutResetMonitors();
-    }
-
-    private void cancelLockoutResetForUser(int userId) {
-        mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
-    }
 
     private void listenForUserSwitches() {
         try {
@@ -1204,12 +1301,6 @@
         }
     }
 
-    private void notifyLockoutResetMonitors() {
-        for (int i = 0; i < mLockoutMonitors.size(); i++) {
-            mLockoutMonitors.get(i).sendLockoutReset();
-        }
-    }
-
     private void removeLockoutResetCallback(
             LockoutResetMonitor monitor) {
         mLockoutMonitors.remove(monitor);
diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java
index 0f57f48..44ac037 100644
--- a/services/core/java/com/android/server/biometrics/EnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java
@@ -39,6 +39,10 @@
     }
 
     @Override
+    public void notifyUserActivity() {
+    }
+
+    @Override
     protected int statsAction() {
         return BiometricsProtoEnums.ACTION_ENUMERATE;
     }
@@ -94,7 +98,9 @@
     public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
             int remaining) {
         try {
-            getListener().onEnumerated(identifier, remaining);
+            if (getListener() != null) {
+                getListener().onEnumerated(identifier, remaining);
+            }
         } catch (RemoteException e) {
             Slog.w(getLogTag(), "Failed to notify enumerated:", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java
index 0509067..a18f336 100644
--- a/services/core/java/com/android/server/biometrics/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/RemovalClient.java
@@ -44,6 +44,10 @@
     }
 
     @Override
+    public void notifyUserActivity() {
+    }
+
+    @Override
     protected int statsAction() {
         return BiometricsProtoEnums.ACTION_REMOVE;
     }
@@ -95,7 +99,9 @@
     private boolean sendRemoved(BiometricAuthenticator.Identifier identifier,
             int remaining) {
         try {
-            getListener().onRemoved(identifier, remaining);
+            if (getListener() != null) {
+                getListener().onRemoved(identifier, remaining);
+            }
         } catch (RemoteException e) {
             Slog.w(getLogTag(), "Failed to notify Removed:", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index eab3820..d2d1482 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -51,9 +51,13 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.DumpUtils;
 import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.AuthenticationClient;
 import com.android.server.biometrics.BiometricServiceBase;
 import com.android.server.biometrics.BiometricUtils;
+import com.android.server.biometrics.ClientMonitor;
+import com.android.server.biometrics.EnumerateClient;
 import com.android.server.biometrics.Metrics;
+import com.android.server.biometrics.RemovalClient;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -79,8 +83,6 @@
     private static final String FACE_DATA_DIR = "facedata";
     private static final String ACTION_LOCKOUT_RESET =
             "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET";
-    private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 3;
-    private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 12;
     private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
 
     private final class FaceAuthClient extends AuthenticationClientImpl {
@@ -96,6 +98,25 @@
         protected int statsModality() {
             return FaceService.this.statsModality();
         }
+
+        @Override
+        public boolean shouldFrameworkHandleLockout() {
+            return false;
+        }
+
+        @Override
+        public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
+                boolean authenticated, ArrayList<Byte> token) {
+            final boolean result = super.onAuthenticated(identifier, authenticated, token);
+
+            // For face, the authentication lifecycle ends either when
+            // 1) Authenticated == true
+            // 2) Error occurred
+            // 3) Authenticated == false
+            // Fingerprint currently does not end when the third condition is met which is a bug,
+            // but let's leave it as-is for now.
+            return result || !authenticated;
+        }
     }
 
     /**
@@ -106,6 +127,7 @@
         /**
          * The following methods contain common code which is shared in biometrics/common.
          */
+
         @Override // Binder call
         public long generateChallenge(IBinder token) {
             checkPermission(MANAGE_BIOMETRIC);
@@ -216,15 +238,14 @@
             }
 
             final boolean restricted = isRestricted();
-            final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper,
-                    mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId, 0 /* groupId */,
-                    userId, restricted, token.toString()) {
+            final RemovalClient client = new RemovalClient(getContext(), getMetrics(),
+                    mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId,
+                    0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils()) {
                 @Override
                 protected int statsModality() {
                     return FaceService.this.statsModality();
                 }
             };
-            client.setShouldNotifyUserActivity(true);
             removeInternal(client);
         }
 
@@ -234,9 +255,9 @@
             checkPermission(MANAGE_BIOMETRIC);
 
             final boolean restricted = isRestricted();
-            final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
-                    mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId,
-                    restricted, getContext().getOpPackageName()) {
+            final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(),
+                    mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId,
+                    userId, restricted, getContext().getOpPackageName()) {
                 @Override
                 protected int statsModality() {
                     return FaceService.this.statsModality();
@@ -317,7 +338,7 @@
                 return null;
             }
 
-            return FaceService.this.getEnrolledFaces(userId);
+            return FaceService.this.getEnrolledTemplates(userId);
         }
 
         @Override // Binder call
@@ -354,10 +375,13 @@
         }
 
         @Override // Binder call
-        public void resetTimeout(byte[] token) {
+        public void resetLockout(byte[] token) {
             checkPermission(MANAGE_BIOMETRIC);
-            // TODO: confirm security token when we move timeout management into the HAL layer.
-            mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
+            try {
+                mDaemonWrapper.resetLockout(token);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Unable to reset lockout", e);
+            }
         }
 
         @Override
@@ -511,7 +535,8 @@
         public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining)
                 throws RemoteException {
             if (mFaceServiceReceiver != null) {
-
+                mFaceServiceReceiver.onEnumerated(identifier.getDeviceId(),
+                        identifier.getBiometricId(), remaining);
             }
         }
     }
@@ -520,80 +545,107 @@
 
     @GuardedBy("this")
     private IBiometricsFace mDaemon;
-    private long mHalDeviceId;
+    // One of the AuthenticationClient constants
+    private int mCurrentUserLockoutMode;
 
     /**
      * Receives callbacks from the HAL.
      */
     private IBiometricsFaceClientCallback mDaemonCallback =
             new IBiometricsFaceClientCallback.Stub() {
-                @Override
-                public void onEnrollResult(final long deviceId, int faceId, int userId,
-                        int remaining) {
-                    mHandler.post(() -> {
-                        final Face face = new Face(getBiometricUtils()
-                                .getUniqueName(getContext(), userId), faceId, deviceId);
-                        FaceService.super.handleEnrollResult(face, remaining);
-                    });
+        @Override
+        public void onEnrollResult(final long deviceId, int faceId, int userId,
+                int remaining) {
+            mHandler.post(() -> {
+                final Face face = new Face(getBiometricUtils()
+                        .getUniqueName(getContext(), userId), faceId, deviceId);
+                FaceService.super.handleEnrollResult(face, remaining);
+            });
+        }
+
+        @Override
+        public void onAcquired(final long deviceId, final int userId,
+                final int acquiredInfo,
+                final int vendorCode) {
+            mHandler.post(() -> {
+                FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
+            });
+        }
+
+        @Override
+        public void onAuthenticated(final long deviceId, final int faceId, final int userId,
+                ArrayList<Byte> token) {
+            mHandler.post(() -> {
+                Face face = new Face("", faceId, deviceId);
+                FaceService.super.handleAuthenticated(face, token);
+            });
+        }
+
+        @Override
+        public void onError(final long deviceId, final int userId, final int error,
+                final int vendorCode) {
+            mHandler.post(() -> {
+                FaceService.super.handleError(deviceId, error, vendorCode);
+
+                // TODO: this chunk of code should be common to all biometric services
+                if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
+                    // If we get HW_UNAVAILABLE, try to connect again later...
+                    Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
+                    synchronized (this) {
+                        mDaemon = null;
+                        mHalDeviceId = 0;
+                        mCurrentUserId = UserHandle.USER_NULL;
+                    }
                 }
+            });
+        }
 
-                @Override
-                public void onAcquired(final long deviceId, final int userId,
-                        final int acquiredInfo,
-                        final int vendorCode) {
-                    mHandler.post(() -> {
-                        FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
-                    });
+        @Override
+        public void onRemoved(final long deviceId, final int faceId, final int userId,
+                final int remaining) {
+            mHandler.post(() -> {
+                final Face face = new Face("", faceId, deviceId);
+                FaceService.super.handleRemoved(face, remaining);
+            });
+        }
+
+        @Override
+        public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
+                throws RemoteException {
+            mHandler.post(() -> {
+                if (!faceIds.isEmpty()) {
+                    for (int i = 0; i < faceIds.size(); i++) {
+                        final Face face = new Face("", faceIds.get(i), deviceId);
+                        // Convert to old old behavior
+                        FaceService.super.handleEnumerate(face, faceIds.size() - i - 1);
+                    }
+                } else {
+                    // For face, the HIDL contract is to receive an empty list when there are no
+                    // templates enrolled. Send a null identifier since we don't consume them
+                    // anywhere, and send remaining == 0 to plumb this with existing common code.
+                    FaceService.super.handleEnumerate(null /* identifier */, 0);
                 }
+            });
+        }
 
-                @Override
-                public void onAuthenticated(final long deviceId, final int faceId, final int userId,
-                        ArrayList<Byte> token) {
-                    mHandler.post(() -> {
-                        Face face = new Face("", faceId, deviceId);
-                        FaceService.super.handleAuthenticated(face, token);
-                    });
+        @Override
+        public void onLockoutChanged(long duration) {
+            Slog.d(TAG, "onLockoutChanged: " + duration);
+            if (duration == 0) {
+                mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
+            } else if (duration == Long.MAX_VALUE) {
+                mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_PERMANENT;
+            } else {
+                mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_TIMED;
+            }
+
+            mHandler.post(() -> {
+                if (duration == 0) {
+                    notifyLockoutResetMonitors();
                 }
-
-                @Override
-                public void onError(final long deviceId, final int userId, final int error,
-                        final int vendorCode) {
-                    mHandler.post(() -> {
-                        FaceService.super.handleError(deviceId, error, vendorCode);
-
-                        // TODO: this chunk of code should be common to all biometric services
-                        if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
-                            // If we get HW_UNAVAILABLE, try to connect again later...
-                            Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
-                            synchronized (this) {
-                                mDaemon = null;
-                                mHalDeviceId = 0;
-                                mCurrentUserId = UserHandle.USER_NULL;
-                            }
-                        }
-                    });
-                }
-
-                @Override
-                public void onRemoved(final long deviceId, final int faceId, final int userId,
-                        final int remaining) {
-                    mHandler.post(() -> {
-                        final Face face = new Face("", faceId, deviceId);
-                        FaceService.super.handleRemoved(face, remaining);
-                    });
-                }
-
-                @Override
-                public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
-                        throws RemoteException {
-                    // TODO
-                }
-
-                @Override
-                public void onLockoutChanged(long duration) {
-
-                }
-            };
+            });
+        }
+    };
 
     /**
      * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
@@ -652,9 +704,22 @@
             for (int i = 0; i < cryptoToken.length; i++) {
                 token.add(cryptoToken[i]);
             }
-            // TODO: plumb requireAttention down from framework
             return daemon.enroll(token, timeout, disabledFeatures);
         }
+
+        @Override
+        public void resetLockout(byte[] cryptoToken) throws RemoteException {
+            IBiometricsFace daemon = getFaceDaemon();
+            if (daemon == null) {
+                Slog.w(TAG, "resetLockout(): no face HAL!");
+                return;
+            }
+            final ArrayList<Byte> token = new ArrayList<>();
+            for (int i = 0; i < cryptoToken.length; i++) {
+                token.add(cryptoToken[i]);
+            }
+            daemon.resetLockout(token);
+        }
     };
 
 
@@ -675,21 +740,16 @@
     }
 
     @Override
+    protected DaemonWrapper getDaemonWrapper() {
+        return mDaemonWrapper;
+    }
+
+    @Override
     protected BiometricUtils getBiometricUtils() {
         return FaceUtils.getInstance();
     }
 
     @Override
-    protected int getFailedAttemptsLockoutTimed() {
-        return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED;
-    }
-
-    @Override
-    protected int getFailedAttemptsLockoutPermanent() {
-        return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT;
-    }
-
-    @Override
     protected Metrics getMetrics() {
         return mFaceMetrics;
     }
@@ -698,7 +758,7 @@
     protected boolean hasReachedEnrollmentLimit(int userId) {
         final int limit = getContext().getResources().getInteger(
                 com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
-        final int enrolled = FaceService.this.getEnrolledFaces(userId).size();
+        final int enrolled = FaceService.this.getEnrolledTemplates(userId).size();
         if (enrolled >= limit) {
             Slog.w(TAG, "Too many faces registered");
             return true;
@@ -766,7 +826,9 @@
 
     @Override
     protected void handleUserSwitching(int userId) {
-        updateActiveGroup(userId, null);
+        super.handleUserSwitching(userId);
+        // Will be updated when we get the callback from HAL
+        mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE;
     }
 
     @Override
@@ -785,7 +847,6 @@
     @Override
     protected void checkUseBiometricPermission() {
         // noop for Face. The permission checks are all done on the incoming binder call.
-        // TODO: Perhaps do the same in FingerprintService
     }
 
     @Override
@@ -795,6 +856,11 @@
     }
 
     @Override
+    protected List<Face> getEnrolledTemplates(int userId) {
+        return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+    }
+
+    @Override
     protected void notifyClientActiveCallbacks(boolean isActive) {
         // noop for Face.
     }
@@ -804,6 +870,11 @@
         return BiometricsProtoEnums.MODALITY_FACE;
     }
 
+    @Override
+    protected int getLockoutMode() {
+        return mCurrentUserLockoutMode;
+    }
+
     /** Gets the face daemon */
     private synchronized IBiometricsFace getFaceDaemon() {
         if (mDaemon == null) {
@@ -833,6 +904,7 @@
             if (mHalDeviceId != 0) {
                 loadAuthenticatorIds();
                 updateActiveGroup(ActivityManager.getCurrentUser(), null);
+                doTemplateCleanupForUser(ActivityManager.getCurrentUser());
             } else {
                 Slog.w(TAG, "Failed to open Face HAL!");
                 MetricsLogger.count(getContext(), "faced_openhal_error", 1);
@@ -870,10 +942,6 @@
         return 0;
     }
 
-    private List<Face> getEnrolledFaces(int userId) {
-        return getBiometricUtils().getBiometricsForUser(getContext(), userId);
-    }
-
     private void dumpInternal(PrintWriter pw) {
         JSONObject dump = new JSONObject();
         try {
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index d8544e3..164468e 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -24,8 +24,13 @@
 import static android.Manifest.permission.USE_FINGERPRINT;
 
 import android.app.ActivityManager;
+import android.app.AlarmManager;
 import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricAuthenticator;
@@ -46,21 +51,25 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SELinux;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
-import android.util.StatsLog;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.DumpUtils;
 import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.AuthenticationClient;
 import com.android.server.biometrics.BiometricServiceBase;
 import com.android.server.biometrics.BiometricUtils;
 import com.android.server.biometrics.ClientMonitor;
 import com.android.server.biometrics.EnumerateClient;
 import com.android.server.biometrics.Metrics;
+import com.android.server.biometrics.RemovalClient;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -85,20 +94,30 @@
 
     protected static final String TAG = "FingerprintService";
     private static final boolean DEBUG = true;
-    private static final boolean CLEANUP_UNUSED_FP = true;
     private static final String FP_DATA_DIR = "fpdata";
     private static final String ACTION_LOCKOUT_RESET =
             "com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET";
     private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
     private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
+    private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000;
+    private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
 
-    // TODO: This should be refactored into BiometricService
-    private final class UserFingerprint {
-        Fingerprint f;
-        int userId;
-        public UserFingerprint(Fingerprint f, int userId) {
-            this.f = f;
-            this.userId = userId;
+    private final class ResetFailedAttemptsForUserRunnable implements Runnable {
+        @Override
+        public void run() {
+            resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+                    ActivityManager.getCurrentUser());
+        }
+    }
+
+    private final class LockoutReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Slog.v(getTag(), "Resetting lockout: " + intent.getAction());
+            if (getLockoutResetIntent().equals(intent.getAction())) {
+                final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
+                resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
+            }
         }
     }
 
@@ -121,6 +140,30 @@
         protected int statsModality() {
             return FingerprintService.this.statsModality();
         }
+
+        @Override
+        public void resetFailedAttempts() {
+            resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+                    ActivityManager.getCurrentUser());
+        }
+
+        @Override
+        public boolean shouldFrameworkHandleLockout() {
+            return true;
+        }
+
+        @Override
+        public int handleFailedAttempt() {
+            final int currentUser = ActivityManager.getCurrentUser();
+            mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
+            mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
+
+            if (getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
+                scheduleLockoutResetForUser(currentUser);
+            }
+
+            return super.handleFailedAttempt();
+        }
     }
 
     /**
@@ -241,15 +284,14 @@
             }
 
             final boolean restricted = isRestricted();
-            final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper,
-                    mHalDeviceId, token, new ServiceListenerImpl(receiver), fingerId, groupId,
-                    userId, restricted, token.toString()) {
+            final RemovalClient client = new RemovalClient(getContext(), getMetrics(),
+                    mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
+                    fingerId, groupId, userId, restricted, token.toString(), getBiometricUtils()) {
                 @Override
                 protected int statsModality() {
                     return FingerprintService.this.statsModality();
                 }
             };
-            client.setShouldNotifyUserActivity(true);
             removeInternal(client);
         }
 
@@ -259,9 +301,9 @@
             checkPermission(MANAGE_FINGERPRINT);
 
             final boolean restricted = isRestricted();
-            final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
-                    mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId,
-                    restricted, getContext().getOpPackageName()) {
+            final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(),
+                    mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId,
+                    userId, restricted, getContext().getOpPackageName()) {
                 @Override
                 protected int statsModality() {
                     return FingerprintService.this.statsModality();
@@ -339,7 +381,7 @@
                 return Collections.emptyList();
             }
 
-            return FingerprintService.this.getEnrolledFingerprints(userId);
+            return FingerprintService.this.getEnrolledTemplates(userId);
         }
 
         @Override // Binder call
@@ -445,7 +487,6 @@
         public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)
                 throws RemoteException {
             if (mFingerprintServiceReceiver != null) {
-                // TODO: Pass up the fp directly instead
                 final Fingerprint fp = (Fingerprint) identifier;
                 mFingerprintServiceReceiver.onEnrollResult(fp.getDeviceId(), fp.getBiometricId(),
                         fp.getGroupId(), remaining);
@@ -493,7 +534,6 @@
         public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining)
                 throws RemoteException {
             if (mFingerprintServiceReceiver != null) {
-                // TODO: Pass up the fp directly instead
                 final Fingerprint fp = (Fingerprint) identifier;
                 mFingerprintServiceReceiver.onRemoved(fp.getDeviceId(), fp.getBiometricId(),
                         fp.getGroupId(), remaining);
@@ -511,105 +551,18 @@
         }
     }
 
-    /**
-     * An internal class to help clean up unknown fingerprints in the hardware and software.
-     */
-    private final class InternalEnumerateClient extends BiometricServiceBase.EnumerateClientImpl {
-
-        private List<Fingerprint> mEnrolledList;
-        private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete
-
-        public InternalEnumerateClient(Context context, DaemonWrapper daemon, long halDeviceId,
-                IBinder token, ServiceListener listener, int groupId, int userId,
-                boolean restricted, String owner, List<Fingerprint> enrolledList) {
-            super(context, daemon, halDeviceId, token, listener, groupId, userId, restricted,
-                    owner);
-            mEnrolledList = enrolledList;
-        }
-
-        private void handleEnumeratedFingerprint(
-                BiometricAuthenticator.Identifier identifier, int remaining) {
-            boolean matched = false;
-            for (int i = 0; i < mEnrolledList.size(); i++) {
-                if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) {
-                    mEnrolledList.remove(i);
-                    matched = true;
-                    break;
-                }
-            }
-
-            // fingerId 0 means no fingerprints are in hardware
-            if (!matched && identifier.getBiometricId() != 0) {
-                mUnknownFingerprints.add((Fingerprint) identifier);
-            }
-        }
-
-        private void doFingerprintCleanup() {
-            if (mEnrolledList == null) {
-                return;
-            }
-
-            for (Fingerprint f : mEnrolledList) {
-                Slog.e(TAG, "doFingerprintCleanup(): Removing dangling enrolled fingerprint: "
-                        + f.getName() + " " + f.getBiometricId() + " " + f.getGroupId()
-                        + " " + f.getDeviceId());
-                FingerprintUtils.getInstance().removeBiometricForUser(getContext(),
-                        getTargetUserId(), f.getBiometricId());
-                StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
-                        BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK);
-            }
-            mEnrolledList.clear();
-        }
-
-        public List<Fingerprint> getUnknownFingerprints() {
-            return mUnknownFingerprints;
-        }
-
-        @Override
-        public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
-                int remaining) {
-            handleEnumeratedFingerprint(identifier, remaining);
-            if (remaining == 0) {
-                doFingerprintCleanup();
-            }
-            return remaining == 0;
-        }
-
-        @Override
-        protected int statsModality() {
-            return FingerprintService.this.statsModality();
-        }
-    }
-
-    /**
-     * An internal class to help clean up unknown fingerprints in hardware and software.
-     */
-    private final class InternalRemovalClient extends BiometricServiceBase.RemovalClientImpl {
-        public InternalRemovalClient(Context context,
-                DaemonWrapper daemon, long halDeviceId, IBinder token,
-                ServiceListener listener, int fingerId, int groupId, int userId, boolean restricted,
-                String owner) {
-            super(context, daemon, halDeviceId, token, listener, fingerId, groupId, userId,
-                    restricted,
-                    owner);
-        }
-
-        @Override
-        protected int statsModality() {
-            return FingerprintService.this.statsModality();
-        }
-    }
-
     private final FingerprintMetrics mFingerprintMetrics = new FingerprintMetrics();
     private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
             new CopyOnWriteArrayList<>();
 
     @GuardedBy("this")
     private IBiometricsFingerprint mDaemon;
-
-    private long mHalDeviceId;
-    private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
-    private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
+    private final SparseBooleanArray mTimedLockoutCleared;
+    private final SparseIntArray mFailedAttempts;
+    private final AlarmManager mAlarmManager;
+    private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
+    protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
+            new ResetFailedAttemptsForUserRunnable();
 
     /**
      * Receives callbacks from the HAL.
@@ -646,13 +599,7 @@
         @Override
         public void onError(final long deviceId, final int error, final int vendorCode) {
             mHandler.post(() -> {
-                ClientMonitor client = getCurrentClient();
-                if (client instanceof InternalRemovalClient
-                        || client instanceof InternalEnumerateClient) {
-                    clearEnumerateState();
-                }
                 FingerprintService.super.handleError(deviceId, error, vendorCode);
-
                 // TODO: this chunk of code should be common to all biometric services
                 if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
                     // If we get HW_UNAVAILABLE, try to connect again later...
@@ -673,11 +620,6 @@
                 ClientMonitor client = getCurrentClient();
                 final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
                 FingerprintService.super.handleRemoved(fp, remaining);
-                if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
-                    cleanupUnknownFingerprints();
-                } else if (client instanceof InternalRemovalClient){
-                    clearEnumerateState();
-                }
             });
         }
 
@@ -685,9 +627,8 @@
         public void onEnumerate(final long deviceId, final int fingerId, final int groupId,
                 final int remaining) {
             mHandler.post(() -> {
-                // TODO: factor out common enumerate logic if possible
                 final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
-                FingerprintService.this.handleEnumerate(fp, remaining);
+                FingerprintService.super.handleEnumerate(fp, remaining);
             });
 
         }
@@ -748,10 +689,22 @@
             }
             return daemon.enroll(cryptoToken, groupId, timeout);
         }
+
+        @Override
+        public void resetLockout(byte[] token) throws RemoteException {
+            // TODO: confirm security token when we move timeout management into the HAL layer.
+            Slog.e(TAG, "Not supported");
+            return;
+        }
     };
 
     public FingerprintService(Context context) {
         super(context);
+        mTimedLockoutCleared = new SparseBooleanArray();
+        mFailedAttempts = new SparseIntArray();
+        mAlarmManager = context.getSystemService(AlarmManager.class);
+        context.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
+                getLockoutBroadcastPermission(), null /* handler */);
     }
 
     @Override
@@ -767,21 +720,16 @@
     }
 
     @Override
+    protected DaemonWrapper getDaemonWrapper() {
+        return mDaemonWrapper;
+    }
+
+    @Override
     protected BiometricUtils getBiometricUtils() {
         return FingerprintUtils.getInstance();
     }
 
     @Override
-    protected int getFailedAttemptsLockoutTimed() {
-        return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED;
-    }
-
-    @Override
-    protected int getFailedAttemptsLockoutPermanent() {
-        return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT;
-    }
-
-    @Override
     protected Metrics getMetrics() {
         return mFingerprintMetrics;
     }
@@ -790,7 +738,7 @@
     protected boolean hasReachedEnrollmentLimit(int userId) {
         final int limit = getContext().getResources().getInteger(
                 com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
-        final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size();
+        final int enrolled = FingerprintService.this.getEnrolledTemplates(userId).size();
         if (enrolled >= limit) {
             Slog.w(TAG, "Too many fingerprints registered");
             return true;
@@ -866,19 +814,6 @@
     }
 
     @Override
-    protected void handleUserSwitching(int userId) {
-        if (getCurrentClient() instanceof InternalRemovalClient
-                || getCurrentClient() instanceof InternalEnumerateClient) {
-            Slog.w(TAG, "User switched while performing cleanup");
-            removeClient(getCurrentClient());
-            clearEnumerateState();
-        }
-        updateActiveGroup(userId, null);
-        doFingerprintCleanupForUser(userId);
-    }
-
-
-    @Override
     protected boolean hasEnrolledBiometrics(int userId) {
         if (userId != UserHandle.getCallingUserId()) {
             checkPermission(INTERACT_ACROSS_USERS);
@@ -913,6 +848,11 @@
     }
 
     @Override
+    protected List<Fingerprint> getEnrolledTemplates(int userId) {
+        return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+    }
+
+    @Override
     protected void notifyClientActiveCallbacks(boolean isActive) {
         List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
         for (int i = 0; i < callbacks.size(); i++) {
@@ -930,6 +870,20 @@
         return BiometricsProtoEnums.MODALITY_FINGERPRINT;
     }
 
+    @Override
+    protected int getLockoutMode() {
+        final int currentUser = ActivityManager.getCurrentUser();
+        final int failedAttempts = mFailedAttempts.get(currentUser, 0);
+        if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
+            return AuthenticationClient.LOCKOUT_PERMANENT;
+        } else if (failedAttempts > 0
+                && !mTimedLockoutCleared.get(currentUser, false)
+                && (failedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
+            return AuthenticationClient.LOCKOUT_TIMED;
+        }
+        return AuthenticationClient.LOCKOUT_NONE;
+    }
+
     /** Gets the fingerprint daemon */
     private synchronized IBiometricsFingerprint getFingerprintDaemon() {
         if (mDaemon == null) {
@@ -959,7 +913,7 @@
             if (mHalDeviceId != 0) {
                 loadAuthenticatorIds();
                 updateActiveGroup(ActivityManager.getCurrentUser(), null);
-                doFingerprintCleanupForUser(ActivityManager.getCurrentUser());
+                doTemplateCleanupForUser(ActivityManager.getCurrentUser());
             } else {
                 Slog.w(TAG, "Failed to open Fingerprint HAL!");
                 MetricsLogger.count(getContext(), "fingerprintd_openhal_error", 1);
@@ -969,79 +923,6 @@
         return mDaemon;
     }
 
-    /**
-     * This method should be called upon connection to the daemon, and when user switches.
-     * @param userId
-     */
-    private void doFingerprintCleanupForUser(int userId) {
-        if (CLEANUP_UNUSED_FP) {
-            enumerateUser(userId);
-        }
-    }
-
-    private void clearEnumerateState() {
-        if (DEBUG) Slog.v(TAG, "clearEnumerateState()");
-        mUnknownFingerprints.clear();
-    }
-
-    private void enumerateUser(int userId) {
-        if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")");
-
-        final boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
-        final List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
-
-        InternalEnumerateClient client = new InternalEnumerateClient(getContext(), mDaemonWrapper,
-                mHalDeviceId, mToken, new ServiceListenerImpl(null), userId, userId, restricted,
-                getContext().getOpPackageName(), enrolledList);
-        enumerateInternal(client);
-    }
-
-    // Remove unknown fingerprints from hardware
-    private void cleanupUnknownFingerprints() {
-        if (!mUnknownFingerprints.isEmpty()) {
-            UserFingerprint uf = mUnknownFingerprints.get(0);
-            mUnknownFingerprints.remove(uf);
-            boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
-            InternalRemovalClient client = new InternalRemovalClient(getContext(), mDaemonWrapper,
-                    mHalDeviceId, mToken, new ServiceListenerImpl(null), uf.f.getBiometricId(),
-                    uf.f.getGroupId(), uf.userId, restricted, getContext().getOpPackageName());
-            removeInternal(client);
-            StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, statsModality(),
-                    BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
-        } else {
-            clearEnumerateState();
-        }
-    }
-
-    private void handleEnumerate(Fingerprint fingerprint, int remaining) {
-        ClientMonitor client = getCurrentClient();
-
-        if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) {
-            return;
-        }
-        client.onEnumerationResult(fingerprint, remaining);
-
-        // All fingerprints in hardware for this user were enumerated
-        if (remaining == 0) {
-            if (client instanceof InternalEnumerateClient) {
-                List<Fingerprint> unknownFingerprints =
-                        ((InternalEnumerateClient) client).getUnknownFingerprints();
-
-                if (!unknownFingerprints.isEmpty()) {
-                    Slog.w(TAG, "Adding " + unknownFingerprints.size() +
-                            " fingerprints for deletion");
-                }
-                for (Fingerprint f : unknownFingerprints) {
-                    mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
-                }
-                removeClient(client);
-                cleanupUnknownFingerprints();
-            } else {
-                removeClient(client);
-            }
-        }
-    }
-
     private long startPreEnroll(IBinder token) {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
         if (daemon == null) {
@@ -1070,8 +951,38 @@
         return 0;
     }
 
-    private List<Fingerprint> getEnrolledFingerprints(int userId) {
-        return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+    // Attempt counter should only be cleared when Keyguard goes away or when
+    // a biometric is successfully authenticated. Lockout should eventually be done below the HAL.
+    // See AuthenticationClient#shouldFrameworkHandleLockout().
+    private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
+        if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
+            Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter);
+        }
+        if (clearAttemptCounter) {
+            mFailedAttempts.put(userId, 0);
+        }
+        mTimedLockoutCleared.put(userId, true);
+        // If we're asked to reset failed attempts externally (i.e. from Keyguard),
+        // the alarm might still be pending; remove it.
+        cancelLockoutResetForUser(userId);
+        notifyLockoutResetMonitors();
+    }
+
+
+    private void cancelLockoutResetForUser(int userId) {
+        mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
+    }
+
+    private void scheduleLockoutResetForUser(int userId) {
+        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
+                getLockoutResetIntentForUser(userId));
+    }
+
+    private PendingIntent getLockoutResetIntentForUser(int userId) {
+        return PendingIntent.getBroadcast(getContext(), userId,
+                new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId),
+                PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
     private void dumpInternal(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java
index eb457b6..cb8a772 100644
--- a/services/core/java/com/android/server/biometrics/iris/IrisService.java
+++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java
@@ -17,12 +17,16 @@
 package com.android.server.biometrics.iris;
 
 import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricsProtoEnums;
 
+import com.android.server.biometrics.AuthenticationClient;
 import com.android.server.biometrics.BiometricServiceBase;
 import com.android.server.biometrics.BiometricUtils;
 import com.android.server.biometrics.Metrics;
 
+import java.util.List;
+
 /**
  * A service to manage multiple clients that want to access the Iris HAL API.
  * The service is responsible for maintaining a list of clients and dispatching all
@@ -61,18 +65,13 @@
     }
 
     @Override
-    protected BiometricUtils getBiometricUtils() {
+    protected DaemonWrapper getDaemonWrapper() {
         return null;
     }
 
     @Override
-    protected int getFailedAttemptsLockoutTimed() {
-        return 0;
-    }
-
-    @Override
-    protected int getFailedAttemptsLockoutPermanent() {
-        return 0;
+    protected BiometricUtils getBiometricUtils() {
+        return null;
     }
 
     @Override
@@ -106,11 +105,6 @@
     }
 
     @Override
-    protected void handleUserSwitching(int userId) {
-
-    }
-
-    @Override
     protected boolean hasEnrolledBiometrics(int userId) {
         return false;
     }
@@ -131,7 +125,17 @@
     }
 
     @Override
+    protected List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(int userId) {
+        return null;
+    }
+
+    @Override
     protected int statsModality() {
         return BiometricsProtoEnums.MODALITY_IRIS;
     }
+
+    @Override
+    protected int getLockoutMode() {
+        return AuthenticationClient.LOCKOUT_NONE;
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 19bdc09..c91e1a1 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1859,7 +1859,7 @@
         final TetherState tetherState = new TetherState(
                 new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
                              makeControlCallback(), mConfig.enableLegacyDhcpServer,
-                             mDeps.getIpServerDependencies(mContext)));
+                             mDeps.getIpServerDependencies()));
         mTetherStates.put(iface, tetherState);
         tetherState.ipServer.start();
     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e1af81b..da547ea 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -241,7 +241,7 @@
         mNetworkCapabilities = new NetworkCapabilities();
         mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
         mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
-        updateCapabilities();
+        updateCapabilities(null /* defaultNetwork */);
 
         loadAlwaysOnPackage();
     }
@@ -268,22 +268,44 @@
         updateAlwaysOnNotification(detailedState);
     }
 
-    public void updateCapabilities() {
-        final Network[] underlyingNetworks = (mConfig != null) ? mConfig.underlyingNetworks : null;
-        // Only apps targeting Q and above can explicitly declare themselves as metered.
-        final boolean isAlwaysMetered =
-                mIsPackageTargetingAtLeastQ && (mConfig == null || mConfig.isMetered);
-        updateCapabilities(mContext.getSystemService(ConnectivityManager.class), underlyingNetworks,
-                mNetworkCapabilities, isAlwaysMetered);
-
-        if (mNetworkAgent != null) {
-            mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+    /**
+     * Updates {@link #mNetworkCapabilities} based on current underlying networks and returns a
+     * defensive copy.
+     *
+     * <p>Does not propagate updated capabilities to apps.
+     *
+     * @param defaultNetwork underlying network for VPNs following platform's default
+     */
+    public synchronized NetworkCapabilities updateCapabilities(
+            @Nullable Network defaultNetwork) {
+        if (mConfig == null) {
+            // VPN is not running.
+            return null;
         }
+
+        Network[] underlyingNetworks = mConfig.underlyingNetworks;
+        if (underlyingNetworks == null && defaultNetwork != null) {
+            // null underlying networks means to track the default.
+            underlyingNetworks = new Network[] { defaultNetwork };
+        }
+        // Only apps targeting Q and above can explicitly declare themselves as metered.
+        final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered;
+
+        applyUnderlyingCapabilities(
+                mContext.getSystemService(ConnectivityManager.class),
+                underlyingNetworks,
+                mNetworkCapabilities,
+                isAlwaysMetered);
+
+        return new NetworkCapabilities(mNetworkCapabilities);
     }
 
     @VisibleForTesting
-    public static void updateCapabilities(ConnectivityManager cm, Network[] underlyingNetworks,
-            NetworkCapabilities caps, boolean isAlwaysMetered) {
+    public static void applyUnderlyingCapabilities(
+            ConnectivityManager cm,
+            Network[] underlyingNetworks,
+            NetworkCapabilities caps,
+            boolean isAlwaysMetered) {
         int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
         int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
@@ -296,6 +318,7 @@
         boolean hadUnderlyingNetworks = false;
         if (null != underlyingNetworks) {
             for (Network underlying : underlyingNetworks) {
+                // TODO(b/124469351): Get capabilities directly from ConnectivityService instead.
                 final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying);
                 if (underlyingCaps == null) continue;
                 hadUnderlyingNetworks = true;
@@ -1007,9 +1030,8 @@
     }
 
     /**
-     * Establish a VPN network and return the file descriptor of the VPN
-     * interface. This methods returns {@code null} if the application is
-     * revoked or not prepared.
+     * Establish a VPN network and return the file descriptor of the VPN interface. This methods
+     * returns {@code null} if the application is revoked or not prepared.
      *
      * @param config The parameters to configure the network.
      * @return The file descriptor of the VPN interface.
@@ -1101,8 +1123,6 @@
                 // as rules are deleted. This prevents data leakage as the rules are moved over.
                 agentDisconnect(oldNetworkAgent);
             }
-            // Set up VPN's capabilities such as meteredness.
-            updateCapabilities();
 
             if (oldConnection != null) {
                 mContext.unbindService(oldConnection);
@@ -1258,6 +1278,11 @@
         return ranges;
     }
 
+    /**
+     * Updates UID ranges for this VPN and also updates its internal capabilities.
+     *
+     * <p>Should be called on primary ConnectivityService thread.
+     */
     public void onUserAdded(int userHandle) {
         // If the user is restricted tie them to the parent user's VPN
         UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
@@ -1268,8 +1293,9 @@
                     try {
                         addUserToRanges(existingRanges, userHandle, mConfig.allowedApplications,
                                 mConfig.disallowedApplications);
+                        // ConnectivityService will call {@link #updateCapabilities} and apply
+                        // those for VPN network.
                         mNetworkCapabilities.setUids(existingRanges);
-                        updateCapabilities();
                     } catch (Exception e) {
                         Log.wtf(TAG, "Failed to add restricted user to owner", e);
                     }
@@ -1279,6 +1305,11 @@
         }
     }
 
+    /**
+     * Updates UID ranges for this VPN and also updates its capabilities.
+     *
+     * <p>Should be called on primary ConnectivityService thread.
+     */
     public void onUserRemoved(int userHandle) {
         // clean up if restricted
         UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
@@ -1290,8 +1321,9 @@
                         final List<UidRange> removedRanges =
                             uidRangesForUser(userHandle, existingRanges);
                         existingRanges.removeAll(removedRanges);
+                        // ConnectivityService will call {@link #updateCapabilities} and
+                        // apply those for VPN network.
                         mNetworkCapabilities.setUids(existingRanges);
-                        updateCapabilities();
                     } catch (Exception e) {
                         Log.wtf(TAG, "Failed to remove restricted user to owner", e);
                     }
@@ -1504,6 +1536,12 @@
         return success;
     }
 
+    /**
+     * Updates underlying network set.
+     *
+     * <p>Note: Does not updates capabilities. Call {@link #updateCapabilities} from
+     * ConnectivityService thread to get updated capabilities.
+     */
     public synchronized boolean setUnderlyingNetworks(Network[] networks) {
         if (!isCallerEstablishedOwnerLocked()) {
             return false;
@@ -1520,7 +1558,6 @@
                 }
             }
         }
-        updateCapabilities();
         return true;
     }
 
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index 3fddac1..173d786 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -61,8 +61,8 @@
     /**
      * Get dependencies to be used by IpServer.
      */
-    public IpServer.Dependencies getIpServerDependencies(Context context) {
-        return new IpServer.Dependencies(context);
+    public IpServer.Dependencies getIpServerDependencies() {
+        return new IpServer.Dependencies();
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index dc5be6a..15c7ef7 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1019,7 +1019,7 @@
         if (mDisplayWhiteBalanceController != null) {
             if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
                 mDisplayWhiteBalanceController.setEnabled(true);
-                mDisplayWhiteBalanceController.updateScreenColorTemperature();
+                mDisplayWhiteBalanceController.updateDisplayColorTemperature();
             } else {
                 mDisplayWhiteBalanceController.setEnabled(false);
             }
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index b9aa34e8..d95e92b 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -29,14 +29,14 @@
 
 /**
  * The DisplayWhiteBalanceController drives display white-balance (automatically correcting the
- * screen color temperature depending on the ambient color temperature).
+ * display color temperature depending on the ambient color temperature).
  *
  * The DisplayWhiteBalanceController:
  * - Uses the AmbientColorTemperatureSensor to detect changes in the ambient color temperature;
  * - Uses the AmbientColorTemperatureFilter to average these changes over time, filter out the
  *   noise, and arrive at an estimate of the actual ambient color temperature;
- * - Uses the DisplayWhiteBalanceThrottler to decide whether the screen color tempearture should be
- *   updated, suppressing changes that are too frequent or too minor.
+ * - Uses the DisplayWhiteBalanceThrottler to decide whether the display color tempearture should
+ *   be updated, suppressing changes that are too frequent or too minor.
  */
 public class DisplayWhiteBalanceController implements
         AmbientSensor.AmbientBrightnessSensor.Callbacks,
@@ -76,8 +76,8 @@
     // Override the ambient color temperature for debugging purposes.
     private float mAmbientColorTemperatureOverride;
 
-    // A piecewise linear relationship between ambient and display color temperatures
-    private Spline.LinearSpline mAmbientToDisplayTemperatureSpline;
+    // A piecewise linear relationship between ambient and display color temperatures.
+    private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline;
 
     /**
      * @param brightnessSensor
@@ -91,7 +91,7 @@
      *      The filter used to average ambient color temperature changes over time, filter out the
      *      noise and arrive at an estimate of the actual ambient color temperature.
      * @param throttler
-     *      The throttler used to determine whether the new screen color temperature should be
+     *      The throttler used to determine whether the new display color temperature should be
      *      updated or not.
      * @param lowLightAmbientBrightnessThreshold
      *      The ambient brightness threshold beneath which we fall back to a fixed ambient color
@@ -99,6 +99,12 @@
      * @param lowLightAmbientColorTemperature
      *      The ambient color temperature to which we fall back when the ambient brightness drops
      *      beneath a certain threshold.
+     * @param ambientColorTemperatures
+     *      The ambient color tempeartures used to map the ambient color temperature to the display
+     *      color temperature (or null if no mapping is necessary).
+     * @param displayColorTemperatures
+     *      The display color temperatures used to map the ambient color temperature to the display
+     *      color temperature (or null if no mapping is necessary).
      *
      * @throws NullPointerException
      *      - brightnessSensor is null;
@@ -114,7 +120,7 @@
             @NonNull AmbientFilter colorTemperatureFilter,
             @NonNull DisplayWhiteBalanceThrottler throttler,
             float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature,
-            float[] ambientTemperatures, float[] displayTemperatures) {
+            float[] ambientColorTemperatures, float[] displayColorTemperatures) {
         validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
                 colorTemperatureFilter, throttler);
         mLoggingEnabled = false;
@@ -134,10 +140,10 @@
         mAmbientColorTemperatureOverride = -1.0f;
 
         try {
-            mAmbientToDisplayTemperatureSpline = new Spline.LinearSpline(ambientTemperatures,
-                    displayTemperatures);
+            mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
+                    ambientColorTemperatures, displayColorTemperatures);
         } catch (Exception e) {
-            mAmbientToDisplayTemperatureSpline = null;
+            mAmbientToDisplayColorTemperatureSpline = null;
         }
 
         mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class);
@@ -160,7 +166,7 @@
     }
 
     /**
-     * Set an object to call back to when the screen color temperature should be updated.
+     * Set an object to call back to when the display color temperature should be updated.
      *
      * @param callbacks
      *      The object to call back to.
@@ -201,7 +207,7 @@
      *
      * This is only applied when the ambient color temperature changes or is updated (in which case
      * it overrides the ambient color temperature estimate); in other words, it doesn't necessarily
-     * change the screen color temperature immediately.
+     * change the display color temperature immediately.
      *
      * @param ambientColorTemperatureOverride
      *      The ambient color temperature override.
@@ -240,9 +246,8 @@
         writer.println("  mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
         writer.println("  mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory);
         writer.println("  mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride);
-        writer.println("  mAmbientToDisplayTemperatureSpline="
-                + (mAmbientToDisplayTemperatureSpline == null ? "unused" :
-                    mAmbientToDisplayTemperatureSpline));
+        writer.println("  mAmbientToDisplayColorTemperatureSpline="
+                + mAmbientToDisplayColorTemperatureSpline);
     }
 
     @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
@@ -266,9 +271,9 @@
         final long time = System.currentTimeMillis();
         float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
 
-        if (mAmbientToDisplayTemperatureSpline != null) {
+        if (mAmbientToDisplayColorTemperatureSpline != null) {
             ambientColorTemperature =
-                mAmbientToDisplayTemperatureSpline.interpolate(ambientColorTemperature);
+                mAmbientToDisplayColorTemperatureSpline.interpolate(ambientColorTemperature);
         }
 
         final float ambientBrightness = mBrightnessFilter.getEstimate(time);
@@ -290,7 +295,7 @@
             ambientColorTemperature = mAmbientColorTemperatureOverride;
         }
 
-        // When the screen color temperature needs to be updated, we call DisplayPowerController to
+        // When the display color temperature needs to be updated, we call DisplayPowerController to
         // call our updateColorTemperature. The reason we don't call it directly is that we want
         // all changes to the system to happen in a predictable order in DPC's main loop
         // (updatePowerState).
@@ -308,9 +313,9 @@
     }
 
     /**
-     * Updates the screen color temperature.
+     * Updates the display color temperature.
      */
-    public void updateScreenColorTemperature() {
+    public void updateDisplayColorTemperature() {
         float ambientColorTemperature = -1.0f;
 
         // If both the pending and the current ambient color temperatures are -1, it means the DWBC
@@ -353,7 +358,7 @@
          * Called whenever the display white-balance state has changed.
          *
          * Usually, this means the estimated ambient color temperature has changed enough, and the
-         * screen color temperature should be updated; but it is also called by
+         * display color temperature should be updated; but it is also called if settings change.
          */
         void updateWhiteBalance();
     }
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
index 56f4ca3..449f115 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -67,14 +67,14 @@
         final float lowLightAmbientColorTemperature = getFloat(resources,
                 com.android.internal.R.dimen
                 .config_displayWhiteBalanceLowLightAmbientColorTemperature);
-        final float[] ambientTemperatures = getFloatArray(resources,
-                com.android.internal.R.array.config_displayWhiteBalanceAmbientTemperatureValues);
-        final float[] displayTemperatures = getFloatArray(resources,
-                com.android.internal.R.array.config_displayWhiteBalanceDisplayTemperatureValues);
+        final float[] ambientColorTemperatures = getFloatArray(resources,
+                com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures);
+        final float[] displayColorTempeartures = getFloatArray(resources,
+                com.android.internal.R.array.config_displayWhiteBalanceDisplayColorTemperatures);
         final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController(
                 brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
                 throttler, lowLightAmbientBrightnessThreshold, lowLightAmbientColorTemperature,
-                ambientTemperatures, displayTemperatures);
+                ambientColorTemperatures, displayColorTempeartures);
         brightnessSensor.setCallbacks(controller);
         colorTemperatureSensor.setCallbacks(controller);
         return controller;
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
index c1f0e98..5941bbb 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
@@ -23,7 +23,7 @@
 
 /**
  * The DisplayWhiteBalanceController uses the DisplayWhiteBalanceThrottler to decide whether the
- * screen color temperature should be updated, suppressing changes that are too frequent or too
+ * display color temperature should be updated, suppressing changes that are too frequent or too
  * minor.
  */
 class DisplayWhiteBalanceThrottler {
diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java
index a68ceed..6899c3f 100644
--- a/services/core/java/com/android/server/gpu/GpuService.java
+++ b/services/core/java/com/android/server/gpu/GpuService.java
@@ -22,24 +22,33 @@
 
 import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.gamedriver.GameDriverProto.Blacklist;
+import android.gamedriver.GameDriverProto.Blacklists;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Handler;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.Base64;
 import android.util.Slog;
 
+import com.android.framework.protobuf.InvalidProtocolBufferException;
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.SystemService;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Service to manage GPU related features.
@@ -52,17 +61,25 @@
     public static final boolean DEBUG = false;
 
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
-    private static final String WHITELIST_FILENAME = "whitelist.txt";
+    private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt";
+    private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP;
 
     private final Context mContext;
     private final String mDriverPackageName;
     private final PackageManager mPackageManager;
+    private final Object mLock = new Object();
+    private ContentResolver mContentResolver;
+    private long mGameDriverVersionCode;
+    private SettingsObserver mSettingsObserver;
+    @GuardedBy("mLock")
+    private Blacklists mBlacklists;
 
     public GpuService(Context context) {
         super(context);
 
         mContext = context;
         mDriverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
+        mGameDriverVersionCode = -1;
         mPackageManager = context.getPackageManager();
         if (mDriverPackageName != null && !mDriverPackageName.isEmpty()) {
             final IntentFilter packageFilter = new IntentFilter();
@@ -82,10 +99,37 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == PHASE_BOOT_COMPLETED) {
+            mContentResolver = mContext.getContentResolver();
+            mSettingsObserver = new SettingsObserver();
             if (mDriverPackageName == null || mDriverPackageName.isEmpty()) {
                 return;
             }
             fetchGameDriverPackageProperties();
+            processBlacklists();
+            setBlacklist();
+        }
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        private final Uri mGameDriverBlackUri =
+                Settings.Global.getUriFor(Settings.Global.GAME_DRIVER_BLACKLISTS);
+
+        SettingsObserver() {
+            super(new Handler());
+            mContentResolver.registerContentObserver(mGameDriverBlackUri, false, this,
+                    UserHandle.USER_ALL);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (uri == null) {
+                return;
+            }
+
+            if (mGameDriverBlackUri.equals(uri)) {
+                processBlacklists();
+                setBlacklist();
+            }
         }
     }
 
@@ -109,6 +153,7 @@
                 case ACTION_PACKAGE_CHANGED:
                 case ACTION_PACKAGE_REMOVED:
                     fetchGameDriverPackageProperties();
+                    setBlacklist();
                     break;
                 default:
                     // do nothing
@@ -138,16 +183,22 @@
             return;
         }
 
+        // Reset the whitelist.
+        Settings.Global.putString(mContentResolver,
+                                  Settings.Global.GAME_DRIVER_WHITELIST, "");
+        mGameDriverVersionCode = driverInfo.longVersionCode;
+
         try {
             final Context driverContext = mContext.createPackageContext(mDriverPackageName,
                                                                         Context.CONTEXT_RESTRICTED);
             final BufferedReader reader = new BufferedReader(
-                    new InputStreamReader(driverContext.getAssets().open(WHITELIST_FILENAME)));
+                    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(mContext.getContentResolver(),
+            Settings.Global.putString(mContentResolver,
                                       Settings.Global.GAME_DRIVER_WHITELIST,
                                       String.join(",", whitelistedPackageNames));
         } catch (PackageManager.NameNotFoundException e) {
@@ -160,4 +211,48 @@
             }
         }
     }
+
+    private void processBlacklists() {
+        // TODO(b/121350991) Switch to DeviceConfig with property listener.
+        String base64String =
+                Settings.Global.getString(mContentResolver, Settings.Global.GAME_DRIVER_BLACKLISTS);
+        if (base64String == null || base64String.isEmpty()) {
+            return;
+        }
+
+        synchronized (mLock) {
+            // Reset all blacklists
+            mBlacklists = null;
+            try {
+                mBlacklists = Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS));
+            } catch (IllegalArgumentException e) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Can't parse blacklist, skip and continue...");
+                }
+            } catch (InvalidProtocolBufferException e) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Can't parse blacklist, skip and continue...");
+                }
+            }
+        }
+    }
+
+    private void setBlacklist() {
+        Settings.Global.putString(mContentResolver,
+                                  Settings.Global.GAME_DRIVER_BLACKLIST, "");
+        synchronized (mLock) {
+            if (mBlacklists == null) {
+                return;
+            }
+            List<Blacklist> blacklists = mBlacklists.getBlacklistsList();
+            for (Blacklist blacklist : blacklists) {
+                if (blacklist.getVersionCode() == mGameDriverVersionCode) {
+                    Settings.Global.putString(mContentResolver,
+                            Settings.Global.GAME_DRIVER_BLACKLIST,
+                            String.join(",", blacklist.getPackageNamesList()));
+                    return;
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 3abacc2..e71b156 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -917,7 +917,7 @@
         synchronized (mLock) {
             boolean enabled =
                     ((mProviderRequest != null && mProviderRequest.reportLocation
-                            && mProviderRequest.forceLocation) || (
+                            && mProviderRequest.locationSettingsIgnored) || (
                             mContext.getSystemService(LocationManager.class).isLocationEnabled()
                                     && !mDisableGps)) && !mShutdown;
             if (enabled == mEnabled) {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a9ae74f..4b4788c 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -55,6 +55,8 @@
 import android.database.ContentObserver;
 import android.database.sqlite.SQLiteDatabase;
 import android.hardware.authsecret.V1_0.IAuthSecret;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.face.FaceManager;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -671,7 +673,6 @@
         mDeviceProvisionedObserver.onSystemReady();
         // TODO: maybe skip this for split system user mode.
         mStorage.prefetchUser(UserHandle.USER_SYSTEM);
-        mStrongAuth.systemReady();
     }
 
     private void migrateOldData() {
@@ -2375,6 +2376,14 @@
             userCredential = null;
         }
 
+        final PackageManager pm = mContext.getPackageManager();
+        // TODO: When lockout is handled under the HAL for all biometrics (fingerprint),
+        // we need to generate challenge for each one, have it signed by GK and reset lockout
+        // for each modality.
+        if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+            challenge = mContext.getSystemService(FaceManager.class).generateChallenge();
+        }
+
         final AuthenticationResult authResult;
         VerifyCredentialResponse response;
         synchronized (mSpManager) {
@@ -2413,6 +2422,17 @@
         if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
             notifyActivePasswordMetricsAvailable(userCredential, userId);
             unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId);
+            // Reset lockout
+            if (BiometricManager.hasBiometrics(mContext)) {
+                BiometricManager bm = mContext.getSystemService(BiometricManager.class);
+                Slog.i(TAG, "Resetting lockout, length: "
+                        + authResult.gkResponse.getPayload().length);
+                bm.resetLockout(authResult.gkResponse.getPayload());
+
+                if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+                    mContext.getSystemService(FaceManager.class).revokeChallenge();
+                }
+            }
 
             final byte[] secret = authResult.authToken.deriveDiskEncryptionKey();
             Slog.i(TAG, "Unlocking user " + userId + " with secret only, length " + secret.length);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index 4480435..a84306c 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -16,15 +16,16 @@
 
 package com.android.server.locksettings;
 
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
+        .STRONG_AUTH_NOT_REQUIRED;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
+        .STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
 
 import android.app.AlarmManager;
 import android.app.AlarmManager.OnAlarmListener;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.IStrongAuthTracker;
 import android.content.Context;
-import android.hardware.biometrics.BiometricManager;
 import android.os.Handler;
 import android.os.Message;
 import android.os.RemoteCallbackList;
@@ -61,7 +62,6 @@
     private final Context mContext;
 
     private AlarmManager mAlarmManager;
-    private BiometricManager mBiometricManager;
 
     public LockSettingsStrongAuth(Context context) {
         mContext = context;
@@ -69,12 +69,6 @@
         mAlarmManager = context.getSystemService(AlarmManager.class);
     }
 
-    public void systemReady() {
-        if (BiometricManager.hasBiometrics(mContext)) {
-            mBiometricManager = mContext.getSystemService(BiometricManager.class);
-        }
-    }
-
     private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) {
         mTrackers.register(tracker);
 
@@ -185,11 +179,6 @@
     }
 
     public void reportSuccessfulStrongAuthUnlock(int userId) {
-        if (mBiometricManager != null) {
-            byte[] token = null; /* TODO: pass real auth token once HAL supports it */
-            mBiometricManager.resetTimeout(token);
-        }
-
         final int argNotUsed = 0;
         mHandler.obtainMessage(MSG_SCHEDULE_STRONG_AUTH_TIMEOUT, userId, argNotUsed).sendToTarget();
     }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index b6ef180..b221241 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -1207,6 +1207,15 @@
             }
         }
 
+        public void setPlaybackSpeed(String packageName, int pid, int uid,
+                ControllerCallbackLink caller, float speed) {
+            try {
+                mCb.notifySetPlaybackSpeed(packageName, pid, uid, caller, speed);
+            } catch (RuntimeException e) {
+                Slog.e(TAG, "Remote failure in setPlaybackSpeed.", e);
+            }
+        }
+
         public void adjustVolume(String packageName, int pid, int uid,
                 ControllerCallbackLink caller, boolean asSystemService, int direction) {
             try {
@@ -1446,6 +1455,13 @@
         }
 
         @Override
+        public void setPlaybackSpeed(String packageName, ControllerCallbackLink caller,
+                float speed) {
+            mSessionCb.setPlaybackSpeed(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+                    caller, speed);
+        }
+
+        @Override
         public void sendCustomAction(String packageName, ControllerCallbackLink caller,
                 String action, Bundle args) {
             mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 9e5b92a..3f15b38 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -17,9 +17,6 @@
 package com.android.server.net;
 
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.provider.Settings.ACTION_VPN_SETTINGS;
 
 import android.app.Notification;
@@ -30,17 +27,14 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
-import android.net.LinkProperties;
 import android.net.LinkAddress;
+import android.net.LinkProperties;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
-import android.net.NetworkPolicyManager;
 import android.os.INetworkManagementService;
-import android.os.RemoteException;
 import android.security.Credentials;
 import android.security.KeyStore;
-import android.system.Os;
 import android.text.TextUtils;
 import android.util.Slog;
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 4bd8f45..6d82c1c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -15,15 +15,15 @@
  */
 package com.android.server.net;
 
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
 
 import android.app.ActivityManager;
 import android.net.NetworkPolicyManager;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index af55605..75b62cb 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -38,6 +38,11 @@
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -45,12 +50,7 @@
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
 import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
 import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
 import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS;
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 400443a..61a1a2f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -985,6 +985,9 @@
     @GuardedBy("mPackages")
     private PackageManagerInternal.DefaultBrowserProvider mDefaultBrowserProvider;
 
+    @GuardedBy("mPackages")
+    private PackageManagerInternal.DefaultHomeProvider mDefaultHomeProvider;
+
     private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
         private Context mContext;
         private ComponentName mIntentFilterVerifierComponent;
@@ -1349,7 +1352,7 @@
     final @Nullable String mRequiredVerifierPackage;
     final @NonNull String mRequiredInstallerPackage;
     final @NonNull String mRequiredUninstallerPackage;
-    final String mRequiredPermissionControllerPackage;
+    final @NonNull String mRequiredPermissionControllerPackage;
     final @Nullable String mSetupWizardPackage;
     final @Nullable String mStorageManagerPackage;
     final @Nullable String mSystemTextClassifierPackage;
@@ -1940,6 +1943,10 @@
                         // We may also need to apply pending (restored) runtime
                         // permission grants within these users.
                         mSettings.applyPendingPermissionGrantsLPw(packageName, userId);
+
+                        // Persistent preferred activity might have came into effect due to this
+                        // install.
+                        updateDefaultHomeLPw(userId);
                     }
                 }
             }
@@ -3306,7 +3313,8 @@
         // feature flags should cause us to invalidate any caches.
         final String cacheName = SystemProperties.digestOf(
                 "ro.build.fingerprint",
-                "persist.sys.isolated_storage");
+                StorageManager.PROP_ISOLATED_STORAGE,
+                StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT);
 
         // Reconcile cache directories, keeping only what we'd actually use.
         for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
@@ -12803,7 +12811,7 @@
     public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
             int restrictionFlags, int userId) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
-                "setPackagesSuspendedAsUser");
+                "setDistractingPackageRestrictionsAsUser");
 
         final int callingUid = Binder.getCallingUid();
         if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID
@@ -19076,6 +19084,7 @@
             pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
             scheduleWritePackageRestrictionsLocked(userId);
             postPreferredActivityChangedBroadcast(userId);
+            updateDefaultHomeLPw(userId);
         }
     }
 
@@ -19226,6 +19235,13 @@
     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
     @GuardedBy("mPackages")
     boolean clearPackagePreferredActivitiesLPw(String packageName, int userId) {
+        return clearPackagePreferredActivitiesLPw(packageName, false, userId);
+    }
+
+    /** This method takes a specific user id as well as UserHandle.USER_ALL. */
+    @GuardedBy("mPackages")
+    private boolean clearPackagePreferredActivitiesLPw(String packageName,
+            boolean skipUpdateDefaultHome, int userId) {
         ArrayList<PreferredActivity> removed = null;
         boolean changed = false;
         for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
@@ -19254,6 +19270,9 @@
                     pir.removeFilter(pa);
                 }
                 changed = true;
+                if (!skipUpdateDefaultHome) {
+                    updateDefaultHomeLPw(thisUserId);
+                }
             }
         }
         if (changed) {
@@ -19313,8 +19332,9 @@
         // writer
         try {
             synchronized (mPackages) {
-                clearPackagePreferredActivitiesLPw(null, userId);
+                clearPackagePreferredActivitiesLPw(null, true, userId);
                 mSettings.applyDefaultPreferredAppsLPw(userId);
+                updateDefaultHomeLPw(userId);
                 // TODO: We have to reset the default SMS and Phone. This requires
                 // significant refactoring to keep all default apps in the package
                 // manager (cleaner but more work) or have the services provide
@@ -19383,6 +19403,7 @@
                     new PersistentPreferredActivity(filter, activity));
             scheduleWritePackageRestrictionsLocked(userId);
             postPreferredActivityChangedBroadcast(userId);
+            updateDefaultHomeLPw(userId);
         }
     }
 
@@ -19426,6 +19447,7 @@
             if (changed) {
                 scheduleWritePackageRestrictionsLocked(userId);
                 postPreferredActivityChangedBroadcast(userId);
+                updateDefaultHomeLPw(userId);
             }
         }
     }
@@ -19513,6 +19535,7 @@
                     (readParser, readUserId) -> {
                         synchronized (mPackages) {
                             mSettings.readPreferredActivitiesLPw(readParser, readUserId);
+                            updateDefaultHomeLPw(readUserId);
                         }
                     });
         } catch (Exception e) {
@@ -19568,8 +19591,17 @@
             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
             restoreFromXml(parser, userId, TAG_DEFAULT_APPS,
                     (parser1, userId1) -> {
+                        String defaultBrowser;
                         synchronized (mPackages) {
                             mSettings.readDefaultAppsLPw(parser1, userId1);
+                            defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1);
+                        }
+                        if (defaultBrowser != null) {
+                            PackageManagerInternal.DefaultBrowserProvider provider;
+                            synchronized (mPackages) {
+                                provider = mDefaultBrowserProvider;
+                            }
+                            provider.setDefaultBrowser(defaultBrowser, userId1);
                         }
                     });
         } catch (Exception e) {
@@ -19927,19 +19959,59 @@
     ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
             int userId) {
         Intent intent  = getHomeIntent();
-        List<ResolveInfo> list = queryIntentActivitiesInternal(intent, null,
+        List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
                 PackageManager.GET_META_DATA, userId);
-        ResolveInfo preferred = findPreferredActivity(intent, null, 0, list, 0,
-                true, false, false, userId);
-
         allHomeCandidates.clear();
-        if (list != null) {
-            allHomeCandidates.addAll(list);
+        if (resolveInfos == null) {
+            return null;
         }
-        return (preferred == null || preferred.activityInfo == null)
-                ? null
-                : new ComponentName(preferred.activityInfo.packageName,
-                        preferred.activityInfo.name);
+        allHomeCandidates.addAll(resolveInfos);
+
+        PackageManagerInternal.DefaultHomeProvider provider;
+        synchronized (mPackages) {
+            provider = mDefaultHomeProvider;
+        }
+        if (provider == null) {
+            Slog.e(TAG, "mDefaultHomeProvider is null");
+            return null;
+        }
+        String packageName = provider.getDefaultHome(userId);
+        if (packageName == null) {
+            return null;
+        }
+        int resolveInfosSize = resolveInfos.size();
+        for (int i = 0; i < resolveInfosSize; i++) {
+            ResolveInfo resolveInfo = resolveInfos.get(i);
+
+            if (resolveInfo.activityInfo != null && TextUtils.equals(
+                    resolveInfo.activityInfo.packageName, packageName)) {
+                return new ComponentName(resolveInfo.activityInfo.packageName,
+                        resolveInfo.activityInfo.name);
+            }
+        }
+        return null;
+    }
+
+    private void updateDefaultHomeLPw(int userId) {
+        Intent intent = getHomeIntent();
+        List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
+                PackageManager.GET_META_DATA, userId);
+        ResolveInfo preferredResolveInfo = findPreferredActivity(intent, null, 0, resolveInfos,
+                0, true, false, false, userId);
+        String packageName = preferredResolveInfo != null
+                && preferredResolveInfo.activityInfo != null
+                ? preferredResolveInfo.activityInfo.packageName : null;
+        String currentPackageName = mDefaultHomeProvider.getDefaultHome(userId);
+        if (TextUtils.equals(currentPackageName, packageName)) {
+            return;
+        }
+        String[] callingPackages = getPackagesForUid(Binder.getCallingUid());
+        if (callingPackages != null && ArrayUtils.contains(callingPackages,
+                mRequiredPermissionControllerPackage)) {
+            // PermissionController manages default home directly.
+            return;
+        }
+        mDefaultHomeProvider.setDefaultHomeAsync(packageName, userId);
     }
 
     @Override
@@ -23829,6 +23901,13 @@
                 mDefaultBrowserProvider = provider;
             }
         }
+
+        @Override
+        public void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider) {
+            synchronized (mPackages) {
+                mDefaultHomeProvider = provider;
+            }
+        }
     }
 
     @GuardedBy("mPackages")
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 30c2281..6c212d6 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -156,12 +156,8 @@
         boolean success = true;
 
         // STOPSHIP: TODO(b/123753157): Verify APKs through Package Verifier.
-        if (!sessionContainsApex(session)) {
-            // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
-            // right away.
-            session.setStagedSessionReady();
-            return;
-        }
+        // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
+        // right away.
 
         final ApexInfoList apexInfoList = new ApexInfoList();
         // APEX checks. For single-package sessions, check if they contain an APEX. For
@@ -227,7 +223,8 @@
         }
 
         session.setStagedSessionReady();
-        if (!mApexManager.markStagedSessionReady(session.sessionId)) {
+        if (sessionContainsApex(session)
+                && !mApexManager.markStagedSessionReady(session.sessionId)) {
             session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                             "APEX staging failed, check logcat messages from apexd for more "
                             + "details.");
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 2036ed7..bd577598 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -721,7 +721,7 @@
         grantSystemFixedPermissionsToSystemPackage(
                 getDefaultSystemHandlerActivityPackage(
                         RingtoneManager.ACTION_RINGTONE_PICKER, userId),
-                userId, STORAGE_PERMISSIONS);
+                userId, STORAGE_PERMISSIONS, MEDIA_AURAL_PERMISSIONS);
 
         // TextClassifier Service
         String textClassifierPackageName =
diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
index 3534cf3..888dd99 100644
--- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
+++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
@@ -17,10 +17,13 @@
 package com.android.server.policy.role;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.role.RoleManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
 import android.os.Debug;
 import android.provider.Settings;
 import android.telecom.TelecomManager;
@@ -33,6 +36,7 @@
 import com.android.server.LocalServices;
 import com.android.server.role.RoleManagerService;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -54,19 +58,44 @@
     @NonNull
     private final Context mContext;
 
-    public LegacyRoleResolutionPolicy(Context context) {
+    public LegacyRoleResolutionPolicy(@NonNull Context context) {
         mContext = context;
     }
 
+    @NonNull
     @Override
-    public List<String> getRoleHolders(String roleName, int userId) {
+    public List<String> getRoleHolders(@NonNull String roleName, @UserIdInt int userId) {
         switch (roleName) {
+            case RoleManager.ROLE_ASSISTANT: {
+                String legacyAssistant = Settings.Secure.getStringForUser(
+                        mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId);
+                if (legacyAssistant == null || legacyAssistant.isEmpty()) {
+                    return Collections.emptyList();
+                } else {
+                    return Collections.singletonList(
+                            ComponentName.unflattenFromString(legacyAssistant).getPackageName());
+                }
+            }
+            case RoleManager.ROLE_BROWSER: {
+                PackageManagerInternal packageManagerInternal = LocalServices.getService(
+                        PackageManagerInternal.class);
+                String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName(
+                        userId);
+                return CollectionUtils.singletonOrEmpty(packageName);
+            }
+            case RoleManager.ROLE_DIALER: {
+                String setting = Settings.Secure.getStringForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.DIALER_DEFAULT_APPLICATION, userId);
+                return CollectionUtils.singletonOrEmpty(!TextUtils.isEmpty(setting)
+                        ? setting
+                        : mContext.getSystemService(TelecomManager.class).getSystemDialerPackage());
+            }
             case RoleManager.ROLE_SMS: {
                 // Moved over from SmsApplication#getApplication
                 String result = Settings.Secure.getStringForUser(
                         mContext.getContentResolver(),
                         Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
-
                 // TODO: STOPSHIP: Remove the following code once we read the value of
                 //  config_defaultSms in RoleControllerService.
                 if (result == null) {
@@ -92,34 +121,13 @@
                     SmsApplication.SmsApplicationData app = applicationData;
                     result = app == null ? null : app.mPackageName;
                 }
-
                 return CollectionUtils.singletonOrEmpty(result);
             }
-            case RoleManager.ROLE_ASSISTANT: {
-                String legacyAssistant = Settings.Secure.getStringForUser(
-                        mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId);
-
-                if (legacyAssistant == null || legacyAssistant.isEmpty()) {
-                    return Collections.emptyList();
-                } else {
-                    return Collections.singletonList(
-                            ComponentName.unflattenFromString(legacyAssistant).getPackageName());
-                }
-            }
-            case RoleManager.ROLE_DIALER: {
-                String setting = Settings.Secure.getStringForUser(
-                        mContext.getContentResolver(),
-                        Settings.Secure.DIALER_DEFAULT_APPLICATION, userId);
-
-                return CollectionUtils.singletonOrEmpty(!TextUtils.isEmpty(setting)
-                        ? setting
-                        : mContext.getSystemService(TelecomManager.class).getSystemDialerPackage());
-            }
-            case RoleManager.ROLE_BROWSER: {
-                PackageManagerInternal packageManagerInternal = LocalServices.getService(
-                        PackageManagerInternal.class);
-                String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName(
-                        userId);
+            case RoleManager.ROLE_HOME: {
+                PackageManager packageManager = mContext.getPackageManager();
+                List<ResolveInfo> resolveInfos = new ArrayList<>();
+                ComponentName componentName = packageManager.getHomeActivities(resolveInfos);
+                String packageName = componentName != null ? componentName.getPackageName() : null;
                 return CollectionUtils.singletonOrEmpty(packageName);
             }
             default: {
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index 4186154..8740256 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -123,6 +123,9 @@
     public AttentionDetector(Runnable onUserAttention, Object lock) {
         mOnUserAttention = onUserAttention;
         mLock = lock;
+
+        // Device starts with an awake state upon boot.
+        mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
     }
 
     public void systemReady(Context context) {
@@ -145,7 +148,7 @@
             if (DEBUG) {
                 Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now));
             }
-            return nextScreenDimming;
+            return whenToCheck;
         } else if (whenToStopExtending < whenToCheck) {
             if (DEBUG) {
                 Slog.d(TAG, "Let device sleep to avoid false results and improve security "
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 21bf9de..d853121 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -111,7 +111,8 @@
     /** @see #getRoleHolders(String, int) */
     public interface RoleHoldersResolver {
         /** @return a list of packages that hold a given role for a given user */
-        List<String> getRoleHolders(String roleName, int userId);
+        @NonNull
+        List<String> getRoleHolders(@NonNull String roleName, @UserIdInt int userId);
     }
 
     /**
@@ -154,6 +155,7 @@
         PackageManagerInternal packageManagerInternal = LocalServices.getService(
                 PackageManagerInternal.class);
         packageManagerInternal.setDefaultBrowserProvider(new DefaultBrowserProvider());
+        packageManagerInternal.setDefaultHomeProvider(new DefaultHomeProvider());
 
         registerUserRemovedReceiver();
     }
@@ -741,4 +743,33 @@
             }
         }
     }
+
+    private class DefaultHomeProvider implements PackageManagerInternal.DefaultHomeProvider {
+
+        @Nullable
+        @Override
+        public String getDefaultHome(@UserIdInt int userId) {
+            return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders(
+                    RoleManager.ROLE_HOME));
+        }
+
+        @Override
+        public void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId) {
+            IRoleManagerCallback callback = new IRoleManagerCallback.Stub() {
+                @Override
+                public void onSuccess() {}
+                @Override
+                public void onFailure() {
+                    Slog.e(LOG_TAG, "Failed to set default home: " + packageName);
+                }
+            };
+            if (packageName != null) {
+                getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_HOME,
+                        packageName, 0, callback);
+            } else {
+                getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0,
+                        callback);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 95c3f4c..ceaf829 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -40,6 +40,7 @@
 import android.os.HandlerThread;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.provider.DeviceConfig;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.SparseBooleanArray;
@@ -61,6 +62,7 @@
 import java.util.Map;
 import java.util.Random;
 import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Implementation of service that manages APK level rollbacks.
@@ -71,13 +73,19 @@
 
     // Rollbacks expire after 48 hours.
     // TODO: How to test rollback expiration works properly?
-    private static final long ROLLBACK_LIFETIME_DURATION_MILLIS = 48 * 60 * 60 * 1000;
+    private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS =
+            TimeUnit.HOURS.toMillis(48);
 
     // Lock used to synchronize accesses to in-memory rollback data
     // structures. By convention, methods with the suffix "Locked" require
     // mLock is held when they are called.
     private final Object mLock = new Object();
 
+    // No need for guarding with lock because value is only accessed in handler thread
+    // and the value will be written on boot complete. Initialization here happens before
+    // handler threads are running so that's fine.
+    private long mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
+
     // Used for generating rollback IDs.
     private final Random mRandom = new SecureRandom();
 
@@ -484,7 +492,25 @@
         });
     }
 
+    private void updateRollbackLifetimeDurationInMillis() {
+        String strRollbackLifetimeInMillis = DeviceConfig.getProperty(
+                DeviceConfig.Rollback.BOOT_NAMESPACE,
+                DeviceConfig.Rollback.ROLLBACK_LIFETIME_IN_MILLIS);
+
+        try {
+            mRollbackLifetimeDurationInMillis = (strRollbackLifetimeInMillis == null)
+                    ? DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS
+                    : Long.parseLong(strRollbackLifetimeInMillis);
+        } catch (NumberFormatException e) {
+            mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS;
+        }
+    }
+
     void onBootCompleted() {
+        getHandler().post(() -> updateRollbackLifetimeDurationInMillis());
+        // Also posts to handler thread
+        scheduleExpiration(0);
+
         getHandler().post(() -> {
             // Check to see if any staged sessions with rollback enabled have
             // been applied.
@@ -565,8 +591,6 @@
         for (RollbackInfo info : mRecentlyExecutedRollbacks) {
             mAllocatedRollbackIds.put(info.getRollbackId(), true);
         }
-
-        scheduleExpiration(0);
     }
 
     /**
@@ -700,8 +724,7 @@
                 if (!data.isAvailable) {
                     continue;
                 }
-
-                if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
+                if (!now.isBefore(data.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) {
                     iter.remove();
                     deleteRollback(data);
                 } else if (oldest == null || oldest.isAfter(data.timestamp)) {
@@ -711,7 +734,7 @@
         }
 
         if (oldest != null) {
-            scheduleExpiration(now.until(oldest.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS),
+            scheduleExpiration(now.until(oldest.plusMillis(mRollbackLifetimeDurationInMillis),
                         ChronoUnit.MILLIS));
         }
     }
@@ -1144,8 +1167,8 @@
                         packages.add(data.packages.get(i).getPackageName());
                     }
                     mPackageHealthObserver.startObservingHealth(packages,
-                            ROLLBACK_LIFETIME_DURATION_MILLIS);
-                    scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS);
+                            mRollbackLifetimeDurationInMillis);
+                    scheduleExpiration(mRollbackLifetimeDurationInMillis);
                 } catch (IOException e) {
                     Log.e(TAG, "Unable to enable rollback", e);
                     deleteRollback(data);
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index f3393e2..edca2e3 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1942,6 +1942,20 @@
         }
     }
 
+    private void pullTimeZoneDataInfo(int tagId,
+            long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+        String tzDbVersion = "Unknown";
+        try {
+            tzDbVersion = android.icu.util.TimeZone.getTZDataVersion();
+        } catch (Exception e) {
+            Log.e(TAG, "Getting tzdb version failed: ", e);
+        }
+
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+        e.writeString(tzDbVersion);
+        pulledData.add(e);
+    }
+
     /**
      * Pulls various data.
      */
@@ -2130,6 +2144,10 @@
                 pullDangerousPermissionState(elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.TIME_ZONE_DATA_INFO: {
+                pullTimeZoneDataInfo(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
index ff01d46..b121298 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
@@ -19,6 +19,7 @@
 import static android.app.StatusBarManager.DISABLE2_NONE;
 import static android.app.StatusBarManager.DISABLE_NONE;
 
+import android.app.StatusBarManager.DisableInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Binder;
@@ -26,6 +27,7 @@
 import android.os.RemoteException;
 import android.os.ShellCommand;
 import android.service.quicksettings.TileService;
+import android.util.Pair;
 
 import java.io.PrintWriter;
 
@@ -68,6 +70,8 @@
                     return runGetStatusIcons();
                 case "disable-for-setup":
                     return runDisableForSetup();
+                case "send-disable-flag":
+                    return runSendDisableFlag();
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -132,6 +136,47 @@
         return 0;
     }
 
+    private int runSendDisableFlag() {
+        String pkg = mContext.getPackageName();
+        int disable1 = DISABLE_NONE;
+        int disable2 = DISABLE2_NONE;
+
+        DisableInfo info = new DisableInfo();
+
+        String arg = getNextArg();
+        while (arg != null) {
+            switch (arg) {
+                case "search":
+                    info.setSearchDisabled(true);
+                    break;
+                case "home":
+                    info.setNagivationHomeDisabled(true);
+                    break;
+                case "recents":
+                    info.setRecentsDisabled(true);
+                    break;
+                case "notification-alerts":
+                    info.setNotificationPeekingDisabled(true);
+                    break;
+                case "statusbar-expansion":
+                    info.setStatusBarExpansionDisabled(true);
+                    break;
+
+                default:
+                    break;
+            }
+
+            arg = getNextArg();
+        }
+
+        Pair<Integer, Integer> flagPair = info.toFlags();
+
+        mInterface.disable(flagPair.first, sToken, pkg);
+        mInterface.disable2(flagPair.second, sToken, pkg);
+
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         final PrintWriter pw = getOutPrintWriter();
@@ -166,6 +211,17 @@
         pw.println("  disable-for-setup DISABLE");
         pw.println("    If true, disable status bar components unsuitable for device setup");
         pw.println("");
+        pw.println("  send-disable-flag FLAG...");
+        pw.println("    Send zero or more disable flags (parsed individually) to StatusBarManager");
+        pw.println("    Valid options:");
+        pw.println("        <blank>             - equivalent to \"none\"");
+        pw.println("        none                - re-enables all components");
+        pw.println("        search              - disable search");
+        pw.println("        home                - disable naviagation home");
+        pw.println("        recents             - disable recents/overview");
+        pw.println("        notification-peek   - disable notification peeking");
+        pw.println("        statusbar-expansion - disable status bar expansion");
+        pw.println("");
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0251efb..f33c518 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2677,8 +2677,9 @@
      * Get the configuration orientation by the requested screen orientation
      * ({@link ActivityInfo.ScreenOrientation}) of this activity.
      *
-     * @return orientation in ({@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT},
-     *         {@link #ORIENTATION_UNDEFINED}).
+     * @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE},
+     *         {@link Configuration#ORIENTATION_PORTRAIT},
+     *         {@link Configuration#ORIENTATION_UNDEFINED}).
      */
     int getRequestedConfigurationOrientation() {
         final int screenOrientation = getOrientation();
@@ -2936,14 +2937,36 @@
                 // should be given the aspect ratio.
                 activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f);
             }
-        } else if (containingRatio < minAspectRatio && minAspectRatio != 0) {
-            if (containingAppWidth < containingAppHeight) {
-                // Width is the shorter side, so we use the height to figure-out what the max. width
-                // should be given the aspect ratio.
+        } else if (containingRatio < minAspectRatio) {
+            boolean adjustWidth;
+            switch (getRequestedConfigurationOrientation()) {
+                case ORIENTATION_LANDSCAPE:
+                    // Width should be the longer side for this landscape app, so we use the width
+                    // to figure-out what the max. height should be given the aspect ratio.
+                    adjustWidth = false;
+                    break;
+                case ORIENTATION_PORTRAIT:
+                    // Height should be the longer side for this portrait app, so we use the height
+                    // to figure-out what the max. width should be given the aspect ratio.
+                    adjustWidth = true;
+                    break;
+                default:
+                    // This app doesn't have a preferred orientation, so we keep the length of the
+                    // longer side, and use it to figure-out the length of the shorter side.
+                    if (containingAppWidth < containingAppHeight) {
+                        // Width is the shorter side, so we use the height to figure-out what the
+                        // max. width should be given the aspect ratio.
+                        adjustWidth = true;
+                    } else {
+                        // Height is the shorter side, so we use the width to figure-out what the
+                        // max. height should be given the aspect ratio.
+                        adjustWidth = false;
+                    }
+                    break;
+            }
+            if (adjustWidth) {
                 activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f);
             } else {
-                // Height is the shorter side, so we use the width to figure-out what the max.
-                // height should be given the aspect ratio.
                 activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f);
             }
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c33a2c1..d40948b 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -830,12 +830,25 @@
                         new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
                                 | PendingIntent.FLAG_ONE_SHOT, null);
 
-                final int flags = intent.getFlags();
                 Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
-                newIntent.setFlags(flags
-                        | FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+
+                int flags = intent.getFlags();
+                flags |= Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+
+                /*
+                 * Prevent reuse of review activity: Each app needs their own review activity. By
+                 * default activities launched with NEW_TASK or NEW_DOCUMENT try to reuse activities
+                 * with the same launch parameters (extras are ignored). Hence to avoid possible
+                 * reuse force a new activity via the MULTIPLE_TASK flag.
+                 *
+                 * Activities that are not launched with NEW_TASK or NEW_DOCUMENT are not re-used,
+                 * hence no need to add the flag in this case.
+                 */
+                if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0) {
+                    flags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+                }
+                newIntent.setFlags(flags);
+
                 newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
                 newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
                 if (resultRecord != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5cfc20b..4795555 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1500,8 +1500,8 @@
         final int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
         final int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
 
-        mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
         mDisplayPolicy.configure(width, height, shortSizeDp);
+        mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
 
         mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
                 calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 2ee30ac..91d573d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2617,9 +2617,8 @@
             DisplayCutout displayCutout) {
         int width = fullWidth;
         if (hasNavigationBar()) {
-            // For a basic navigation bar, when we are in landscape mode we place
-            // the navigation bar to the side.
-            if (navigationBarCanMove() && fullWidth > fullHeight) {
+            final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
+            if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {
                 width -= getNavigationBarWidth(rotation, uiMode);
             }
         }
@@ -2646,9 +2645,8 @@
             DisplayCutout displayCutout) {
         int height = fullHeight;
         if (hasNavigationBar()) {
-            // For a basic navigation bar, when we are in portrait mode we place
-            // the navigation bar to the bottom.
-            if (!navigationBarCanMove() || fullWidth < fullHeight) {
+            final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
+            if (navBarPosition == NAV_BAR_BOTTOM) {
                 height -= getNavigationBarHeight(rotation, uiMode);
             }
         }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 5f341ee..543f196 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -38,6 +38,7 @@
 import android.provider.Settings;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.DisplayCutout;
 import android.view.Surface;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -70,6 +71,8 @@
     private final int mDeskDockRotation;
     private final int mUndockedHdmiRotation;
 
+    private final float mCloseToSquareMaxAspectRatio;
+
     private OrientationListener mOrientationListener;
     private StatusBarManagerInternal mStatusBarManagerInternal;
     private SettingsObserver mSettingsObserver;
@@ -132,6 +135,9 @@
         mUndockedHdmiRotation = readRotation(
                 com.android.internal.R.integer.config_undockedHdmiRotation);
 
+        mCloseToSquareMaxAspectRatio = mContext.getResources().getFloat(
+                com.android.internal.R.dimen.config_closeToSquareDisplayMaxAspectRatio);
+
         if (isDefaultDisplay) {
             final Handler uiHandler = UiThread.getHandler();
             mOrientationListener = new OrientationListener(mContext, uiHandler);
@@ -212,10 +218,12 @@
         // so if the orientation is forced, we need to respect that no matter what.
         final boolean isTv = mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_LEANBACK);
+        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)
+                ((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
@@ -227,6 +235,18 @@
         setFixedToUserRotation(forceDefaultOrienation);
     }
 
+    private boolean isNonDecorDisplayCloseToSquare(int rotation, int width, int height) {
+        final DisplayCutout displayCutout =
+                mDisplayContent.calculateDisplayCutoutForRotation(rotation).getDisplayCutout();
+        final int uiMode = mService.mPolicy.getUiMode();
+        final int w = mDisplayPolicy.getNonDecorDisplayWidth(
+                width, height, rotation, uiMode, displayCutout);
+        final int h = mDisplayPolicy.getNonDecorDisplayHeight(
+                width, height, rotation, uiMode, displayCutout);
+        final float aspectRatio = Math.max(w, h) / (float) Math.min(w, h);
+        return aspectRatio <= mCloseToSquareMaxAspectRatio;
+    }
+
     void setRotation(int rotation) {
         if (mOrientationListener != null) {
             mOrientationListener.setCurrentRotation(rotation);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c747c6d..168c9ad 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -446,7 +446,8 @@
     final boolean mLimitedAlphaCompositing;
     final int mMaxUiWidth;
 
-    final WindowManagerPolicy mPolicy;
+    @VisibleForTesting
+    WindowManagerPolicy mPolicy;
 
     final IActivityManager mActivityManager;
     // TODO: Probably not needed once activities are fully in WM.
@@ -4263,9 +4264,12 @@
             if (mMaxUiWidth > 0) {
                 mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth));
             }
-            applyForcedPropertiesForDefaultDisplay();
+            final boolean changed = applyForcedPropertiesForDefaultDisplay();
             mAnimator.ready();
             mDisplayReady = true;
+            if (changed) {
+                reconfigureDisplayLocked(getDefaultDisplayContentLocked());
+            }
             mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TOUCHSCREEN);
         }
@@ -4865,7 +4869,8 @@
     }
 
     /** The global settings only apply to default display. */
-    private void applyForcedPropertiesForDefaultDisplay() {
+    private boolean applyForcedPropertiesForDefaultDisplay() {
+        boolean changed = false;
         final DisplayContent displayContent = getDefaultDisplayContentLocked();
         // Display size.
         String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
@@ -4885,6 +4890,7 @@
                         Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + height);
                         displayContent.updateBaseDisplayMetrics(width, height,
                                 displayContent.mBaseDisplayDensity);
+                        changed = true;
                     }
                 } catch (NumberFormatException ex) {
                 }
@@ -4893,17 +4899,20 @@
 
         // Display density.
         final int density = getForcedDisplayDensityForUserLocked(mCurrentUserId);
-        if (density != 0) {
+        if (density != 0 && density != displayContent.mBaseDisplayDensity) {
             displayContent.mBaseDisplayDensity = density;
+            changed = true;
         }
 
         // Display scaling mode.
         int mode = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.DISPLAY_SCALING_FORCE, 0);
-        if (mode != 0) {
+        if (displayContent.mDisplayScalingDisabled != (mode != 0)) {
             Slog.i(TAG_WM, "FORCED DISPLAY SCALING DISABLED");
             displayContent.mDisplayScalingDisabled = true;
+            changed = true;
         }
+        return changed;
     }
 
     @Override
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 5c19ad3..9cbb58d 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "BatteryStatsService"
 //#define LOG_NDEBUG 0
 
+#include <climits>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -28,6 +29,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <unordered_map>
+#include <utility>
 
 #include <android/hardware/power/1.0/IPower.h>
 #include <android/hardware/power/1.1/IPower.h>
@@ -87,6 +89,15 @@
 std::function<jint(JNIEnv*, jobject)> gGetPlatformLowPowerStatsImpl = {};
 std::function<jint(JNIEnv*, jobject)> gGetSubsystemLowPowerStatsImpl = {};
 
+// Cellular/Wifi power monitor rail information
+static jmethodID jupdateRailData = NULL;
+static jmethodID jsetRailStatsAvailability = NULL;
+
+std::function<void(JNIEnv*, jobject)> gGetRailEnergyPowerStatsImpl = {};
+
+std::unordered_map<uint32_t, std::pair<std::string, std::string>> gPowerStatsHalRailNames = {};
+static bool power_monitor_available = false;
+
 // The caller must be holding gPowerHalMutex.
 static void deinitPowerStatsLocked() {
     gPowerStatsHalV1_0 = nullptr;
@@ -258,6 +269,7 @@
     gPowerStatsHalStateNames.clear();
     gPowerStatsHalPlatformIds.clear();
     gPowerStatsHalSubsystemIds.clear();
+    gPowerStatsHalRailNames.clear();
 
     Return<void> ret;
     ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
@@ -301,6 +313,27 @@
         return false;
     }
 
+    // Get Power monitor rails available
+    ret = gPowerStatsHalV1_0->getRailInfo([](auto rails, auto status) {
+        if (status != Status::SUCCESS) {
+            ALOGW("Rail information is not available");
+            power_monitor_available = false;
+            return;
+        }
+
+        // Fill out rail names/subsystems into gPowerStatsHalRailNames
+        for (auto rail : rails) {
+            gPowerStatsHalRailNames.emplace(rail.index,
+                std::make_pair(rail.railName, rail.subsysName));
+        }
+        if (!gPowerStatsHalRailNames.empty()) {
+            power_monitor_available = true;
+        }
+    });
+    if (!checkResultLocked(ret, __func__)) {
+        return false;
+    }
+
     return (!gPowerStatsHalEntityNames.empty()) && (!gPowerStatsHalStateNames.empty());
 }
 
@@ -517,6 +550,50 @@
     return total_added;
 }
 
+static void getPowerStatsHalRailEnergyData(JNIEnv* env, jobject jrailStats) {
+    using android::hardware::power::stats::V1_0::Status;
+    using android::hardware::power::stats::V1_0::EnergyData;
+
+    if (!getPowerStatsHalLocked()) {
+        ALOGE("failed to get power stats");
+        return;
+    }
+
+    if (!power_monitor_available) {
+        env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false);
+        ALOGW("Rail energy data is not available");
+        return;
+    }
+
+    // Get power rail energySinceBoot data
+    Return<void> ret = gPowerStatsHalV1_0->getEnergyData({},
+        [&env, &jrailStats](auto energyData, auto status) {
+            if (status == Status::NOT_SUPPORTED) {
+                ALOGW("getEnergyData is not supported");
+                return;
+            }
+
+            for (auto data : energyData) {
+                if (!(data.timestamp > LLONG_MAX || data.energy > LLONG_MAX)) {
+                    env->CallVoidMethod(jrailStats,
+                        jupdateRailData,
+                        data.index,
+                        env->NewStringUTF(
+                            gPowerStatsHalRailNames.at(data.index).first.c_str()),
+                        env->NewStringUTF(
+                            gPowerStatsHalRailNames.at(data.index).second.c_str()),
+                        data.timestamp,
+                        data.energy);
+                } else {
+                    ALOGE("Java long overflow seen. Rail index %d not updated", data.index);
+                }
+            }
+        });
+    if (!checkResultLocked(ret, __func__)) {
+        ALOGE("getEnergyData failed");
+    }
+}
+
 // The caller must be holding powerHalMutex.
 static void getPowerHalLowPowerData(JNIEnv* env, jobject jrpmStats) {
     sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
@@ -761,11 +838,13 @@
         gGetLowPowerStatsImpl = getPowerStatsHalLowPowerData;
         gGetPlatformLowPowerStatsImpl = getPowerStatsHalPlatformData;
         gGetSubsystemLowPowerStatsImpl = getPowerStatsHalSubsystemData;
+        gGetRailEnergyPowerStatsImpl = getPowerStatsHalRailEnergyData;
     } else if (android::hardware::power::V1_0::IPower::getService() != nullptr) {
         ALOGI("Using power HAL");
         gGetLowPowerStatsImpl = getPowerHalLowPowerData;
         gGetPlatformLowPowerStatsImpl = getPowerHalPlatformData;
         gGetSubsystemLowPowerStatsImpl = getPowerHalSubsystemData;
+        gGetRailEnergyPowerStatsImpl = NULL;
     }
 }
 
@@ -835,11 +914,44 @@
     return -1;
 }
 
+static void getRailEnergyPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrailStats) {
+    if (jrailStats == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                "The railstats jni input jobject jrailStats is null.");
+        return;
+    }
+    if (jupdateRailData == NULL) {
+        ALOGE("A railstats jni jmethodID is null.");
+        return;
+    }
+
+    std::lock_guard<std::mutex> lock(gPowerHalMutex);
+
+    if (!gGetRailEnergyPowerStatsImpl) {
+        setUpPowerStatsLocked();
+    }
+
+    if (gGetRailEnergyPowerStatsImpl)  {
+        gGetRailEnergyPowerStatsImpl(env, jrailStats);
+        return;
+    }
+
+    if (jsetRailStatsAvailability == NULL) {
+        ALOGE("setRailStatsAvailability jni jmethodID is null.");
+        return;
+    }
+    env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false);
+    ALOGE("Unable to load Power.Stats.HAL. Setting rail availability to false");
+    return;
+}
+
 static const JNINativeMethod method_table[] = {
     { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
     { "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats },
     { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
     { "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats },
+    { "getRailEnergyPowerStats", "(Lcom/android/internal/os/RailStats;)V",
+        (void*)getRailEnergyPowerStats },
 };
 
 int register_android_server_BatteryStatsService(JNIEnv *env)
@@ -850,8 +962,9 @@
             env->FindClass("com/android/internal/os/RpmStats$PowerStatePlatformSleepState");
     jclass clsPowerStateSubsystem =
             env->FindClass("com/android/internal/os/RpmStats$PowerStateSubsystem");
+    jclass clsRailStats = env->FindClass("com/android/internal/os/RailStats");
     if (clsRpmStats == NULL || clsPowerStatePlatformSleepState == NULL
-            || clsPowerStateSubsystem == NULL) {
+            || clsPowerStateSubsystem == NULL || clsRailStats == NULL) {
         ALOGE("A rpmstats jni jclass is null.");
     } else {
         jgetAndUpdatePlatformState = env->GetMethodID(clsRpmStats, "getAndUpdatePlatformState",
@@ -862,6 +975,10 @@
                 "(Ljava/lang/String;JI)V");
         jputState = env->GetMethodID(clsPowerStateSubsystem, "putState",
                 "(Ljava/lang/String;JI)V");
+        jupdateRailData = env->GetMethodID(clsRailStats, "updateRailData",
+                "(JLjava/lang/String;Ljava/lang/String;JJ)V");
+        jsetRailStatsAvailability = env->GetMethodID(clsRailStats, "setRailStatsAvailability",
+                "(Z)V");
     }
 
     return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService",
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a6017f2..aae159c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -36,6 +36,7 @@
 import android.content.res.Resources.Theme;
 import android.database.sqlite.SQLiteCompatibilityWalFlags;
 import android.database.sqlite.SQLiteGlobal;
+import android.net.NetworkStackClient;
 import android.os.BaseBundle;
 import android.os.Binder;
 import android.os.Build;
@@ -1350,9 +1351,7 @@
 
             traceBeginAndSlog("StartNetworkStack");
             try {
-                final android.net.NetworkStack networkStack =
-                        context.getSystemService(android.net.NetworkStack.class);
-                networkStack.start(context);
+                NetworkStackClient.getInstance().start(context);
             } catch (Throwable e) {
                 reportWtf("starting Network Stack", e);
             }
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 638ec95..9946cc3 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -1,6 +1,9 @@
 java_library_static {
     name: "services.net",
     srcs: ["java/**/*.java"],
+    static_libs: [
+        "netd_aidl_interface-java",
+    ]
 }
 
 filegroup {
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
new file mode 100644
index 0000000..1eb7b98
--- /dev/null
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.ip.IIpClientCallbacks;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+
+/**
+ * Service used to communicate with the network stack, which is running in a separate module.
+ * @hide
+ */
+public class NetworkStackClient {
+    private static final String TAG = NetworkStackClient.class.getSimpleName();
+
+    private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
+
+    private static NetworkStackClient sInstance;
+
+    @NonNull
+    @GuardedBy("mPendingNetStackRequests")
+    private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
+    @Nullable
+    @GuardedBy("mPendingNetStackRequests")
+    private INetworkStackConnector mConnector;
+
+    private volatile boolean mNetworkStackStartRequested = false;
+
+    private interface NetworkStackCallback {
+        void onNetworkStackConnected(INetworkStackConnector connector);
+    }
+
+    private NetworkStackClient() { }
+
+    /**
+     * Get the NetworkStackClient singleton instance.
+     */
+    public static synchronized NetworkStackClient getInstance() {
+        if (sInstance == null) {
+            sInstance = new NetworkStackClient();
+        }
+        return sInstance;
+    }
+
+    /**
+     * Create a DHCP server according to the specified parameters.
+     *
+     * <p>The server will be returned asynchronously through the provided callbacks.
+     */
+    public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
+            final IDhcpServerCallbacks cb) {
+        requestConnector(connector -> {
+            try {
+                connector.makeDhcpServer(ifName, params, cb);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        });
+    }
+
+    /**
+     * Create an IpClient on the specified interface.
+     *
+     * <p>The IpClient will be returned asynchronously through the provided callbacks.
+     */
+    public void makeIpClient(String ifName, IIpClientCallbacks cb) {
+        requestConnector(connector -> {
+            try {
+                connector.makeIpClient(ifName, cb);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        });
+    }
+
+    /**
+     * Create a NetworkMonitor.
+     *
+     * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
+     */
+    public void makeNetworkMonitor(
+            NetworkParcelable network, String name, INetworkMonitorCallbacks cb) {
+        requestConnector(connector -> {
+            try {
+                connector.makeNetworkMonitor(network, name, cb);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        });
+    }
+
+    private class NetworkStackConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            registerNetworkStackService(service);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            // TODO: crash/reboot the system ?
+            Slog.wtf(TAG, "Lost network stack connector");
+        }
+    };
+
+    private void registerNetworkStackService(@NonNull IBinder service) {
+        final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service);
+
+        ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
+                DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+
+        final ArrayList<NetworkStackCallback> requests;
+        synchronized (mPendingNetStackRequests) {
+            requests = new ArrayList<>(mPendingNetStackRequests);
+            mPendingNetStackRequests.clear();
+            mConnector = connector;
+        }
+
+        for (NetworkStackCallback r : requests) {
+            r.onNetworkStackConnected(connector);
+        }
+    }
+
+    /**
+     * Start the network stack. Should be called only once on device startup.
+     *
+     * <p>This method will start the network stack either in the network stack process, or inside
+     * the system server on devices that do not support the network stack module. The network stack
+     * connector will then be delivered asynchronously to clients that requested it before it was
+     * started.
+     */
+    public void start(Context context) {
+        mNetworkStackStartRequested = true;
+        // Try to bind in-process if the library is available
+        IBinder connector = null;
+        try {
+            final Class service = Class.forName(
+                    "com.android.server.NetworkStackService",
+                    true /* initialize */,
+                    context.getClassLoader());
+            connector = (IBinder) service.getMethod("makeConnector", Context.class)
+                    .invoke(null, context);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
+            // TODO: crash/reboot system here ?
+            return;
+        } catch (ClassNotFoundException e) {
+            // Normal behavior if stack is provided by the app: fall through
+        }
+
+        // In-process network stack. Add the service to the service manager here.
+        if (connector != null) {
+            registerNetworkStackService(connector);
+            return;
+        }
+        // Start the network stack process. The service will be added to the service manager in
+        // NetworkStackConnection.onServiceConnected().
+        final Intent intent = new Intent(INetworkStackConnector.class.getName());
+        final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
+        intent.setComponent(comp);
+
+        if (comp == null) {
+            Slog.wtf(TAG, "Could not resolve the network stack with " + intent);
+            // TODO: crash/reboot system server ?
+            return;
+        }
+        final PackageManager pm = context.getPackageManager();
+        int uid = -1;
+        try {
+            uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.wtf("Network stack package not found", e);
+            // Fall through
+        }
+        if (uid != Process.NETWORK_STACK_UID) {
+            throw new SecurityException("Invalid network stack UID: " + uid);
+        }
+
+        final int hasPermission =
+                pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName());
+        if (hasPermission != PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK);
+        }
+
+        if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
+                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
+            Slog.wtf(TAG,
+                    "Could not bind to network stack in-process, or in app with " + intent);
+            // TODO: crash/reboot system server if no network stack after a timeout ?
+        }
+    }
+
+    /**
+     * For non-system server clients, get the connector registered by the system server.
+     */
+    private INetworkStackConnector getRemoteConnector() {
+        // Block until the NetworkStack connector is registered in ServiceManager.
+        // <p>This is only useful for non-system processes that do not have a way to be notified of
+        // registration completion. Adding a callback system would be too heavy weight considering
+        // that the connector is registered on boot, so it is unlikely that a client would request
+        // it before it is registered.
+        // TODO: consider blocking boot on registration and simplify much of the logic in this class
+        IBinder connector;
+        try {
+            final long before = System.currentTimeMillis();
+            while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
+                Thread.sleep(20);
+                if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
+                    Slog.e(TAG, "Timeout waiting for NetworkStack connector");
+                    return null;
+                }
+            }
+        } catch (InterruptedException e) {
+            Slog.e(TAG, "Error waiting for NetworkStack connector", e);
+            return null;
+        }
+
+        return INetworkStackConnector.Stub.asInterface(connector);
+    }
+
+    private void requestConnector(@NonNull NetworkStackCallback request) {
+        // TODO: PID check.
+        final int caller = Binder.getCallingUid();
+        if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) {
+            // Don't even attempt to obtain the connector and give a nice error message
+            throw new SecurityException(
+                    "Only the system server should try to bind to the network stack.");
+        }
+
+        if (!mNetworkStackStartRequested) {
+            // The network stack is not being started in this process, e.g. this process is not
+            // the system server. Get a remote connector registered by the system server.
+            final INetworkStackConnector connector = getRemoteConnector();
+            synchronized (mPendingNetStackRequests) {
+                mConnector = connector;
+            }
+            request.onNetworkStackConnected(connector);
+            return;
+        }
+
+        final INetworkStackConnector connector;
+        synchronized (mPendingNetStackRequests) {
+            connector = mConnector;
+            if (connector == null) {
+                mPendingNetStackRequests.add(request);
+                return;
+            }
+        }
+
+        request.onNetworkStackConnected(connector);
+    }
+}
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 2a2a67a..bf917bf 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -23,8 +23,7 @@
 import android.net.DhcpResultsParcelable;
 import android.net.LinkProperties;
 import android.net.LinkPropertiesParcelable;
-import android.net.NetworkStack;
-import android.net.ip.IIpClientCallbacks;
+import android.net.NetworkStackClient;
 import android.os.ConditionVariable;
 
 import java.io.FileDescriptor;
@@ -76,30 +75,17 @@
      *
      * <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of
      * {@link IIpClientCallbacks}.
-     * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)}
+     * @see {@link NetworkStackClient#makeIpClient(String, IIpClientCallbacks)}
      */
     public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
-        context.getSystemService(NetworkStack.class)
-                .makeIpClient(ifName, new IpClientCallbacksProxy(callback));
-    }
-
-    /**
-     * Create a new IpClient.
-     *
-     * <p>This is a convenience method to allow clients to use {@link IpClientCallbacksProxy}
-     * instead of {@link IIpClientCallbacks}.
-     * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)}
-     */
-    public static void makeIpClient(
-            Context context, String ifName, IpClientCallbacksProxy callback) {
-        context.getSystemService(NetworkStack.class)
-                .makeIpClient(ifName, callback);
+        // TODO: migrate clients and remove context argument
+        NetworkStackClient.getInstance().makeIpClient(ifName, new IpClientCallbacksProxy(callback));
     }
 
     /**
      * Wrapper to relay calls from {@link IIpClientCallbacks} to {@link IpClientCallbacks}.
      */
-    public static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
+    private static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
         protected final IpClientCallbacks mCb;
 
         /**
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 7910c9a..34fc735 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -22,7 +22,6 @@
 import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
 import static android.net.util.NetworkConstants.asByte;
 
-import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.INetd;
 import android.net.INetworkStackStatusCallback;
@@ -31,7 +30,7 @@
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
-import android.net.NetworkStack;
+import android.net.NetworkStackClient;
 import android.net.RouteInfo;
 import android.net.dhcp.DhcpServerCallbacks;
 import android.net.dhcp.DhcpServingParamsParcel;
@@ -132,10 +131,6 @@
     }
 
     public static class Dependencies {
-        private final Context mContext;
-        public Dependencies(Context context) {
-            mContext = context;
-        }
         public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
             return new RouterAdvertisementDaemon(ifParams);
         }
@@ -153,7 +148,7 @@
          */
         public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
                 DhcpServerCallbacks cb) {
-            mContext.getSystemService(NetworkStack.class).makeDhcpServer(ifName, params, cb);
+            NetworkStackClient.getInstance().makeDhcpServer(ifName, params, cb);
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
index 4bac200..ebbebcb 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
@@ -16,12 +16,12 @@
 
 package com.android.server;
 
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
 import static android.util.DebugUtils.valueToString;
 
 import static org.junit.Assert.assertEquals;
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index a3f36b7..3e5ce46 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -57,6 +57,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.backup.utils.RandomAccessFileUtils;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -125,6 +127,7 @@
     private File mTestDir;
     private File mSuppressFile;
     private File mActivatedFile;
+    private File mRememberActivatedFile;
 
     @Before
     public void setUp() throws Exception {
@@ -153,6 +156,8 @@
 
         mActivatedFile = new File(mTestDir, "activate-" + NON_USER_SYSTEM);
         TrampolineTestable.sActivatedFiles.append(NON_USER_SYSTEM, mActivatedFile);
+        mRememberActivatedFile = new File(mTestDir, "rem-activate-" + NON_USER_SYSTEM);
+        TrampolineTestable.sRememberActivatedFiles.append(NON_USER_SYSTEM, mRememberActivatedFile);
 
         mTrampoline = new TrampolineTestable(mContextMock);
     }
@@ -411,6 +416,34 @@
     }
 
     @Test
+    public void setBackupServiceActive_forNonSystemUser_remembersActivated() {
+        mTrampoline.initializeService();
+
+        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+        assertTrue(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, false));
+    }
+
+    @Test
+    public void setBackupServiceActiveFalse_forNonSystemUser_remembersActivated() {
+        mTrampoline.initializeService();
+
+        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
+
+        assertFalse(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, true));
+    }
+
+    @Test
+    public void setBackupServiceActiveTwice_forNonSystemUser_remembersLastActivated() {
+        mTrampoline.initializeService();
+
+        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
+
+        assertFalse(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, true));
+    }
+
+    @Test
     public void dataChanged_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.dataChanged(PACKAGE_NAME);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
@@ -1291,6 +1324,7 @@
         static BackupManagerService sBackupManagerServiceMock = null;
         static File sSuppressFile = null;
         static SparseArray<File> sActivatedFiles = new SparseArray<>();
+        static SparseArray<File> sRememberActivatedFiles = new SparseArray<>();
         static UserManager sUserManagerMock = null;
         private int mCreateServiceCallsCount = 0;
 
@@ -1314,6 +1348,11 @@
         }
 
         @Override
+        protected File getRememberActivatedFileForNonSystemUser(int userId) {
+            return sRememberActivatedFiles.get(userId);
+        }
+
+        @Override
         protected File getActivatedFileForNonSystemUser(int userId) {
             return sActivatedFiles.get(userId);
         }
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java
new file mode 100644
index 0000000..eaa9c45
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.common.io.Files;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class FileUtilsTest {
+    private static File sTemporaryDir;
+    private File mTemporaryFile;
+
+    @BeforeClass
+    public static void setUpClass() {
+        sTemporaryDir = Files.createTempDir();
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+        if (sTemporaryDir != null) {
+            sTemporaryDir.delete();
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mTemporaryFile = new File(sTemporaryDir, "fileutilstest.txt");
+    }
+
+    /** Test that if file does not exist, {@link FileUtils#createNewFile()} creates the file. */
+    @Test
+    public void testEnsureFileExists_fileDoesNotAlreadyExist_getsCreated() {
+        assertThat(!mTemporaryFile.exists());
+
+        FileUtils.createNewFile(mTemporaryFile);
+
+        assertThat(mTemporaryFile.exists());
+    }
+
+    /** Test that if file does exist, {@link FileUtils#createNewFile()} does not error out. */
+    @Test
+    public void testEnsureFileExists_fileAlreadyExists_doesNotErrorOut() throws IOException {
+        mTemporaryFile.createNewFile();
+
+        FileUtils.createNewFile(mTemporaryFile);
+
+        assertThat(mTemporaryFile.exists());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java
new file mode 100644
index 0000000..ca699bd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.utils;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RandomAccessFileUtilsTest {
+    private File mTemporaryFile;
+
+    @Before
+    public void setUp() throws Exception {
+        mTemporaryFile = File.createTempFile("fileutilstest", ".txt");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mTemporaryFile != null) {
+            mTemporaryFile.delete();
+        }
+    }
+
+    /**
+     * Test that if we write true, we read back true.
+     */
+    @Test
+    public void testWriteTrue_readReturnsTrue() {
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+
+        assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true);
+    }
+
+    /**
+     * Test that if we write false, we read back false.
+     */
+    @Test
+    public void testWriteFalse_readReturnsFalse() {
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+
+        assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false);
+    }
+
+    /**
+     * Test that if we write true twice, we read back true.
+     */
+    @Test
+    public void testWriteTrueTwice_readReturnsTrue() {
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+
+        assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true);
+    }
+
+    /**
+     * Test that if we write false twice, we read back false.
+     */
+    @Test
+    public void testWriteFalseTwice_readReturnsFalse() {
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+
+        assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false);
+    }
+
+    /**
+     * Test that if we write true and then false, we read back false.
+     */
+    @Test
+    public void testWriteTrueFalse_readReturnsFalse() {
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+
+        assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false);
+    }
+
+    /**
+     * Test that if we write false and then true, we read back true.
+     */
+    @Test
+    public void testWriteFalseTrue_readReturnsTrue() {
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, false);
+        RandomAccessFileUtils.writeBoolean(mTemporaryFile, true);
+
+        assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
index 9f1cbcd..6a937fa 100644
--- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
@@ -98,6 +98,15 @@
     }
 
     @Test
+    public void testUpdateUserActivity_schedulesTheNextCheck() {
+        long now = SystemClock.uptimeMillis();
+        mNextDimming = now;
+        mAttentionDetector.onUserActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH);
+        long nextTimeout = mAttentionDetector.updateUserActivity(mNextDimming + 5000L);
+        assertThat(nextTimeout).isEqualTo(mNextDimming + 5000L);
+    }
+
+    @Test
     public void testOnUserActivity_ignoresAfterMaximumExtension() {
         long now = SystemClock.uptimeMillis();
         mAttentionDetector.onUserActivity(now - 15000L, PowerManager.USER_ACTIVITY_EVENT_TOUCH);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index a7520dc..2627ec7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -30,6 +30,7 @@
 
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
 import android.view.Display;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner;
@@ -48,6 +49,7 @@
  *  atest WmTests:AppChangeTransitionTests
  */
 @SmallTest
+@Presubmit
 public class AppChangeTransitionTests extends WindowTestsBase {
 
     private TaskStack mStack;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index cd13209..1dd72ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -51,7 +51,6 @@
 import android.view.Surface;
 import android.view.WindowManager;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -142,7 +141,6 @@
         mToken.removeImmediately();
     }
 
-    @FlakyTest(detail = "Promote to presubmit when shown to be stable.")
     @Test
     public void testLandscapeSeascapeRotationByApp() {
         // Some plumbing to get the service ready for rotation updates.
@@ -303,7 +301,6 @@
     }
 
     @Test
-    @FlakyTest(detail = "Promote once confirmed non-flaky")
     public void testStuckExitingWindow() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
@@ -346,7 +343,6 @@
         assertNoStartingWindow(mToken);
     }
 
-    @FlakyTest(detail = "Promote to presubmit when shown to be stable.")
     @Test
     public void testAddRemoveRace() {
         // There was once a race condition between adding and removing starting windows
diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
index 3f83cae..1e02a12 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
@@ -46,6 +46,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 import android.view.IWindowManager;
 
@@ -71,6 +72,7 @@
  *  atest WmTests:AssistDataRequesterTest
  */
 @MediumTest
+@Presubmit
 public class AssistDataRequesterTest extends ActivityTestsBase {
 
     private static final String TAG = AssistDataRequesterTest.class.getSimpleName();
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 198e7ce..b15e99a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -60,6 +60,7 @@
 import com.android.server.UiThread;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.After;
 import org.junit.Before;
@@ -113,6 +114,7 @@
     public static void setUpOnce() {
         sMockWm = mock(WindowManagerService.class);
         sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
+        sMockWm.mPolicy = mock(WindowManagerPolicy.class);
     }
 
     @Before
@@ -807,6 +809,8 @@
 
             mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class);
             mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay;
+            when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt()))
+                    .thenReturn(WmDisplayCutout.NO_CUTOUT);
 
             mMockDisplayPolicy = mock(DisplayPolicy.class);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java
index ce22788..df26679 100644
--- a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java
@@ -34,6 +34,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
 import android.util.SparseBooleanArray;
 
 import com.android.server.wm.LockTaskController.LockTaskToken;
@@ -43,6 +44,7 @@
 
 import java.lang.reflect.Constructor;
 
+@Presubmit
 public class KeyguardDisableHandlerTest {
 
     private KeyguardDisableHandler mKeyguardDisable;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index cc6a58a..763ea62 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -112,8 +112,8 @@
         }
     }
 
-    @FlakyTest(bugId = 117117823)
     @Test
+    @FlakyTest(bugId = 117117823)
     public void testIncludedApps_expectTargetAndVisible() {
         mWm.setRecentsAnimationController(mController);
         final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index c595868..2377df4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -34,10 +34,12 @@
 
 import android.app.IActivityTaskManager;
 import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.Display;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -50,6 +52,8 @@
  *  atest WmTests:TaskPositionerTests
  */
 @SmallTest
+@Presubmit
+@FlakyTest
 public class TaskPositionerTests extends WindowTestsBase {
 
     private static final boolean DEBUGGING = false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
index 649b785..99ceb20 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
@@ -31,6 +31,7 @@
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.PointF;
+import android.platform.test.annotations.Presubmit;
 import android.view.DisplayInfo;
 
 import org.junit.Before;
@@ -42,6 +43,7 @@
  * Build/Install/Run:
  *  atest WmTests:CoordinateTransformsTest
  */
+@Presubmit
 public class CoordinateTransformsTest {
 
     private static final int W = 200;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 93f758c..b9440eb 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1193,7 +1193,7 @@
         }
 
         @Override
-        public void setTranscription(IVoiceInteractionService service, String transcription) {
+        public void setUiHints(IVoiceInteractionService service, Bundle hints) {
             synchronized (this) {
                 enforceIsCurrentVoiceInteractionService(service);
 
@@ -1202,47 +1202,9 @@
                     final IVoiceInteractionSessionListener listener =
                             mVoiceInteractionSessionListeners.getBroadcastItem(i);
                     try {
-                        listener.onTranscriptionUpdate(transcription);
+                        listener.onSetUiHints(hints);
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "Error delivering voice transcription.", e);
-                    }
-                }
-                mVoiceInteractionSessionListeners.finishBroadcast();
-            }
-        }
-
-        @Override
-        public void clearTranscription(IVoiceInteractionService service, boolean immediate) {
-            synchronized (this) {
-                enforceIsCurrentVoiceInteractionService(service);
-
-                final int size = mVoiceInteractionSessionListeners.beginBroadcast();
-                for (int i = 0; i < size; ++i) {
-                    final IVoiceInteractionSessionListener listener =
-                            mVoiceInteractionSessionListeners.getBroadcastItem(i);
-                    try {
-                        listener.onTranscriptionComplete(immediate);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Error delivering transcription complete event.", e);
-                    }
-                }
-                mVoiceInteractionSessionListeners.finishBroadcast();
-            }
-        }
-
-        @Override
-        public void setVoiceState(IVoiceInteractionService service, int state) {
-            synchronized (this) {
-                enforceIsCurrentVoiceInteractionService(service);
-
-                final int size = mVoiceInteractionSessionListeners.beginBroadcast();
-                for (int i = 0; i < size; ++i) {
-                    final IVoiceInteractionSessionListener listener =
-                            mVoiceInteractionSessionListeners.getBroadcastItem(i);
-                    try {
-                        listener.onVoiceStateChange(state);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Error delivering voice state change.", e);
+                        Slog.e(TAG, "Error delivering UI hints.", e);
                     }
                 }
                 mVoiceInteractionSessionListeners.finishBroadcast();
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index e99a289..d509168 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -291,6 +291,19 @@
             "android.telecom.extra.OUTGOING_CALL_EXTRAS";
 
     /**
+     * An optional boolean extra on {@link android.content.Intent#ACTION_CALL_EMERGENCY} to tell
+     * whether the user's dial intent is emergency; this is required to specify when the dialed
+     * number is ambiguous, identified as both emergency number and any other non-emergency number;
+     * e.g. in some situation, 611 could be both an emergency number in a country and a
+     * non-emergency number of a carrier's customer service hotline.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL =
+            "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
+
+    /**
      * @hide
      */
     public static final String EXTRA_UNKNOWN_CALL_HANDLE =
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 3814333..dba437a 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -141,6 +141,14 @@
         return mCpid;
     }
 
+    /**
+     * @return 16-bit UMTS Absolute RF Channel Number,
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+     */
+    public int getUarfcn() {
+        return mUarfcn;
+    }
+
     /** @hide */
     @Override
     public int getChannelNumber() {
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
index 53d69f4..24db438 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -26,11 +26,12 @@
 import android.content.pm.UserInfo;
 import android.location.LocationManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Process;
-import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
+import android.widget.Toast;
 
 import java.util.List;
 
@@ -41,59 +42,234 @@
 public final class LocationAccessPolicy {
     private static final String TAG = "LocationAccessPolicy";
     private static final boolean DBG = false;
+    public static final int MAX_SDK_FOR_ANY_ENFORCEMENT = Build.VERSION_CODES.P;
 
-    /**
-     * API to determine if the caller has permissions to get cell location.
-     *
-     * @param pkgName Package name of the application requesting access
-     * @param uid The uid of the package
-     * @param pid The pid of the package
-     * @param throwOnDeniedPermission Whether to throw if the location permission is denied.
-     * @return boolean true or false if permissions is granted
-     */
-    public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
-            int uid, int pid, boolean throwOnDeniedPermission) throws SecurityException {
-        Trace.beginSection("TelephonyLocationCheck");
-        try {
-            // Always allow the phone process and system server to access location. This avoid
-            // breaking legacy code that rely on public-facing APIs to access cell location, and
-            // it doesn't create an info leak risk because the cell location is stored in the phone
-            // process anyway, and the system server already has location access.
-            if (uid == Process.PHONE_UID || uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) {
-                return true;
-            }
+    public enum LocationPermissionResult {
+        ALLOWED,
+        /**
+         * Indicates that the denial is due to a transient device state
+         * (e.g. app-ops, location master switch)
+         */
+        DENIED_SOFT,
+        /**
+         * Indicates that the denial is due to a misconfigured app (e.g. missing entry in manifest)
+         */
+        DENIED_HARD,
+    }
 
-            // We always require the location permission and also require the
-            // location mode to be on for non-legacy apps. Legacy apps are
-            // required to be in the foreground to at least mitigate the case
-            // where a legacy app the user is not using tracks their location.
-            // Granting ACCESS_FINE_LOCATION to an app automatically grants it
-            // ACCESS_COARSE_LOCATION.
-            if (throwOnDeniedPermission) {
-                context.enforcePermission(Manifest.permission.ACCESS_COARSE_LOCATION,
-                        pid, uid, "canAccessCellLocation");
-            } else if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION,
-                    pid, uid) == PackageManager.PERMISSION_DENIED) {
-                if (DBG) Log.w(TAG, "Permission checked failed (" + pid + "," + uid + ")");
-                return false;
-            }
-            final int opCode = AppOpsManager.permissionToOpCode(
-                    Manifest.permission.ACCESS_COARSE_LOCATION);
-            if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
-                    .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
-                if (DBG) Log.w(TAG, "AppOp check failed (" + uid + "," + pkgName + ")");
-                return false;
-            }
-            if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
-                if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")");
-                return false;
-            }
-            // If the user or profile is current, permission is granted.
-            // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
-            return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
-        } finally {
-            Trace.endSection();
+    public static class LocationPermissionQuery {
+        public final String callingPackage;
+        public final int callingUid;
+        public final int callingPid;
+        public final int minSdkVersionForCoarse;
+        public final int minSdkVersionForFine;
+        public final String method;
+
+        private LocationPermissionQuery(String callingPackage, int callingUid, int callingPid,
+                int minSdkVersionForCoarse, int minSdkVersionForFine, String method) {
+            this.callingPackage = callingPackage;
+            this.callingUid = callingUid;
+            this.callingPid = callingPid;
+            this.minSdkVersionForCoarse = minSdkVersionForCoarse;
+            this.minSdkVersionForFine = minSdkVersionForFine;
+            this.method = method;
         }
+
+        public static class Builder {
+            private String mCallingPackage;
+            private int mCallingUid;
+            private int mCallingPid;
+            private int mMinSdkVersionForCoarse = Integer.MAX_VALUE;
+            private int mMinSdkVersionForFine = Integer.MAX_VALUE;
+            private String mMethod;
+
+            /**
+             * Mandatory parameter, used for performing permission checks.
+             */
+            public Builder setCallingPackage(String callingPackage) {
+                mCallingPackage = callingPackage;
+                return this;
+            }
+
+            /**
+             * Mandatory parameter, used for performing permission checks.
+             */
+            public Builder setCallingUid(int callingUid) {
+                mCallingUid = callingUid;
+                return this;
+            }
+
+            /**
+             * Mandatory parameter, used for performing permission checks.
+             */
+            public Builder setCallingPid(int callingPid) {
+                mCallingPid = callingPid;
+                return this;
+            }
+
+            /**
+             * Apps that target at least this sdk version will be checked for coarse location
+             * permission. Defaults to INT_MAX (which means don't check)
+             */
+            public Builder setMinSdkVersionForCoarse(
+                    int minSdkVersionForCoarse) {
+                mMinSdkVersionForCoarse = minSdkVersionForCoarse;
+                return this;
+            }
+
+            /**
+             * Apps that target at least this sdk version will be checked for fine location
+             * permission. Defaults to INT_MAX (which means don't check)
+             */
+            public Builder setMinSdkVersionForFine(
+                    int minSdkVersionForFine) {
+                mMinSdkVersionForFine = minSdkVersionForFine;
+                return this;
+            }
+
+            /**
+             * Optional, for logging purposes only.
+             */
+            public Builder setMethod(String method) {
+                mMethod = method;
+                return this;
+            }
+
+            public LocationPermissionQuery build() {
+                return new LocationPermissionQuery(mCallingPackage, mCallingUid,
+                        mCallingPid, mMinSdkVersionForCoarse, mMinSdkVersionForFine, mMethod);
+            }
+        }
+    }
+
+    private static void logError(Context context, String errorMsg) {
+        Log.e(TAG, errorMsg);
+        try {
+            if (Build.IS_DEBUGGABLE) {
+                Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show();
+            }
+        } catch (Throwable t) {
+            // whatever, not important
+        }
+    }
+
+    private static LocationPermissionResult appOpsModeToPermissionResult(int appOpsMode) {
+        switch (appOpsMode) {
+            case AppOpsManager.MODE_ALLOWED:
+                return LocationPermissionResult.ALLOWED;
+            case AppOpsManager.MODE_ERRORED:
+                return LocationPermissionResult.DENIED_HARD;
+            default:
+                return LocationPermissionResult.DENIED_SOFT;
+        }
+    }
+
+    private static LocationPermissionResult checkAppLocationPermissionHelper(Context context,
+            LocationPermissionQuery query, String permissionToCheck) {
+        String locationTypeForLog =
+                Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck)
+                        ? "fine" : "coarse";
+
+        // Do the app-ops and the manifest check without any of the allow-overrides first.
+        boolean hasManifestPermission = checkManifestPermission(context, query.callingPid,
+                query.callingUid, permissionToCheck);
+
+        int appOpMode = context.getSystemService(AppOpsManager.class)
+                .noteOpNoThrow(AppOpsManager.permissionToOpCode(permissionToCheck),
+                        query.callingUid, query.callingPackage);
+
+        if (hasManifestPermission && appOpMode == AppOpsManager.MODE_ALLOWED) {
+            // If the app did everything right, return without logging.
+            return LocationPermissionResult.ALLOWED;
+        }
+
+        // If the app has the manifest permission but not the app-op permission, it means that
+        // it's aware of the requirement and the user denied permission explicitly. If we see
+        // this, don't let any of the overrides happen.
+        if (hasManifestPermission) {
+            Log.i(TAG, query.callingPackage + " is aware of " + locationTypeForLog + " but the"
+                    + " app-ops permission is specifically denied.");
+            return appOpsModeToPermissionResult(appOpMode);
+        }
+
+        int minSdkVersion = Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck)
+                ? query.minSdkVersionForFine : query.minSdkVersionForCoarse;
+
+        // If the app fails for some reason, see if it should be allowed to proceed.
+        if (minSdkVersion > MAX_SDK_FOR_ANY_ENFORCEMENT) {
+            String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
+                    + " because we're not enforcing API " + query.minSdkVersionForFine + " yet."
+                    + " Please fix this app because it will break in the future. Called from "
+                    + query.method;
+            logError(context, errorMsg);
+            return null;
+        } else if (!isAppAtLeastSdkVersion(context, query.callingPackage, minSdkVersion)) {
+            String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
+                    + " because it doesn't target API " + query.minSdkVersionForFine + " yet."
+                    + " Please fix this app. Called from " + query.method;
+            logError(context, errorMsg);
+            return null;
+        } else {
+            // If we're not allowing it due to the above two conditions, this means that the app
+            // did not declare the permission in their manifest.
+            return LocationPermissionResult.DENIED_HARD;
+        }
+    }
+
+    public static LocationPermissionResult checkLocationPermission(
+            Context context, LocationPermissionQuery query) {
+        // Always allow the phone process and system server to access location. This avoid
+        // breaking legacy code that rely on public-facing APIs to access cell location, and
+        // it doesn't create an info leak risk because the cell location is stored in the phone
+        // process anyway, and the system server already has location access.
+        if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
+                || query.callingUid == Process.ROOT_UID) {
+            return LocationPermissionResult.ALLOWED;
+        }
+
+        // Check the system-wide requirements. If the location master switch is off or
+        // the app's profile isn't in foreground, return a soft denial.
+        if (!checkSystemLocationAccess(context, query.callingUid, query.callingPid)) {
+            return LocationPermissionResult.DENIED_SOFT;
+        }
+
+        // Do the check for fine, then for coarse.
+        if (query.minSdkVersionForFine < Integer.MAX_VALUE) {
+            LocationPermissionResult resultForFine = checkAppLocationPermissionHelper(
+                    context, query, Manifest.permission.ACCESS_FINE_LOCATION);
+            if (resultForFine != null) {
+                return resultForFine;
+            }
+        }
+
+        if (query.minSdkVersionForCoarse < Integer.MAX_VALUE) {
+            LocationPermissionResult resultForCoarse = checkAppLocationPermissionHelper(
+                    context, query, Manifest.permission.ACCESS_COARSE_LOCATION);
+            if (resultForCoarse != null) {
+                return resultForCoarse;
+            }
+        }
+
+        // At this point, we're out of location checks to do. If the app bypassed all the previous
+        // ones due to the SDK grandfathering schemes, allow it access.
+        return LocationPermissionResult.ALLOWED;
+    }
+
+
+    private static boolean checkManifestPermission(Context context, int pid, int uid,
+            String permissionToCheck) {
+        return context.checkPermission(permissionToCheck, pid, uid)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private static boolean checkSystemLocationAccess(@NonNull Context context, int uid, int pid) {
+        if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
+            if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")");
+            return false;
+        }
+        // If the user or profile is current, permission is granted.
+        // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+        return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, uid, pid);
     }
 
     private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
@@ -105,10 +281,10 @@
         return locationManager.isLocationEnabledForUser(UserHandle.of(userId));
     }
 
-    private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
-        return context.checkCallingOrSelfPermission(
-                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
-                == PackageManager.PERMISSION_GRANTED;
+    private static boolean checkInteractAcrossUsersFull(
+            @NonNull Context context, int pid, int uid) {
+        return checkManifestPermission(context, pid, uid,
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL);
     }
 
     private static boolean isCurrentProfile(@NonNull Context context, int uid) {
@@ -132,4 +308,18 @@
             Binder.restoreCallingIdentity(token);
         }
     }
-}
+
+    private static boolean isAppAtLeastSdkVersion(Context context, String pkgName, int sdkVersion) {
+        try {
+            if (context.getPackageManager().getApplicationInfo(pkgName, 0).targetSdkVersion
+                    >= sdkVersion) {
+                return true;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // In case of exception, assume known app (more strict checking)
+            // Note: This case will never happen since checkPackage is
+            // called to verify validity before checking app's version.
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index c37b492..6e6d59e 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -152,7 +152,7 @@
     private final int[] mAvailableServices;
 
     @Nullable
-    private final CellIdentity mCellIdentity;
+    private CellIdentity mCellIdentity;
 
     @Nullable
     private VoiceSpecificRegistrationStates mVoiceSpecificStates;
@@ -521,4 +521,22 @@
             return new NetworkRegistrationState[size];
         }
     };
+
+    /**
+     * @hide
+     */
+    public NetworkRegistrationState sanitizeLocationInfo() {
+        NetworkRegistrationState result = copy();
+        result.mCellIdentity = null;
+        return result;
+    }
+
+    private NetworkRegistrationState copy() {
+        Parcel p = Parcel.obtain();
+        this.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        NetworkRegistrationState result = new NetworkRegistrationState(p);
+        p.recycle();
+        return result;
+    }
 }
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 2c9ba1d..3ce646c 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -46,7 +46,7 @@
  * Override the methods for the state that you wish to receive updates for, and
  * pass your PhoneStateListener object, along with bitwise-or of the LISTEN_
  * flags to {@link TelephonyManager#listen TelephonyManager.listen()}. Methods are
- * called when the state changes, os well as once on initial registration.
+ * called when the state changes, as well as once on initial registration.
  * <p>
  * Note that access to some telephony information is
  * permission-protected. Your application won't receive updates for protected
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 402763e..a1aee6d 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -36,6 +36,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * Contains phone state and service related information.
@@ -1885,4 +1886,29 @@
                 ? range1
                 : range2;
     }
+
+    /**
+     * Returns a copy of self with location-identifying information removed.
+     * Always clears the NetworkRegistrationState's CellIdentity fields, but if removeCoarseLocation
+     * is true, clears other info as well.
+     * @hide
+     */
+    public ServiceState sanitizeLocationInfo(boolean removeCoarseLocation) {
+        ServiceState state = new ServiceState(this);
+        if (state.mNetworkRegistrationStates != null) {
+            state.mNetworkRegistrationStates = state.mNetworkRegistrationStates.stream()
+                    .map(NetworkRegistrationState::sanitizeLocationInfo)
+                    .collect(Collectors.toList());
+        }
+        if (!removeCoarseLocation) return state;
+
+        state.mDataOperatorAlphaLong = null;
+        state.mDataOperatorAlphaShort = null;
+        state.mDataOperatorNumeric = null;
+        state.mVoiceOperatorAlphaLong = null;
+        state.mVoiceOperatorAlphaShort = null;
+        state.mVoiceOperatorNumeric = null;
+
+        return state;
+    }
 }
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 099015f..2aa4768 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -981,4 +981,13 @@
 
         return false;
     }
+
+    /**
+     * {@hide}
+     * Returns the recipient address(receiver) of this SMS message in String form or null if
+     * unavailable.
+     */
+    public String getRecipientAddress() {
+        return mWrappedSmsMessage.getRecipientAddress();
+    }
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 3a4d33c..94f26a8 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -866,7 +866,8 @@
         }
 
         /**
-         * Callback invoked when there is any change to any SubscriptionInfo. Typically
+         * Callback invoked when there is any change to any SubscriptionInfo, as well as once on
+         * registering for changes with {@link #addOnSubscriptionsChangedListener}. Typically
          * this method would invoke {@link #getActiveSubscriptionInfoList}
          */
         public void onSubscriptionsChanged() {
@@ -918,7 +919,9 @@
     /**
      * Register for changes to the list of active {@link SubscriptionInfo} records or to the
      * individual records themselves. When a change occurs the onSubscriptionsChanged method of
-     * the listener will be invoked immediately if there has been a notification.
+     * the listener will be invoked immediately if there has been a notification. The
+     * onSubscriptionChanged method will also be triggered once initially when calling this
+     * function.
      *
      * @param listener an instance of {@link OnSubscriptionsChangedListener} with
      *                 onSubscriptionsChanged overridden.
@@ -1233,7 +1236,7 @@
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
-        return getActiveSubscriptionInfoList(false);
+        return getActiveSubscriptionInfoList(/* userVisibleonly */true);
     }
 
     /**
@@ -1861,7 +1864,7 @@
                 iSub.setDefaultSmsSubId(subscriptionId);
             }
         } catch (RemoteException ex) {
-            // ignore it
+            ex.rethrowFromSystemServer();
         }
     }
 
@@ -2855,15 +2858,24 @@
     /**
      * Whether system UI should hide a subscription. If it's a bundled opportunistic
      * subscription, it shouldn't show up in anywhere in Settings app, dialer app,
-     * or status bar.
+     * or status bar. Exception is if caller is carrier app, in which case they will
+     * want to see their own hidden subscriptions.
      *
      * @param info the subscriptionInfo to check against.
      * @return true if this subscription should be hidden.
      *
      * @hide
      */
-    public static boolean shouldHideSubscription(SubscriptionInfo info) {
-        return (info != null && !TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic());
+    private boolean shouldHideSubscription(SubscriptionInfo info) {
+        if (info == null) return false;
+
+        // If hasCarrierPrivileges or canManageSubscription returns true, it means caller
+        // has carrier privilege.
+        boolean hasCarrierPrivilegePermission = (info.isEmbedded() && canManageSubscription(info))
+                || TelephonyManager.from(mContext).hasCarrierPrivileges(info.getSubscriptionId());
+
+        return (!TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic()
+                && !hasCarrierPrivilegePermission);
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f9cf253..ced4f4a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1698,10 +1698,7 @@
      * @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API.
      */
     @Deprecated
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.ACCESS_COARSE_LOCATION,
-            android.Manifest.permission.ACCESS_FINE_LOCATION
-    })
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public CellLocation getCellLocation() {
         try {
             ITelephony telephony = getITelephony();
@@ -4951,7 +4948,7 @@
      * @return List of {@link android.telephony.CellInfo}; null if cell
      * information is unavailable.
      */
-    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public List<CellInfo> getAllCellInfo() {
         try {
             ITelephony telephony = getITelephony();
@@ -5028,7 +5025,7 @@
      * @param executor the executor on which callback will be invoked.
      * @param callback a callback to receive CellInfo.
      */
-    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public void requestCellInfoUpdate(
             @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
         try {
@@ -5067,7 +5064,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_COARSE_LOCATION,
+    @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_FINE_LOCATION,
             android.Manifest.permission.MODIFY_PHONE_STATE})
     public void requestCellInfoUpdate(@NonNull WorkSource workSource,
             @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
@@ -6657,9 +6654,10 @@
      *
      * <p> Note that this scan can take a long time (sometimes minutes) to happen.
      *
-     * <p>Requires Permission:
+     * <p>Requires Permissions:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or that the calling app has carrier
      * privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
      *
      * @return {@link CellNetworkScanResult} with the status
      * {@link CellNetworkScanResult#STATUS_SUCCESS} and a list of
@@ -6668,12 +6666,15 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.ACCESS_COARSE_LOCATION
+    })
     public CellNetworkScanResult getAvailableNetworks() {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getCellNetworkScanResults(getSubId());
+                return telephony.getCellNetworkScanResults(getSubId(), getOpPackageName());
             }
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getAvailableNetworks RemoteException", ex);
@@ -6692,7 +6693,8 @@
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
-     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * app has carrier privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
      *
      * @param request Contains all the RAT with bands/channels that need to be scanned.
      * @param executor The executor through which the callback should be invoked. Since the scan
@@ -6703,7 +6705,10 @@
      * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public NetworkScan requestNetworkScan(
             NetworkScanRequest request, Executor executor,
             TelephonyScanManager.NetworkScanCallback callback) {
@@ -6712,7 +6717,8 @@
                 mTelephonyScanManager = new TelephonyScanManager();
             }
         }
-        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback);
+        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback,
+                getOpPackageName());
     }
 
     /**
@@ -6722,7 +6728,10 @@
      * @removed
      */
     @Deprecated
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public NetworkScan requestNetworkScan(
         NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
         return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback);
@@ -8723,10 +8732,14 @@
      * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
-     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
+     * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_COARSE_LOCATION
+    })
     public ServiceState getServiceState() {
         return getServiceStateForSubscriber(getSubId());
     }
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 96ff332..91f74b8 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -29,14 +29,14 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.util.Log;
 import android.util.SparseArray;
+
+import com.android.internal.telephony.ITelephony;
+
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.Executor;
 
-import com.android.internal.telephony.ITelephony;
-
 /**
  * Manages the radio access network scan requests and callbacks.
  */
@@ -183,6 +183,7 @@
      *
      * <p>
      * Requires Permission:
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
      *
@@ -192,11 +193,13 @@
      * @hide
      */
     public NetworkScan requestNetworkScan(int subId,
-            NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
+            NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
+            String callingPackage) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                int scanId = telephony.requestNetworkScan(subId, request, mMessenger, new Binder());
+                int scanId = telephony.requestNetworkScan(
+                        subId, request, mMessenger, new Binder(), callingPackage);
                 saveScanInfo(scanId, request, executor, callback);
                 return new NetworkScan(scanId, subId);
             }
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 59167b7..73f0556 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -350,6 +350,9 @@
     /** Indicates if the call is for testing purpose */
     private boolean mEmergencyCallTesting = false;
 
+    /** Indicates if we have known the intent of the user for the call is emergency */
+    private boolean mHasKnownUserIntentEmergency = false;
+
     /**
      * Extras associated with this {@link ImsCallProfile}.
      * <p>
@@ -789,12 +792,13 @@
      *
      * @hide
      */
-    public void setEmergencyCallInfo(EmergencyNumber num) {
+    public void setEmergencyCallInfo(EmergencyNumber num, boolean hasKnownUserIntentEmergency) {
         setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmaskInternalDial());
         setEmergencyUrns(num.getEmergencyUrns());
         setEmergencyCallRouting(num.getEmergencyCallRouting());
         setEmergencyCallTesting(num.getEmergencyNumberSourceBitmask()
                 == EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST);
+        setHasKnownUserIntentEmergency(hasKnownUserIntentEmergency);
     }
 
     /**
@@ -860,6 +864,19 @@
     }
 
     /**
+     * Set if we have known the user intent of the call is emergency.
+     *
+     * This is only used to specify when the dialed number is ambiguous when it can be identified
+     * as both emergency number and any other non-emergency number; e.g. in some situation, 611
+     * could be both an emergency number in a country and a non-emergency number of a carrier's
+     * customer service hotline.
+     */
+    @VisibleForTesting
+    public void setHasKnownUserIntentEmergency(boolean hasKnownUserIntentEmergency) {
+        mHasKnownUserIntentEmergency = hasKnownUserIntentEmergency;
+    }
+
+    /**
      * Get the emergency service categories, only valid if {@link #getServiceType} returns
      * {@link #SERVICE_TYPE_EMERGENCY}
      *
@@ -916,4 +933,16 @@
     public boolean isEmergencyCallTesting() {
         return mEmergencyCallTesting;
     }
+
+    /**
+     * Checks if we have known the user intent of the call is emergency.
+     *
+     * This is only used to specify when the dialed number is ambiguous when it can be identified
+     * as both emergency number and any other non-emergency number; e.g. in some situation, 611
+     * could be both an emergency number in a country and a non-emergency number of a carrier's
+     * customer service hotline.
+     */
+    public boolean hasKnownUserIntentEmergency() {
+        return mHasKnownUserIntentEmergency;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index caa367f..7089ee5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -765,7 +765,7 @@
      * @param subId the id of the subscription.
      * @return CellNetworkScanResult containing status of scan and networks.
      */
-    CellNetworkScanResult getCellNetworkScanResults(int subId);
+    CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage);
 
     /**
      * Perform a radio network scan and return the id of this scan.
@@ -774,10 +774,11 @@
      * @param request Defines all the configs for network scan.
      * @param messenger Callback messages will be sent using this messenger.
      * @param binder the binder object instantiated in TelephonyManager.
+     * @param callingPackage the calling package
      * @return An id for this scan.
      */
     int requestNetworkScan(int subId, in NetworkScanRequest request, in Messenger messenger,
-            in IBinder binder);
+            in IBinder binder, in String callingPackage);
 
     /**
      * Stop an existing radio network scan.
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index 190eac4..ffdc4b6 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -41,6 +41,9 @@
     @UnsupportedAppUsage
     protected SmsAddress mOriginatingAddress;
 
+    /** {@hide} The address of the receiver */
+    protected SmsAddress mRecipientAddress;
+
     /** {@hide} The message body as a string. May be null if the message isn't text */
     @UnsupportedAppUsage
     protected String mMessageBody;
@@ -457,4 +460,17 @@
 
         return ted;
     }
+
+    /**
+     * {@hide}
+     * Returns the receiver address of this SMS message in String
+     * form or null if unavailable
+     */
+    public String getRecipientAddress() {
+        if (mRecipientAddress == null) {
+            return null;
+        }
+
+        return mRecipientAddress.getAddressString();
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 1da5eac..a31fa0b 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -601,18 +601,24 @@
 
                             } else if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK) {
                                 if (numberType == 2)
-                                    Rlog.e(LOG_TAG, "TODO: Originating Addr is email id");
+                                    Rlog.e(LOG_TAG, "TODO: Addr is email id");
                                 else
                                     Rlog.e(LOG_TAG,
-                                          "TODO: Originating Addr is data network address");
+                                          "TODO: Addr is data network address");
                             } else {
-                                Rlog.e(LOG_TAG, "Originating Addr is of incorrect type");
+                                Rlog.e(LOG_TAG, "Addr is of incorrect type");
                             }
                         } else {
                             Rlog.e(LOG_TAG, "Incorrect Digit mode");
                         }
                         addr.origBytes = data;
-                        Rlog.i(LOG_TAG, "Originating Addr=" + addr.toString());
+                        Rlog.pii(LOG_TAG, "Addr=" + addr.toString());
+                        mOriginatingAddress = addr;
+                        if (parameterId == DESTINATION_ADDRESS) {
+                            // Original address awlays indicates one sender's address for 3GPP2
+                            // Here add recipient address support along with 3GPP
+                            mRecipientAddress = addr;
+                        }
                         break;
                     case ORIGINATING_SUB_ADDRESS:
                     case DESTINATION_SUB_ADDRESS:
@@ -667,7 +673,7 @@
     }
 
     /**
-     * Parses a SMS message from its BearerData stream. (mobile-terminated only)
+     * Parses a SMS message from its BearerData stream.
      */
     public void parseSms() {
         // Message Waiting Info Record defined in 3GPP2 C.S-0005, 3.7.5.6
@@ -697,16 +703,15 @@
         }
 
         if (mOriginatingAddress != null) {
-            mOriginatingAddress.address = new String(mOriginatingAddress.origBytes);
-            if (mOriginatingAddress.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) {
-                if (mOriginatingAddress.address.charAt(0) != '+') {
-                    mOriginatingAddress.address = "+" + mOriginatingAddress.address;
-                }
-            }
+            decodeSmsDisplayAddress(mOriginatingAddress);
             if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: "
                     + mOriginatingAddress.address);
         }
 
+        if (mRecipientAddress != null) {
+            decodeSmsDisplayAddress(mRecipientAddress);
+        }
+
         if (mBearerData.msgCenterTimeStamp != null) {
             mScTimeMillis = mBearerData.msgCenterTimeStamp.toMillis(true);
         }
@@ -731,7 +736,8 @@
                 status = mBearerData.errorClass << 8;
                 status |= mBearerData.messageStatus;
             }
-        } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER) {
+        } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER
+                && mBearerData.messageType != BearerData.MESSAGE_TYPE_SUBMIT) {
             throw new RuntimeException("Unsupported message type: " + mBearerData.messageType);
         }
 
@@ -743,6 +749,16 @@
         }
     }
 
+    private void decodeSmsDisplayAddress(SmsAddress addr) {
+        addr.address = new String(addr.origBytes);
+        if (addr.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) {
+            if (addr.address.charAt(0) != '+') {
+                addr.address = "+" + addr.address;
+            }
+        }
+        Rlog.pii(LOG_TAG, " decodeSmsDisplayAddress = " + addr.address);
+    }
+
     /**
      * Parses a broadcast SMS, possibly containing a CMAS alert.
      *
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 015efa6..19465a4 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -71,9 +71,6 @@
     // e.g. 23.040 9.2.2.1
     private boolean mReplyPathPresent = false;
 
-    /** The address of the receiver. */
-    private GsmSmsAddress mRecipientAddress;
-
     /**
      *  TP-Status - status of a previously submitted SMS.
      *  This field applies to SMS-STATUS-REPORT messages.  0 indicates success;
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index d5549cc..07b3a97 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -439,6 +439,14 @@
                 return true;
             }
         });
+        menu.add("Require unknown permission").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+            @Override public boolean onMenuItemClick(MenuItem item) {
+                final Intent intent = new Intent(SLOW_RECEIVER_ACTION);
+                intent.putExtra(SLOW_RECEIVER_EXTRA, 5038);
+                sendOrderedBroadcast(intent, "com.google.android.test.activity.permission.UNDEFINED");
+                return true;
+            }
+        });
         menu.add("Stack Doc").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
             @Override public boolean onMenuItemClick(MenuItem item) {
                 ActivityManager.AppTask task = findDocTask();
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 1a4ec94..7b8c154 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -1028,8 +1028,28 @@
         </activity>
 
         <activity
-            android:name="PositionListenerActivity"
-            android:label="RenderNode/PositionListener"
+                android:name="PositionListenerActivity"
+                android:label="RenderNode/PositionListener"
+                android:screenOrientation="fullSensor">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name="CustomRenderer"
+            android:label="HardwareRenderer/HelloTakeSurface"
+            android:screenOrientation="fullSensor">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.android.test.hwui.TEST" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name="MyLittleTextureView"
+            android:label="HardwareRenderer/MyLittleTextureView"
             android:screenOrientation="fullSensor">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
new file mode 100644
index 0000000..60bd60f
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.HardwareRenderer;
+import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+public class CustomRenderer extends Activity {
+    private RenderNode mContent = new RenderNode("CustomRenderer");
+    private HardwareRenderer mRenderer = new HardwareRenderer();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().takeSurface(mSurfaceCallbacks);
+    }
+
+    private SurfaceHolder.Callback2 mSurfaceCallbacks = new SurfaceHolder.Callback2() {
+
+        @Override
+        public void surfaceRedrawNeeded(SurfaceHolder holder) {
+        }
+
+        @Override
+        public void surfaceCreated(SurfaceHolder holder) {
+        }
+
+        @Override
+        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+            mContent.setLeftTopRightBottom(0, 0, width, height);
+            RecordingCanvas canvas = mContent.startRecording();
+            canvas.drawColor(Color.WHITE);
+            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            paint.setColor(Color.BLACK);
+            paint.setTextAlign(Paint.Align.CENTER);
+            paint.setTextSize(Math.min(width, height) * .05f);
+            canvas.drawText("Hello custom renderer!", width / 2, height / 2, paint);
+            mContent.endRecording();
+
+            mRenderer.setContentRoot(mContent);
+            mRenderer.setSurface(holder.getSurface());
+            mRenderer.createRenderRequest()
+                    .setVsyncTime(System.nanoTime())
+                    .setFrameCommitCallback(Runnable::run, () -> {
+                        Log.d("CustomRenderer", "Frame committed!");
+                    })
+                    .syncAndDraw();
+        }
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder holder) {
+            mRenderer.destroy();
+        }
+    };
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
new file mode 100644
index 0000000..8bd7d79
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
@@ -0,0 +1,87 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorSpace;
+import android.graphics.HardwareRenderer;
+import android.graphics.Outline;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RenderNode;
+import android.hardware.HardwareBuffer;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Bundle;
+import android.widget.ImageView;
+
+public class MyLittleTextureView extends Activity {
+    private RenderNode mContent = new RenderNode("CustomRenderer");
+    private HardwareRenderer mRenderer = new HardwareRenderer();
+    private ImageView mImageView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mImageView = new ImageView(this);
+        mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+        setContentView(mImageView);
+
+        ImageReader reader = ImageReader.newInstance(100, 100, PixelFormat.RGBA_8888, 3,
+                HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT);
+        mRenderer.setSurface(reader.getSurface());
+        mRenderer.setLightSourceAlpha(0.0f, 1.0f);
+        mRenderer.setLightSourceGeometry(100 / 2f, 0f, 800.0f, 20.0f);
+        mContent.setLeftTopRightBottom(0, 0, 100, 100);
+
+        Rect childRect = new Rect(25, 25, 65, 65);
+        RenderNode childNode = new RenderNode("shadowCaster");
+        childNode.setLeftTopRightBottom(childRect.left, childRect.top,
+                childRect.right, childRect.bottom);
+        Outline outline = new Outline();
+        outline.setRect(new Rect(0, 0, childRect.width(), childRect.height()));
+        outline.setAlpha(1f);
+        childNode.setOutline(outline);
+        {
+            Canvas canvas = childNode.startRecording();
+            canvas.drawColor(Color.BLUE);
+        }
+        childNode.endRecording();
+        childNode.setElevation(20f);
+
+        {
+            Canvas canvas = mContent.startRecording();
+            canvas.drawColor(Color.WHITE);
+            canvas.enableZ();
+            canvas.drawRenderNode(childNode);
+            canvas.disableZ();
+        }
+        mContent.endRecording();
+        mRenderer.setContentRoot(mContent);
+        mRenderer.createRenderRequest()
+                .setWaitForPresent(true)
+                .syncAndDraw();
+        Image image = reader.acquireNextImage();
+        Bitmap bitmap = Bitmap.wrapHardwareBuffer(image.getHardwareBuffer(),
+                ColorSpace.get(ColorSpace.Named.SRGB));
+        mImageView.setImageBitmap(bitmap);
+        image.close();
+    }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index a7c95c7..a10fb4e 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -20,6 +20,7 @@
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.net.ConnectivityManager.NETID_UNSET;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
@@ -123,7 +124,7 @@
 import android.net.NetworkParcelable;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
-import android.net.NetworkStack;
+import android.net.NetworkStackClient;
 import android.net.NetworkUtils;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
@@ -245,7 +246,7 @@
     @Mock INetworkStatsService mStatsService;
     @Mock INetworkPolicyManager mNpm;
     @Mock INetd mMockNetd;
-    @Mock NetworkStack mNetworkStack;
+    @Mock NetworkStackClient mNetworkStack;
 
     private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
 
@@ -901,11 +902,14 @@
 
         public void setUids(Set<UidRange> uids) {
             mNetworkCapabilities.setUids(uids);
-            updateCapabilities();
+            updateCapabilities(null /* defaultNetwork */);
         }
 
         @Override
         public int getNetId() {
+            if (mMockNetworkAgent == null) {
+                return NETID_UNSET;
+            }
             return mMockNetworkAgent.getNetwork().netId;
         }
 
@@ -927,12 +931,13 @@
         }
 
         @Override
-        public void updateCapabilities() {
-            if (!mConnected) return;
-            super.updateCapabilities();
-            // Because super.updateCapabilities will update the capabilities of the agent but not
-            // the mock agent, the mock agent needs to know about them.
+        public NetworkCapabilities updateCapabilities(Network defaultNetwork) {
+            if (!mConnected) return null;
+            super.updateCapabilities(defaultNetwork);
+            // Because super.updateCapabilities will update the capabilities of the agent but
+            // not the mock agent, the mock agent needs to know about them.
             copyCapabilitiesToNetworkAgent();
+            return new NetworkCapabilities(mNetworkCapabilities);
         }
 
         private void copyCapabilitiesToNetworkAgent() {
@@ -1077,6 +1082,11 @@
         }
 
         @Override
+        protected NetworkStackClient getNetworkStack() {
+            return mNetworkStack;
+        }
+
+        @Override
         public WakeupMessage makeWakeupMessage(
                 Context context, Handler handler, String cmdName, int cmd, Object obj) {
             return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
@@ -3817,11 +3827,14 @@
     }
 
     @Test
-    public void testNattSocketKeepalives() throws Exception {
+    public void testNattSocketKeepalives_SingleThreadExecutor() throws Exception {
         final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor();
         doTestNattSocketKeepalivesWithExecutor(executorSingleThread);
         executorSingleThread.shutdown();
+    }
 
+    @Test
+    public void testNattSocketKeepalives_InlineExecutor() throws Exception {
         final Executor executorInline = (Runnable r) -> r.run();
         doTestNattSocketKeepalivesWithExecutor(executorInline);
     }
@@ -3963,6 +3976,7 @@
         testSocket2.close();
 
         mWiFiNetworkAgent.disconnect();
+        waitFor(mWiFiNetworkAgent.getDisconnectedCV());
     }
 
     @Test
@@ -4686,6 +4700,7 @@
 
         vpnNetworkAgent.connect(false);
         mMockVpn.connect();
+        mMockVpn.setUnderlyingNetworks(new Network[0]);
 
         genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
         genericNotVpnNetworkCallback.assertNoCallback();
@@ -4718,6 +4733,7 @@
 
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setUids(ranges);
+        vpnNetworkAgent.setUids(ranges);
 
         genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
         genericNotVpnNetworkCallback.assertNoCallback();
@@ -4751,12 +4767,11 @@
     }
 
     @Test
-    public void testVpnWithAndWithoutInternet() {
+    public void testVpnWithoutInternet() {
         final int uid = Process.myUid();
 
         final TestNetworkCallback defaultCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultCallback);
-        defaultCallback.assertNoCallback();
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
@@ -4778,11 +4793,30 @@
         vpnNetworkAgent.disconnect();
         defaultCallback.assertNoCallback();
 
-        vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        mCm.unregisterNetworkCallback(defaultCallback);
+    }
+
+    @Test
+    public void testVpnWithInternet() {
+        final int uid = Process.myUid();
+
+        final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+
+        defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+        assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+
+        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
         mMockVpn.setUids(ranges);
         vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */);
         mMockVpn.connect();
+
         defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
@@ -4790,14 +4824,6 @@
         defaultCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
         defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
 
-        vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
-        ranges.clear();
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
-        mMockVpn.connect();
-        defaultCallback.assertNoCallback();
-
         mCm.unregisterNetworkCallback(defaultCallback);
     }
 
@@ -4900,6 +4926,70 @@
     }
 
     @Test
+    public void testNullUnderlyingNetworks() {
+        final int uid = Process.myUid();
+
+        final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
+                .removeCapability(NET_CAPABILITY_NOT_VPN)
+                .addTransportType(TRANSPORT_VPN)
+                .build();
+        NetworkCapabilities nc;
+        mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
+        vpnNetworkCallback.assertNoCallback();
+
+        final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        ranges.add(new UidRange(uid, uid));
+        mMockVpn.setNetworkAgent(vpnNetworkAgent);
+        mMockVpn.connect();
+        mMockVpn.setUids(ranges);
+        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+
+        vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+        nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+        assertTrue(nc.hasTransport(TRANSPORT_VPN));
+        assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
+        assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+        // By default, VPN is set to track default network (i.e. its underlying networks is null).
+        // In case of no default network, VPN is considered metered.
+        assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+
+        // Connect to Cell; Cell is the default network.
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+
+        vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+                && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+                vpnNetworkAgent);
+
+        // Connect to WiFi; WiFi is the new default.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+
+        vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+                && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
+                && caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+                vpnNetworkAgent);
+
+        // Disconnect Cell. The default network did not change, so there shouldn't be any changes in
+        // the capabilities.
+        mCellNetworkAgent.disconnect();
+
+        // Disconnect wifi too. Now we have no default network.
+        mWiFiNetworkAgent.disconnect();
+
+        vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+                && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+                vpnNetworkAgent);
+
+        mMockVpn.disconnect();
+    }
+
+    @Test
     public void testNetworkBlockedStatus() {
         final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
         final NetworkRequest cellRequest = new NetworkRequest.Builder()
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index e877a8f..5057443 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -38,7 +38,6 @@
 import android.net.NetworkFactory;
 import android.net.NetworkInfo;
 import android.net.NetworkMisc;
-import android.net.NetworkStack;
 import android.os.INetworkManagementService;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -75,16 +74,12 @@
     @Mock NetworkMisc mMisc;
     @Mock NetworkNotificationManager mNotifier;
     @Mock Resources mResources;
-    @Mock NetworkStack mNetworkStack;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mCtx.getResources()).thenReturn(mResources);
         when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity");
-        when(mCtx.getSystemServiceName(NetworkStack.class))
-                .thenReturn(Context.NETWORK_STACK_SERVICE);
-        when(mCtx.getSystemService(Context.NETWORK_STACK_SERVICE)).thenReturn(mNetworkStack);
 
         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
     }
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index a4a735d..533d7ad 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -195,10 +195,6 @@
     }
 
     public class MockIpServerDependencies extends IpServer.Dependencies {
-        MockIpServerDependencies() {
-            super(null);
-        }
-
         @Override
         public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
                 InterfaceParams ifParams) {
@@ -266,7 +262,7 @@
         }
 
         @Override
-        public IpServer.Dependencies getIpServerDependencies(Context context) {
+        public IpServer.Dependencies getIpServerDependencies() {
             return mIpServerDependencies;
         }
 
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 46de3d0..f169d6b 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -566,7 +566,7 @@
 
         final NetworkCapabilities caps = new NetworkCapabilities();
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
         assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -577,7 +577,7 @@
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager,
                 new Network[] {mobile},
                 caps,
@@ -591,7 +591,7 @@
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
         assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -602,7 +602,7 @@
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
         assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -613,7 +613,7 @@
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager,
                 new Network[] {mobile, wifi},
                 caps,
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 73b568e..9a1d942 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -151,10 +151,12 @@
 // Represents an overlayable <item> declaration within an <overlayable> tag.
 message OverlayableItem {
   enum Policy {
-    PUBLIC = 0;
-    SYSTEM = 1;
-    VENDOR = 2;
-    PRODUCT = 3;
+    NONE = 0;
+    PUBLIC = 1;
+    SYSTEM = 2;
+    VENDOR = 3;
+    PRODUCT = 4;
+    SIGNATURE = 5;
   }
 
   // The location of the <item> declaration in source.
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 488de87..af9fdfb 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -276,14 +276,19 @@
 
     /**
      * Returns the service set identifier (SSID) of the current 802.11 network.
+     * <p>
      * If the SSID can be decoded as UTF-8, it will be returned surrounded by double
-     * quotation marks. Otherwise, it is returned as a string of hex digits. The
-     * SSID may be &lt;unknown ssid&gt; if there is no network currently connected,
-     * or if the caller has insufficient permissions to access the SSID.
-     *
+     * quotation marks. Otherwise, it is returned as a string of hex digits.
+     * The SSID may be
+     * <lt>&lt;unknown ssid&gt;, if there is no network currently connected or if the caller has
+     * insufficient permissions to access the SSID.<lt>
+     * </p>
+     * <p>
      * Prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method
      * always returned the SSID with no quotes around it.
-     * @return the SSID
+     * </p>
+     *
+     * @return the SSID.
      */
     public String getSSID() {
         if (mWifiSsid != null) {
@@ -312,7 +317,13 @@
 
     /**
      * Return the basic service set identifier (BSSID) of the current access point.
-     * The BSSID may be {@code null} if there is no network currently connected.
+     * <p>
+     * The BSSID may be
+     * <lt>{@code null}, if there is no network currently connected.</lt>
+     * <lt>{@code "02:00:00:00:00:00"}, if the caller has insufficient permissions to access the
+     * BSSID.<lt>
+     * </p>
+     *
      * @return the BSSID, in the form of a six-byte MAC address: {@code XX:XX:XX:XX:XX:XX}
      */
     public String getBSSID() {
@@ -511,9 +522,13 @@
 
     /**
      * Each configured network has a unique small integer ID, used to identify
-     * the network when performing operations on the supplicant. This method
-     * returns the ID for the currently connected network.
-     * @return the network ID, or -1 if there is no currently connected network
+     * the network. This method returns the ID for the currently connected network.
+     * <p>
+     * The networkId may be {@code -1} if there is no currently connected network or if the caller
+     * has insufficient permissions to access the network ID.
+     * </p>
+     *
+     * @return the network ID.
      */
     public int getNetworkId() {
         return mNetworkId;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index 72e57a1..8a1b21c 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -67,7 +67,7 @@
     /** The network id in the wpa_supplicant */
     private int mNetId;
 
-    /** The frequency used by this group */
+    /** The frequency (in MHz) used by this group */
     private int mFrequency;
 
     /** P2P group started string pattern */
@@ -273,7 +273,7 @@
         this.mNetId = netId;
     }
 
-    /** Get the operating frequency of the p2p group */
+    /** Get the operating frequency (in MHz) of the p2p group */
     public int getFrequency() {
         return mFrequency;
     }