Merge "Foldables: Customize folded area"
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 8947cfa..1ee7405 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",
@@ -772,8 +773,8 @@
         "android.hardware.vibrator-V1.2-java",
         "android.hardware.vibrator-V1.3-java",
         "android.hardware.wifi-V1.0-java-constants",
-        "networkstack-aidl-interfaces-java",
-        "netd_aidl_interface-java",
+        "networkstack-aidl-framework-java",
+        "netd_aidl_parcelables-java",
         "devicepolicyprotosnano",
     ],
 
@@ -893,10 +894,8 @@
     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",
         "core/java/android/net/INetworkStackConnector.aidl",
         "core/java/android/net/INetworkStackStatusCallback.aidl",
         "core/java/android/net/InitialConfigurationParcelable.aidl",
@@ -915,6 +914,16 @@
         "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl",
         "core/java/android/net/ip/IIpClient.aidl",
         "core/java/android/net/ip/IIpClientCallbacks.aidl",
+    ],
+    api_dir: "aidl/networkstack",
+}
+
+aidl_interface {
+    name: "networkstack-aidl-framework",
+    local_include_dir: "core/java",
+    srcs: [
+        "core/java/android/net/TcpKeepalivePacketDataParcelable.aidl",
+        "core/java/android/net/IIpMemoryStore.aidl",
         "core/java/android/net/ipmemorystore/**/*.aidl",
     ],
     api_dir: "aidl/networkstack",
@@ -1187,12 +1196,21 @@
     "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) " +
      // Federate Support Library references against local API file.
      "-federate SupportLib https://developer.android.com " +
-     "-federationapi SupportLib $(location current/support-api.txt) "
+     "-federationapi SupportLib $(location :current-support-api) "
 
 framework_docs_only_libs = [
     "voip-common",
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/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
index 9234849..0c30302 100644
--- a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
@@ -40,7 +40,7 @@
     public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
     private final KernelCpuThreadReader mKernelCpuThreadReader =
-            KernelCpuThreadReader.create(8, uid -> 1000 <= uid && uid < 2000);
+            KernelCpuThreadReader.create(8, uid -> 1000 <= uid && uid < 2000, 0);
 
     @Test
     public void timeReadCurrentProcessCpuUsage() {
diff --git a/api/current.txt b/api/current.txt
index 16bc8c8..fe86cbe 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2222,6 +2222,7 @@
     field public static final int TextAppearance_WindowTitle = 16973907; // 0x1030053
     field public static final int Theme = 16973829; // 0x1030005
     field public static final int ThemeOverlay = 16974407; // 0x1030247
+    field public static final int ThemeOverlay_DeviceDefault_Accent_DayNight = 16974564; // 0x10302e4
     field public static final int ThemeOverlay_Material = 16974408; // 0x1030248
     field public static final int ThemeOverlay_Material_ActionBar = 16974409; // 0x1030249
     field public static final int ThemeOverlay_Material_Dark = 16974411; // 0x103024b
@@ -2233,6 +2234,7 @@
     field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009
     field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a
     field public static final int Theme_DeviceDefault = 16974120; // 0x1030128
+    field public static final int Theme_DeviceDefault_DayNight = 16974563; // 0x10302e3
     field public static final int Theme_DeviceDefault_Dialog = 16974126; // 0x103012e
     field public static final int Theme_DeviceDefault_DialogWhenLarge = 16974134; // 0x1030136
     field public static final int Theme_DeviceDefault_DialogWhenLarge_NoActionBar = 16974135; // 0x1030137
@@ -6684,7 +6686,7 @@
     method public boolean installKeyPair(@Nullable android.content.ComponentName, @NonNull java.security.PrivateKey, @NonNull java.security.cert.Certificate, @NonNull String);
     method public boolean installKeyPair(@Nullable android.content.ComponentName, @NonNull java.security.PrivateKey, @NonNull java.security.cert.Certificate[], @NonNull String, boolean);
     method public boolean installKeyPair(@Nullable android.content.ComponentName, @NonNull java.security.PrivateKey, @NonNull java.security.cert.Certificate[], @NonNull String, int);
-    method public void installSystemUpdate(@NonNull android.content.ComponentName, @NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.app.admin.DevicePolicyManager.InstallUpdateCallback);
+    method public void installSystemUpdate(@NonNull android.content.ComponentName, @NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback);
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(@NonNull android.content.ComponentName);
     method public boolean isAffiliatedUser();
@@ -6951,8 +6953,8 @@
     field public static final int WIPE_SILENTLY = 8; // 0x8
   }
 
-  public abstract static class DevicePolicyManager.InstallUpdateCallback {
-    ctor public DevicePolicyManager.InstallUpdateCallback();
+  public abstract static class DevicePolicyManager.InstallSystemUpdateCallback {
+    ctor public DevicePolicyManager.InstallSystemUpdateCallback();
     method public void onInstallUpdateError(int, String);
     field public static final int UPDATE_ERROR_BATTERY_LOW = 5; // 0x5
     field public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4; // 0x4
@@ -13064,6 +13066,10 @@
     method public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String, String);
     method @Deprecated public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String[], String, String);
     method public int delete(@NonNull android.database.sqlite.SQLiteDatabase, @Nullable String, @Nullable String[]);
+    method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
+    method public boolean getDistinct();
+    method public java.util.Map<java.lang.String,java.lang.String> getProjectionMap();
+    method public boolean getStrict();
     method public String getTables();
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String);
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String);
@@ -14096,6 +14102,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);
@@ -23380,7 +23414,7 @@
     method @NonNull public android.media.AudioPresentation.Builder setProgramId(int);
   }
 
-  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
+  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
     ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method @Deprecated public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
@@ -23415,6 +23449,8 @@
     method public void release();
     method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method @Deprecated public void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+    method public boolean setMicrophoneDirection(int);
+    method public boolean setMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float);
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -26064,6 +26100,15 @@
     field public static final android.media.MediaTimestamp TIMESTAMP_UNKNOWN;
   }
 
+  public interface MicrophoneDirection {
+    method public boolean setMicrophoneDirection(int);
+    method public boolean setMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float);
+    field public static final int MIC_DIRECTION_BACK = 2; // 0x2
+    field public static final int MIC_DIRECTION_EXTERNAL = 3; // 0x3
+    field public static final int MIC_DIRECTION_FRONT = 1; // 0x1
+    field public static final int MIC_DIRECTION_UNSPECIFIED = 0; // 0x0
+  }
+
   public final class MicrophoneInfo {
     method @NonNull public String getAddress();
     method public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getChannelMapping();
@@ -27445,6 +27490,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 +27541,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 +35097,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";
@@ -41558,6 +41605,7 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
     field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions";
     field public static final String KEY_IMPORTANCE = "key_importance";
+    field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     field public static final String KEY_TEXT_REPLIES = "key_text_replies";
     field public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
   }
@@ -41615,12 +41663,14 @@
     method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onNotificationDirectReplied(@NonNull String);
-    method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
+    method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
     method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
+    method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, String);
     method public void onNotificationsSeen(java.util.List<java.lang.String>);
     method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
+    method public final void unsnoozeNotification(String);
     field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
     field public static final int SOURCE_FROM_APP = 0; // 0x0
     field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
@@ -41953,7 +42003,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 +42012,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 +44443,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 +45118,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 +45153,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 +45195,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);
@@ -45216,7 +45265,6 @@
     field public static final String EXTRA_STATE_RINGING;
     field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
     field public static final String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
-    field public static final int INVALID_CARD_ID = -1; // 0xffffffff
     field public static final String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
     field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
     field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
@@ -45255,7 +45303,9 @@
     field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3
     field public static final int SIM_STATE_READY = 5; // 0x5
     field public static final int SIM_STATE_UNKNOWN = 0; // 0x0
+    field public static final int UNINITIALIZED_CARD_ID = -2; // 0xfffffffe
     field public static final int UNKNOWN_CARRIER_ID = -1; // 0xffffffff
+    field public static final int UNSUPPORTED_CARD_ID = -1; // 0xffffffff
     field public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; // 0xfffffffe
     field public static final int USSD_RETURN_FAILURE = -1; // 0xffffffff
     field public static final String VVM_TYPE_CVVM = "vvm_type_cvvm";
@@ -48997,6 +49047,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);
@@ -49216,6 +49267,7 @@
     ctor public ContextThemeWrapper(android.content.Context, android.content.res.Resources.Theme);
     method public void applyOverrideConfiguration(android.content.res.Configuration);
     method protected void onApplyThemeResource(android.content.res.Resources.Theme, int, boolean);
+    method public void setTheme(@Nullable android.content.res.Resources.Theme);
   }
 
   public final class Display {
@@ -50804,7 +50856,7 @@
     method @android.view.ViewDebug.ExportedProperty(category="drawing") public float getAlpha();
     method public android.view.animation.Animation getAnimation();
     method public android.os.IBinder getApplicationWindowToken();
-    method @NonNull public java.util.List<java.lang.Integer> getAttributeResolutionStack();
+    method @NonNull public java.util.List<java.lang.Integer> getAttributeResolutionStack(@AttrRes int);
     method @NonNull public java.util.Map<java.lang.Integer,java.lang.Integer> getAttributeSourceResourceMap();
     method @android.view.ViewDebug.ExportedProperty @Nullable public String[] getAutofillHints();
     method public final android.view.autofill.AutofillId getAutofillId();
@@ -50854,6 +50906,8 @@
     method public void getHitRect(android.graphics.Rect);
     method public int getHorizontalFadingEdgeLength();
     method protected int getHorizontalScrollbarHeight();
+    method @Nullable public android.graphics.drawable.Drawable getHorizontalScrollbarThumbDrawable();
+    method @Nullable public android.graphics.drawable.Drawable getHorizontalScrollbarTrackDrawable();
     method @android.view.ViewDebug.CapturedViewProperty @IdRes public int getId();
     method @android.view.ViewDebug.ExportedProperty(category="accessibility", mapping={@android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO, to="auto"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES, to="yes"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO, to="no"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS, to="noHideDescendants")}) public int getImportantForAccessibility();
     method @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_AUTO, to="auto"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_YES, to="yes"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_NO, to="no"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS, to="yesExcludeDescendants"), @android.view.ViewDebug.IntToString(from=android.view.View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS, to="noExcludeDescendants")}) public int getImportantForAutofill();
@@ -50944,6 +50998,8 @@
     method public long getUniqueDrawingId();
     method public int getVerticalFadingEdgeLength();
     method public int getVerticalScrollbarPosition();
+    method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarThumbDrawable();
+    method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarTrackDrawable();
     method public int getVerticalScrollbarWidth();
     method public android.view.ViewTreeObserver getViewTreeObserver();
     method @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.View.VISIBLE, to="VISIBLE"), @android.view.ViewDebug.IntToString(from=android.view.View.INVISIBLE, to="INVISIBLE"), @android.view.ViewDebug.IntToString(from=android.view.View.GONE, to="GONE")}) public int getVisibility();
@@ -51187,6 +51243,8 @@
     method public void setHasTransientState(boolean);
     method public void setHorizontalFadingEdgeEnabled(boolean);
     method public void setHorizontalScrollBarEnabled(boolean);
+    method public void setHorizontalScrollbarThumbDrawable(@Nullable android.graphics.drawable.Drawable);
+    method public void setHorizontalScrollbarTrackDrawable(@Nullable android.graphics.drawable.Drawable);
     method public void setHovered(boolean);
     method public void setId(@IdRes int);
     method public void setImportantForAccessibility(int);
@@ -51276,6 +51334,8 @@
     method public void setVerticalFadingEdgeEnabled(boolean);
     method public void setVerticalScrollBarEnabled(boolean);
     method public void setVerticalScrollbarPosition(int);
+    method public void setVerticalScrollbarThumbDrawable(@Nullable android.graphics.drawable.Drawable);
+    method public void setVerticalScrollbarTrackDrawable(@Nullable android.graphics.drawable.Drawable);
     method public void setVisibility(int);
     method @Deprecated public void setWillNotCacheDrawing(boolean);
     method public void setWillNotDraw(boolean);
@@ -51734,6 +51794,7 @@
     method public android.view.View getChildAt(int);
     method public int getChildCount();
     method protected int getChildDrawingOrder(int, int);
+    method public final int getChildDrawingOrder(int);
     method public static int getChildMeasureSpec(int, int, int);
     method protected boolean getChildStaticTransformation(android.view.View, android.view.animation.Transformation);
     method public boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
@@ -53462,6 +53523,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);
@@ -53469,6 +53531,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 {
@@ -55729,6 +55792,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();
@@ -55753,6 +55817,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..50a2553 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);
   }
@@ -3405,13 +3409,13 @@
 
   public final class AudioFocusInfo implements android.os.Parcelable {
     method public int describeContents();
-    method public android.media.AudioAttributes getAttributes();
-    method public String getClientId();
+    method @NonNull public android.media.AudioAttributes getAttributes();
+    method @NonNull public String getClientId();
     method public int getClientUid();
     method public int getFlags();
     method public int getGainRequest();
     method public int getLossReceived();
-    method public String getPackageName();
+    method @NonNull public String getPackageName();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.media.AudioFocusInfo> CREATOR;
   }
@@ -3471,7 +3475,7 @@
     field public static final int PLAYER_TYPE_UNKNOWN = -1; // 0xffffffff
   }
 
-  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
+  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
     ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
   }
 
@@ -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";
   }
 
@@ -6519,6 +6522,15 @@
 
 package android.service.notification {
 
+  public final class Adjustment implements android.os.Parcelable {
+    ctor protected Adjustment(android.os.Parcel);
+    field public static final String KEY_PEOPLE = "key_people";
+  }
+
+  public final class NotificationStats implements android.os.Parcelable {
+    ctor protected NotificationStats(android.os.Parcel);
+  }
+
   public final class SnoozeCriterion implements android.os.Parcelable {
     ctor public SnoozeCriterion(String, CharSequence, CharSequence);
     ctor protected SnoozeCriterion(android.os.Parcel);
@@ -6974,6 +6986,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 +7873,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 +8261,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 +8274,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 +9317,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 +9340,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 +9349,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..16098c1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -516,13 +516,20 @@
     method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method @Nullable public abstract String[] getNamesForUids(int[]);
     method public abstract String getPermissionControllerPackageName();
+    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(String, String, @NonNull android.os.UserHandle);
     method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
     method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
     method public String getWellbeingPackageName();
     method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract void updatePermissionFlags(String, String, int, int, @NonNull android.os.UserHandle);
     field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
     field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
+    field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40
+    field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
+    field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
+    field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2
+    field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
     field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
     field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
@@ -802,6 +809,7 @@
 
   public class LocationManager {
     method public String[] getBackgroundThrottlingWhitelist();
+    method public String[] getIgnoreSettingsWhitelist();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(android.location.LocationRequest, android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, android.os.UserHandle);
@@ -936,7 +944,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 +1004,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 +1824,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 +2083,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 +2111,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 +2701,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 +2728,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 +2737,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
@@ -2743,7 +2748,15 @@
   public final class ContentCaptureManager {
     method public boolean isContentCaptureFeatureEnabled();
     method public void setContentCaptureFeatureEnabled(boolean);
+    field public static final String DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY = "idle_flush_frequency";
+    field public static final String DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL = "logging_level";
+    field public static final String DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE = "log_history_size";
+    field public static final String DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE = "max_buffer_size";
     field public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED = "service_explicitly_enabled";
+    field public static final String DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY = "text_change_flush_frequency";
+    field public static final int LOGGING_LEVEL_DEBUG = 1; // 0x1
+    field public static final int LOGGING_LEVEL_OFF = 0; // 0x0
+    field public static final int LOGGING_LEVEL_VERBOSE = 2; // 0x2
   }
 
   public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
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/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 80ed807..13f5c8a 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -19,6 +19,7 @@
 #include "FieldValue.h"
 #include "HashableDimensionKey.h"
 #include "math.h"
+#include "statslog.h"
 
 namespace android {
 namespace os {
@@ -122,6 +123,24 @@
     return false;
 }
 
+int32_t getUidIfExists(const FieldValue& value) {
+    bool isUid = false;
+    // the field is uid field if the field is the uid field in attribution node or marked as
+    // is_uid in atoms.proto
+    if (isAttributionUidField(value)) {
+        isUid = true;
+    } else {
+        auto it = android::util::AtomsInfo::kAtomsWithUidField.find(value.mField.getTag());
+        if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
+            int uidField = it->second;  // uidField is the field number in proto
+            isUid = value.mField.getDepth() == 0 && value.mField.getPosAtDepth(0) == uidField &&
+                    value.mValue.getType() == INT;
+        }
+    }
+
+    return isUid ? value.mValue.int_value : -1;
+}
+
 bool isAttributionUidField(const Field& field, const Value& value) {
     int f = field.getField() & 0xff007f;
     if (f == 0x10001 && value.getType() == INT) {
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index a5d00ac..6729e05 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -386,6 +386,9 @@
 
 bool isAttributionUidField(const FieldValue& value);
 
+/* returns uid if the field is uid field, or -1 if the field is not a uid field */
+int getUidIfExists(const FieldValue& value);
+
 void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
 
 bool isAttributionUidField(const Field& field, const Value& value);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index b478fed..c542b62 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();
@@ -155,7 +159,7 @@
            }
 
       }))  {
-    mUidMap = new UidMap();
+    mUidMap = UidMap::getInstance();
     mPullerManager = new StatsPullerManager();
     StatsPuller::SetUidMap(mUidMap);
     mConfigManager = new ConfigManager();
@@ -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/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
index 8d73699..019a9f7 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.cpp
+++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp
@@ -78,8 +78,8 @@
     }
     if (!mSubscriptions.empty()) {
         VLOG("AlarmTracker triggers the subscribers.");
-        triggerSubscribers(mAlarmConfig.id(), DEFAULT_METRIC_DIMENSION_KEY, mConfigKey,
-                           mSubscriptions);
+        triggerSubscribers(mAlarmConfig.id(), 0 /*metricId N/A*/, DEFAULT_METRIC_DIMENSION_KEY,
+                           0 /* metricValue N/A */, mConfigKey, mSubscriptions);
     }
     firedAlarms.erase(mInternalAlarm);
     mAlarmSec = findNextAlarmSec((timestampNs-1) / NS_PER_SEC + 1); // round up
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index ee111cd..d1dcb5df 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -207,7 +207,8 @@
            getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
 }
 
-void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key) {
+void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId,
+                                    const MetricDimensionKey& key, int64_t metricValue) {
     // TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on
     // real time right now.
     if (isInRefractoryPeriod(timestampNs, key)) {
@@ -225,7 +226,7 @@
     if (!mSubscriptions.empty()) {
         ALOGI("An anomaly (%lld) %s has occurred! Informing subscribers.",
                 mAlert.id(), key.toString().c_str());
-        informSubscribers(key);
+        informSubscribers(key, metricId, metricValue);
     } else {
         ALOGI("An anomaly has occurred! (But no subscriber for that alert.)");
     }
@@ -238,11 +239,11 @@
 }
 
 void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs,
-                                             const int64_t& currBucketNum,
+                                             const int64_t& currBucketNum, int64_t metricId,
                                              const MetricDimensionKey& key,
                                              const int64_t& currentBucketValue) {
     if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
-        declareAnomaly(timestampNs, key);
+        declareAnomaly(timestampNs, metricId, key, currentBucketValue);
     }
 }
 
@@ -255,8 +256,9 @@
     return false;
 }
 
-void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) {
-    triggerSubscribers(mAlert.id(), key, mConfigKey, mSubscriptions);
+void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id,
+                                       int64_t metricValue) {
+    triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 927e2df..e941473 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -67,14 +67,16 @@
                        const int64_t& currentBucketValue);
 
     // Informs incidentd about the detected alert.
-    void declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key);
+    void declareAnomaly(const int64_t& timestampNs, int64_t metricId, const MetricDimensionKey& key,
+                        int64_t metricValue);
 
     // Detects if, based on past buckets plus the new currentBucketValue (which generally
     // represents the partially-filled current bucket), an anomaly has happened, and if so,
     // declares an anomaly and informs relevant subscribers.
     // Also advances to currBucketNum-1.
     void detectAndDeclareAnomaly(const int64_t& timestampNs, const int64_t& currBucketNum,
-                                 const MetricDimensionKey& key, const int64_t& currentBucketValue);
+                                 int64_t metricId, const MetricDimensionKey& key,
+                                 const int64_t& currentBucketValue);
 
     // Init the AlarmMonitor which is shared across anomaly trackers.
     virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) {
@@ -176,7 +178,7 @@
     virtual void resetStorage();
 
     // Informs the subscribers (incidentd, perfetto, broadcasts, etc) that an anomaly has occurred.
-    void informSubscribers(const MetricDimensionKey& key);
+    void informSubscribers(const MetricDimensionKey& key, int64_t metricId, int64_t metricValue);
 
     FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
     FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 3acfd17..2b56810 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -65,7 +65,9 @@
 
     // If the alarm is set in the past but hasn't fired yet (due to lag), catch it now.
     if (itr->second != nullptr && timestampNs >= (int64_t)NS_PER_SEC * itr->second->timestampSec) {
-        declareAnomaly(timestampNs, dimensionKey);
+        declareAnomaly(timestampNs, mAlert.metric_id(), dimensionKey,
+                       mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) -
+                               itr->second->timestampSec);
     }
     if (mAlarmMonitor != nullptr) {
         mAlarmMonitor->remove(itr->second);
@@ -100,7 +102,9 @@
 
     // Now declare each of these alarms to have fired.
     for (const auto& kv : matchedAlarms) {
-        declareAnomaly(timestampNs, kv.first);
+        declareAnomaly(
+                timestampNs, mAlert.metric_id(), kv.first,
+                mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - kv.second->timestampSec);
         mAlarms.erase(kv.first);
         firedAlarms.erase(kv.second);  // No one else can also own it, so we're done with it.
     }
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
index 6b46b8b..548a686 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.cpp
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -30,9 +30,8 @@
 namespace os {
 namespace statsd {
 
-void triggerSubscribers(const int64_t rule_id,
-                        const MetricDimensionKey& dimensionKey,
-                        const ConfigKey& configKey,
+void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionKey& dimensionKey,
+                        int64_t metricValue, const ConfigKey& configKey,
                         const std::vector<Subscription>& subscriptions) {
     VLOG("informSubscribers called.");
     if (subscriptions.empty()) {
@@ -50,13 +49,14 @@
         }
         switch (subscription.subscriber_information_case()) {
             case Subscription::SubscriberInformationCase::kIncidentdDetails:
-                if (!GenerateIncidentReport(subscription.incidentd_details(), rule_id, configKey)) {
+                if (!GenerateIncidentReport(subscription.incidentd_details(), ruleId, metricId,
+                                            dimensionKey, metricValue, configKey)) {
                     ALOGW("Failed to generate incident report.");
                 }
                 break;
             case Subscription::SubscriberInformationCase::kPerfettoDetails:
                 if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(),
-                                                            subscription.id(), rule_id, configKey)) {
+                                                            subscription.id(), ruleId, configKey)) {
                     ALOGW("Failed to generate perfetto traces.");
                 }
                 break;
@@ -66,7 +66,7 @@
                 break;
             case Subscription::SubscriberInformationCase::kPerfprofdDetails:
                 if (!CollectPerfprofdTraceAndUploadToDropbox(subscription.perfprofd_details(),
-                                                             rule_id, configKey)) {
+                                                             ruleId, configKey)) {
                     ALOGW("Failed to generate perfprofd traces.");
                 }
                 break;
@@ -76,7 +76,6 @@
     }
 }
 
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/anomaly/subscriber_util.h b/cmds/statsd/src/anomaly/subscriber_util.h
index dba8981..1df3c89 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.h
+++ b/cmds/statsd/src/anomaly/subscriber_util.h
@@ -24,10 +24,9 @@
 namespace os {
 namespace statsd {
 
-void triggerSubscribers(const int64_t rule_id,
-                        const MetricDimensionKey& dimensionKey,
-                        const ConfigKey& configKey,
-                        const std::vector<Subscription>& subscriptions);
+void triggerSubscribers(const int64_t ruleId, const int64_t metricId,
+                        const MetricDimensionKey& dimensionKey, int64_t metricValue,
+                        const ConfigKey& configKey, const std::vector<Subscription>& subscriptions);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 29f67c7..2632294d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -302,6 +302,8 @@
         RoleHolder role_holder = 10049;
         DangerousPermissionState dangerous_permission_state = 10050;
         TrainInfo train_info = 10051;
+        TimeZoneDataInfo time_zone_data_info = 10052;
+        SDCardInfo sdcard_info = 10053;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -3156,6 +3158,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 +3191,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. */
@@ -3217,6 +3232,28 @@
     optional int32 cycle_count = 1;
 }
 
+/**
+ * Logs that an SD card is mounted and information about it, its type (public or private) and the
+ * size in bytes.
+ * Pulled from:
+ *   StatsCompanionService
+ */
+
+message SDCardInfo {
+
+    enum Type {
+        UNKNOWN = 0;
+        TYPE_PUBLIC = 1;
+        TYPE_PRIVATE = 2;
+        OTHERS = 3;
+    }
+
+    // Type of the SD card: TYPE_PUBLIC if portable and TYPE_PRIVATE if internal.
+    optional Type type = 1;
+    // Total size of the sd card in bytes.
+    optional int64 size_bytes = 2;
+}
+
 /*
  * Logs when a connection becomes available and lost.
  * Logged in StatsCompanionService.java
@@ -5511,13 +5548,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 +5588,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..1513834 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,14 @@
         // 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)}},
+        // SDCardInfo
+        {android::util::SDCARD_INFO,
+         {.puller = new StatsCompanionServicePuller(android::util::SDCARD_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..e84f88d 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) {
@@ -303,7 +303,7 @@
         if (prev != mCurrentFullCounters->end()) {
             countWholeBucket += prev->second;
         }
-        tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
+        tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey,
                                          countWholeBucket);
     }
 
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..6301793 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.
@@ -485,8 +485,8 @@
                 gaugeVal = value.long_value;
             }
             for (auto& tracker : mAnomalyTrackers) {
-                tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
-                                                 gaugeVal);
+                tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId,
+                                                 eventKey, gaugeVal);
             }
         }
     }
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..3cf378d 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();
@@ -693,8 +697,8 @@
             wholeBucketVal += prev->second;
         }
         for (auto& tracker : mAnomalyTrackers) {
-            tracker->detectAndDeclareAnomaly(
-                eventTimeNs, mCurrentBucketNum, eventKey, wholeBucketVal);
+            tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey,
+                                             wholeBucketVal);
         }
     }
 }
@@ -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/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index ccb1d43..081e61e 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -155,8 +155,8 @@
                                  const int64_t& currentBucketValue) {
         for (auto& anomalyTracker : mAnomalyTrackers) {
             if (anomalyTracker != nullptr) {
-                anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mEventKey,
-                                                        currentBucketValue);
+                anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mTrackerId,
+                                                        mEventKey, currentBucketValue);
             }
         }
     }
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 5cf012638..d4b57dd 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -73,6 +73,11 @@
 
 UidMap::~UidMap() {}
 
+sp<UidMap> UidMap::getInstance() {
+    static sp<UidMap> sInstance = new UidMap();
+    return sInstance;
+}
+
 bool UidMap::hasApp(int uid, const string& packageName) const {
     lock_guard<mutex> lock(mMutex);
 
@@ -336,6 +341,61 @@
     return mBytesUsed;
 }
 
+void UidMap::writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings,
+                                 bool includeInstaller, const std::set<int32_t>& interestingUids,
+                                 std::set<string>* str_set, ProtoOutputStream* proto) {
+    lock_guard<mutex> lock(mMutex);
+
+    writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller, interestingUids,
+                              str_set, proto);
+}
+
+void UidMap::writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings,
+                                       bool includeInstaller,
+                                       const std::set<int32_t>& interestingUids,
+                                       std::set<string>* str_set, ProtoOutputStream* proto) {
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp);
+    for (const auto& kv : mMap) {
+        if (!interestingUids.empty() &&
+            interestingUids.find(kv.first.first) == interestingUids.end()) {
+            continue;
+        }
+        uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+                                      FIELD_ID_SNAPSHOT_PACKAGE_INFO);
+        if (str_set != nullptr) {
+            str_set->insert(kv.first.second);
+            proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH,
+                         (long long)Hash64(kv.first.second));
+            if (includeVersionStrings) {
+                str_set->insert(kv.second.versionString);
+                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH,
+                             (long long)Hash64(kv.second.versionString));
+            }
+            if (includeInstaller) {
+                str_set->insert(kv.second.installer);
+                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH,
+                             (long long)Hash64(kv.second.installer));
+            }
+        } else {
+            proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
+            if (includeVersionStrings) {
+                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING,
+                             kv.second.versionString);
+            }
+            if (includeInstaller) {
+                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER,
+                             kv.second.installer);
+            }
+        }
+
+        proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
+                     (long long)kv.second.versionCode);
+        proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first);
+        proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted);
+        proto->end(token);
+    }
+}
+
 void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set,
                           bool includeVersionStrings, bool includeInstaller,
                           ProtoOutputStream* proto) {
@@ -381,43 +441,9 @@
     // Write snapshot from current uid map state.
     uint64_t snapshotsToken =
             proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS);
-    proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp);
-    for (const auto& kv : mMap) {
-        uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
-                                      FIELD_ID_SNAPSHOT_PACKAGE_INFO);
-
-        if (str_set != nullptr) {
-            str_set->insert(kv.first.second);
-            proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH,
-                         (long long)Hash64(kv.first.second));
-            if (includeVersionStrings) {
-                str_set->insert(kv.second.versionString);
-                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH,
-                             (long long)Hash64(kv.second.versionString));
-            }
-            if (includeInstaller) {
-                str_set->insert(kv.second.installer);
-                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH,
-                             (long long)Hash64(kv.second.installer));
-            }
-        } else {
-            proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
-            if (includeVersionStrings) {
-                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING,
-                             kv.second.versionString);
-            }
-            if (includeInstaller) {
-                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER,
-                             kv.second.installer);
-            }
-        }
-
-        proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
-                     (long long)kv.second.versionCode);
-        proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first);
-        proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted);
-        proto->end(token);
-    }
+    writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller,
+                              std::set<int32_t>() /*empty uid set means including every uid*/,
+                              str_set, proto);
     proto->end(snapshotsToken);
 
     int64_t prevMin = getMinimumTimestampNs();
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 75ff507..a7c5fb2 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -91,6 +91,8 @@
     UidMap();
     ~UidMap();
     static const std::map<std::string, uint32_t> sAidToUidMapping;
+
+    static sp<UidMap> getInstance();
     /*
      * All three inputs must be the same size, and the jth element in each array refers to the same
      * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
@@ -152,12 +154,25 @@
 
     std::set<int32_t> getAppUid(const string& package) const;
 
+    // Write current PackageInfoSnapshot to ProtoOutputStream.
+    // interestingUids: If not empty, only write the package info for these uids. If empty, write
+    //                  package info for all uids.
+    // str_set: if not null, add new string to the set and write str_hash to proto
+    //          if null, write string to proto.
+    void writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings, bool includeInstaller,
+                             const std::set<int32_t>& interestingUids, std::set<string>* str_set,
+                             ProtoOutputStream* proto);
+
 private:
     std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const;
     string normalizeAppName(const string& appName) const;
 
     void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output);
 
+    void writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings,
+                                   bool includeInstaller, const std::set<int32_t>& interestingUids,
+                                   std::set<string>* str_set, ProtoOutputStream* proto);
+
     mutable mutex mMutex;
     mutable mutex mIsolatedMutex;
 
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index f7428a5..623d8bc 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;
 
@@ -456,3 +457,17 @@
     }
     repeated LogLossStats detected_log_loss = 16;
 }
+
+message AlertTriggerDetails {
+    message MetricValue {
+        optional int64 metric_id = 1;
+        optional DimensionsValue dimension_in_what = 2;
+        optional DimensionsValue dimension_in_condition = 3;
+        optional int64 value = 4;
+    }
+    oneof value {
+        MetricValue trigger_metric = 1;
+        EventMetricData trigger_event = 2;
+    }
+    optional UidMapping.PackageInfoSnapshot package_info = 3;
+}
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 0ebf2ca..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_INT32 | 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/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
index 42cac0c..0ed2d75 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
@@ -16,7 +16,10 @@
 #define DEBUG false
 #include "Log.h"
 
+#include "FieldValue.h"
 #include "IncidentdReporter.h"
+#include "packages/UidMap.h"
+#include "stats_log_util.h"
 
 #include <android/os/IIncidentManager.h>
 #include <android/os/IncidentReportArgs.h>
@@ -43,8 +46,18 @@
 const int FIELD_ID_CONFIG_KEY_UID = 1;
 const int FIELD_ID_CONFIG_KEY_ID = 2;
 
+const int FIELD_ID_TRIGGER_DETAILS = 4;
+const int FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC = 1;
+const int FIELD_ID_METRIC_VALUE_METRIC_ID = 1;
+const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT = 2;
+const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION = 3;
+const int FIELD_ID_METRIC_VALUE_VALUE = 4;
+
+const int FIELD_ID_PACKAGE_INFO = 3;
+
 namespace {
-void getProtoData(const int64_t& rule_id, const ConfigKey& configKey, vector<uint8_t>* protoData) {
+void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensionKey& dimensionKey,
+                  int64_t metricValue, const ConfigKey& configKey, vector<uint8_t>* protoData) {
     ProtoOutputStream headerProto;
     headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id);
     uint64_t token =
@@ -53,6 +66,58 @@
     headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_KEY_ID, (long long)configKey.GetId());
     headerProto.end(token);
 
+    token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS);
+
+    // MetricValue trigger_metric = 1;
+    uint64_t metricToken =
+            headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC);
+    // message MetricValue {
+    // optional int64 metric_id = 1;
+    headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_METRIC_ID, (long long)metricId);
+    // optional DimensionsValue dimension_in_what = 2;
+    uint64_t dimToken =
+            headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT);
+    writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), nullptr, &headerProto);
+    headerProto.end(dimToken);
+
+    // optional DimensionsValue dimension_in_condition = 3;
+    dimToken = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION);
+    writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), nullptr, &headerProto);
+    headerProto.end(dimToken);
+
+    // optional int64 value = 4;
+    headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_VALUE, (long long)metricValue);
+
+    // }
+    headerProto.end(metricToken);
+
+    // write relevant uid package info
+    std::set<int32_t> uids;
+
+    for (const auto& dim : dimensionKey.getDimensionKeyInWhat().getValues()) {
+        int uid = getUidIfExists(dim);
+        // any uid <= 2000 are predefined AID_*
+        if (uid > 2000) {
+            uids.insert(uid);
+        }
+    }
+
+    for (const auto& dim : dimensionKey.getDimensionKeyInCondition().getValues()) {
+        int uid = getUidIfExists(dim);
+        if (uid > 2000) {
+            uids.insert(uid);
+        }
+    }
+
+    if (!uids.empty()) {
+        uint64_t token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PACKAGE_INFO);
+        UidMap::getInstance()->writeUidMapSnapshot(getElapsedRealtimeNs(), true, true, uids,
+                                                   nullptr /*string set*/, &headerProto);
+        headerProto.end(token);
+    }
+
+    headerProto.end(token);
+
     protoData->resize(headerProto.size());
     size_t pos = 0;
     auto iter = headerProto.data();
@@ -65,7 +130,8 @@
 }
 }  // namespace
 
-bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id,
+bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId,
+                            const MetricDimensionKey& dimensionKey, int64_t metricValue,
                             const ConfigKey& configKey) {
     if (config.section_size() == 0) {
         VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id,
@@ -76,7 +142,7 @@
     IncidentReportArgs incidentReport;
 
     vector<uint8_t> protoData;
-    getProtoData(rule_id, configKey, &protoData);
+    getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, &protoData);
     incidentReport.addHeader(protoData);
 
     for (int i = 0; i < config.section_size(); i++) {
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h
index 1b83fe2..e78a4d9 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.h
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "HashableDimensionKey.h"
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alert, IncidentdDetails
 
@@ -26,7 +27,8 @@
 /**
  * Calls incidentd to trigger an incident report and put in dropbox for uploading.
  */
-bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id,
+bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId,
+                            const MetricDimensionKey& dimensionKey, int64_t metricValue,
                             const ConfigKey& configKey);
 
 }  // 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/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 960fbda..c10703c 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -90,7 +90,8 @@
                                const std::shared_ptr<DimToValMap>& bucket,
                                const int64_t& eventTimestamp) {
     for (const auto& kv : *bucket) {
-        tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, kv.first, kv.second);
+        tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, 0 /*metric_id*/, kv.first,
+                                        kv.second);
     }
 }
 
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/Activity.java b/core/java/android/app/Activity.java
index 5f778da..e55c964 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -71,6 +71,7 @@
 import android.os.Looper;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.StrictMode;
@@ -2297,7 +2298,7 @@
     public final void requestShowKeyboardShortcuts() {
         Intent intent = new Intent(Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS);
         intent.setPackage(KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME);
-        sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+        sendBroadcastAsUser(intent, Process.myUserHandle());
     }
 
     /**
@@ -2306,7 +2307,7 @@
     public final void dismissKeyboardShortcutsHelper() {
         Intent intent = new Intent(Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS);
         intent.setPackage(KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME);
-        sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+        sendBroadcastAsUser(intent, Process.myUserHandle());
     }
 
     @Override
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/Service.java b/core/java/android/app/Service.java
index 87bf5ed..f116e13 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.pm.ServiceInfo.ForegroundServiceType;
 import android.content.res.Configuration;
 import android.os.Build;
 import android.os.IBinder;
@@ -735,7 +736,7 @@
    * @see {@link android.content.pm.ServiceInfo} for the set of FOREGROUND_SERVICE_TYPE flags.
    */
     public final void startForeground(int id, @NonNull Notification notification,
-            int foregroundServiceType) {
+            @ForegroundServiceType int foregroundServiceType) {
         try {
             mActivityManager.setServiceForeground(
                     new ComponentName(this, mClassName), mToken, id,
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..c12a92f 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
@@ -1139,7 +1129,11 @@
                     IBinder b = ServiceManager
                             .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
                     IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b);
-                    return new ContentCaptureManager(outerContext, service);
+                    if (service != null) {
+                        // When feature is disabled, we return a null manager to apps so the
+                        // performance impact is practically zero
+                        return new ContentCaptureManager(outerContext, service);
+                    }
                 }
                 return null;
             }});
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index a554882..46316e1 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -229,7 +229,11 @@
      * <strong>Note:</strong> On API 22 and below, changes to the night mode
      * are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car}
      * or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a
-     * device. Starting in API 23, changes to night mode are always effective.
+     * device. On API 23 through API 28, changes to night mode are always effective.
+     * <p>
+     * Starting in API 29, when the device is in car mode and this method is called, night mode
+     * will change, but the new setting is not persisted and the previously persisted setting
+     * will be restored when the device exits car mode.
      * <p>
      * Changes to night mode take effect globally and will result in a configuration change
      * (and potentially an Activity lifecycle event) being applied to all running apps.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 2f70c9d..806536b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2125,7 +2125,7 @@
      * Callback used in {@link #installSystemUpdate} to indicate that there was an error while
      * trying to install an update.
      */
-    public abstract static class InstallUpdateCallback {
+    public abstract static class InstallSystemUpdateCallback {
         /** Represents an unknown error while trying to install an update. */
         public static final int UPDATE_ERROR_UNKNOWN = 1;
 
@@ -2144,7 +2144,12 @@
         /** Represents the battery being too low to apply an update. */
         public static final int UPDATE_ERROR_BATTERY_LOW = 5;
 
-        /** Method invoked when there was an error while installing an update. */
+        /**
+         * Method invoked when there was an error while installing an update.
+         *
+         * <p>The given error message is not intended to be user-facing. It is intended to be
+         * reported back to the IT admin to be read.
+         */
         public void onInstallUpdateError(
                 @InstallUpdateCallbackErrorConstants int errorCode, String errorMessage) {
         }
@@ -2154,11 +2159,11 @@
      * @hide
      */
     @IntDef(prefix = { "UPDATE_ERROR_" }, value = {
-            InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
-            InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION,
-            InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
-            InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND,
-            InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW
+            InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN,
+            InstallSystemUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION,
+            InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+            InstallSystemUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND,
+            InstallSystemUpdateCallback.UPDATE_ERROR_BATTERY_LOW
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface InstallUpdateCallbackErrorConstants {}
@@ -10431,9 +10436,9 @@
      * doesn't necessarily mean that the update has been applied successfully. The caller should
      * additionally check the system version with {@link android.os.Build#FINGERPRINT} or {@link
      * android.os.Build.VERSION}. If an error occurs during processing the OTA before the reboot,
-     * the caller will be notified by {@link InstallUpdateCallback}. If device does not have
+     * the caller will be notified by {@link InstallSystemUpdateCallback}. If device does not have
      * sufficient battery level, the installation will fail with error {@link
-     * InstallUpdateCallback#UPDATE_ERROR_BATTERY_LOW}.
+     * InstallSystemUpdateCallback#UPDATE_ERROR_BATTERY_LOW}.
      *
      * @param admin The {@link DeviceAdminReceiver} that this request is associated with.
      * @param updateFilePath An Uri of the file that contains the update. The file should be
@@ -10445,7 +10450,7 @@
     public void installSystemUpdate(
             @NonNull ComponentName admin, @NonNull Uri updateFilePath,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull InstallUpdateCallback callback) {
+            @NonNull InstallSystemUpdateCallback callback) {
         throwIfParentInstance("installUpdate");
         if (mService == null) {
             return;
@@ -10465,19 +10470,20 @@
         } catch (FileNotFoundException e) {
             Log.w(TAG, e);
             executeCallback(
-                    InstallUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND, Log.getStackTraceString(e),
+                    InstallSystemUpdateCallback.UPDATE_ERROR_FILE_NOT_FOUND,
+                    Log.getStackTraceString(e),
                     executor, callback);
         } catch (IOException e) {
             Log.w(TAG, e);
             executeCallback(
-                    InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e),
+                    InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e),
                     executor, callback);
         }
     }
 
     private void executeCallback(int errorCode, String errorMessage,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull InstallUpdateCallback callback) {
+            @NonNull InstallSystemUpdateCallback callback) {
         executor.execute(() -> callback.onInstallUpdateError(errorCode, errorMessage));
     }
 
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 94a2a3e..97efa01 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -465,8 +465,6 @@
                 mActivities.put(instanceId, eventType);
                 break;
             case ACTIVITY_STOPPED:
-                mActivities.put(instanceId, eventType);
-                break;
             case ACTIVITY_DESTROYED:
                 // remove activity from the map.
                 mActivities.delete(instanceId);
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/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 36ffb0e..14e7725 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -295,8 +295,6 @@
     void restoreDefaultApps(in byte[] backup, int userId);
     byte[] getIntentFilterVerificationBackup(int userId);
     void restoreIntentFilterVerification(in byte[] backup, int userId);
-    byte[] getPermissionGrantBackup(int userId);
-    void restorePermissionGrants(in byte[] backup, int userId);
 
     /**
      * Report the set of 'Home' activity candidates, plus (if any) which of them
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index eaf6c5a..0041921 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2943,6 +2943,7 @@
     * @hide
     */
     @SystemApi
+    @TestApi
     public static final int FLAG_PERMISSION_USER_SET = 1 << 0;
 
     /**
@@ -2953,6 +2954,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int FLAG_PERMISSION_USER_FIXED =  1 << 1;
 
     /**
@@ -2976,6 +2978,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE =  1 << 3;
 
     /**
@@ -3005,6 +3008,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int FLAG_PERMISSION_REVIEW_REQUIRED =  1 << 6;
 
     /**
@@ -3014,6 +3018,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED =  1 << 7;
 
     /**
@@ -3795,6 +3800,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
             android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
@@ -3815,6 +3821,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
             android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
@@ -5910,24 +5917,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 +5946,6 @@
      * @see #setDistractingPackageRestrictions(String[], int)
      * @hide
      */
-    @SystemApi
     @IntDef(flag = true, prefix = {"RESTRICTION_"}, value = {
             RESTRICTION_NONE,
             RESTRICTION_HIDE_FROM_SUGGESTIONS,
@@ -5957,14 +5963,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/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 60475de..4a2f800 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -151,6 +151,7 @@
      * @hide
      */
     @IntDef(flag = true, prefix = { "FOREGROUND_SERVICE_TYPE_" }, value = {
+            FOREGROUND_SERVICE_TYPE_MANIFEST,
             FOREGROUND_SERVICE_TYPE_NONE,
             FOREGROUND_SERVICE_TYPE_DATA_SYNC,
             FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
@@ -180,10 +181,10 @@
     }
 
     /**
-     * Return the current foreground service type.
-     * @return the current foreground service type.
+     * Return foreground service type specified in the manifest..
+     * @return foreground service type specified in the manifest.
      */
-    public int getForegroundServiceType() {
+    public @ForegroundServiceType int getForegroundServiceType() {
         return mForegroundServiceType;
     }
 
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 5722e7b..ea489c4 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -77,6 +77,14 @@
     }
 
     /**
+     * Get if the query is marked as DISTINCT, as last configured by
+     * {@link #setDistinct(boolean)}.
+     */
+    public boolean getDistinct() {
+        return mDistinct;
+    }
+
+    /**
      * Returns the list of tables being queried
      *
      * @return the list of tables being queried
@@ -167,6 +175,14 @@
     }
 
     /**
+     * Gets the projection map for the query, as last configured by
+     * {@link #setProjectionMap(Map)}.
+     */
+    public Map<String, String> getProjectionMap() {
+        return mProjectionMap;
+    }
+
+    /**
      * Sets a projection greylist of columns that will be allowed through, even
      * when {@link #setStrict(boolean)} is enabled. This provides a way for
      * abusive custom columns like {@code COUNT(*)} to continue working.
@@ -178,6 +194,16 @@
     }
 
     /**
+     * Gets the projection greylist for the query, as last configured by
+     * {@link #setProjectionGreylist(List)}.
+     *
+     * @hide
+     */
+    public List<Pattern> getProjectionGreylist() {
+        return mProjectionGreylist;
+    }
+
+    /**
      * Sets the cursor factory to be used for the query.  You can use
      * one factory for all queries on a database but it is normally
      * easier to specify the factory when doing this query.
@@ -189,6 +215,14 @@
     }
 
     /**
+     * Sets the cursor factory to be used for the query, as last configured by
+     * {@link #setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory)}.
+     */
+    public SQLiteDatabase.CursorFactory getCursorFactory() {
+        return mFactory;
+    }
+
+    /**
      * When set, the selection is verified against malicious arguments.
      * When using this class to create a statement using
      * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)},
@@ -214,6 +248,14 @@
     }
 
     /**
+     * Get if the query is marked as strict, as last configured by
+     * {@link #setStrict(boolean)}.
+     */
+    public boolean getStrict() {
+        return mStrict;
+    }
+
+    /**
      * Build an SQL query string from the given clauses.
      *
      * @param distinct true if you want each row to be unique, false otherwise.
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/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java
index 2593c85..c99ecb32 100644
--- a/core/java/android/os/connectivity/CellularBatteryStats.java
+++ b/core/java/android/os/connectivity/CellularBatteryStats.java
@@ -44,6 +44,7 @@
   private long[] mTimeInRatMs;
   private long[] mTimeInRxSignalStrengthLevelMs;
   private long[] mTxTimeMs;
+  private long mMonitoredRailChargeConsumedMaMs;
 
   public static final Parcelable.Creator<CellularBatteryStats> CREATOR = new
       Parcelable.Creator<CellularBatteryStats>() {
@@ -74,6 +75,7 @@
     out.writeLongArray(mTimeInRatMs);
     out.writeLongArray(mTimeInRxSignalStrengthLevelMs);
     out.writeLongArray(mTxTimeMs);
+    out.writeLong(mMonitoredRailChargeConsumedMaMs);
   }
 
   public void readFromParcel(Parcel in) {
@@ -90,6 +92,7 @@
     in.readLongArray(mTimeInRatMs);
     in.readLongArray(mTimeInRxSignalStrengthLevelMs);
     in.readLongArray(mTxTimeMs);
+    mMonitoredRailChargeConsumedMaMs = in.readLong();
   }
 
   public long getLoggingDurationMs() {
@@ -144,6 +147,10 @@
     return mTxTimeMs;
   }
 
+  public long getMonitoredRailChargeConsumedMaMs() {
+    return mMonitoredRailChargeConsumedMaMs;
+  }
+
   public void setLoggingDurationMs(long t) {
     mLoggingDurationMs = t;
     return;
@@ -211,6 +218,11 @@
     return;
   }
 
+  public void setMonitoredRailChargeConsumedMaMs(long monitoredRailEnergyConsumedMaMs) {
+    mMonitoredRailChargeConsumedMaMs = monitoredRailEnergyConsumedMaMs;
+    return;
+  }
+
   public int describeContents() {
     return 0;
   }
@@ -237,6 +249,7 @@
     Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0);
     mTxTimeMs = new long[ModemActivityInfo.TX_POWER_LEVELS];
     Arrays.fill(mTxTimeMs, 0);
+    mMonitoredRailChargeConsumedMaMs = 0;
     return;
   }
 }
\ No newline at end of file
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/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 89967c3..addfe3d 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -624,7 +624,7 @@
          *
          * <p>Needs to be called when canceling this task as it might be hung.
          */
-        void interruptRead() {
+        void interruptWrite() {
             IoUtils.closeQuietly(mLocalPipe);
         }
 
@@ -806,18 +806,19 @@
 
         @Override
         public void run(@NonNull IPermissionController service) {
+            mBackupSender.execute(mBackup);
+
             ParcelFileDescriptor remotePipe = mBackupSender.getRemotePipe();
             try {
                 service.restoreRuntimePermissionBackup(mUser, remotePipe);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error sending runtime permission backup", e);
                 mBackupSender.cancel(false);
+                mBackupSender.interruptWrite();
             } finally {
                 // Remote pipe end is duped by binder call. Local copy is not needed anymore
                 IoUtils.closeQuietly(remotePipe);
             }
-
-            mBackupSender.execute(mBackup);
         }
     }
 
diff --git a/core/java/android/permission/PermissionManagerInternal.java b/core/java/android/permission/PermissionManagerInternal.java
new file mode 100644
index 0000000..92dbab3
--- /dev/null
+++ b/core/java/android/permission/PermissionManagerInternal.java
@@ -0,0 +1,62 @@
+/*
+ * 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.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.UserHandle;
+
+/**
+ * Internal interfaces to be used by other components within the system server.
+ *
+ * <p>Only for use within the system server.
+ *
+ * @hide
+ */
+public abstract class PermissionManagerInternal {
+    /**
+     * Get the state of the runtime permissions as xml file.
+     *
+     * @param user The user the data should be extracted for
+     *
+     * @return The state as a xml file
+     */
+    public abstract @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user);
+
+    /**
+     * Restore a permission state previously backed up via {@link #backupRuntimePermissions}.
+     *
+     * <p>If not all state can be restored, the un-restoreable state will be delayed and can be
+     * re-tried via {@link #restoreDelayedRuntimePermissions}.
+     *
+     * @param backup The state as an xml file
+     * @param user The user the data should be restored for
+     */
+    public abstract void restoreRuntimePermissions(@NonNull byte[] backup,
+            @NonNull UserHandle user);
+
+    /**
+     * Try to apply permission backup of a package that was previously not applied.
+     *
+     * @param packageName The package that is newly installed
+     * @param user The user the package is installed for
+     *
+     * @see #restoreRuntimePermissions
+     */
+    public abstract void restoreDelayedRuntimePermissions(@NonNull String packageName,
+            @NonNull UserHandle user);
+}
diff --git a/core/java/android/provider/BaseColumns.java b/core/java/android/provider/BaseColumns.java
index f594c19..00c9e72 100644
--- a/core/java/android/provider/BaseColumns.java
+++ b/core/java/android/provider/BaseColumns.java
@@ -16,17 +16,18 @@
 
 package android.provider;
 
-public interface BaseColumns
-{
+import android.database.Cursor;
+
+public interface BaseColumns {
     /**
      * The unique ID for a row.
-     * <P>Type: INTEGER (long)</P>
      */
+    @Column(Cursor.FIELD_TYPE_INTEGER)
     public static final String _ID = "_id";
 
     /**
      * The count of rows in a directory.
-     * <P>Type: INTEGER</P>
      */
+    // @Column(Cursor.FIELD_TYPE_INTEGER)
     public static final String _COUNT = "_count";
 }
diff --git a/core/java/android/provider/Column.java b/core/java/android/provider/Column.java
new file mode 100644
index 0000000..1364fb8
--- /dev/null
+++ b/core/java/android/provider/Column.java
@@ -0,0 +1,50 @@
+/*
+ * 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.provider;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a field is a {@link ContentProvider} column. It can be used as a
+ * key for {@link ContentValues} when inserting or updating data, or as a
+ * projection when querying.
+ *
+ * @hide
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({FIELD})
+public @interface Column {
+    /**
+     * The {@link Cursor#getType(int)} of the data stored in this column.
+     */
+    int value();
+
+    /**
+     * This column is read-only and cannot be defined during insert or updates.
+     */
+    boolean readOnly() default false;
+}
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/MediaStore.java b/core/java/android/provider/MediaStore.java
index 0b38420..1b10bef 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -857,8 +857,6 @@
          * this path. Instead of trying to open this path directly, apps should
          * use {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
          * access.
-         * <p>
-         * Type: TEXT
          *
          * @deprecated Apps may not have filesystem permissions to directly
          *             access this path. Instead of trying to open this path
@@ -869,6 +867,7 @@
          *             {@link android.os.Build.VERSION_CODES#Q} or higher.
          */
         @Deprecated
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String DATA = "_data";
 
         /**
@@ -883,44 +882,44 @@
          * If you require the hash of a specific item, you can call
          * {@link ContentResolver#canonicalize(Uri)}, which will block until the
          * hash is calculated.
-         * <p>
-         * Type: BLOB
+         *
          * @removed
          */
         @Deprecated
+        @Column(value = Cursor.FIELD_TYPE_BLOB, readOnly = true)
         public static final String HASH = "_hash";
 
         /**
          * The size of the file in bytes
-         * <P>Type: INTEGER (long)</P>
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String SIZE = "_size";
 
         /**
          * The display name of the file
-         * <P>Type: TEXT</P>
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String DISPLAY_NAME = "_display_name";
 
         /**
          * The title of the content
-         * <P>Type: TEXT</P>
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String TITLE = "title";
 
         /**
          * The time the file was added to the media provider
          * Units are seconds since 1970.
-         * <P>Type: INTEGER (long)</P>
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String DATE_ADDED = "date_added";
 
         /**
          * The time the file was last modified
          * Units are seconds since 1970.
          * NOTE: This is for internal use by the media scanner.  Do not modify this field.
-         * <P>Type: INTEGER (long)</P>
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String DATE_MODIFIED = "date_modified";
 
         /**
@@ -938,9 +937,8 @@
          * {@code format} of {@code audio/ogg} would be ignored.
          * <p>
          * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String MIME_TYPE = "mime_type";
 
         /**
@@ -948,35 +946,34 @@
          * Used to pass the new file's object handle through the media scanner
          * from MTP to the media provider
          * For internal use only by MTP, media scanner and media provider.
-         * <P>Type: INTEGER</P>
          * @hide
          */
+        @Deprecated
+        // @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String MEDIA_SCANNER_NEW_OBJECT_ID = "media_scanner_new_object_id";
 
         /**
          * Non-zero if the media file is drm-protected
-         * <P>Type: INTEGER (boolean)</P>
          * @hide
          */
         @UnsupportedAppUsage
+        @Deprecated
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_DRM = "is_drm";
 
         /**
          * Flag indicating if a media item is pending, and still being inserted
          * by its owner.
-         * <p>
-         * Type: BOOLEAN
          *
          * @see MediaColumns#IS_PENDING
          * @see MediaStore#setIncludePending(Uri)
          * @see MediaStore#createPending(Context, PendingParams)
          */
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_PENDING = "is_pending";
 
         /**
          * Flag indicating if a media item is trashed.
-         * <p>
-         * Type: BOOLEAN
          *
          * @see MediaColumns#IS_TRASHED
          * @see MediaStore#setIncludeTrashed(Uri)
@@ -985,57 +982,54 @@
          * @removed
          */
         @Deprecated
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_TRASHED = "is_trashed";
 
         /**
          * The time the file should be considered expired. Units are seconds
          * since 1970. Typically only meaningful in the context of
          * {@link #IS_PENDING} or {@link #IS_TRASHED}.
-         * <p>
-         * Type: INTEGER
          * @removed
          */
         @Deprecated
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String DATE_EXPIRES = "date_expires";
 
         /**
          * The width of the image/video in pixels.
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String WIDTH = "width";
 
         /**
          * The height of the image/video in pixels.
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String HEIGHT = "height";
 
         /**
          * Package name that contributed this media. The value may be
          * {@code NULL} if ownership cannot be reliably determined.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String OWNER_PACKAGE_NAME = "owner_package_name";
 
         /**
          * The primary directory name this media exists under. The value may be
          * {@code NULL} if the media doesn't have a primary directory name.
-         * <p>
-         * Type: TEXT
          *
          * @see PendingParams#setPrimaryDirectory(String)
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String PRIMARY_DIRECTORY = "primary_directory";
 
         /**
          * The secondary directory name this media exists under. The value may
          * be {@code NULL} if the media doesn't have a secondary directory name.
-         * <p>
-         * Type: TEXT
          *
          * @see PendingParams#setSecondaryDirectory(String)
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String SECONDARY_DIRECTORY = "secondary_directory";
 
         /**
@@ -1046,11 +1040,8 @@
          * <p>
          * Each "document ID" is created once for each new resource. Different
          * renditions of that resource are expected to have different IDs.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String DOCUMENT_ID = "document_id";
 
         /**
@@ -1061,11 +1052,8 @@
          * <p>
          * This "instance ID" changes with each save operation of a specific
          * "document ID".
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String INSTANCE_ID = "instance_id";
 
         /**
@@ -1077,11 +1065,8 @@
          * For example, when you save a PSD document as a JPEG, then convert the
          * JPEG to GIF format, the "original document ID" of both the JPEG and
          * GIF files is the "document ID" of the original PSD file.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String ORIGINAL_DOCUMENT_ID = "original_document_id";
     }
 
@@ -1172,43 +1157,46 @@
         public interface FileColumns extends MediaColumns {
             /**
              * The MTP storage ID of the file
-             * <P>Type: INTEGER</P>
              * @hide
              */
             @UnsupportedAppUsage
+            @Deprecated
+            // @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String STORAGE_ID = "storage_id";
 
             /**
              * The MTP format code of the file
-             * <P>Type: INTEGER</P>
              * @hide
              */
             @UnsupportedAppUsage
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String FORMAT = "format";
 
             /**
              * The index of the parent directory of the file
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String PARENT = "parent";
 
             /**
              * The MIME type of the file
              * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String MIME_TYPE = "mime_type";
 
             /**
              * The title of the content
              * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE = "title";
 
             /**
              * The media type (audio, video, image or playlist)
              * of the file, or 0 for not a media file
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String MEDIA_TYPE = "media_type";
 
             /**
@@ -1241,6 +1229,7 @@
              * Column indicating if the file is part of Downloads collection.
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_DOWNLOAD = "is_download";
         }
     }
@@ -1260,23 +1249,20 @@
     public interface DownloadColumns extends MediaColumns {
         /**
          * Uri indicating where the file has been downloaded from.
-         * <p>
-         * Type: TEXT
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         String DOWNLOAD_URI = "download_uri";
 
         /**
          * Uri indicating HTTP referer of {@link #DOWNLOAD_URI}.
-         * <p>
-         * Type: TEXT
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         String REFERER_URI = "referer_uri";
 
         /**
          * The description of the download.
-         * <p>
-         * Type: Text
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         String DESCRIPTION = "description";
     }
 
@@ -1442,29 +1428,28 @@
         public interface ImageColumns extends MediaColumns {
             /**
              * The description of the image
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String DESCRIPTION = "description";
 
             /**
              * The picasa id of the image
-             * <P>Type: TEXT</P>
              *
              * @deprecated this value was only relevant for images hosted on
              *             Picasa, which are no longer supported.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String PICASA_ID = "picasa_id";
 
             /**
              * Whether the video should be published as public or private
-             * <P>Type: INTEGER</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String IS_PRIVATE = "isprivate";
 
             /**
              * The latitude where the image was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -1472,11 +1457,11 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LATITUDE = "latitude";
 
             /**
              * The longitude where the image was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -1484,40 +1469,40 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LONGITUDE = "longitude";
 
             /**
              * The date & time that the image was taken in units
              * of milliseconds since jan 1, 1970.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_TAKEN = "datetaken";
 
             /**
              * The orientation for the image expressed as degrees.
              * Only degrees 0, 90, 180, 270 will work.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ORIENTATION = "orientation";
 
             /**
              * The mini thumb id.
-             * <P>Type: INTEGER</P>
              *
              * @deprecated all thumbnails should be obtained via
              *             {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
              *             value is no longer supported.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
              * The primary bucket ID of this media item. This can be useful to
              * present the user a first-level clustering of related media items.
              * This is a read-only column that is automatically computed.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String BUCKET_ID = "bucket_id";
 
             /**
@@ -1525,9 +1510,8 @@
              * useful to present the user a first-level clustering of related
              * media items. This is a read-only column that is automatically
              * computed.
-             * <p>
-             * Type: TEXT
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
 
             /**
@@ -1541,9 +1525,8 @@
              * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG}
              * will have the same {@link #GROUP_ID} because the first portion of
              * their filenames is identical.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String GROUP_ID = "group_id";
         }
 
@@ -1848,8 +1831,6 @@
              * apps should use
              * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
              * access.
-             * <p>
-             * Type: TEXT
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -1860,39 +1841,45 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String DATA = "_data";
 
             /**
              * The original image for the thumbnal
-             * <P>Type: INTEGER (ID from Images table)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String IMAGE_ID = "image_id";
 
             /**
              * The kind of the thumbnail
-             * <P>Type: INTEGER (One of the values below)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String KIND = "kind";
 
             public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
             public static final int FULL_SCREEN_KIND = ThumbnailConstants.FULL_SCREEN_KIND;
             public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
+
             /**
              * The blob raw data of thumbnail
-             * <P>Type: DATA STREAM</P>
+             *
+             * @deprecated this column never existed internally, and could never
+             *             have returned valid data.
              */
+            @Deprecated
+            @Column(Cursor.FIELD_TYPE_BLOB)
             public static final String THUMB_DATA = "thumb_data";
 
             /**
              * The width of the thumbnal
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String WIDTH = "width";
 
             /**
              * The height of the thumbnail
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String HEIGHT = "height";
         }
     }
@@ -1909,79 +1896,80 @@
             /**
              * A non human readable key calculated from the TITLE, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE_KEY = "title_key";
 
             /**
              * The duration of the audio file, in ms
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DURATION = "duration";
 
             /**
              * The position, in ms, playback was at when playback for this file
              * was last stopped.
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String BOOKMARK = "bookmark";
 
             /**
              * The id of the artist who created the audio file, if any
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ARTIST_ID = "artist_id";
 
             /**
              * The artist who created the audio file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * The artist credited for the album that contains the audio file
-             * <P>Type: TEXT</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_ARTIST = "album_artist";
 
             /**
              * Whether the song is part of a compilation
-             * <P>Type: TEXT</P>
              * @hide
              */
+            @Deprecated
+            // @Column(Cursor.FIELD_TYPE_STRING)
             public static final String COMPILATION = "compilation";
 
             /**
              * A non human readable key calculated from the ARTIST, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST_KEY = "artist_key";
 
             /**
              * The composer of the audio file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String COMPOSER = "composer";
 
             /**
              * The id of the album the audio file is from, if any
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ALBUM_ID = "album_id";
 
             /**
              * The album the audio file is from, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM = "album";
 
             /**
              * A non human readable key calculated from the ALBUM, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_KEY = "album_key";
 
             /**
@@ -1990,63 +1978,63 @@
              * disc number. For multi-disc sets, this number will
              * be 1xxx for tracks on the first disc, 2xxx for tracks
              * on the second disc, etc.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String TRACK = "track";
 
             /**
              * The year the audio file was recorded, if any
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String YEAR = "year";
 
             /**
              * Non-zero if the audio file is music
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_MUSIC = "is_music";
 
             /**
              * Non-zero if the audio file is a podcast
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_PODCAST = "is_podcast";
 
             /**
              * Non-zero if the audio file may be a ringtone
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_RINGTONE = "is_ringtone";
 
             /**
              * Non-zero if the audio file may be an alarm
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_ALARM = "is_alarm";
 
             /**
              * Non-zero if the audio file may be a notification sound
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_NOTIFICATION = "is_notification";
 
             /**
              * Non-zero if the audio file is an audiobook
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_AUDIOBOOK = "is_audiobook";
 
             /**
              * The genre of the audio file, if any
-             * <P>Type: TEXT</P>
              * Does not exist in the database - only used by the media scanner for inserts.
              * @hide
              */
+            @Deprecated
+            // @Column(Cursor.FIELD_TYPE_STRING)
             public static final String GENRE = "genre";
 
             /**
              * The resource URI of a localized title, if any
-             * <P>Type: TEXT</P>
              * Conforms to this pattern:
              *   Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE}
              *   Authority: Package Name of ringtone title provider
@@ -2054,6 +2042,7 @@
              *   Second Path Segment: Resource ID of title
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE_RESOURCE_URI = "title_resource_uri";
         }
 
@@ -2142,6 +2131,7 @@
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path.
              */
+            @Deprecated
             public static @Nullable Uri getContentUriForPath(@NonNull String path) {
                 return getContentUri(getVolumeName(new File(path)));
             }
@@ -2202,8 +2192,8 @@
         public interface GenresColumns {
             /**
              * The name of the genre
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String NAME = "name";
         }
 
@@ -2287,14 +2277,14 @@
 
                 /**
                  * The ID of the audio file
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String AUDIO_ID = "audio_id";
 
                 /**
                  * The ID of the genre
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String GENRE_ID = "genre_id";
             }
         }
@@ -2305,8 +2295,8 @@
         public interface PlaylistsColumns {
             /**
              * The name of the playlist
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String NAME = "name";
 
             /**
@@ -2317,8 +2307,6 @@
              * apps should use
              * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
              * access.
-             * <p>
-             * Type: TEXT
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -2329,21 +2317,22 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String DATA = "_data";
 
             /**
              * The time the file was added to the media provider
              * Units are seconds since 1970.
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_ADDED = "date_added";
 
             /**
              * The time the file was last modified
              * Units are seconds since 1970.
              * NOTE: This is for internal use by the media scanner.  Do not modify this field.
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_MODIFIED = "date_modified";
         }
 
@@ -2426,6 +2415,7 @@
                 /**
                  * The ID within the playlist.
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String _ID = "_id";
 
                 /**
@@ -2436,20 +2426,20 @@
 
                 /**
                  * The ID of the audio file
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String AUDIO_ID = "audio_id";
 
                 /**
                  * The ID of the playlist
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String PLAYLIST_ID = "playlist_id";
 
                 /**
                  * The order of the songs in the playlist
-                 * <P>Type: INTEGER (long)></P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String PLAY_ORDER = "play_order";
 
                 /**
@@ -2465,25 +2455,27 @@
         public interface ArtistColumns {
             /**
              * The artist who created the audio file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * A non human readable key calculated from the ARTIST, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST_KEY = "artist_key";
 
             /**
              * The number of albums in the database for this artist
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_ALBUMS = "number_of_albums";
 
             /**
              * The number of albums in the database for this artist
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_TRACKS = "number_of_tracks";
         }
 
@@ -2551,34 +2543,34 @@
 
             /**
              * The id for the album
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ALBUM_ID = "album_id";
 
             /**
              * The album on which the audio file appears, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM = "album";
 
             /**
              * The artist whose songs appear on this album
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * The number of songs on this album
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_SONGS = "numsongs";
 
             /**
              * This column is available when getting album info via artist,
              * and indicates the number of songs on the album by the given
              * artist.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist";
 
             /**
@@ -2586,8 +2578,8 @@
              * on this album were released. This will often
              * be the same as {@link #LAST_YEAR}, but for compilation albums
              * they might differ.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String FIRST_YEAR = "minyear";
 
             /**
@@ -2595,20 +2587,19 @@
              * on this album were released. This will often
              * be the same as {@link #FIRST_YEAR}, but for compilation albums
              * they might differ.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String LAST_YEAR = "maxyear";
 
             /**
              * A non human readable key calculated from the ALBUM, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_KEY = "album_key";
 
             /**
              * Cached album art.
-             * <P>Type: TEXT</P>
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -2619,6 +2610,7 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String ALBUM_ART = "album_art";
         }
 
@@ -2676,6 +2668,43 @@
             // Not instantiable.
             private Radio() { }
         }
+
+        /**
+         * This class provides utility methods to obtain thumbnails for various
+         * {@link Audio} items.
+         *
+         * @deprecated Callers should migrate to using
+         *             {@link ContentResolver#loadThumbnail}, since it offers
+         *             richer control over requested thumbnail sizes and
+         *             cancellation behavior.
+         * @hide
+         */
+        @Deprecated
+        public static class Thumbnails implements BaseColumns {
+            /**
+             * Path to the thumbnail file on disk.
+             * <p>
+             * Note that apps may not have filesystem permissions to directly
+             * access this path. Instead of trying to open this path directly,
+             * apps should use
+             * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+             * access.
+             *
+             * @deprecated Apps may not have filesystem permissions to directly
+             *             access this path. Instead of trying to open this path
+             *             directly, apps should use
+             *             {@link ContentResolver#loadThumbnail}
+             *             to gain access. This value will always be
+             *             {@code NULL} for apps targeting
+             *             {@link android.os.Build.VERSION_CODES#Q} or higher.
+             */
+            @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
+            public static final String DATA = "_data";
+
+            @Column(Cursor.FIELD_TYPE_INTEGER)
+            public static final String ALBUM_ID = "album_id";
+        }
     }
 
     public static final class Video {
@@ -2693,61 +2722,60 @@
 
             /**
              * The duration of the video file, in ms
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DURATION = "duration";
 
             /**
              * The artist who created the video file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * The album the video file is from, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM = "album";
 
             /**
              * The resolution of the video file, formatted as "XxY"
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String RESOLUTION = "resolution";
 
             /**
              * The description of the video recording
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String DESCRIPTION = "description";
 
             /**
              * Whether the video should be published as public or private
-             * <P>Type: INTEGER</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String IS_PRIVATE = "isprivate";
 
             /**
              * The user-added tags associated with a video
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String TAGS = "tags";
 
             /**
              * The YouTube category of the video
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String CATEGORY = "category";
 
             /**
              * The language of the video
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String LANGUAGE = "language";
 
             /**
              * The latitude where the video was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -2755,11 +2783,11 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LATITUDE = "latitude";
 
             /**
              * The longitude where the video was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -2767,33 +2795,33 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LONGITUDE = "longitude";
 
             /**
              * The date & time that the video was taken in units
              * of milliseconds since jan 1, 1970.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_TAKEN = "datetaken";
 
             /**
              * The mini thumb id.
-             * <P>Type: INTEGER</P>
              *
              * @deprecated all thumbnails should be obtained via
              *             {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
              *             value is no longer supported.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
              * The primary bucket ID of this media item. This can be useful to
              * present the user a first-level clustering of related media items.
              * This is a read-only column that is automatically computed.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String BUCKET_ID = "bucket_id";
 
             /**
@@ -2801,9 +2829,8 @@
              * useful to present the user a first-level clustering of related
              * media items. This is a read-only column that is automatically
              * computed.
-             * <p>
-             * Type: TEXT
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
 
             /**
@@ -2817,9 +2844,8 @@
              * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG}
              * will have the same {@link #GROUP_ID} because the first portion of
              * their filenames is identical.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String GROUP_ID = "group_id";
 
             /**
@@ -2827,29 +2853,29 @@
              * video should start playing at the next time it is opened. If the value is null or
              * out of the range 0..DURATION-1 then the video should start playing from the
              * beginning.
-             * <P>Type: INTEGER</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String BOOKMARK = "bookmark";
 
             /**
              * The standard of color aspects
-             * <P>Type: INTEGER</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String COLOR_STANDARD = "color_standard";
 
             /**
              * The transfer of color aspects
-             * <P>Type: INTEGER</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String COLOR_TRANSFER = "color_transfer";
 
             /**
              * The range of color aspects
-             * <P>Type: INTEGER</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String COLOR_RANGE = "color_range";
         }
 
@@ -3016,8 +3042,6 @@
 
             /**
              * Path to the thumbnail file on disk.
-             * <p>
-             * Type: TEXT
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -3028,18 +3052,19 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String DATA = "_data";
 
             /**
              * The original image for the thumbnal
-             * <P>Type: INTEGER (ID from Video table)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String VIDEO_ID = "video_id";
 
             /**
              * The kind of the thumbnail
-             * <P>Type: INTEGER (One of the values below)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String KIND = "kind";
 
             public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
@@ -3048,14 +3073,14 @@
 
             /**
              * The width of the thumbnal
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String WIDTH = "width";
 
             /**
              * The height of the thumbnail
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String HEIGHT = "height";
         }
     }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2b638f6..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
@@ -14368,6 +14392,7 @@
          * <pre>
          *     num_buckets          (int)
          *     collected_uids       (string)
+         *     minimum_total_cpu_usage_millis (int)
          * </pre>
          *
          * @hide
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/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
index ca6676d..bbcf7a9 100644
--- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
@@ -28,7 +28,8 @@
 /**
  * Not needed anymore...
  *
- * @deprecated
+ * @deprecated ContentCaptureService should use
+ * {@code #onContentCaptureEvent(ContentCaptureSessionId, ContentCaptureEvent)} instead.
  *
  * @hide
  */
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/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index ffb524d..a46d047 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -76,6 +76,11 @@
  * filter with the appropriate action, the {@link #CATEGORY_EUICC_UI} category, and a non-zero
  * priority.
  *
+ * <p>Old implementations of EuiccService may support passing in slot IDs equal to
+ * {@link android.telephony.SubscriptionManager#INVALID_SIM_SLOT_INDEX}, which allows the LPA to
+ * decide which eUICC to target when there are multiple eUICCs. This behavior is not supported in
+ * Android Q or later.
+ *
  * @hide
  */
 @SystemApi
@@ -546,7 +551,7 @@
                         int resultCode = EuiccService.this.onDownloadSubscription(
                                 slotId, subscription, switchAfterDownload, forceDeactivateSim);
                         result = new DownloadSubscriptionResult(resultCode,
-                            0 /* resolvableErrors */, TelephonyManager.INVALID_CARD_ID);
+                            0 /* resolvableErrors */, TelephonyManager.UNSUPPORTED_CARD_ID);
                     }
                     try {
                         callback.onComplete(result);
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 93189b3..2961426 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -15,6 +15,7 @@
  */
 package android.service.notification;
 
+import android.annotation.SystemApi;
 import android.app.Notification;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -36,13 +37,13 @@
      * See {@link android.app.Notification.Builder#addPerson(String)}.
      * @hide
      */
+    @SystemApi
     public static final String KEY_PEOPLE = "key_people";
     /**
      * Parcelable {@code ArrayList} of {@link SnoozeCriterion}. These criteria may be visible to
      * users. If a user chooses to snooze a notification until one of these criterion, the
      * assistant will be notified via
      * {@link NotificationAssistantService#onNotificationSnoozedUntilContext}.
-     * @hide
      */
     public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     /**
@@ -111,7 +112,11 @@
         mUser = user;
     }
 
-    private Adjustment(Parcel in) {
+    /**
+     * @hide
+     */
+    @SystemApi
+    protected Adjustment(Parcel in) {
         if (in.readInt() == 1) {
             mPackage = in.readString();
         } else {
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index e93b158..a697248 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.admin.DevicePolicyManager;
@@ -103,7 +104,6 @@
      *
      * @param sbn the notification to snooze
      * @param snoozeCriterionId the {@link SnoozeCriterion#getId()} representing a device context.
-     * @hide
      */
     abstract public void onNotificationSnoozedUntilContext(StatusBarNotification sbn,
             String snoozeCriterionId);
@@ -111,12 +111,13 @@
     /**
      * A notification was posted by an app. Called before post.
      *
+     * <p>Note: this method is only called if you don't override
+     * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)}.</p>
+     *
      * @param sbn the new notification
      * @return an adjustment or null to take no action, within 100ms.
      */
-    public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
-        return null;
-    }
+    abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn);
 
     /**
      * A notification was posted by an app. Called before post.
@@ -240,12 +241,11 @@
     /**
      * Inform the notification manager about un-snoozing a specific notification.
      * <p>
-     * This should only be used for notifications snoozed by this listener using
-     * {@link #snoozeNotification(String, String)}. Once un-snoozed, you will get a
+     * This should only be used for notifications snoozed because of a contextual snooze suggestion
+     * you provided via {@link Adjustment#KEY_SNOOZE_CRITERIA}. Once un-snoozed, you will get a
      * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
      * notification.
      * @param key The key of the notification to snooze
-     * @hide
      */
     public final void unsnoozeNotification(String key) {
         if (!isBound()) return;
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
index 814b477..ebfabbf 100644
--- a/core/java/android/service/notification/NotificationStats.java
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -16,6 +16,7 @@
 package android.service.notification;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.app.RemoteInput;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -98,7 +99,11 @@
     public NotificationStats() {
     }
 
-    private NotificationStats(Parcel in) {
+    /**
+     * @hide
+     */
+    @SystemApi
+    protected NotificationStats(Parcel in) {
         mSeen = in.readByte() != 0;
         mExpanded = in.readByte() != 0;
         mDirectReplied = in.readByte() != 0;
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/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java
index c77500a..696e048 100644
--- a/core/java/android/view/ContextThemeWrapper.java
+++ b/core/java/android/view/ContextThemeWrapper.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.Nullable;
 import android.annotation.StyleRes;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -23,6 +24,7 @@
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.os.Build;
 
 /**
  * A context wrapper that allows you to modify or replace the theme of the
@@ -31,7 +33,7 @@
 public class ContextThemeWrapper extends ContextWrapper {
     @UnsupportedAppUsage
     private int mThemeResource;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768723)
     private Resources.Theme mTheme;
     @UnsupportedAppUsage
     private LayoutInflater mInflater;
@@ -146,6 +148,15 @@
         }
     }
 
+    /**
+     * Set the configure the current theme. If null is provided then the default Theme is returned
+     * on the next call to {@link #getTheme()}
+     * @param theme Theme to consume in the wrapper, a value of null resets the theme to the default
+     */
+    public void setTheme(@Nullable Resources.Theme theme) {
+        mTheme = theme;
+    }
+
     /** @hide */
     @Override
     @UnsupportedAppUsage
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 49bae28..cb5100a 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -76,7 +76,7 @@
     private final int mLayerStack;
     private final int mFlags;
     private final int mType;
-    private final String mAddress;
+    private final DisplayAddress mAddress;
     private final int mOwnerUid;
     private final String mOwnerPackageName;
     private final Resources mResources;
@@ -557,7 +557,7 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public String getAddress() {
+    public DisplayAddress getAddress() {
         return mAddress;
     }
 
diff --git a/core/java/android/view/DisplayAddress.java b/core/java/android/view/DisplayAddress.java
new file mode 100644
index 0000000..17ea4c4
--- /dev/null
+++ b/core/java/android/view/DisplayAddress.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** Display identifier that is stable across reboots.
+ *
+ * @hide
+ */
+public abstract class DisplayAddress implements Parcelable {
+    /**
+     * Creates an address for a physical display given its stable ID.
+     *
+     * A physical display ID is stable if the display can be identified using EDID information.
+     *
+     * @param physicalDisplayId A physical display ID.
+     * @return The {@link Physical} address, or {@code null} if the ID is not stable.
+     * @see SurfaceControl#getPhysicalDisplayIds
+     */
+    @Nullable
+    public static Physical fromPhysicalDisplayId(long physicalDisplayId) {
+        final Physical address = new Physical(physicalDisplayId);
+        return address.getModel() == 0 ? null : address;
+    }
+
+    /**
+     * Creates an address for a network display given its MAC address.
+     *
+     * @param macAddress A MAC address in colon notation.
+     * @return The {@link Network} address.
+     */
+    @NonNull
+    public static Network fromMacAddress(String macAddress) {
+        return new Network(macAddress);
+    }
+
+    /**
+     * Address for a physically connected display.
+     *
+     * A {@link Physical} address is represented by a 64-bit identifier combining the port and model
+     * of a display. The port, located in the least significant byte, uniquely identifies a physical
+     * connector on the device for display output like eDP or HDMI. The model, located in the upper
+     * bits, uniquely identifies a display model across manufacturers by encoding EDID information.
+     */
+    public static final class Physical extends DisplayAddress {
+        private static final int PHYSICAL_DISPLAY_ID_MODEL_SHIFT = 8;
+        private static final int PORT_MASK = 0xFF;
+
+        private final long mPhysicalDisplayId;
+
+        /**
+         * Physical port to which the display is connected.
+         */
+        public byte getPort() {
+            return (byte) mPhysicalDisplayId;
+        }
+
+        /**
+         * Model identifier unique across manufacturers.
+         */
+        public long getModel() {
+            return mPhysicalDisplayId >>> PHYSICAL_DISPLAY_ID_MODEL_SHIFT;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            return other instanceof Physical
+                    && mPhysicalDisplayId == ((Physical) other).mPhysicalDisplayId;
+        }
+
+        @Override
+        public String toString() {
+            return new StringBuilder("{")
+                    .append("port=").append(getPort() & PORT_MASK)
+                    .append(", model=0x").append(Long.toHexString(getModel()))
+                    .append("}")
+                    .toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return Long.hashCode(mPhysicalDisplayId);
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeLong(mPhysicalDisplayId);
+        }
+
+        private Physical(long physicalDisplayId) {
+            mPhysicalDisplayId = physicalDisplayId;
+        }
+
+        public static final Parcelable.Creator<Physical> CREATOR =
+                new Parcelable.Creator<Physical>() {
+                    @Override
+                    public Physical createFromParcel(Parcel in) {
+                        return new Physical(in.readLong());
+                    }
+
+                    @Override
+                    public Physical[] newArray(int size) {
+                        return new Physical[size];
+                    }
+                };
+    }
+
+    /**
+     * Address for a network-connected display.
+     */
+    public static final class Network extends DisplayAddress {
+        private final String mMacAddress;
+
+        @Override
+        public boolean equals(Object other) {
+            return other instanceof Network && mMacAddress.equals(((Network) other).mMacAddress);
+        }
+
+        @Override
+        public String toString() {
+            return mMacAddress;
+        }
+
+        @Override
+        public int hashCode() {
+            return mMacAddress.hashCode();
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(mMacAddress);
+        }
+
+        private Network(String macAddress) {
+            mMacAddress = macAddress;
+        }
+
+        public static final Parcelable.Creator<Network> CREATOR =
+                new Parcelable.Creator<Network>() {
+                    @Override
+                    public Network createFromParcel(Parcel in) {
+                        return new Network(in.readString());
+                    }
+
+                    @Override
+                    public Network[] newArray(int size) {
+                        return new Network[size];
+                    }
+                };
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index ad8fee9..3aa779b 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -61,7 +61,7 @@
      * Display address, or null if none.
      * Interpretation varies by display type.
      */
-    public String address;
+    public DisplayAddress address;
 
     /**
      * The human-readable name of the display.
@@ -385,7 +385,7 @@
         layerStack = source.readInt();
         flags = source.readInt();
         type = source.readInt();
-        address = source.readString();
+        address = source.readParcelable(null);
         name = source.readString();
         appWidth = source.readInt();
         appHeight = source.readInt();
@@ -432,7 +432,7 @@
         dest.writeInt(layerStack);
         dest.writeInt(this.flags);
         dest.writeInt(type);
-        dest.writeString(address);
+        dest.writeParcelable(address, flags);
         dest.writeString(name);
         dest.writeInt(appWidth);
         dest.writeInt(appHeight);
diff --git a/core/java/android/view/GhostView.java b/core/java/android/view/GhostView.java
index 74c801b..3286bd6 100644
--- a/core/java/android/view/GhostView.java
+++ b/core/java/android/view/GhostView.java
@@ -20,6 +20,7 @@
 import android.graphics.Matrix;
 import android.graphics.RecordingCanvas;
 import android.graphics.RenderNode;
+import android.os.Build;
 import android.widget.FrameLayout;
 
 import java.util.ArrayList;
@@ -135,12 +136,12 @@
         return ghostView;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public static GhostView addGhost(View view, ViewGroup viewGroup) {
         return addGhost(view, viewGroup, null);
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public static void removeGhost(View view) {
         GhostView ghostView = view.mGhostView;
         if (ghostView != null) {
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index b28ac47..f13cb5a 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -129,7 +129,7 @@
     static final Class<?>[] mConstructorSignature = new Class[] {
             Context.class, AttributeSet.class};
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769490)
     private static final HashMap<String, Constructor<? extends View>> sConstructorMap =
             new HashMap<String, Constructor<? extends View>>();
 
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 83df33e..2b440dc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -24,6 +24,7 @@
 
 import android.animation.AnimatorInflater;
 import android.animation.StateListAnimator;
+import android.annotation.AttrRes;
 import android.annotation.CallSuper;
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
@@ -5108,7 +5109,7 @@
     private SparseIntArray mAttributeSourceResId;
 
     @Nullable
-    private int[] mAttributeResolutionStack;
+    private SparseArray<int[]> mAttributeResolutionStacks;
 
     @StyleRes
     private int mExplicitStyle;
@@ -5963,11 +5964,12 @@
      * <b>Note:</b> this method will only return actual values if the view attribute debugging
      * is enabled in Android developer options.
      *
+     * @param attribute Attribute resource ID for which the resolution stack should be returned.
      * @return ordered list of resource ID that are considered when resolving attribute values for
      * this {@link View}.
      */
     @NonNull
-    public List<Integer> getAttributeResolutionStack() {
+    public List<Integer> getAttributeResolutionStack(@AttrRes int attribute) {
         ArrayList<Integer> stack = new ArrayList<>();
         if (!sDebugViewAttributes) {
             return stack;
@@ -5975,8 +5977,12 @@
         if (mSourceLayoutId != ID_NULL) {
             stack.add(mSourceLayoutId);
         }
-        for (int i = 0; i < mAttributeResolutionStack.length; i++) {
-            stack.add(mAttributeResolutionStack[i]);
+        int[] attributeResolutionStack = mAttributeResolutionStacks.get(attribute);
+        if (attributeResolutionStack == null) {
+            return stack;
+        }
+        for (int i = 0; i < attributeResolutionStack.length; i++) {
+            stack.add(attributeResolutionStack[i]);
         }
         return stack;
     }
@@ -6143,9 +6149,13 @@
             return;
         }
 
-        mAttributeResolutionStack = context.getTheme().getAttributeResolutionStack(
+        int[] attributeResolutionStack = context.getTheme().getAttributeResolutionStack(
                 defStyleAttr, defStyleRes, mExplicitStyle);
 
+        if (mAttributeResolutionStacks == null) {
+            mAttributeResolutionStacks = new SparseArray<>();
+        }
+
         if (mAttributeSourceResId == null) {
             mAttributeSourceResId = new SparseIntArray();
         }
@@ -6154,6 +6164,7 @@
         for (int j = 0; j < indexCount; ++j) {
             final int index = t.getIndex(j);
             mAttributeSourceResId.append(styleable[index], t.getSourceResourceId(index, 0));
+            mAttributeResolutionStacks.append(styleable[index], attributeResolutionStack);
         }
     }
 
@@ -6456,6 +6467,16 @@
         arr.recycle();
     }
 
+    private void initializeScrollBarDrawable() {
+        initScrollCache();
+
+        if (mScrollCache.scrollBar == null) {
+            mScrollCache.scrollBar = new ScrollBarDrawable();
+            mScrollCache.scrollBar.setState(getDrawableState());
+            mScrollCache.scrollBar.setCallback(this);
+        }
+    }
+
     /**
      * <p>
      * Initializes the scrollbars from a given set of styled attributes. This
@@ -6541,6 +6562,106 @@
         resolvePadding();
     }
 
+    /**
+     * Defines the vertical scrollbar thumb drawable
+     * @attr ref android.R.styleable#View_scrollbarThumbVertical
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isVerticalScrollBarEnabled()
+     * @see #setVerticalScrollBarEnabled(boolean)
+     */
+    public void setVerticalScrollbarThumbDrawable(@Nullable Drawable drawable) {
+        initializeScrollBarDrawable();
+        mScrollCache.scrollBar.setVerticalThumbDrawable(drawable);
+    }
+
+    /**
+     * Defines the vertical scrollbar track drawable
+     * @attr ref android.R.styleable#View_scrollbarTrackVertical
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isVerticalScrollBarEnabled()
+     * @see #setVerticalScrollBarEnabled(boolean)
+     */
+    public void setVerticalScrollbarTrackDrawable(@Nullable Drawable drawable) {
+        initializeScrollBarDrawable();
+        mScrollCache.scrollBar.setVerticalTrackDrawable(drawable);
+    }
+
+    /**
+     * Defines the horizontal thumb drawable
+     * @attr ref android.R.styleable#View_scrollbarThumbHorizontal
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isHorizontalScrollBarEnabled()
+     * @see #setHorizontalScrollBarEnabled(boolean)
+     */
+    public void setHorizontalScrollbarThumbDrawable(@Nullable Drawable drawable) {
+        initializeScrollBarDrawable();
+        mScrollCache.scrollBar.setHorizontalThumbDrawable(drawable);
+    }
+
+    /**
+     * Defines the horizontal track drawable
+     * @attr ref android.R.styleable#View_scrollbarTrackHorizontal
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isHorizontalScrollBarEnabled()
+     * @see #setHorizontalScrollBarEnabled(boolean)
+     */
+    public void setHorizontalScrollbarTrackDrawable(@Nullable Drawable drawable) {
+        initializeScrollBarDrawable();
+        mScrollCache.scrollBar.setHorizontalTrackDrawable(drawable);
+    }
+
+    /**
+     * Returns the currently configured Drawable for the thumb of the vertical scroll bar if it
+     * exists, null otherwise.
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isVerticalScrollBarEnabled()
+     * @see #setVerticalScrollBarEnabled(boolean)
+     */
+    public @Nullable Drawable getVerticalScrollbarThumbDrawable() {
+        return mScrollCache != null ? mScrollCache.scrollBar.getVerticalThumbDrawable() : null;
+    }
+
+    /**
+     * Returns the currently configured Drawable for the track of the vertical scroll bar if it
+     * exists, null otherwise.
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isVerticalScrollBarEnabled()
+     * @see #setVerticalScrollBarEnabled(boolean)
+     */
+    public @Nullable Drawable getVerticalScrollbarTrackDrawable() {
+        return mScrollCache != null ? mScrollCache.scrollBar.getVerticalTrackDrawable() : null;
+    }
+
+    /**
+     * Returns the currently configured Drawable for the thumb of the horizontal scroll bar if it
+     * exists, null otherwise.
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isHorizontalScrollBarEnabled()
+     * @see #setHorizontalScrollBarEnabled(boolean)
+     */
+    public @Nullable Drawable getHorizontalScrollbarThumbDrawable() {
+        return mScrollCache != null ? mScrollCache.scrollBar.getHorizontalThumbDrawable() : null;
+    }
+
+    /**
+     * Returns the currently configured Drawable for the track of the horizontal scroll bar if it
+     * exists, null otherwise.
+     *
+     * @see #awakenScrollBars(int)
+     * @see #isHorizontalScrollBarEnabled()
+     * @see #setHorizontalScrollBarEnabled(boolean)
+     */
+    public @Nullable Drawable getHorizontalScrollbarTrackDrawable() {
+        return mScrollCache != null ? mScrollCache.scrollBar.getHorizontalTrackDrawable() : null;
+    }
+
     private void initializeScrollIndicatorsInternal() {
         // Some day maybe we'll break this into top/left/start/etc. and let the
         // client control it. Until then, you can have any scroll indicator you
@@ -9366,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/ViewGroup.java b/core/java/android/view/ViewGroup.java
index aaf1d11..f2474a5 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4301,6 +4301,20 @@
         return i;
     }
 
+    /**
+     * The public version of getChildDrawingOrder().
+     *
+     * Returns the index of the child to draw for this iteration.
+     *
+     * @param i The current iteration.
+     * @return The index of the child to draw this iteration.
+     *
+     * @see #getChildDrawingOrder(int, int)}
+     */
+    public final int getChildDrawingOrder(int i) {
+        return getChildDrawingOrder(getChildCount(), i);
+    }
+
     private boolean hasChildWithZ() {
         for (int i = 0; i < mChildrenCount; i++) {
             if (mChildren[i].getZ() != 0) return true;
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/ContentCaptureHelper.java b/core/java/android/view/contentcapture/ContentCaptureHelper.java
index 508880f..1cf27fc 100644
--- a/core/java/android/view/contentcapture/ContentCaptureHelper.java
+++ b/core/java/android/view/contentcapture/ContentCaptureHelper.java
@@ -15,16 +15,29 @@
  */
 package android.view.contentcapture;
 
+import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL;
+import static android.view.contentcapture.ContentCaptureManager.LOGGING_LEVEL_DEBUG;
+import static android.view.contentcapture.ContentCaptureManager.LOGGING_LEVEL_OFF;
+import static android.view.contentcapture.ContentCaptureManager.LOGGING_LEVEL_VERBOSE;
+
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Build;
+import android.provider.DeviceConfig;
+import android.util.Log;
+import android.view.contentcapture.ContentCaptureManager.LoggingLevel;
 
 /**
- * Helpe class for this package.
+ * Helper class for this package and server's.
+ *
+ * @hide
  */
-final class ContentCaptureHelper {
+public final class ContentCaptureHelper {
 
-    // TODO(b/121044306): define a way to dynamically set them(for example, using settings?)
-    static final boolean VERBOSE = false;
-    static final boolean DEBUG = true; // STOPSHIP if not set to false
+    private static final String TAG = ContentCaptureHelper.class.getSimpleName();
+
+    public static boolean sVerbose = false;
+    public static boolean sDebug = true;
 
     /**
      * Used to log text that could contain PII.
@@ -34,6 +47,61 @@
         return text == null ? null : text.length() + "_chars";
     }
 
+    /**
+     * Gets the value of a device config property from the Content Capture namespace.
+     */
+    public static int getIntDeviceConfigProperty(@NonNull String key, int defaultValue) {
+        final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, key);
+        if (value == null) return defaultValue;
+
+        try {
+            return Integer.parseInt(value);
+        } catch (Exception e) {
+            Log.w(TAG, "error parsing value (" + value + ") of property " + key + ": " + e);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Sets the value of the static logging level constants based on device config.
+     */
+    public static void setLoggingLevel() {
+        final int defaultLevel = Build.IS_DEBUGGABLE ? LOGGING_LEVEL_DEBUG : LOGGING_LEVEL_OFF;
+        final int level = getIntDeviceConfigProperty(DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL,
+                defaultLevel);
+        Log.i(TAG, "Setting logging level to " + getLoggingLevelAsString(level));
+        sVerbose = sDebug = false;
+        switch (level) {
+            case LOGGING_LEVEL_VERBOSE:
+                sVerbose = true;
+                // fall through
+            case LOGGING_LEVEL_DEBUG:
+                sDebug = true;
+                return;
+            case LOGGING_LEVEL_OFF:
+                // You log nothing, Jon Snow!
+                return;
+            default:
+                Log.w(TAG, "setLoggingLevel(): invalud level: " + level);
+        }
+    }
+
+    /**
+     * Gets a user-friendly value for a content capture logging level.
+     */
+    public static String getLoggingLevelAsString(@LoggingLevel int level) {
+        switch (level) {
+            case LOGGING_LEVEL_OFF:
+                return "OFF";
+            case LOGGING_LEVEL_DEBUG:
+                return "DEBUG";
+            case LOGGING_LEVEL_VERBOSE:
+                return "VERBOSE";
+            default:
+                return "UNKNOWN-" + level;
+        }
+    }
+
     private ContentCaptureHelper() {
         throw new UnsupportedOperationException("contains only static methods");
     }
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 634443d..9906308 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -15,9 +15,10 @@
  */
 package android.view.contentcapture;
 
-import static android.view.contentcapture.ContentCaptureHelper.DEBUG;
-import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
+import static android.view.contentcapture.ContentCaptureHelper.sDebug;
+import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -38,16 +39,11 @@
 import com.android.internal.util.SyncResultReceiver;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
-/*
- * NOTE: all methods in this class should return right away, or do the real work in a handler
- * thread.
- *
- * Hence, the only field that must be thread-safe is mEnabled, which is called at the beginning
- * of every method.
- */
 /**
- * TODO(b/123577059): add javadocs / implement
+ * TODO(b/123577059): add javadocs / mention it can be null
  */
 @SystemService(Context.CONTENT_CAPTURE_MANAGER_SERVICE)
 public final class ContentCaptureManager {
@@ -85,12 +81,81 @@
     public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED =
             "service_explicitly_enabled";
 
+    /**
+     * Maximum number of events that are buffered before sent to the app.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE = "max_buffer_size";
+
+    /**
+     * Frequency (in ms) of buffer flushes when no events are received.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY = "idle_flush_frequency";
+
+    /**
+     * Frequency (in ms) of buffer flushes when no events are received and the last one was a
+     * text change event.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY =
+            "text_change_flush_frequency";
+
+    /**
+     * Size of events that are logging on {@code dump}.
+     *
+     * <p>Set it to {@code 0} or less to disable history.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE = "log_history_size";
+
+    /**
+     * Sets the logging level for {@code logcat} statements.
+     *
+     * <p>Valid values are: {@link #LOGGING_LEVEL_OFF}, {@value #LOGGING_LEVEL_DEBUG}, and
+     * {@link #LOGGING_LEVEL_VERBOSE}.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL = "logging_level";
+
+
+    /** @hide */
+    @TestApi
+    public static final int LOGGING_LEVEL_OFF = 0;
+
+    /** @hide */
+    @TestApi
+    public static final int LOGGING_LEVEL_DEBUG = 1;
+
+    /** @hide */
+    @TestApi
+    public static final int LOGGING_LEVEL_VERBOSE = 2;
+
+    /** @hide */
+    @IntDef(flag = false, value = {
+            LOGGING_LEVEL_OFF,
+            LOGGING_LEVEL_DEBUG,
+            LOGGING_LEVEL_VERBOSE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LoggingLevel {}
+
     private final Object mLock = new Object();
 
     @NonNull
     private final Context mContext;
 
-    @Nullable
+    @NonNull
     private final IContentCaptureManager mService;
 
     // Flags used for starting session.
@@ -107,11 +172,17 @@
 
     /** @hide */
     public ContentCaptureManager(@NonNull Context context,
-            @Nullable IContentCaptureManager service) {
+            @NonNull IContentCaptureManager service) {
         mContext = Preconditions.checkNotNull(context, "context cannot be null");
-        if (VERBOSE) Log.v(TAG, "Constructor for " + context.getPackageName());
+        mService = Preconditions.checkNotNull(service, "service cannot be null");
 
-        mService = service;
+        // TODO(b/123096662): right now we're reading the device config values here, but ideally
+        // it should be read on ContentCaptureManagerService and passed back when the activity
+        // started.
+        ContentCaptureHelper.setLoggingLevel();
+
+        if (sVerbose) Log.v(TAG, "Constructor for " + context.getPackageName());
+
         // TODO(b/119220549): we might not even need a handler, as the IPCs are oneway. But if we
         // do, then we should optimize it to run the tests after the Choreographer finishes the most
         // important steps of the frame.
@@ -133,7 +204,7 @@
         synchronized (mLock) {
             if (mMainSession == null) {
                 mMainSession = new MainContentCaptureSession(mContext, this, mHandler, mService);
-                if (VERBOSE) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
+                if (sVerbose) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
             }
             return mMainSession;
         }
@@ -199,8 +270,6 @@
      * </ul>
      */
     public boolean isContentCaptureEnabled() {
-        if (mService == null) return false;
-
         final MainContentCaptureSession mainSession;
         synchronized (mLock) {
             mainSession = mMainSession;
@@ -219,7 +288,7 @@
      * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
      */
     public void setContentCaptureEnabled(boolean enabled) {
-        if (DEBUG) {
+        if (sDebug) {
             Log.d(TAG, "setContentCaptureEnabled(): setting to " + enabled + " for " + mContext);
         }
 
@@ -242,8 +311,6 @@
     @SystemApi
     @TestApi
     public boolean isContentCaptureFeatureEnabled() {
-        if (mService == null) return false;
-
         final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
         final int resultCode;
         try {
@@ -275,7 +342,7 @@
     @SystemApi
     @TestApi
     public void setContentCaptureFeatureEnabled(boolean enabled) {
-        if (DEBUG) Log.d(TAG, "setContentCaptureFeatureEnabled(): setting to " + enabled);
+        if (sDebug) Log.d(TAG, "setContentCaptureFeatureEnabled(): setting to " + enabled);
 
         final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
         final int resultCode;
@@ -314,22 +381,23 @@
 
     /** @hide */
     public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.println("ContentCaptureManager");
+        final String prefix2 = prefix + "  ";
         synchronized (mLock) {
-            pw.print(prefix); pw.println("ContentCaptureManager");
-            pw.print(prefix); pw.print("isContentCaptureEnabled(): ");
+            pw.print(prefix2); pw.print("isContentCaptureEnabled(): ");
             pw.println(isContentCaptureEnabled());
+            pw.print(prefix); pw.print("Debug: "); pw.print(sDebug);
+            pw.print(" Verbose: "); pw.println(sVerbose);
             pw.print(prefix); pw.print("Context: "); pw.println(mContext);
             pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId());
-            if (mService != null) {
-                pw.print(prefix); pw.print("Service: "); pw.println(mService);
-            }
+            pw.print(prefix); pw.print("Service: "); pw.println(mService);
             pw.print(prefix); pw.print("Flags: "); pw.println(mFlags);
             if (mMainSession != null) {
-                final String prefix2 = prefix + "  ";
-                pw.print(prefix); pw.println("Main session:");
-                mMainSession.dump(prefix2, pw);
+                final String prefix3 = prefix2 + "  ";
+                pw.print(prefix2); pw.println("Main session:");
+                mMainSession.dump(prefix3, pw);
             } else {
-                pw.print(prefix); pw.println("No sessions");
+                pw.print(prefix2); pw.println("No sessions");
             }
         }
     }
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index e028961..ec3b44a 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -15,8 +15,8 @@
  */
 package android.view.contentcapture;
 
-import static android.view.contentcapture.ContentCaptureHelper.DEBUG;
-import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
+import static android.view.contentcapture.ContentCaptureHelper.sDebug;
+import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
@@ -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();
@@ -219,7 +233,7 @@
     public final ContentCaptureSession createContentCaptureSession(
             @NonNull ContentCaptureContext context) {
         final ContentCaptureSession child = newChild(context);
-        if (DEBUG) {
+        if (sDebug) {
             Log.d(TAG, "createContentCaptureSession(" + context + ": parent=" + mId + ", child="
                     + child.mId);
         }
@@ -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.
@@ -247,20 +285,20 @@
     public final void destroy() {
         synchronized (mLock) {
             if (mDestroyed) {
-                if (DEBUG) Log.d(TAG, "destroy(" + mId + "): already destroyed");
+                if (sDebug) Log.d(TAG, "destroy(" + mId + "): already destroyed");
                 return;
             }
             mDestroyed = true;
 
             // TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
             // id) and send it to the cache of batched commands
-            if (VERBOSE) {
+            if (sVerbose) {
                 Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
             }
             // Finish children first
             if (mChildren != null) {
                 final int numberChildren = mChildren.size();
-                if (VERBOSE) Log.v(TAG, "Destroying " + numberChildren + " children first");
+                if (sVerbose) Log.v(TAG, "Destroying " + numberChildren + " children first");
                 for (int i = 0; i < numberChildren; i++) {
                     final ContentCaptureSession child = mChildren.get(i);
                     try {
@@ -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..f4021b1 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;
@@ -22,9 +23,13 @@
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
-import static android.view.contentcapture.ContentCaptureHelper.DEBUG;
-import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
+import static android.view.contentcapture.ContentCaptureHelper.getIntDeviceConfigProperty;
 import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
+import static android.view.contentcapture.ContentCaptureHelper.sDebug;
+import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
+import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY;
+import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE;
+import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -63,6 +68,7 @@
 
     private static final String TAG = MainContentCaptureSession.class.getSimpleName();
 
+    // For readability purposes...
     private static final boolean FORCE_FLUSH = true;
 
     /**
@@ -70,17 +76,9 @@
      */
     private static final int MSG_FLUSH = 1;
 
-    /**
-     * Maximum number of events that are buffered before sent to the app.
-     */
-    // TODO(b/121044064): use settings
-    private static final int MAX_BUFFER_SIZE = 100;
-
-    /**
-     * Frequency the buffer is flushed if stale.
-     */
-    // TODO(b/121044064): use settings
-    private static final int FLUSHING_FREQUENCY_MS = 5_000;
+    private static final int DEFAULT_MAX_BUFFER_SIZE = 100;
+    private static final int DEFAULT_FLUSHING_FREQUENCY_MS = 5_000;
+    private static final int DEFAULT_LOG_HISTORY_SIZE = 10;
 
     /**
      * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
@@ -104,14 +102,14 @@
      * Interface to the system_server binder object - it's only used to start the session (and
      * notify when the session is finished).
      */
-    @Nullable // TODO(b/122959591): shoul never be null, we should make main session null instead
+    @NonNull
     private final IContentCaptureManager mSystemServerInterface;
 
     /**
      * Direct interface to the service binder object - it's used to send the events, including the
      * last ones (when the session is finished)
      */
-    @Nullable
+    @NonNull
     private IContentCaptureDirectManager mDirectServiceInterface;
     @Nullable
     private DeathRecipient mDirectServiceVulture;
@@ -130,20 +128,42 @@
     @Nullable
     private ArrayList<ContentCaptureEvent> mEvents;
 
+    /**
+     * Maximum number of events that are buffered before sent to the app.
+     */
+    private final int mMaxBufferSize;
+
+    /**
+     * Frequency the buffer is flushed if idle.
+     */
+    private final int mIdleFlushingFrequencyMs;
+
     // Used just for debugging purposes (on dump)
     private long mNextFlush;
 
-    // TODO(b/121044064): use settings to set size
-    private final LocalLog mFlushHistory = new LocalLog(10);
+    @Nullable
+    private final LocalLog mFlushHistory;
 
     /** @hide */
     protected MainContentCaptureSession(@NonNull Context context,
             @NonNull ContentCaptureManager manager, @NonNull Handler handler,
-            @Nullable IContentCaptureManager systemServerInterface) {
+            @NonNull IContentCaptureManager systemServerInterface) {
         mContext = context;
         mManager = manager;
         mHandler = handler;
         mSystemServerInterface = systemServerInterface;
+
+        // TODO(b/123096662): right now we're reading the device config values here, but ideally
+        // it should be read on ContentCaptureManagerService and passed back when the activity
+        // started.
+        mMaxBufferSize = getIntDeviceConfigProperty(DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE,
+                DEFAULT_MAX_BUFFER_SIZE);
+        mIdleFlushingFrequencyMs = getIntDeviceConfigProperty(
+                DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY, DEFAULT_FLUSHING_FREQUENCY_MS);
+        final int logHistorySize = getIntDeviceConfigProperty(
+                DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, DEFAULT_LOG_HISTORY_SIZE);
+
+        mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null;
     }
 
     @Override
@@ -168,14 +188,14 @@
             int flags) {
         if (!isContentCaptureEnabled()) return;
 
-        if (VERBOSE) {
+        if (sVerbose) {
             Log.v(TAG, "start(): token=" + token + ", comp="
                     + ComponentName.flattenToShortString(component));
         }
 
         if (hasStarted()) {
             // TODO(b/122959591): make sure this is expected (and when), or use Log.w
-            if (DEBUG) {
+            if (sDebug) {
                 Log.d(TAG, "ignoring handleStartSession(" + token + "/"
                         + ComponentName.flattenToShortString(component) + " while on state "
                         + getStateAsString(mState));
@@ -186,14 +206,12 @@
         mApplicationToken = token;
         mComponentName = component;
 
-        if (VERBOSE) {
+        if (sVerbose) {
             Log.v(TAG, "handleStartSession(): token=" + token + ", act="
                     + getDebugState() + ", id=" + mId);
         }
 
         try {
-            if (mSystemServerInterface == null) return;
-
             mSystemServerInterface.startSession(mApplicationToken, component, mId, flags,
                     new IResultReceiver.Stub() {
                         @Override
@@ -253,7 +271,7 @@
             mState = resultCode;
             mDisabled.set(false);
         }
-        if (VERBOSE) {
+        if (sVerbose) {
             Log.v(TAG, "handleSessionStarted() result: id=" + mId + " resultCode=" + resultCode
                     + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
                     + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size()));
@@ -268,26 +286,27 @@
     @UiThread
     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 (sVerbose) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
+        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()) {
             // This happens when the event was queued in the handler before the sesison was ready,
             // then handleSessionStarted() returned and set it as disabled - we need to drop it,
             // otherwise it will keep triggering handleScheduleFlush()
-            if (VERBOSE) Log.v(TAG, "handleSendEvent(): ignoring when disabled");
+            if (sVerbose) Log.v(TAG, "handleSendEvent(): ignoring when disabled");
             return;
         }
         if (mEvents == null) {
-            if (VERBOSE) {
-                Log.v(TAG, "handleSendEvent(): creating buffer for " + MAX_BUFFER_SIZE + " events");
+            if (sVerbose) {
+                Log.v(TAG, "handleSendEvent(): creating buffer for " + mMaxBufferSize + " events");
             }
-            mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
+            mEvents = new ArrayList<>(mMaxBufferSize);
         }
 
         // Some type of events can be merged together
@@ -299,7 +318,7 @@
             // TODO(b/121045053): check if flags match
             if (lastEvent.getType() == TYPE_VIEW_TEXT_CHANGED
                     && lastEvent.getId().equals(event.getId())) {
-                if (VERBOSE) {
+                if (sVerbose) {
                     Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text="
                             + getSanitizedString(event.getText()));
                 }
@@ -313,7 +332,7 @@
             final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1);
             if (lastEvent.getType() == TYPE_VIEW_DISAPPEARED
                     && event.getSessionId().equals(lastEvent.getSessionId())) {
-                if (VERBOSE) {
+                if (sVerbose) {
                     Log.v(TAG, "Buffering TYPE_VIEW_DISAPPEARED events for session "
                             + lastEvent.getSessionId());
                 }
@@ -328,20 +347,20 @@
 
         final int numberEvents = mEvents.size();
 
-        final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
+        final boolean bufferEvent = numberEvents < mMaxBufferSize;
 
         if (bufferEvent && !forceFlush) {
             scheduleFlush(FLUSH_REASON_IDLE_TIMEOUT, /* checkExisting= */ true);
             return;
         }
 
-        if (mState != STATE_ACTIVE && numberEvents >= MAX_BUFFER_SIZE) {
+        if (mState != STATE_ACTIVE && numberEvents >= mMaxBufferSize) {
             // Callback from startSession hasn't been called yet - typically happens on system
             // apps that are started before the system service
             // TODO(b/122959591): try to ignore session while system is not ready / boot
             // not complete instead. Similarly, the manager service should return right away
             // when the user does not have a service set
-            if (DEBUG) {
+            if (sDebug) {
                 Log.d(TAG, "Closing session for " + getDebugState()
                         + " after " + numberEvents + " delayed events");
             }
@@ -396,12 +415,12 @@
 
     @UiThread
     private void scheduleFlush(@FlushReason int reason, boolean checkExisting) {
-        if (VERBOSE) {
+        if (sVerbose) {
             Log.v(TAG, "handleScheduleFlush(" + getDebugState(reason)
                     + ", checkExisting=" + checkExisting);
         }
         if (!hasStarted()) {
-            if (VERBOSE) Log.v(TAG, "handleScheduleFlush(): session not started yet");
+            if (sVerbose) Log.v(TAG, "handleScheduleFlush(): session not started yet");
             return;
         }
 
@@ -416,19 +435,19 @@
             // "Renew" the flush message by removing the previous one
             mHandler.removeMessages(MSG_FLUSH);
         }
-        mNextFlush = System.currentTimeMillis() + FLUSHING_FREQUENCY_MS;
-        if (VERBOSE) {
+        mNextFlush = System.currentTimeMillis() + mIdleFlushingFrequencyMs;
+        if (sVerbose) {
             Log.v(TAG, "handleScheduleFlush(): scheduled to flush in "
-                    + FLUSHING_FREQUENCY_MS + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
+                    + mIdleFlushingFrequencyMs + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
         }
         // Post using a Runnable directly to trim a few μs from PooledLambda.obtainMessage()
-        mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, FLUSHING_FREQUENCY_MS);
+        mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, mIdleFlushingFrequencyMs);
     }
 
     @UiThread
     private void flushIfNeeded(@FlushReason int reason) {
         if (mEvents == null || mEvents.isEmpty()) {
-            if (VERBOSE) Log.v(TAG, "Nothing to flush");
+            if (sVerbose) Log.v(TAG, "Nothing to flush");
             return;
         }
         flush(reason);
@@ -446,7 +465,7 @@
         }
 
         if (mDirectServiceInterface == null) {
-            if (VERBOSE) {
+            if (sVerbose) {
                 Log.v(TAG, "handleForceFlush(" + getDebugState(reason) + "): hold your horses, "
                         + "client not ready: " + mEvents);
             }
@@ -458,14 +477,16 @@
 
         final int numberEvents = mEvents.size();
         final String reasonString = getflushReasonAsString(reason);
-        if (DEBUG) {
+        if (sDebug) {
             Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState(reason));
         }
-        // Logs reason, size, max size, idle timeout
-        final String logRecord = "r=" + reasonString + " s=" + numberEvents
-                + " m=" + MAX_BUFFER_SIZE + " i=" + FLUSHING_FREQUENCY_MS;
-        try {
+        if (mFlushHistory != null) {
+            // Logs reason, size, max size, idle timeout
+            final String logRecord = "r=" + reasonString + " s=" + numberEvents
+                    + " m=" + mMaxBufferSize + " i=" + mIdleFlushingFrequencyMs;
             mFlushHistory.log(logRecord);
+        }
+        try {
             mHandler.removeMessages(MSG_FLUSH);
 
             final ParceledListSlice<ContentCaptureEvent> events = clearEvents();
@@ -476,6 +497,11 @@
         }
     }
 
+    @Override
+    public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
+        notifyContextUpdated(mId, context);
+    }
+
     /**
      * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
      */
@@ -493,15 +519,13 @@
 
     @UiThread
     private void destroySession() {
-        if (DEBUG) {
+        if (sDebug) {
             Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
                     + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
                     + getDebugState());
         }
 
         try {
-            if (mSystemServerInterface == null) return;
-
             mSystemServerInterface.finishSession(mId);
         } catch (RemoteException e) {
             Log.e(TAG, "Error destroying system-service session " + mId + " for "
@@ -513,7 +537,7 @@
     // clearings out.
     @UiThread
     private void resetSession(int newState) {
-        if (VERBOSE) {
+        if (sVerbose) {
             Log.v(TAG, "handleResetSession(" + getActivityName() + "): from "
                     + getStateAsString(mState) + " to " + getStateAsString(newState));
         }
@@ -613,14 +637,19 @@
         }
     }
 
+    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) {
+        super.dump(prefix, pw);
+
         pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
         pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
-        if (mSystemServerInterface != null) {
-            pw.print(prefix); pw.print("mSystemServerInterface: ");
-            pw.println(mSystemServerInterface);
-        }
+        pw.print(prefix); pw.print("mSystemServerInterface: ");
         if (mDirectServiceInterface != null) {
             pw.print(prefix); pw.print("mDirectServiceInterface: ");
             pw.println(mDirectServiceInterface);
@@ -638,8 +667,8 @@
         if (mEvents != null && !mEvents.isEmpty()) {
             final int numberEvents = mEvents.size();
             pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents);
-            pw.print('/'); pw.println(MAX_BUFFER_SIZE);
-            if (VERBOSE && numberEvents > 0) {
+            pw.print('/'); pw.println(mMaxBufferSize);
+            if (sVerbose && numberEvents > 0) {
                 final String prefix3 = prefix + "  ";
                 for (int i = 0; i < numberEvents; i++) {
                     final ContentCaptureEvent event = mEvents.get(i);
@@ -647,13 +676,17 @@
                     pw.println();
                 }
             }
-            pw.print(prefix); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
+            pw.print(prefix); pw.print("flush frequency: "); pw.println(mIdleFlushingFrequencyMs);
             pw.print(prefix); pw.print("next flush: ");
             TimeUtils.formatDuration(mNextFlush - System.currentTimeMillis(), pw);
             pw.print(" ("); pw.print(TimeUtils.logTimeOfDay(mNextFlush)); pw.println(")");
         }
-        pw.print(prefix); pw.println("flush history:");
-        mFlushHistory.reverseDump(/* fd= */ null, pw, /* args= */ null); pw.println();
+        if (mFlushHistory != null) {
+            pw.print(prefix); pw.println("flush history:");
+            mFlushHistory.reverseDump(/* fd= */ null, pw, /* args= */ null); pw.println();
+        } else {
+            pw.print(prefix); pw.println("not logging flush history");
+        }
 
         super.dump(prefix, pw);
     }
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/AbsListView.java b/core/java/android/widget/AbsListView.java
index 4b7c393..f01babe 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -325,13 +325,13 @@
     /**
      * The current position of the selector in the list.
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     int mSelectorPosition = INVALID_POSITION;
 
     /**
      * Defines the selector's location and dimension at drawing time
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     Rect mSelectorRect = new Rect();
 
     /**
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/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index e91f87e..1bed32ec 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -17,12 +17,15 @@
 package android.widget;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.view.View;
 
 import com.android.internal.widget.ScrollBarUtils;
 
@@ -36,7 +39,7 @@
 public class ScrollBarDrawable extends Drawable implements Drawable.Callback {
     private Drawable mVerticalTrack;
     private Drawable mHorizontalTrack;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768422)
     private Drawable mVerticalThumb;
     private Drawable mHorizontalThumb;
 
@@ -226,7 +229,10 @@
         }
     }
 
-    @UnsupportedAppUsage
+    /**
+     * @see android.view.View#setVerticalThumbDrawable(Drawable)
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public void setVerticalThumbDrawable(Drawable thumb) {
         if (mVerticalThumb != null) {
             mVerticalThumb.setCallback(null);
@@ -236,6 +242,37 @@
         mVerticalThumb = thumb;
     }
 
+    /**
+     * @see View#getVerticalTrackDrawable()
+     */
+    public @Nullable Drawable getVerticalTrackDrawable() {
+        return mVerticalTrack;
+    }
+
+    /**
+     * @see View#getVerticalThumbDrawable()
+     */
+    public @Nullable Drawable getVerticalThumbDrawable() {
+        return mVerticalThumb;
+    }
+
+    /**
+     * @see View#getHorizontalTrackDrawable()
+     */
+    public @Nullable Drawable getHorizontalTrackDrawable() {
+        return mHorizontalTrack;
+    }
+
+    /**
+     * @see View#getHorizontalThumbDrawable()
+     */
+    public @Nullable Drawable getHorizontalThumbDrawable() {
+        return mHorizontalThumb;
+    }
+
+    /**
+     * @see android.view.View#setVerticalTrackDrawable(Drawable)
+     */
     public void setVerticalTrackDrawable(Drawable track) {
         if (mVerticalTrack != null) {
             mVerticalTrack.setCallback(null);
@@ -245,7 +282,10 @@
         mVerticalTrack = track;
     }
 
-    @UnsupportedAppUsage
+    /**
+     * @see android.view.View#setHorizontalThumbDrawable(Drawable)
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public void setHorizontalThumbDrawable(Drawable thumb) {
         if (mHorizontalThumb != null) {
             mHorizontalThumb.setCallback(null);
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index eef40e1..d037337 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -95,7 +95,7 @@
     public static final int LENGTH_LONG = 1;
 
     final Context mContext;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     final TN mTN;
     @UnsupportedAppUsage
     int mDuration;
@@ -354,7 +354,7 @@
     }
 
     private static class TN extends ITransientNotification.Stub {
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
 
         private static final int SHOW = 0;
@@ -362,18 +362,18 @@
         private static final int CANCEL = 2;
         final Handler mHandler;
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         int mGravity;
         int mX;
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         int mY;
         float mHorizontalMargin;
         float mVerticalMargin;
 
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         View mView;
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         View mNextView;
         int mDuration;
 
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/BackgroundThread.java b/core/java/com/android/internal/os/BackgroundThread.java
index eada142..22c832e 100644
--- a/core/java/com/android/internal/os/BackgroundThread.java
+++ b/core/java/com/android/internal/os/BackgroundThread.java
@@ -17,10 +17,13 @@
 package com.android.internal.os;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Shared singleton background thread for each process.
  */
@@ -29,6 +32,7 @@
     private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
     private static BackgroundThread sInstance;
     private static Handler sHandler;
+    private static HandlerExecutor sHandlerExecutor;
 
     private BackgroundThread() {
         super("android.bg", android.os.Process.THREAD_PRIORITY_BACKGROUND);
@@ -43,6 +47,7 @@
             looper.setSlowLogThresholdMs(
                     SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -59,4 +64,11 @@
             return sHandler;
         }
     }
+
+    public static Executor getExecutor() {
+        synchronized (BackgroundThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
 }
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/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index b1328e8..6bbfc2b 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -109,6 +109,12 @@
     private Predicate<Integer> mUidPredicate;
 
     /**
+     * If a thread has strictly less than {@code minimumTotalCpuUsageMillis} total CPU usage, it
+     * will not be reported
+     */
+    private int mMinimumTotalCpuUsageMillis;
+
+    /**
      * Where the proc filesystem is mounted
      */
     private final Path mProcPath;
@@ -142,10 +148,12 @@
     public KernelCpuThreadReader(
             int numBuckets,
             Predicate<Integer> uidPredicate,
+            int minimumTotalCpuUsageMillis,
             Path procPath,
             Path initialTimeInStatePath,
             Injector injector) throws IOException {
         mUidPredicate = uidPredicate;
+        mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis;
         mProcPath = procPath;
         mProcTimeInStateReader = new ProcTimeInStateReader(initialTimeInStatePath);
         mInjector = injector;
@@ -158,11 +166,13 @@
      * @return the reader, null if an exception was thrown during creation
      */
     @Nullable
-    public static KernelCpuThreadReader create(int numBuckets, Predicate<Integer> uidPredicate) {
+    public static KernelCpuThreadReader create(
+            int numBuckets, Predicate<Integer> uidPredicate, int minimumTotalCpuUsageMillis) {
         try {
             return new KernelCpuThreadReader(
                     numBuckets,
                     uidPredicate,
+                    minimumTotalCpuUsageMillis,
                     DEFAULT_PROC_PATH,
                     DEFAULT_INITIAL_TIME_IN_STATE_PATH,
                     new Injector());
@@ -308,6 +318,18 @@
     }
 
     /**
+     * If a thread has strictly less than {@code minimumTotalCpuUsageMillis} total CPU usage, it
+     * will not be reported
+     */
+    void setMinimumTotalCpuUsageMillis(int minimumTotalCpuUsageMillis) {
+        if (minimumTotalCpuUsageMillis < 0) {
+            Slog.w(TAG, "Negative minimumTotalCpuUsageMillis: " + minimumTotalCpuUsageMillis);
+            return;
+        }
+        mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis;
+    }
+
+    /**
      * Get the CPU frequencies that correspond to the times reported in
      * {@link ThreadCpuUsage#usageTimesMillis}
      */
@@ -346,6 +368,15 @@
         }
         int[] cpuUsages = mFrequencyBucketCreator.getBucketedValues(cpuUsagesLong);
 
+        // Check if the total CPU usage below the threshold
+        int totalCpuUsage = 0;
+        for (int i = 0; i < cpuUsages.length; i++) {
+            totalCpuUsage += cpuUsages[i];
+        }
+        if (totalCpuUsage < mMinimumTotalCpuUsageMillis) {
+            return null;
+        }
+
         return new ThreadCpuUsage(threadId, threadName, cpuUsages);
     }
 
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
index 77f6a17..718bcb4 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
@@ -59,6 +59,13 @@
     private static final String COLLECTED_UIDS_SETTINGS_KEY = "collected_uids";
     private static final String COLLECTED_UIDS_DEFAULT = "1000-1000";
 
+    /**
+     * Minimum total CPU usage to report
+     */
+    private static final String MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY =
+            "minimum_total_cpu_usage_millis";
+    private static final int MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT = 0;
+
     private final Context mContext;
 
     @Nullable
@@ -87,7 +94,8 @@
         mContext = context;
         mKernelCpuThreadReader = KernelCpuThreadReader.create(
                 NUM_BUCKETS_DEFAULT,
-                UidPredicate.fromString(COLLECTED_UIDS_DEFAULT));
+                UidPredicate.fromString(COLLECTED_UIDS_DEFAULT),
+                MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT);
     }
 
     @Override
@@ -124,6 +132,9 @@
         mKernelCpuThreadReader.setNumBuckets(
                 parser.getInt(NUM_BUCKETS_SETTINGS_KEY, NUM_BUCKETS_DEFAULT));
         mKernelCpuThreadReader.setUidPredicate(uidPredicate);
+        mKernelCpuThreadReader.setMinimumTotalCpuUsageMillis(parser.getInt(
+                MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY,
+                MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT));
     }
 
     /**
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/java/com/android/server/backup/PermissionBackupHelper.java b/core/java/com/android/server/backup/PermissionBackupHelper.java
index c0ba181..c7c423b 100644
--- a/core/java/com/android/server/backup/PermissionBackupHelper.java
+++ b/core/java/com/android/server/backup/PermissionBackupHelper.java
@@ -16,11 +16,14 @@
 
 package com.android.server.backup;
 
-import android.app.AppGlobals;
+import android.annotation.NonNull;
 import android.app.backup.BlobBackupHelper;
-import android.content.pm.IPackageManager;
+import android.os.UserHandle;
+import android.permission.PermissionManagerInternal;
 import android.util.Slog;
 
+import com.android.server.LocalServices;
+
 public class PermissionBackupHelper extends BlobBackupHelper {
     private static final String TAG = "PermissionBackup";
     private static final boolean DEBUG = false;
@@ -31,24 +34,26 @@
     // key under which the permission-grant state blob is committed to backup
     private static final String KEY_PERMISSIONS = "permissions";
 
-    private final int mUserId;
+    private final @NonNull UserHandle mUser;
+
+    private final @NonNull PermissionManagerInternal mPermissionManager;
 
     public PermissionBackupHelper(int userId) {
         super(STATE_VERSION, KEY_PERMISSIONS);
 
-        mUserId = userId;
+        mUser = UserHandle.of(userId);
+        mPermissionManager = LocalServices.getService(PermissionManagerInternal.class);
     }
 
     @Override
     protected byte[] getBackupPayload(String key) {
-        IPackageManager pm = AppGlobals.getPackageManager();
         if (DEBUG) {
             Slog.d(TAG, "Handling backup of " + key);
         }
         try {
             switch (key) {
                 case KEY_PERMISSIONS:
-                    return pm.getPermissionGrantBackup(mUserId);
+                    return mPermissionManager.backupRuntimePermissions(mUser);
 
                 default:
                     Slog.w(TAG, "Unexpected backup key " + key);
@@ -61,14 +66,13 @@
 
     @Override
     protected void applyRestoredPayload(String key, byte[] payload) {
-        IPackageManager pm = AppGlobals.getPackageManager();
         if (DEBUG) {
             Slog.d(TAG, "Handling restore of " + key);
         }
         try {
             switch (key) {
                 case KEY_PERMISSIONS:
-                    pm.restorePermissionGrants(payload, mUserId);
+                    mPermissionManager.restoreRuntimePermissions(payload, mUser);
                     break;
 
                 default:
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index af0b7c3..7406136 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -230,6 +230,7 @@
     ],
 
     static_libs: [
+        "libasync_safe",
         "libgif",
         "libseccomp_policy",
         "libgrallocusage",
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 e817217..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_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index a2ed7d3..bd998999 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1918,7 +1918,6 @@
     for (jint i = 0; i < nb; i++) {
         deviceTypesVector.push_back((audio_devices_t) typesPtr[i]);
     }
-    env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
 
     // check each address is a string and add device type/address to list for device affinity
     Vector<AudioDeviceTypeAddr> deviceVector;
@@ -1932,6 +1931,7 @@
         AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address);
         deviceVector.add(dev);
     }
+    env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
 
     status_t status = AudioSystem::setUidDeviceAffinities((uid_t) uid, deviceVector);
     return (jint) nativeToJavaStatus(status);
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/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 5cecf66a..7b4e4ea 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -25,6 +25,8 @@
 
 #define LOG_TAG "Zygote"
 
+#include <async_safe/log.h>
+
 // sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
 #include <sys/mount.h>
 #include <linux/fs.h>
@@ -303,27 +305,23 @@
   int saved_errno = errno;
 
   while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
-     // Log process-death status that we care about.  In general it is
-     // not safe to call LOG(...) from a signal handler because of
-     // possible reentrancy.  However, we know a priori that the
-     // current implementation of LOG() is safe to call from a SIGCHLD
-     // handler in the zygote process.  If the LOG() implementation
-     // changes its locking strategy or its use of syscalls within the
-     // lazy-init critical section, its use here may become unsafe.
+     // Log process-death status that we care about.
     if (WIFEXITED(status)) {
-      ALOGI("Process %d exited cleanly (%d)", pid, WEXITSTATUS(status));
+      async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG,
+                            "Process %d exited cleanly (%d)", pid, WEXITSTATUS(status));
     } else if (WIFSIGNALED(status)) {
-      ALOGI("Process %d exited due to signal (%d)", pid, WTERMSIG(status));
-      if (WCOREDUMP(status)) {
-        ALOGI("Process %d dumped core.", pid);
-      }
+      async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG,
+                            "Process %d exited due to signal %d (%s)%s", pid,
+                            WTERMSIG(status), strsignal(WTERMSIG(status)),
+                            WCOREDUMP(status) ? "; core dumped" : "");
     }
 
     // If the just-crashed process is the system_server, bring down zygote
     // so that it is restarted by init and system server will be restarted
     // from there.
     if (pid == gSystemServerPid) {
-      ALOGE("Exit zygote because system server (%d) has terminated", pid);
+      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                            "Exit zygote because system server (pid %d) has terminated", pid);
       kill(getpid(), SIGKILL);
     }
 
@@ -336,14 +334,17 @@
   // Note that we shouldn't consider ECHILD an error because
   // the secondary zygote might have no children left to wait for.
   if (pid < 0 && errno != ECHILD) {
-    ALOGW("Zygote SIGCHLD error in waitpid: %s", strerror(errno));
+    async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG,
+                          "Zygote SIGCHLD error in waitpid: %s", strerror(errno));
   }
 
   if (blastulas_removed > 0) {
     if (write(gBlastulaPoolEventFD, &blastulas_removed, sizeof(blastulas_removed)) == -1) {
       // If this write fails something went terribly wrong.  We will now kill
       // the zygote and let the system bring it back up.
-      ALOGE("Zygote failed to write to blastula pool event FD: %s", strerror(errno));
+      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                            "Zygote failed to write to blastula pool event FD: %s",
+                            strerror(errno));
       kill(getpid(), SIGKILL);
     }
   }
@@ -612,7 +613,7 @@
     }
 }
 
-static void CreatePkgSandbox(uid_t uid, const std::string& package_name, fail_fn_t fail_fn) {
+static void CreatePkgSandboxTarget(uid_t uid, const std::string& package_name, fail_fn_t fail_fn) {
     // Create /mnt/user/0/package/<package-name>
     userid_t user_id = multiuser_get_user_id(uid);
     std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id);
@@ -622,7 +623,7 @@
     CreateDir(pkg_sandbox_dir, 0700, AID_ROOT, AID_ROOT, fail_fn);
 
     StringAppendF(&pkg_sandbox_dir, "/%s", package_name.c_str());
-    CreateDir(pkg_sandbox_dir, 0700, AID_ROOT, AID_ROOT, fail_fn);
+    CreateDir(pkg_sandbox_dir, 0755, uid, uid, fail_fn);
 }
 
 static void BindMount(const std::string& sourceDir, const std::string& targetDir,
@@ -642,29 +643,98 @@
                                 fail_fn_t fail_fn) {
     std::string mntSourceDir = StringPrintf("%s/Android/%s/%s",
             mntSourceRoot.c_str(), dirName, packageName.c_str());
-    CreateDir(mntSourceDir, 0755, uid, uid, fail_fn);
 
     std::string mntTargetDir = StringPrintf("%s/Android/%s/%s",
             mntTargetRoot.c_str(), dirName, packageName.c_str());
-    CreateDir(mntTargetDir, 0755, uid, uid, fail_fn);
 
     BindMount(mntSourceDir, mntTargetDir, fail_fn);
 }
 
-
-static void createPkgSpecificDirRoots(const std::string& parentDir,
-                                      bool createSandbox,
-                                      mode_t mode, uid_t uid, gid_t gid,
-                                      fail_fn_t fail_fn) {
-    std::string androidDir = StringPrintf("%s/Android", parentDir.c_str());
-    CreateDir(androidDir, mode, uid, gid, fail_fn);
-    std::vector<std::string> dirs = {"data", "media", "obb"};
-    if (createSandbox) {
-        dirs.push_back("sandbox");
+static void CreateSubDirs(int dirfd, const std::string& parentDirPath,
+                          const std::vector<std::string>& subDirs,
+                          fail_fn_t fail_fn) {
+    for (auto& dirName : subDirs) {
+        struct stat sb;
+        if (TEMP_FAILURE_RETRY(fstatat(dirfd, dirName.c_str(), &sb, 0)) == 0) {
+            if (S_ISDIR(sb.st_mode)) {
+                continue;
+            } else if (TEMP_FAILURE_RETRY(unlinkat(dirfd, dirName.c_str(), 0)) == -1) {
+                fail_fn(CREATE_ERROR("Failed to unlinkat on %s/%s: %s",
+                        parentDirPath.c_str(), dirName.c_str(), strerror(errno)));
+            }
+        } else if (errno != ENOENT) {
+            fail_fn(CREATE_ERROR("Failed to fstatat on %s/%s: %s",
+                    parentDirPath.c_str(), dirName.c_str(), strerror(errno)));
+        }
+        if (TEMP_FAILURE_RETRY(mkdirat(dirfd, dirName.c_str(), 0700)) == -1) {
+            fail_fn(CREATE_ERROR("Failed to mkdirat on %s/%s: %s",
+                    parentDirPath.c_str(), dirName.c_str(), strerror(errno)));
+        }
     }
-    for (auto& dir : dirs) {
-        std::string path = StringPrintf("%s/%s", androidDir.c_str(), dir.c_str());
-        CreateDir(path, mode, uid, gid, fail_fn);
+}
+
+static void EnsurePkgSpecificDirs(const std::string& path,
+                                  const std::vector<std::string>& packageNames,
+                                  bool createSandboxDir,
+                                  fail_fn_t fail_fn) {
+    std::string androidDir = StringPrintf("%s/Android", path.c_str());
+    android::base::unique_fd androidFd(
+            open(androidDir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+    if (androidFd.get() < 0) {
+        if (errno == ENOENT || errno == ENOTDIR) {
+            if (errno == ENOTDIR && TEMP_FAILURE_RETRY(unlink(androidDir.c_str())) == -1) {
+                fail_fn(CREATE_ERROR("Failed to unlink %s: %s",
+                        androidDir.c_str(), strerror(errno)));
+            }
+            if (TEMP_FAILURE_RETRY(mkdir(androidDir.c_str(), 0700)) == -1) {
+                fail_fn(CREATE_ERROR("Failed to mkdir %s: %s",
+                        androidDir.c_str(), strerror(errno)));
+            }
+            androidFd.reset(open(androidDir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+        }
+
+        if (androidFd.get() < 0) {
+            fail_fn(CREATE_ERROR("Failed to open %s: %s", androidDir.c_str(), strerror(errno)));
+        }
+    }
+
+    std::vector<std::string> dataMediaObbDirs = {"data", "media", "obb"};
+    if (createSandboxDir) {
+        dataMediaObbDirs.push_back("sandbox");
+    }
+    CreateSubDirs(androidFd.get(), androidDir, dataMediaObbDirs, fail_fn);
+    if (createSandboxDir) {
+        dataMediaObbDirs.pop_back();
+    }
+    for (auto& dirName : dataMediaObbDirs) {
+        std::string dataDir = StringPrintf("%s/%s", androidDir.c_str(), dirName.c_str());
+        android::base::unique_fd dataFd(
+                openat(androidFd, dirName.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+        if (dataFd.get() < 0) {
+            fail_fn(CREATE_ERROR("Failed to openat %s/%s: %s",
+                    androidDir.c_str(), dirName.c_str(), strerror(errno)));
+        }
+        CreateSubDirs(dataFd.get(), dataDir, packageNames, fail_fn);
+    }
+}
+
+static void CreatePkgSandboxSource(const std::string& sandboxSource, fail_fn_t fail_fn) {
+
+    struct stat sb;
+    if (TEMP_FAILURE_RETRY(stat(sandboxSource.c_str(), &sb)) == 0) {
+        if (S_ISDIR(sb.st_mode)) {
+            return;
+        } else if (TEMP_FAILURE_RETRY(unlink(sandboxSource.c_str())) == -1) {
+            fail_fn(CREATE_ERROR("Failed to unlink %s: %s",
+                    sandboxSource.c_str(), strerror(errno)));
+        }
+    } else if (errno != ENOENT) {
+        fail_fn(CREATE_ERROR("Failed to stat %s: %s",
+                sandboxSource.c_str(), strerror(errno)));
+    }
+    if (TEMP_FAILURE_RETRY(mkdir(sandboxSource.c_str(), 0700)) == -1) {
+        fail_fn(CREATE_ERROR("Failed to mkdir %s: %s",
+                sandboxSource.c_str(), strerror(errno)));
     }
 }
 
@@ -680,21 +750,21 @@
             StringAppendF(&mntTarget, "/%d", userId);
         }
 
-        if (TEMP_FAILURE_RETRY(access(mntSource.c_str(), F_OK)) < 0) {
+        if (TEMP_FAILURE_RETRY(access(mntSource.c_str(), F_OK)) == -1) {
             ALOGE("Can't access %s: %s", mntSource.c_str(), strerror(errno));
             continue;
         }
 
-        // Create /mnt/runtime/write/emulated/0/Android/{data,media,obb,sandbox}
-        createPkgSpecificDirRoots(mntSource, true, 0700, AID_ROOT, AID_ROOT, fail_fn);
+        // Ensure /mnt/runtime/write/emulated/0/Android/{data,media,obb}
+        EnsurePkgSpecificDirs(mntSource, packageNames, true, fail_fn);
 
         std::string sandboxSource = StringPrintf("%s/Android/sandbox/%s",
             mntSource.c_str(), sandboxId.c_str());
-        CreateDir(sandboxSource, 0755, uid, uid, fail_fn);
+        CreatePkgSandboxSource(sandboxSource, fail_fn);
         BindMount(sandboxSource, mntTarget, fail_fn);
 
-        // Create /storage/emulated/0/Android/{data,media,obb}
-        createPkgSpecificDirRoots(mntTarget, false, 0755, uid, uid, fail_fn);
+        // Ensure /storage/emulated/0/Android/{data,media,obb}
+        EnsurePkgSpecificDirs(mntTarget, packageNames, false, fail_fn);
         for (auto& package : packageNames) {
             MountPkgSpecificDir(mntSource, mntTarget, package, uid, "data", fail_fn);
             MountPkgSpecificDir(mntSource, mntTarget, package, uid, "media", fail_fn);
@@ -775,15 +845,14 @@
             userid_t user_id = multiuser_get_user_id(uid);
             std::string pkgSandboxDir =
                 StringPrintf("/mnt/user/%d/package/%s", user_id, package_name.c_str());
-            struct stat sb;
             bool sandboxAlreadyCreated = true;
-            if (TEMP_FAILURE_RETRY(lstat(pkgSandboxDir.c_str(), &sb)) == -1) {
+            if (TEMP_FAILURE_RETRY(access(pkgSandboxDir.c_str(), F_OK)) == -1) {
                 if (errno == ENOENT) {
                     ALOGD("Sandbox not yet created for %s", pkgSandboxDir.c_str());
                     sandboxAlreadyCreated = false;
-                    CreatePkgSandbox(uid, package_name, fail_fn);
+                    CreatePkgSandboxTarget(uid, package_name, fail_fn);
                 } else {
-                    fail_fn(CREATE_ERROR("Failed to lstat %s: %s",
+                    fail_fn(CREATE_ERROR("Failed to access %s: %s",
                                          pkgSandboxDir.c_str(), strerror(errno)));
                 }
             }
@@ -794,7 +863,7 @@
                                      pkgSandboxDir.c_str(), strerror(errno)));
             }
 
-            if (access("/storage/obb_mount", F_OK) == 0) {
+            if (TEMP_FAILURE_RETRY(access("/storage/obb_mount", F_OK)) == 0) {
                 if (mount_mode != MOUNT_EXTERNAL_INSTALLER) {
                     remove("/storage/obb_mount");
                 }
@@ -1384,8 +1453,8 @@
       RuntimeAbort(env, __LINE__, "Bad gids array");
     }
 
-    for (int gid_index = gids_num; --gids_num >= 0;) {
-      if (native_gid_proxy[gid_index] == AID_WAKELOCK) {
+    for (int gids_index = 0; gids_index < gids_num; ++gids_index) {
+      if (native_gid_proxy[gids_index] == AID_WAKELOCK) {
         gid_wakelock_found = true;
         break;
       }
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/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 64d91ad..13fef67 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -66,7 +66,7 @@
                 android:id="@+id/media_actions"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_gravity="bottom|end"
+                android:layout_gravity="top|end"
                 android:layout_marginStart="10dp"
                 android:layoutDirection="ltr"
                 android:orientation="horizontal"
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-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
index 949c12e..0721f6f 100644
--- a/core/res/res/values-night/themes_device_defaults.xml
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -65,4 +65,7 @@
     <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert" />
 
     <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault" />
+
+    <style name="ThemeOverlay.DeviceDefault.Accent.DayNight"
+           parent="@style/ThemeOverlay.DeviceDefault.Accent" />
 </resources>
\ No newline at end of file
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 9aed9df..e65e7da 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>
 
@@ -3255,7 +3255,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>
 
@@ -3276,6 +3276,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>
@@ -3909,19 +3914,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/public.xml b/core/res/res/values/public.xml
index 5e65605..3580dd4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2951,6 +2951,8 @@
     <public-group type="style" first-id="0x010302e2">
         <!-- @hide @SystemApi -->
         <public name="Theme.DeviceDefault.DocumentsUI" />
+        <public name="Theme.DeviceDefault.DayNight" />
+        <public name="ThemeOverlay.DeviceDefault.Accent.DayNight" />
     </public-group>
 
     <public-group type="id" first-id="0x01020046">
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 062382a..7ef5e02 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" />
@@ -3654,7 +3656,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/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 1603508..194c86c 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1652,6 +1652,7 @@
 
     <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
 
+    <!-- DeviceDefault theme for day/night activities. -->
     <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault.Light" />
 
     <!-- Theme used for the intent picker activity. -->
@@ -1697,6 +1698,10 @@
         <item name="colorAccent">@color/accent_device_default_light</item>
     </style>
 
+    <!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. -->
+    <style name="ThemeOverlay.DeviceDefault.Accent.DayNight"
+           parent="@style/ThemeOverlay.DeviceDefault.Accent.Light" />
+
     <style name="ThemeOverlay.DeviceDefault.Dark.ActionBar.Accent" parent="ThemeOverlay.Material.Dark.ActionBar">
         <item name="colorAccent">@color/accent_device_default_dark</item>
     </style>
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/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
index 64b7c2c..1c84829 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
@@ -124,7 +124,7 @@
 
         // Get thread data from KernelCpuThreadReader
         final KernelCpuThreadReader kernelCpuThreadReader =
-                KernelCpuThreadReader.create(8, uid -> uid == Process.myUid());
+                KernelCpuThreadReader.create(8, uid -> uid == Process.myUid(), 0);
         assertNotNull(kernelCpuThreadReader);
         final ProcessCpuUsage currentProcessCpuUsage =
                 kernelCpuThreadReader.getCurrentProcessCpuUsage();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index b9744f5..442ece5 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -103,6 +103,7 @@
         final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
                 8,
                 uid -> 1000 <= uid && uid < 2000,
+                0,
                 mProcDirectory.toPath(),
                 mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"),
                 processUtils);
@@ -144,6 +145,7 @@
         final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
                 8,
                 uidPredicate,
+                0,
                 mProcDirectory.toPath(),
                 mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"),
                 processUtils);
@@ -162,6 +164,60 @@
         }
     }
 
+    @Test
+    public void testReader_filtersLowUsage() throws IOException {
+        int[] uids = new int[]{0, 1, 2, 3, 4};
+        int[] cpuUsage = new int[]{10, 0, 2, 100, 3};
+        int[] expectedUids = new int[]{0, 3, 4};
+        Predicate<Integer> uidPredicate = uid -> true;
+        KernelCpuThreadReader.Injector processUtils =
+                new KernelCpuThreadReader.Injector() {
+                    @Override
+                    public int myPid() {
+                        return 0;
+                    }
+
+                    @Override
+                    public int myUid() {
+                        return 0;
+                    }
+
+                    @Override
+                    public int getUidForPid(int pid) {
+                        return pid;
+                    }
+                };
+
+        for (int i = 0; i < uids.length; i++) {
+            int uid = uids[i];
+            setupDirectory(
+                    mProcDirectory.toPath().resolve(String.valueOf(uid)),
+                    new int[]{uid * 10},
+                    "process" + uid,
+                    new String[]{"thread" + uid},
+                    new int[]{1000},
+                    new int[][]{{cpuUsage[i]}});
+        }
+        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+                8,
+                uidPredicate,
+                30,
+                mProcDirectory.toPath(),
+                mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"),
+                processUtils);
+        ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids =
+                kernelCpuThreadReader.getProcessCpuUsageByUids();
+        processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.uid));
+
+        assertEquals(expectedUids.length, processCpuUsageByUids.size());
+        for (int i = 0; i < expectedUids.length; i++) {
+            KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+                    processCpuUsageByUids.get(i);
+            assertEquals(expectedUids[i], processCpuUsage.uid);
+        }
+
+    }
+
     private void setupDirectory(Path processPath, int[] threadIds, String processName,
             String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) throws IOException {
         // Make /proc/$PID
@@ -328,7 +384,6 @@
                         new long[]{1, 1, 1, 1, 1, 1, 1, 1}));
     }
 
-
     @Test
     public void testGetBigFrequenciesStartIndex_simple() {
         assertEquals(
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 7382213..915cf95 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -328,4 +328,9 @@
         <permission name="android.permission.CONTROL_VPN"/>
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.dynandroid">
+        <permission name="android.permission.REBOOT"/>
+        <permission name="android.permission.MANAGE_DYNAMIC_ANDROID"/>
+    </privapp-permissions>
+
 </permissions>
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/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index aa29174..3dc884e 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -17,7 +17,6 @@
 package android.security.keystore;
 
 import android.security.Credentials;
-import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
@@ -204,7 +203,12 @@
                         }
                     }
                 }
-
+                if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) {
+                    if (mKeySizeBits != 168) {
+                        throw new InvalidAlgorithmParameterException(
+                            "3DES key size must be 168 bits.");
+                    }
+                }
                 if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
                     if (mKeySizeBits < 64) {
                         throw new InvalidAlgorithmParameterException(
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 742813e..875b90b 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1647,6 +1647,10 @@
     // The overlay must reside of the product partition or must have existed on the product
     // partition before an upgrade to overlay these resources.
     POLICY_PRODUCT_PARTITION = 0x00000008,
+
+    // The overlay must be signed with the same signature as the actor of the target resource,
+    // which can be separate or the same as the target package with the resource.
+    POLICY_SIGNATURE = 0x00000010,
   };
   uint32_t policy_flags;
 
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/hwui/thread/ThreadBase.h b/libs/hwui/thread/ThreadBase.h
index 8cdcc46..0289d3f 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/thread/ThreadBase.h
@@ -68,10 +68,12 @@
     void processQueue() { mQueue.process(); }
 
     virtual bool threadLoop() override {
+        Looper::setForThread(mLooper);
         while (!exitPending()) {
             waitForWork();
             processQueue();
         }
+        Looper::setForThread(nullptr);
         return false;
     }
 
diff --git a/libs/incident/proto/android/os/header.proto b/libs/incident/proto/android/os/header.proto
index a84dc48..d463f87 100644
--- a/libs/incident/proto/android/os/header.proto
+++ b/libs/incident/proto/android/os/header.proto
@@ -37,4 +37,9 @@
       optional int64 id = 2; // The unique id of the statsd config.
     }
     optional StatsdConfigKey config_key = 3;
+
+    // Details about the trigger. com.android.os.AlertTriggerDetails
+    // Only use bytes type here to avoid indirect dependency on atoms.proto
+    // And this header passes through incidentd without incidentd parsing it.
+    optional bytes trigger_details = 4;
 }
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/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 1aeefb8..57a0a72 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -114,6 +114,7 @@
     // for reporting callback completion
     void locationCallbackFinished(ILocationListener listener);
 
-    // used by gts tests to verify throttling whitelist
+    // used by gts tests to verify whitelists
     String[] getBackgroundThrottlingWhitelist();
+    String[] getIgnoreSettingsWhitelist();
 }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index c027fd4..586ee2a 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -414,6 +414,18 @@
     }
 
     /**
+     * @hide
+     */
+    @TestApi
+    public String[] getIgnoreSettingsWhitelist() {
+        try {
+            return mService.getIgnoreSettingsWhitelist();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @hide - hide this constructor because it has a parameter
      * of type ILocationManager, which is a system private class. The
      * right way to create an instance of this class is using the
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/AudioFocusInfo.java b/media/java/android/media/AudioFocusInfo.java
index 0a9ca02..3594ee7 100644
--- a/media/java/android/media/AudioFocusInfo.java
+++ b/media/java/android/media/AudioFocusInfo.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -29,10 +30,10 @@
 @SystemApi
 public final class AudioFocusInfo implements Parcelable {
 
-    private final AudioAttributes mAttributes;
+    private final @NonNull AudioAttributes mAttributes;
     private final int mClientUid;
-    private final String mClientId;
-    private final String mPackageName;
+    private final @NonNull String mClientId;
+    private final @NonNull String mPackageName;
     private final int mSdkTarget;
     private int mGainRequest;
     private int mLossReceived;
@@ -80,13 +81,21 @@
      * The audio attributes for the audio focus request.
      * @return non-null {@link AudioAttributes}.
      */
-    public AudioAttributes getAttributes() { return mAttributes; }
+    public @NonNull AudioAttributes getAttributes() {
+        return mAttributes;
+    }
 
-    public int getClientUid() { return mClientUid; }
+    public int getClientUid() {
+        return mClientUid;
+    }
 
-    public String getClientId() { return mClientId; }
+    public @NonNull String getClientId() {
+        return mClientId;
+    }
 
-    public String getPackageName() { return mPackageName; }
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
 
     /**
      * The type of audio focus gain request.
diff --git a/media/java/android/media/AudioFocusRequest.java b/media/java/android/media/AudioFocusRequest.java
index b9731d1..4e70501 100644
--- a/media/java/android/media/AudioFocusRequest.java
+++ b/media/java/android/media/AudioFocusRequest.java
@@ -225,9 +225,9 @@
     /** @hide */
     public static final String KEY_ACCESSIBILITY_FORCE_FOCUS_DUCKING = "a11y_force_ducking";
 
-    private final OnAudioFocusChangeListener mFocusListener; // may be null
-    private final Handler mListenerHandler;                  // may be null
-    private final AudioAttributes mAttr;                     // never null
+    private final @Nullable OnAudioFocusChangeListener mFocusListener;
+    private final @Nullable Handler mListenerHandler;
+    private final @NonNull AudioAttributes mAttr;
     private final int mFocusGain;
     private final int mFlags;
 
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 92afe7e..24a3a9b 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1713,12 +1714,11 @@
     /**
      * Specifies the logical microphone (for processing).
      *
-     * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*)
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @param direction Direction constant.
+     * @return true if sucessful.
      */
-    public int setMicrophoneDirection(int direction) {
-        return native_set_microphone_direction(direction);
+    public boolean setMicrophoneDirection(int direction) {
+        return native_set_microphone_direction(direction) == 0;
     }
 
     /**
@@ -1727,11 +1727,10 @@
      *
      * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle),
      * though 0 (no zoom) to 1 (maximum zoom).
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @return true if sucessful.
      */
-    public int setMicrophoneFieldDimension(float zoom) {
-        return native_set_microphone_field_dimension(zoom);
+    public boolean setMicrophoneFieldDimension(@FloatRange(from = -1.0, to = 1.0) float zoom) {
+        return native_set_microphone_field_dimension(zoom) == 0;
     }
 
     //---------------------------------------------------------
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index ad25a06..5f324f7 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import static android.media.MediaPlayer.MEDIA_ERROR_UNSUPPORTED;
+
 import android.annotation.UnsupportedAppUsage;
 import android.net.NetworkUtils;
 import android.os.IBinder;
@@ -23,21 +25,19 @@
 import android.util.Log;
 
 import java.io.BufferedInputStream;
-import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.CookieHandler;
-import java.net.CookieManager;
-import java.net.Proxy;
-import java.net.URL;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.NoRouteToHostException;
 import java.net.ProtocolException;
+import java.net.Proxy;
+import java.net.URL;
 import java.net.UnknownServiceException;
 import java.util.HashMap;
 import java.util.Map;
-
-import static android.media.MediaPlayer.MEDIA_ERROR_UNSUPPORTED;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /** @hide */
 public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
@@ -67,6 +67,7 @@
     // from com.squareup.okhttp.internal.http
     private final static int HTTP_TEMP_REDIRECT = 307;
     private final static int MAX_REDIRECTS = 20;
+    private AtomicBoolean mIsConnected = new AtomicBoolean(false);
 
     @UnsupportedAppUsage
     public MediaHTTPConnection() {
@@ -90,6 +91,7 @@
             mAllowCrossDomainRedirect = true;
             mURL = new URL(uri);
             mHeaders = convertHeaderStringToMap(headers);
+            mIsConnected.set(true);
         } catch (MalformedURLException e) {
             return null;
         }
@@ -140,7 +142,14 @@
     @Override
     @UnsupportedAppUsage
     public void disconnect() {
-        teardownConnection();
+        if (mIsConnected.getAndSet(false)) {
+            (new Thread() {
+                @Override
+                public void run() {
+                    teardownConnection();
+                }
+            }).start();
+        }
         mHeaders = null;
         mURL = null;
     }
@@ -325,7 +334,14 @@
     @Override
     @UnsupportedAppUsage
     public int readAt(long offset, int size) {
-        return native_readAt(offset, size);
+        if (!mIsConnected.get()) {
+            return -1;
+        }
+        int result = native_readAt(offset, size);
+        if (!mIsConnected.get()) {
+            return -1;
+        }
+        return result;
     }
 
     private int readAt(long offset, byte[] data, int size) {
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/MicrophoneDirection.java b/media/java/android/media/MicrophoneDirection.java
index 99201c0..489e268 100644
--- a/media/java/android/media/MicrophoneDirection.java
+++ b/media/java/android/media/MicrophoneDirection.java
@@ -16,38 +16,46 @@
 
 package android.media;
 
-/**
- * @hide
- */
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 public interface MicrophoneDirection {
     /**
-     * @hide
+     * Don't do any directionality processing of the activated microphone(s).
      */
     int MIC_DIRECTION_UNSPECIFIED = 0;
-
     /**
-     * @hide
+     * Optimize capture for audio coming from the screen-side of the device.
      */
     int MIC_DIRECTION_FRONT = 1;
-
     /**
-     * @hide
+     * Optimize capture for audio coming from the side of the device opposite the screen.
      */
     int MIC_DIRECTION_BACK = 2;
-
     /**
-     * @hide
+     * Optimize capture for audio coming from an off-device microphone.
      */
     int MIC_DIRECTION_EXTERNAL = 3;
 
+    /** @hide */
+    @IntDef({
+            MIC_DIRECTION_UNSPECIFIED,
+            MIC_DIRECTION_FRONT,
+            MIC_DIRECTION_BACK,
+            MIC_DIRECTION_EXTERNAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Directionmode{};
     /**
      * Specifies the logical microphone (for processing).
      *
-     * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*)
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @param direction Direction constant.
+     * @return true if sucessful.
      */
-    int setMicrophoneDirection(int direction);
+    boolean setMicrophoneDirection(@Directionmode int direction);
 
     /**
      * Specifies the zoom factor (i.e. the field dimension) for the selected microphone
@@ -55,8 +63,7 @@
      *
      * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle),
      * though 0 (no zoom) to 1 (maximum zoom).
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @return true if sucessful.
      */
-    int setMicrophoneFieldDimension(float zoom);
+    boolean setMicrophoneFieldDimension(@FloatRange(from = -1.0, to = 1.0) float zoom);
 }
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/media/native/midi/Android.bp b/media/native/midi/Android.bp
index d069bd2..58317ed 100644
--- a/media/native/midi/Android.bp
+++ b/media/native/midi/Android.bp
@@ -16,7 +16,7 @@
     name: "libamidi",
 
     srcs: [
-        "midi.cpp",
+        "amidi.cpp",
         ":IMidiDeviceServer.aidl",
     ],
 
@@ -48,10 +48,10 @@
 
     from: "include",
 
-    to: "amidi",
+    to: "",
 
-    srcs: ["include/midi.h"],
-    license: "include/NOTICE",
+    srcs: ["include/amidi/AMidi.h"],
+    license: "include/amidi/NOTICE",
 }
 
 ndk_library {
diff --git a/media/native/midi/midi.cpp b/media/native/midi/amidi.cpp
similarity index 99%
rename from media/native/midi/midi.cpp
rename to media/native/midi/amidi.cpp
index a5bdba8..1e9a194 100644
--- a/media/native/midi/midi.cpp
+++ b/media/native/midi/amidi.cpp
@@ -28,8 +28,8 @@
 #include "android/media/midi/BpMidiDeviceServer.h"
 #include "media/MidiDeviceInfo.h"
 
-#include "include/midi.h"
-#include "midi_internal.h"
+#include "include/amidi/AMidi.h"
+#include "amidi_internal.h"
 
 using namespace android::media::midi;
 
diff --git a/media/native/midi/midi_internal.h b/media/native/midi/amidi_internal.h
similarity index 93%
rename from media/native/midi/midi_internal.h
rename to media/native/midi/amidi_internal.h
index cb3ecce..fce8596 100644
--- a/media/native/midi/midi_internal.h
+++ b/media/native/midi/amidi_internal.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_MEDIA_MIDI_INTERNAL_H_
-#define ANDROID_MEDIA_MIDI_INTERNAL_H_
+#ifndef ANDROID_MEDIA_AMIDI_INTERNAL_H_
+#define ANDROID_MEDIA_AMIDI_INTERNAL_H_
 
 #include <jni.h>
 
@@ -38,4 +38,4 @@
     AMidiDeviceInfo deviceInfo; /* Attributes of the device. */
 };
 
-#endif // ANDROID_MEDIA_MIDI_INTERNAL_H_
+#endif // ANDROID_MEDIA_AMIDI_INTERNAL_H_
diff --git a/media/native/midi/include/midi.h b/media/native/midi/include/amidi/AMidi.h
similarity index 79%
rename from media/native/midi/include/midi.h
rename to media/native/midi/include/amidi/AMidi.h
index 755d09f..0d60b0d 100644
--- a/media/native/midi/include/midi.h
+++ b/media/native/midi/include/amidi/AMidi.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_MEDIA_MIDI_H_
-#define ANDROID_MEDIA_MIDI_H_
+#ifndef ANDROID_MEDIA_AMIDI_H_
+#define ANDROID_MEDIA_AMIDI_H_
 
 #include <stdarg.h>
 #include <stdint.h>
@@ -66,9 +66,9 @@
  * @param outDevicePtrPtr  Points to the pointer to receive the AMidiDevice
  *
  * @return AMEDIA_OK on success, or a negative error value:
- *  @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT} - the midiDeviceObj
+ *  @see AMEDIA_ERROR_INVALID_OBJECT - the midiDeviceObj
  *    is null or already connected to a native AMidiDevice
-  *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - an unknown error occurred.
+  *  @see AMEDIA_ERROR_UNKNOWN - an unknown error occurred.
  */
 media_status_t AMIDI_API AMidiDevice_fromJava(
         JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr) __INTRODUCED_IN(29);
@@ -80,13 +80,10 @@
  *
  * @return AMEDIA_OK on success,
  * or a negative error value:
- *  @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER}
- *  - the device parameter is NULL.
- *  @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT}
- *  - the device is not consistent with the associated Java MidiDevice.
- *  @see AMEDIA_ERROR_INVALID_OBJECT {@link AMEDIA_ERROR_INVALID_OBJECT}
- *  - the JNI interface initialization to the associated java MidiDevice failed.
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info.
+ *  @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL.
+ *  @see AMEDIA_ERROR_INVALID_OBJECT - the device is not consistent with the associated Java MidiDevice.
+ *  @see AMEDIA_ERROR_INVALID_OBJECT - the JNI interface initialization to the associated java MidiDevice failed.
+ *  @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info.
  */
 media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice) __INTRODUCED_IN(29);
 
@@ -100,9 +97,8 @@
  *  AMIDI_DEVICE_TYPE_VIRTUAL
  *  AMIDI_DEVICE_TYPE_BLUETOOTH
  * or a negative error value:
- *  @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device
- *  parameter is NULL.
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown error.
+ *  @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL.
+ *  @see AMEDIA_ERROR_UNKNOWN - Unknown error.
  */
 int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) __INTRODUCED_IN(29);
 
@@ -113,9 +109,8 @@
  *
  * @return If successful, returns the number of MIDI input (sending) ports available on the
  * device. If an error occurs, returns a negative value indicating the error:
- *  @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device
- *  parameter is NULL.
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info.
+ *  @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL.
+ *  @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info.
  */
 ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
 
@@ -126,9 +121,8 @@
  *
  * @return If successful, returns the number of MIDI output (receiving) ports available on the
  * device. If an error occurs, returns a negative value indicating the error:
- *  @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} - the device
- *  parameter is NULL.
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN}- couldn't retrieve the device info.
+ *  @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL.
+ *  @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info.
  */
 ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
 
@@ -146,7 +140,7 @@
  * @param outOutputPortPtr Receives the native API port identifier of the opened port.
  *
  * @return AMEDIA_OK, or a negative error code:
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
+ *  @see AMEDIA_ERROR_UNKNOWN - Unknown Error.
  */
 media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
                              AMidiOutputPort **outOutputPortPtr) __INTRODUCED_IN(29);
@@ -174,7 +168,7 @@
  *  (the current value of the running Java Virtual Machine's high-resolution time source,
  *  in nanoseconds)
  * @return the number of messages received (either 0 or 1), or a negative error code:
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
+ *  @see AMEDIA_ERROR_UNKNOWN - Unknown Error.
  */
 ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
          uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *outTimestampPtr) __INTRODUCED_IN(29);
@@ -193,7 +187,7 @@
  * @param outInputPortPtr Receives the native API port identifier of the opened port.
  *
  * @return AMEDIA_OK, or a negative error code:
- *  @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
+ *  @see AMEDIA_ERROR_UNKNOWN - Unknown Error.
  */
 media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
                             AMidiInputPort **outInputPortPtr) __INTRODUCED_IN(29);
@@ -206,8 +200,7 @@
  * @param numBytes     Specifies the number of bytes to write.
  *
  * @return The number of bytes sent, which could be less than specified or a negative error code:
- * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port
- *   was NULL, the specified buffer was NULL.
+ * @see AMEDIA_ERROR_INVALID_PARAMETER - The specified port was NULL, the specified buffer was NULL.
  */
 ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
                    size_t numBytes) __INTRODUCED_IN(29);
@@ -221,8 +214,7 @@
  * @param timestamp    The CLOCK_MONOTONIC time in nanoseconds to associate with the sent data.
  *
  * @return The number of bytes sent, which could be less than specified or a negative error code:
- * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port
- *   was NULL, the specified buffer was NULL.
+ * @see AMEDIA_ERROR_INVALID_PARAMETER - The specified port was NULL, the specified buffer was NULL.
  */
 ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
         const uint8_t *buffer, size_t numBytes, int64_t timestamp) __INTRODUCED_IN(29);
@@ -233,10 +225,9 @@
  *
  * @param inputPort The identifier of the port to send the flush command to.
  *
- * @returns @see AMEDIA_OK {@link AMEDIA_OK} if successful, otherwise a negative error code:
- * @see AMEDIA_ERROR_INVALID_PARAMETER {@link AMEDIA_ERROR_INVALID_PARAMETER} The specified port
- *   was NULL
- * @see AMEDIA_ERROR_UNSUPPORTED {@link AMEDIA_ERROR_UNSUPPORTED} The FLUSH command couldn't
+ * @returns @see AMEDIA_OK if successful, otherwise a negative error code:
+ * @see AMEDIA_ERROR_INVALID_PARAMETER - The specified port was NULL
+ * @see AMEDIA_ERROR_UNSUPPORTED - The FLUSH command couldn't
  * be sent.
  */
 media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
@@ -252,4 +243,4 @@
 }
 #endif
 
-#endif /* ANDROID_MEDIA_MIDI_H_ */
+#endif /* ANDROID_MEDIA_AMIDI_H_ */
diff --git a/media/native/midi/include/NOTICE b/media/native/midi/include/amidi/NOTICE
similarity index 100%
rename from media/native/midi/include/NOTICE
rename to media/native/midi/include/amidi/NOTICE
diff --git a/packages/CaptivePortalLogin/Android.bp b/packages/CaptivePortalLogin/Android.bp
index 7acdfa1..a71977f 100644
--- a/packages/CaptivePortalLogin/Android.bp
+++ b/packages/CaptivePortalLogin/Android.bp
@@ -18,7 +18,7 @@
     name: "CaptivePortalLogin",
     srcs: ["src/**/*.java"],
     sdk_version: "system_current",
-    certificate: "platform",
+    certificate: "networkstack",
     static_libs: [
         "androidx.legacy_legacy-support-v4",
         "metrics-constants-protos",
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index e15dca0..0894ee5 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -23,8 +23,8 @@
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
-    <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <uses-permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" />
+    <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
 
     <application android:label="@string/app_name"
                  android:usesCleartextTraffic="true"
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/DynamicAndroidInstallationService/Android.mk b/packages/DynamicAndroidInstallationService/Android.mk
new file mode 100644
index 0000000..13d96ac
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/Android.mk
@@ -0,0 +1,19 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, res)
+
+LOCAL_USE_AAPT2 := true
+
+LOCAL_PACKAGE_NAME := DynamicAndroidInstallationService
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+
+include $(BUILD_PACKAGE)
diff --git a/packages/DynamicAndroidInstallationService/AndroidManifest.xml b/packages/DynamicAndroidInstallationService/AndroidManifest.xml
new file mode 100644
index 0000000..1c1c72c
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.dynandroid"
+        android:sharedUserId="android.uid.system">
+
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.MANAGE_DYNAMNIC_ANDROID" />
+    <uses-permission android:name="android.permission.REBOOT" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+
+    <application
+        android:allowBackup="false"
+        android:label="@string/app_name">
+
+        <service
+            android:name=".DynamicAndroidInstallationService"
+            android:enabled="true"
+            android:exported="true"
+            android:permission="android.permission.MANAGE_DYNAMNIC_ANDROID"
+            android:process=":dynandroid">
+            <intent-filter>
+                <action android:name="android.content.action.NOTIFY_IF_IN_USE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </service>
+
+        <activity android:name=".VerificationActivity"
+            android:exported="true"
+            android:permission="android.permission.MANAGE_DYNAMNIC_ANDROID"
+            android:theme="@android:style/Theme.Material.Light.Dialog.NoActionBar"
+            android:process=":dynandroid">
+            <intent-filter>
+                <action android:name="android.content.action.START_INSTALL" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <receiver
+            android:name=".BootCompletedReceiver"
+            android:enabled="true"
+            android:exported="false">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
diff --git a/packages/DynamicAndroidInstallationService/MODULE_LICENSE_APACHE2 b/packages/DynamicAndroidInstallationService/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/MODULE_LICENSE_APACHE2
diff --git a/packages/DynamicAndroidInstallationService/NOTICE b/packages/DynamicAndroidInstallationService/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/packages/DynamicAndroidInstallationService/res/drawable/ic_system_update_googblue_24dp.xml b/packages/DynamicAndroidInstallationService/res/drawable/ic_system_update_googblue_24dp.xml
new file mode 100644
index 0000000..acf1567
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/res/drawable/ic_system_update_googblue_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19L7,19L7,5h10v14zM16,13h-3L13,8h-2v5L8,13l4,4 4,-4z"
+      android:fillColor="#4285F4"/>
+</vector>
diff --git a/packages/DynamicAndroidInstallationService/res/values/strings.xml b/packages/DynamicAndroidInstallationService/res/values/strings.xml
new file mode 100644
index 0000000..221e1d7
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/res/values/strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- application name [CHAR LIMIT=32] -->
+    <string name="app_name">AndroidOnTap Installer</string>
+
+    <!-- notification channel name [CHAR LIMIT=32] -->
+    <string name="notification_channel_name">AndroidOnTap Installer</string>
+
+    <!-- password page title [CHAR LIMIT=32] -->
+    <string name="keyguard_title">AndroidOnTap Installer</string>
+
+    <!-- password page description [CHAR LIMIT=128] -->
+    <string name="keyguard_description">Please enter your password and continue to AndroidOnTap installation</string>
+
+    <!-- Displayed on notification: DynAndroid installation is completed [CHAR LIMIT=128] -->
+    <string name="notification_install_completed">Installation is completed, you can reboot into the new installed system now.</string>
+    <!-- Displayed on notification: DynAndroid installation is in progress [CHAR LIMIT=128] -->
+    <string name="notification_install_inprogress">Installation is in progress.</string>
+    <!-- Displayed on notification: DynAndroid installation is in progress [CHAR LIMIT=128] -->
+    <string name="notification_install_failed">Installation Failed.</string>
+    <!-- Displayed on notification: We are running in AndroidOnTap [CHAR LIMIT=128] -->
+    <string name="notification_dynandroid_in_use">We are running in AndroidOnTap.</string>
+
+    <!-- Action on notification: Cancel installation [CHAR LIMIT=16] -->
+    <string name="notification_action_cancel">Cancel</string>
+    <!-- Action on notification: Discard installation [CHAR LIMIT=16] -->
+    <string name="notification_action_discard">Discard</string>
+    <!-- Action on notification: Uninstall AndroidOnTap [CHAR LIMIT=16] -->
+    <string name="notification_action_uninstall">Uninstall</string>
+    <!-- Action on notification: Reboot to AndroidOnTap [CHAR LIMIT=16] -->
+    <string name="notification_action_reboot_to_dynandroid">Reboot</string>
+
+</resources>
diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java
new file mode 100644
index 0000000..dd1be89
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/BootCompletedReceiver.java
@@ -0,0 +1,50 @@
+/*
+ * 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.dynandroid;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DynamicAndroidClient;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.util.Log;
+
+
+/**
+ * A BoardcastReceiver waiting for ACTION_BOOT_COMPLETED and ask
+ * the service to display a notification if we are currently running
+ * in DynamicAndroid.
+ */
+public class BootCompletedReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "BootCompletedReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+
+        Log.d(TAG, "Broadcast received: " + action);
+
+        if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+            Intent startServiceIntent = new Intent(
+                    context, DynamicAndroidInstallationService.class);
+
+            startServiceIntent.setAction(DynamicAndroidClient.ACTION_NOTIFY_IF_IN_USE);
+            context.startServiceAsUser(startServiceIntent, UserHandle.SYSTEM);
+        }
+    }
+}
diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/DynamicAndroidInstallationService.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/DynamicAndroidInstallationService.java
new file mode 100644
index 0000000..7755cbc
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/DynamicAndroidInstallationService.java
@@ -0,0 +1,483 @@
+/*
+ * 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.dynandroid;
+
+import static android.content.DynamicAndroidClient.ACTION_NOTIFY_IF_IN_USE;
+import static android.content.DynamicAndroidClient.ACTION_START_INSTALL;
+import static android.content.DynamicAndroidClient.CAUSE_ERROR_EXCEPTION;
+import static android.content.DynamicAndroidClient.CAUSE_ERROR_INVALID_URL;
+import static android.content.DynamicAndroidClient.CAUSE_ERROR_IO;
+import static android.content.DynamicAndroidClient.CAUSE_INSTALL_CANCELLED;
+import static android.content.DynamicAndroidClient.CAUSE_INSTALL_COMPLETED;
+import static android.content.DynamicAndroidClient.CAUSE_NOT_SPECIFIED;
+import static android.content.DynamicAndroidClient.STATUS_IN_PROGRESS;
+import static android.content.DynamicAndroidClient.STATUS_IN_USE;
+import static android.content.DynamicAndroidClient.STATUS_NOT_STARTED;
+import static android.content.DynamicAndroidClient.STATUS_READY;
+import static android.os.AsyncTask.Status.FINISHED;
+import static android.os.AsyncTask.Status.PENDING;
+import static android.os.AsyncTask.Status.RUNNING;
+
+import static com.android.dynandroid.InstallationAsyncTask.RESULT_ERROR_EXCEPTION;
+import static com.android.dynandroid.InstallationAsyncTask.RESULT_ERROR_INVALID_URL;
+import static com.android.dynandroid.InstallationAsyncTask.RESULT_ERROR_IO;
+import static com.android.dynandroid.InstallationAsyncTask.RESULT_OK;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.DynamicAndroidClient;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.DynamicAndroidManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * This class is the service in charge of DynamicAndroid installation.
+ * It also posts status to notification bar and wait for user's
+ * cancel and confirm commnands.
+ */
+public class DynamicAndroidInstallationService extends Service
+        implements InstallationAsyncTask.InstallStatusListener {
+
+    private static final String TAG = "DynAndroidInstallationService";
+
+    /*
+     * Intent actions
+     */
+    private static final String ACTION_CANCEL_INSTALL =
+            "com.android.dynandroid.ACTION_CANCEL_INSTALL";
+    private static final String ACTION_REBOOT_TO_DYN_ANDROID =
+            "com.android.dynandroid.ACTION_REBOOT_TO_DYN_ANDROID";
+    private static final String ACTION_REBOOT_TO_NORMAL =
+            "com.android.dynandroid.ACTION_REBOOT_TO_NORMAL";
+
+    /*
+     * For notification
+     */
+    private static final String NOTIFICATION_CHANNEL_ID = "com.android.dynandroid";
+    private static final int NOTIFICATION_ID = 1;
+
+    /*
+     * IPC
+     */
+    /** Keeps track of all current registered clients. */
+    ArrayList<Messenger> mClients = new ArrayList<>();
+
+    /** Handler of incoming messages from clients. */
+    final Messenger mMessenger = new Messenger(new IncomingHandler(this));
+
+    static class IncomingHandler extends Handler {
+        private final WeakReference<DynamicAndroidInstallationService> mWeakService;
+
+        IncomingHandler(DynamicAndroidInstallationService service) {
+            mWeakService = new WeakReference<>(service);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            DynamicAndroidInstallationService service = mWeakService.get();
+
+            if (service != null) {
+                service.handleMessage(msg);
+            }
+        }
+    }
+
+    private DynamicAndroidManager mDynAndroid;
+    private NotificationManager mNM;
+
+    private long mSystemSize;
+    private long mInstalledSize;
+    private boolean mJustCancelledByUser;
+
+    private PendingIntent mPiCancel;
+    private PendingIntent mPiRebootToDynamicAndroid;
+    private PendingIntent mPiUninstallAndReboot;
+
+    private InstallationAsyncTask mInstallTask;
+
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        prepareNotification();
+
+        mDynAndroid = (DynamicAndroidManager) getSystemService(Context.DYNAMIC_ANDROID_SERVICE);
+    }
+
+    @Override
+    public void onDestroy() {
+        // Cancel the persistent notification.
+        mNM.cancel(NOTIFICATION_ID);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        String action = intent.getAction();
+
+        Log.d(TAG, "onStartCommand(): action=" + action);
+
+        if (ACTION_START_INSTALL.equals(action)) {
+            executeInstallCommand(intent);
+        } else if (ACTION_CANCEL_INSTALL.equals(action)) {
+            executeCancelCommand();
+        } else if (ACTION_REBOOT_TO_DYN_ANDROID.equals(action)) {
+            executeRebootToDynAndroidCommand();
+        } else if (ACTION_REBOOT_TO_NORMAL.equals(action)) {
+            executeRebootToNormalCommand();
+        } else if (ACTION_NOTIFY_IF_IN_USE.equals(action)) {
+            executeNotifyIfInUseCommand();
+        }
+
+        return Service.START_NOT_STICKY;
+    }
+
+    @Override
+    public void onProgressUpdate(long installedSize) {
+        mInstalledSize = installedSize;
+        postStatus(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED);
+    }
+
+    @Override
+    public void onResult(int result) {
+        if (result == RESULT_OK) {
+            postStatus(STATUS_READY, CAUSE_INSTALL_COMPLETED);
+            return;
+        }
+
+        // if it's not successful, reset the task and stop self.
+        resetTaskAndStop();
+
+        switch (result) {
+            case RESULT_ERROR_IO:
+                postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_IO);
+                break;
+
+            case RESULT_ERROR_INVALID_URL:
+                postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_INVALID_URL);
+                break;
+
+            case RESULT_ERROR_EXCEPTION:
+                postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_EXCEPTION);
+                break;
+        }
+    }
+
+    @Override
+    public void onCancelled() {
+        resetTaskAndStop();
+        postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED);
+    }
+
+    private void executeInstallCommand(Intent intent) {
+        if (!verifyRequest(intent)) {
+            Log.e(TAG, "Verification failed. Did you use VerificationActivity?");
+            return;
+        }
+
+        if (mInstallTask != null) {
+            Log.e(TAG, "There is already an install task running");
+            return;
+        }
+
+        if (isInDynamicAndroid()) {
+            Log.e(TAG, "We are already running in DynamicAndroid");
+            return;
+        }
+
+        String url = intent.getStringExtra(DynamicAndroidClient.KEY_SYSTEM_URL);
+        mSystemSize = intent.getLongExtra(DynamicAndroidClient.KEY_SYSTEM_SIZE, 0);
+        long userdata = intent.getLongExtra(DynamicAndroidClient.KEY_USERDATA_SIZE, 0);
+
+        mInstallTask = new InstallationAsyncTask(url, mSystemSize, userdata, mDynAndroid, this);
+        mInstallTask.execute();
+
+        // start fore ground
+        startForeground(NOTIFICATION_ID,
+                buildNotification(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED));
+    }
+
+    private void executeCancelCommand() {
+        if (mInstallTask == null || mInstallTask.getStatus() == PENDING) {
+            Log.e(TAG, "Cancel command triggered, but there is no task running");
+            mNM.cancel(NOTIFICATION_ID);
+
+            return;
+        }
+
+        mJustCancelledByUser = true;
+
+        if (mInstallTask.cancel(false)) {
+            // Will cleanup and post status in onCancelled()
+            Log.d(TAG, "Cancel request filed successfully");
+        } else {
+            Log.d(TAG, "Requested cancel, completed task will be discarded");
+
+            resetTaskAndStop();
+            postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED);
+        }
+
+    }
+
+    private void executeRebootToDynAndroidCommand() {
+        if (mInstallTask == null || mInstallTask.getStatus() != FINISHED) {
+            Log.e(TAG, "Trying to reboot to DynamicAndroid, but there is no complete installation");
+            return;
+        }
+
+        if (!mInstallTask.commit()) {
+            // TODO: b/123673280 better UI response
+            Log.e(TAG, "Failed to commit installation because of native runtime error.");
+            mNM.cancel(NOTIFICATION_ID);
+
+            return;
+        }
+
+        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+
+        if (powerManager != null) {
+            powerManager.reboot("dynandroid");
+        }
+    }
+
+    private void executeRebootToNormalCommand() {
+        mDynAndroid.remove();
+
+        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+
+        if (powerManager != null) {
+            powerManager.reboot(null);
+        }
+    }
+
+    private void executeNotifyIfInUseCommand() {
+        if (isInDynamicAndroid()) {
+            startForeground(NOTIFICATION_ID,
+                    buildNotification(STATUS_IN_USE, CAUSE_NOT_SPECIFIED));
+        }
+    }
+
+    private void resetTaskAndStop() {
+        mInstallTask = null;
+
+        stopForeground(true);
+
+        // stop self, but this service is not destroyed yet if it's still bound
+        stopSelf();
+    }
+
+    private void prepareNotification() {
+        NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+                getString(R.string.notification_channel_name),
+                NotificationManager.IMPORTANCE_LOW);
+
+        mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+
+        if (mNM != null) {
+            mNM.createNotificationChannel(chan);
+        }
+
+        Intent intentCancel = new Intent(this, DynamicAndroidInstallationService.class);
+        intentCancel.setAction(ACTION_CANCEL_INSTALL);
+        mPiCancel = PendingIntent.getService(this, 0, intentCancel, 0);
+
+        Intent intentRebootToDyn = new Intent(this, DynamicAndroidInstallationService.class);
+        intentRebootToDyn.setAction(ACTION_REBOOT_TO_DYN_ANDROID);
+        mPiRebootToDynamicAndroid = PendingIntent.getService(this, 0, intentRebootToDyn, 0);
+
+        Intent intentUninstallAndReboot = new Intent(this, DynamicAndroidInstallationService.class);
+        intentUninstallAndReboot.setAction(ACTION_REBOOT_TO_NORMAL);
+        mPiUninstallAndReboot = PendingIntent.getService(this, 0, intentUninstallAndReboot, 0);
+    }
+
+    private Notification buildNotification(int status, int cause) {
+        Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
+                .setSmallIcon(R.drawable.ic_system_update_googblue_24dp)
+                .setProgress(0, 0, false);
+
+        switch (status) {
+            case STATUS_IN_PROGRESS:
+                builder.setContentText(getString(R.string.notification_install_inprogress));
+
+                int max = (int) Math.max(mSystemSize >> 20, 1);
+                int progress = (int) mInstalledSize >> 20;
+
+                builder.setProgress(max, progress, false);
+
+                builder.addAction(new Notification.Action.Builder(
+                        null, getString(R.string.notification_action_cancel),
+                        mPiCancel).build());
+
+                break;
+
+            case STATUS_READY:
+                builder.setContentText(getString(R.string.notification_install_completed));
+
+                builder.addAction(new Notification.Action.Builder(
+                        null, getString(R.string.notification_action_reboot_to_dynandroid),
+                        mPiRebootToDynamicAndroid).build());
+
+                builder.addAction(new Notification.Action.Builder(
+                        null, getString(R.string.notification_action_cancel),
+                        mPiCancel).build());
+
+                break;
+
+            case STATUS_IN_USE:
+                builder.setContentText(getString(R.string.notification_dynandroid_in_use));
+
+                builder.addAction(new Notification.Action.Builder(
+                        null, getString(R.string.notification_action_uninstall),
+                        mPiUninstallAndReboot).build());
+
+                break;
+
+            case STATUS_NOT_STARTED:
+                if (cause != CAUSE_NOT_SPECIFIED && cause != CAUSE_INSTALL_CANCELLED) {
+                    builder.setContentText(getString(R.string.notification_install_failed));
+                } else {
+                    // no need to notify the user if the task is not started, or cancelled.
+                }
+                break;
+
+            default:
+                throw new IllegalStateException("status is invalid");
+        }
+
+        return builder.build();
+    }
+
+    private boolean verifyRequest(Intent intent) {
+        String url = intent.getStringExtra(DynamicAndroidClient.KEY_SYSTEM_URL);
+
+        return VerificationActivity.isVerified(url);
+    }
+
+    private void postStatus(int status, int cause) {
+        Log.d(TAG, "postStatus(): statusCode=" + status + ", causeCode=" + cause);
+
+        boolean notifyOnNotificationBar = true;
+
+        if (status == STATUS_NOT_STARTED
+                && cause == CAUSE_INSTALL_CANCELLED
+                && mJustCancelledByUser) {
+            // if task is cancelled by user, do not notify them
+            notifyOnNotificationBar = false;
+            mJustCancelledByUser = false;
+        }
+
+        if (notifyOnNotificationBar) {
+            mNM.notify(NOTIFICATION_ID, buildNotification(status, cause));
+        }
+
+        for (int i = mClients.size() - 1; i >= 0; i--) {
+            try {
+                notifyOneClient(mClients.get(i), status, cause);
+            } catch (RemoteException e) {
+                mClients.remove(i);
+            }
+        }
+    }
+
+    private void notifyOneClient(Messenger client, int status, int cause) throws RemoteException {
+        Bundle bundle = new Bundle();
+
+        bundle.putLong(DynamicAndroidClient.KEY_INSTALLED_SIZE, mInstalledSize);
+
+        client.send(Message.obtain(null,
+                  DynamicAndroidClient.MSG_POST_STATUS, status, cause, bundle));
+    }
+
+    private int getStatus() {
+        if (isInDynamicAndroid()) {
+            return STATUS_IN_USE;
+
+        } else if (mInstallTask == null) {
+            return STATUS_NOT_STARTED;
+
+        }
+
+        switch (mInstallTask.getStatus()) {
+            case PENDING:
+                return STATUS_NOT_STARTED;
+
+            case RUNNING:
+                return STATUS_IN_PROGRESS;
+
+            case FINISHED:
+                int result = mInstallTask.getResult();
+
+                if (result == RESULT_OK) {
+                    return STATUS_READY;
+                } else {
+                    throw new IllegalStateException("A failed InstallationTask is not reset");
+                }
+
+            default:
+                return STATUS_NOT_STARTED;
+        }
+    }
+
+    private boolean isInDynamicAndroid() {
+        return mDynAndroid.isInUse();
+    }
+
+    void handleMessage(Message msg) {
+        switch (msg.what) {
+            case DynamicAndroidClient.MSG_REGISTER_LISTENER:
+                try {
+                    Messenger client = msg.replyTo;
+
+                    int status = getStatus();
+
+                    // tell just registered client my status, but do not specify cause
+                    notifyOneClient(client, status, CAUSE_NOT_SPECIFIED);
+
+                    mClients.add(client);
+                } catch (RemoteException e) {
+                    // do nothing if we cannot send update to the client
+                    e.printStackTrace();
+                }
+
+                break;
+            case DynamicAndroidClient.MSG_UNREGISTER_LISTENER:
+                mClients.remove(msg.replyTo);
+                break;
+            default:
+                // do nothing
+        }
+    }
+}
diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
new file mode 100644
index 0000000..3c759e9
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
@@ -0,0 +1,205 @@
+/*
+ * 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.dynandroid;
+
+import android.os.AsyncTask;
+import android.os.DynamicAndroidManager;
+import android.util.Log;
+import android.webkit.URLUtil;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.zip.GZIPInputStream;
+
+
+class InstallationAsyncTask extends AsyncTask<String, Long, Integer> {
+
+    private static final String TAG = "InstallationAsyncTask";
+
+    private static final int READ_BUFFER_SIZE = 1 << 19;
+
+    private class InvalidImageUrlException extends RuntimeException {
+        private InvalidImageUrlException(String message) {
+            super(message);
+        }
+    }
+
+
+    /** Not completed, including being cancelled */
+    static final int NO_RESULT = 0;
+    static final int RESULT_OK = 1;
+    static final int RESULT_ERROR_IO = 2;
+    static final int RESULT_ERROR_INVALID_URL = 3;
+    static final int RESULT_ERROR_EXCEPTION = 6;
+
+    interface InstallStatusListener {
+        void onProgressUpdate(long installedSize);
+        void onResult(int resultCode);
+        void onCancelled();
+    }
+
+    private final String mUrl;
+    private final long mSystemSize;
+    private final long mUserdataSize;
+    private final DynamicAndroidManager mDynamicAndroid;
+    private final InstallStatusListener mListener;
+    private DynamicAndroidManager.Session mInstallationSession;
+
+    private long mInstalledSize;
+    private long mReportedInstalledSize;
+    private int mResult = NO_RESULT;
+
+    private InputStream mStream;
+
+
+    InstallationAsyncTask(String url, long systemSize, long userdataSize,
+            DynamicAndroidManager dynAndroid, InstallStatusListener listener) {
+        mUrl = url;
+        mSystemSize = systemSize;
+        mUserdataSize = userdataSize;
+        mDynamicAndroid = dynAndroid;
+        mListener = listener;
+    }
+
+    @Override
+    protected void onPreExecute() {
+        mListener.onProgressUpdate(0);
+    }
+
+    @Override
+    protected Integer doInBackground(String... voids) {
+        Log.d(TAG, "Start doInBackground(), URL: " + mUrl);
+
+        try {
+            // call start in background
+            mInstallationSession = mDynamicAndroid.startInstallation(mSystemSize, mUserdataSize);
+
+            if (mInstallationSession == null) {
+                Log.e(TAG, "Failed to start installation with requested size: "
+                        + (mSystemSize + mUserdataSize));
+
+                return RESULT_ERROR_IO;
+            }
+
+            initInputStream();
+
+            byte[] bytes = new byte[READ_BUFFER_SIZE];
+
+            int numBytesRead;
+            long minStepToReport = mSystemSize / 100;
+
+            Log.d(TAG, "Start installation loop");
+            while ((numBytesRead = mStream.read(bytes, 0, READ_BUFFER_SIZE)) != -1) {
+                if (isCancelled()) {
+                    break;
+                }
+
+                byte[] writeBuffer = numBytesRead == READ_BUFFER_SIZE
+                        ? bytes : Arrays.copyOf(bytes, numBytesRead);
+
+                if (!mInstallationSession.write(writeBuffer)) {
+                    throw new IOException("Failed write() to DynamicAndroid");
+                }
+
+                mInstalledSize += numBytesRead;
+
+                if (mInstalledSize > mReportedInstalledSize + minStepToReport) {
+                    publishProgress(mInstalledSize);
+                    mReportedInstalledSize = mInstalledSize;
+                }
+            }
+
+            return RESULT_OK;
+
+        } catch (IOException e) {
+            e.printStackTrace();
+            return RESULT_ERROR_IO;
+
+        } catch (InvalidImageUrlException e) {
+            e.printStackTrace();
+            return RESULT_ERROR_INVALID_URL;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return RESULT_ERROR_EXCEPTION;
+
+        } finally {
+            close();
+        }
+    }
+
+    @Override
+    protected void onCancelled() {
+        Log.d(TAG, "onCancelled(), URL: " + mUrl);
+
+        close();
+
+        mListener.onCancelled();
+    }
+
+    @Override
+    protected void onPostExecute(Integer result) {
+        Log.d(TAG, "onPostExecute(), URL: " + mUrl + ", result: " + result);
+
+        close();
+
+        mResult = result;
+        mListener.onResult(mResult);
+    }
+
+    @Override
+    protected void onProgressUpdate(Long... values) {
+        long progress = values[0];
+        mListener.onProgressUpdate(progress);
+    }
+
+    private void initInputStream() throws IOException, InvalidImageUrlException {
+        if (URLUtil.isNetworkUrl(mUrl) || URLUtil.isFileUrl(mUrl)) {
+            mStream = new BufferedInputStream(new GZIPInputStream(new URL(mUrl).openStream()));
+        } else {
+            throw new InvalidImageUrlException(
+                    String.format(Locale.US, "Unsupported file source: %s", mUrl));
+        }
+    }
+
+    private void close() {
+        try {
+            if (mStream != null) {
+                mStream.close();
+                mStream = null;
+            }
+        } catch (IOException e) {
+            // ignore
+        }
+    }
+
+    int getResult() {
+        return mResult;
+    }
+
+    boolean commit() {
+        if (mInstallationSession == null) {
+            return false;
+        }
+
+        return mInstallationSession.commit();
+    }
+}
diff --git a/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/VerificationActivity.java b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/VerificationActivity.java
new file mode 100644
index 0000000..c18c4fe
--- /dev/null
+++ b/packages/DynamicAndroidInstallationService/src/com/android/dynandroid/VerificationActivity.java
@@ -0,0 +1,104 @@
+/*
+ * 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.dynandroid;
+
+import static android.content.DynamicAndroidClient.KEY_SYSTEM_SIZE;
+import static android.content.DynamicAndroidClient.KEY_SYSTEM_URL;
+import static android.content.DynamicAndroidClient.KEY_USERDATA_SIZE;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.DynamicAndroidClient;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+
+
+/**
+ * This Activity starts KeyguardManager and ask the user to confirm
+ * before any installation request. If the device is not protected by
+ * a password, it approves the request by default.
+ */
+public class VerificationActivity extends Activity {
+
+    private static final String TAG = "VerificationActivity";
+
+    private static final int REQUEST_CODE = 1;
+
+    // For install request verification
+    private static String sVerifiedUrl;
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+
+        if (km != null) {
+            String title = getString(R.string.keyguard_title);
+            String description = getString(R.string.keyguard_description);
+            Intent intent = km.createConfirmDeviceCredentialIntent(title, description);
+
+            if (intent == null) {
+                Log.d(TAG, "This device is not protected by a password/pin");
+                startInstallationService();
+                finish();
+            } else {
+                startActivityForResult(intent, REQUEST_CODE);
+            }
+        } else {
+            finish();
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
+            startInstallationService();
+        }
+
+        finish();
+    }
+
+    private void startInstallationService() {
+        // retrieve data from calling intent
+        Intent callingIntent = getIntent();
+
+        String url = callingIntent.getStringExtra(KEY_SYSTEM_URL);
+        long systemSize = callingIntent.getLongExtra(KEY_SYSTEM_SIZE, 0);
+        long userdataSize = callingIntent.getLongExtra(KEY_USERDATA_SIZE, 0);
+
+        sVerifiedUrl = url;
+
+        // start service
+        Intent intent = new Intent(this, DynamicAndroidInstallationService.class);
+        intent.setAction(DynamicAndroidClient.ACTION_START_INSTALL);
+        intent.putExtra(KEY_SYSTEM_URL, url);
+        intent.putExtra(KEY_SYSTEM_SIZE, systemSize);
+        intent.putExtra(KEY_USERDATA_SIZE, userdataSize);
+
+        Log.d(TAG, "Starting Installation Service");
+        startServiceAsUser(intent, UserHandle.SYSTEM);
+    }
+
+    static boolean isVerified(String url) {
+        return sVerifiedUrl != null && sVerifiedUrl.equals(url);
+    }
+}
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index c5e598d..31a3ff4 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -221,6 +221,12 @@
     }
 
     @Override
+    public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
+        // we use the version with channel, so this is never called.
+        return null;
+    }
+
+    @Override
     public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
             NotificationChannel channel) {
         if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey() + " on " + channel.getId());
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index d656593..b700bf3 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -35,11 +35,12 @@
 android_app {
     name: "NetworkStack",
     sdk_version: "system_current",
-    certificate: "platform",
+    certificate: "networkstack",
     privileged: true,
     static_libs: [
         "NetworkStackLib"
     ],
+    jarjar_rules: "jarjar-rules-shared.txt",
     manifest: "AndroidManifest.xml",
     required: ["NetworkStackPermissionStub"],
 }
\ No newline at end of file
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index e4d3591..0476712 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -24,13 +24,12 @@
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
-    <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <!-- Signature permission defined in NetworkStackStub -->
     <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
     <!-- Send latency broadcast as current user -->
     <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/jarjar-rules-shared.txt b/packages/NetworkStack/jarjar-rules-shared.txt
new file mode 100644
index 0000000..a8c712a
--- /dev/null
+++ b/packages/NetworkStack/jarjar-rules-shared.txt
@@ -0,0 +1,19 @@
+rule com.android.internal.util.** android.net.networkstack.util.@1
+
+rule android.net.shared.Inet4AddressUtils* android.net.networkstack.shared.Inet4AddressUtils@1
+rule android.net.shared.InetAddressUtils* android.net.networkstack.shared.InetAddressUtils@1
+
+# Ignore DhcpResultsParcelable, but jarjar DhcpResults
+# TODO: move DhcpResults into services.net and delete from here
+rule android.net.DhcpResultsParcelable* @0
+rule android.net.DhcpResults* android.net.networkstack.DhcpResults@1
+rule android.net.LocalLog* android.net.networkstack.LocalLog@1
+
+# TODO: remove from framework dependencies, then remove here
+rule android.net.InterfaceConfigurationParcel* android.net.networkstack.InterfaceConfigurationParcel@1
+rule android.net.TetherStatsParcel* android.net.networkstack.TetherStatsParcel@1
+
+# Used by UidRange, which is used by framework classes such as NetworkCapabilities.
+rule android.net.UidRangeParcel* android.net.networkstack.UidRangeParcel@1
+# TODO: move TcpKeepalivePacketData to services.net and delete
+rule android.net.TcpKeepalivePacketDataParcelable* android.net.networkstack.TcpKeepalivePacketDataParcelable@1
\ No newline at end of file
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
index 96d1a28..97d26c7 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.net.shared.FdEventsReader;
+import android.net.util.FdEventsReader;
 import android.os.Handler;
 import android.system.Os;
 
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/core/java/android/net/shared/FdEventsReader.java b/packages/NetworkStack/src/android/net/util/FdEventsReader.java
similarity index 98%
rename from core/java/android/net/shared/FdEventsReader.java
rename to packages/NetworkStack/src/android/net/util/FdEventsReader.java
index bffbfb1..1380ea7 100644
--- a/core/java/android/net/shared/FdEventsReader.java
+++ b/packages/NetworkStack/src/android/net/util/FdEventsReader.java
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package android.net.shared;
+package android.net.util;
 
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.net.util.SocketUtils;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.MessageQueue;
diff --git a/packages/NetworkStack/src/android/net/util/PacketReader.java b/packages/NetworkStack/src/android/net/util/PacketReader.java
index 94b1e9f..4aec6b6 100644
--- a/packages/NetworkStack/src/android/net/util/PacketReader.java
+++ b/packages/NetworkStack/src/android/net/util/PacketReader.java
@@ -18,7 +18,6 @@
 
 import static java.lang.Math.max;
 
-import android.net.shared.FdEventsReader;
 import android.os.Handler;
 import android.system.Os;
 
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/NetworkStackPermissionStub/Android.bp b/packages/NetworkStackPermissionStub/Android.bp
index 94870c9..dd70cf5 100644
--- a/packages/NetworkStackPermissionStub/Android.bp
+++ b/packages/NetworkStackPermissionStub/Android.bp
@@ -21,7 +21,7 @@
     // a classes.dex.
     srcs: ["src/**/*.java"],
     platform_apis: true,
-    certificate: "platform",
+    certificate: "networkstack",
     privileged: true,
     manifest: "AndroidManifest.xml",
 }
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 36ee813..caa928f 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -21,6 +21,7 @@
         "SettingsLibActionButtonsPreference",
         "SettingsLibEntityHeaderWidgets",
         "SettingsLibBarChartPreference",
+        "SettingsLibProgressBar",
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
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/ProgressBar/Android.bp b/packages/SettingsLib/ProgressBar/Android.bp
new file mode 100644
index 0000000..eae21d8
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/Android.bp
@@ -0,0 +1,9 @@
+android_library {
+    name: "SettingsLibProgressBar",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/ProgressBar/AndroidManifest.xml b/packages/SettingsLib/ProgressBar/AndroidManifest.xml
new file mode 100644
index 0000000..256b8f3
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml b/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
new file mode 100644
index 0000000..2b7535a
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<!-- Variant of progress_indeterminate_horizontal_material in frameworks/base/core/res, which
+     draws the whole height of the progress bar instead having blank space above and below the
+     bar. -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@drawable/vector_drawable_progress_indeterminate_horizontal_trimmed" >
+    <target
+        android:name="rect2_grp"
+        android:animation="@*android:anim/progress_indeterminate_horizontal_rect2" />
+    <target
+        android:name="rect1_grp"
+        android:animation="@*android:anim/progress_indeterminate_horizontal_rect1" />
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/ProgressBar/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml b/packages/SettingsLib/ProgressBar/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
new file mode 100644
index 0000000..2f604d0
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<!-- Variant of vector_drawable_progress_indeterminate_horizontal in frameworks/base/core/res, which
+     draws the whole height of the progress bar instead having blank space above and below the
+     bar. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="10dp"
+        android:width="360dp"
+        android:viewportHeight="10"
+        android:viewportWidth="360" >
+    <group
+        android:name="progress_group"
+        android:translateX="180"
+        android:translateY="5" >
+        <path
+            android:name="background_track"
+            android:pathData="M -180.0,-5.0 l 360.0,0 l 0,10.0 l -360.0,0 Z"
+            android:fillColor="?android:attr/colorControlActivated"
+            android:fillAlpha="?android:attr/disabledAlpha"/>
+        <group
+            android:name="rect2_grp"
+            android:translateX="-197.60001"
+            android:scaleX="0.1" >
+            <path
+                android:name="rect2"
+                android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+                android:fillColor="?android:attr/colorControlActivated" />
+        </group>
+        <group
+            android:name="rect1_grp"
+            android:translateX="-522.59998"
+            android:scaleX="0.1" >
+            <path
+                android:name="rect1"
+                android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+                android:fillColor="?android:attr/colorControlActivated" />
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml b/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml
new file mode 100644
index 0000000..268858b
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="3dp">
+    <View
+        android:id="@+id/progress_bar_background"
+        style="@style/TrimmedHorizontalProgressBar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/colorSecondary" />
+    <ProgressBar
+        android:id="@+id/progress_bar_animation"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        style="@style/TrimmedHorizontalProgressBar"
+        android:indeterminate="true" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/ProgressBar/res/values/styles.xml b/packages/SettingsLib/ProgressBar/res/values/styles.xml
new file mode 100644
index 0000000..5f57c1d
--- /dev/null
+++ b/packages/SettingsLib/ProgressBar/res/values/styles.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="TrimmedHorizontalProgressBar"
+           parent="android:Widget.Material.ProgressBar.Horizontal">
+        <item name="android:indeterminateDrawable">
+            @drawable/progress_indeterminate_horizontal_material_trimmed
+        </item>
+        <item name="android:minHeight">3dp</item>
+        <item name="android:maxHeight">3dp</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk
index 8d24eab..8c309ff 100644
--- a/packages/SettingsLib/common.mk
+++ b/packages/SettingsLib/common.mk
@@ -31,4 +31,3 @@
     androidx.legacy_legacy-preference-v14 \
     SettingsLib
 
-LOCAL_RESOURCE_DIR += $(call my-dir)/res
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/SliceBroadcastRelay.java b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
index e92b36a..b7f7ad2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
+++ b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
@@ -23,6 +23,10 @@
 import android.net.Uri;
 import android.os.Process;
 import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.Set;
 
 /**
  * Utility class that allows Settings to use SystemUI to relay broadcasts related to pinned slices.
@@ -38,12 +42,22 @@
     public static final String EXTRA_URI = "uri";
     public static final String EXTRA_RECEIVER = "receiver";
     public static final String EXTRA_FILTER = "filter";
+    private static final String TAG = "SliceBroadcastRelay";
 
-    public static void registerReceiver(Context context, Uri registerKey,
+    private static final Set<Uri> sRegisteredUris = new ArraySet<>();
+
+    /**
+     * Associate intent filter/sliceUri with corresponding receiver.
+     */
+    public static void registerReceiver(Context context, Uri sliceUri,
             Class<? extends BroadcastReceiver> receiver, IntentFilter filter) {
+
+        Log.d(TAG, "Registering Uri for broadcast relay: " + sliceUri);
+        sRegisteredUris.add(sliceUri);
+
         Intent registerBroadcast = new Intent(ACTION_REGISTER);
         registerBroadcast.setPackage(SYSTEMUI_PACKAGE);
-        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey,
+        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(sliceUri,
                 Process.myUserHandle().getIdentifier()));
         registerBroadcast.putExtra(EXTRA_RECEIVER,
                 new ComponentName(context.getPackageName(), receiver.getName()));
@@ -52,12 +66,21 @@
         context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM);
     }
 
-    public static void unregisterReceivers(Context context, Uri registerKey) {
-        Intent registerBroadcast = new Intent(ACTION_UNREGISTER);
+    /**
+     * Unregisters all receivers for a given slice uri.
+     */
+
+    public static void unregisterReceivers(Context context, Uri sliceUri) {
+        if (!sRegisteredUris.contains(sliceUri)) {
+            return;
+        }
+        Log.d(TAG, "Unregistering uri broadcast relay: " + sliceUri);
+        final Intent registerBroadcast = new Intent(ACTION_UNREGISTER);
         registerBroadcast.setPackage(SYSTEMUI_PACKAGE);
-        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey,
+        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(sliceUri,
                 Process.myUserHandle().getIdentifier()));
 
         context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM);
+        sRegisteredUris.remove(sliceUri);
     }
 }
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/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index ac2c2c9..43affcd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1065,7 +1065,7 @@
     }
 
     public boolean isSaved() {
-        return networkId != WifiConfiguration.INVALID_NETWORK_ID;
+        return mConfig != null;
     }
 
     public Object getTag() {
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/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
index f1158ef..b7b21fa 100644
--- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -16,8 +16,8 @@
 -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="#4a4a4a" />
+    <solid android:color="#242424" /> <!-- 14% of white -->
     <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding"
-        android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding"/>
+        android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" />
     <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
 </shape>
\ No newline at end of file
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/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
index 58fe811..f64a64e6 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -15,6 +15,7 @@
      limitations under the License.
 -->
 
+
 <com.android.systemui.privacy.OngoingPrivacyChip
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/privacy_chip"
@@ -22,47 +23,39 @@
     android:layout_width="wrap_content"
     android:layout_marginLeft="@dimen/ongoing_appops_chip_margin"
     android:layout_marginRight="@dimen/ongoing_appops_chip_margin"
-    android:layout_marginTop="@dimen/ongoing_appops_top_chip_margin"
-    android:layout_marginBottom="@dimen/ongoing_appops_top_chip_margin"
-    android:gravity="center_vertical|center_horizontal"
     android:layout_gravity="center_vertical|start"
+    android:gravity="center_vertical"
     android:orientation="horizontal"
-    android:paddingStart="@dimen/ongoing_appops_chip_side_padding"
-    android:paddingEnd="@dimen/ongoing_appops_chip_side_padding"
     android:focusable="true">
 
-        <TextView
-            android:id="@+id/in_use_text"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_vertical|start"
-            android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin_collapsed"
-            android:gravity="center_vertical"
-            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-            android:textColor="@color/status_bar_clock_color"
-            android:text="@string/ongoing_privacy_chip_in_use"
-            />
-
         <LinearLayout
-            android:id="@+id/icons_container"
-            android:layout_height="match_parent"
+            android:id="@+id/background"
+            android:layout_height="@dimen/ongoing_appops_chip_height"
             android:layout_width="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:gravity="center_vertical"
-            />
+        >
+                <LinearLayout
+                    android:id="@+id/icons_container"
+                    android:layout_height="match_parent"
+                    android:layout_width="wrap_content"
+                    android:layout_marginStart="@dimen/ongoing_appops_chip_items_margin"
+                    android:layout_gravity="center_vertical"
+                    android:gravity="center_vertical"
+                    />
 
-        <TextView
-            android:id="@+id/text_container"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            android:singleLine="true"
-            android:ellipsize="end"
-            android:lines="1"
-            android:layout_gravity="center_vertical|end"
-            android:gravity="center_vertical"
-            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-            android:textColor="@color/status_bar_clock_color"
-            android:layout_marginStart="@dimen/ongoing_appops_chip_icon_margin_collapsed"
-            android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin_collapsed"
-        />
+                <TextView
+                    android:id="@+id/text_container"
+                    android:layout_height="match_parent"
+                    android:layout_width="wrap_content"
+                    android:layout_gravity="center_vertical|end"
+                    android:paddingStart="@dimen/ongoing_appops_chip_text_padding"
+                    android:paddingEnd="@dimen/ongoing_appops_chip_text_padding"
+                    android:gravity="center_vertical"
+                    android:singleLine="true"
+                    android:ellipsize="end"
+                    android:lines="1"
+                    android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+                    android:textSize="@dimen/ongoing_appops_chip_text_size"
+                    android:textColor="@color/status_bar_clock_color"
+                />
+          </LinearLayout>
 </com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index df858f0..bb0c6f6 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -34,5 +34,4 @@
     <bool name="quick_settings_wide">true</bool>
     <dimen name="qs_detail_margin_top">0dp</dimen>
     <dimen name="qs_paged_tile_layout_padding_bottom">0dp</dimen>
-    <dimen name="ongoing_appops_top_chip_margin">2dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1e1245f..1c7ee36 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -980,26 +980,32 @@
     <dimen name="ongoing_appops_dialog_items_bottom_margin">24dp</dimen>
     <!-- Top and bottom margin of title in Ongoing App Ops dialog -->
     <dimen name="ongoing_appops_dialog_title_margin_top_bottom">18dp</dimen>
-    <!-- Side margins around the Ongoing App Ops chip-->
-    <dimen name="ongoing_appops_chip_margin">12dp</dimen>
-    <!-- Top and bottom margins around the Ongoing App Ops chip -->
-    <dimen name="ongoing_appops_top_chip_margin">12dp</dimen>
-    <!-- Start and End padding for Ongoing App Ops chip -->
-    <dimen name="ongoing_appops_chip_side_padding">6dp</dimen>
-    <!-- Padding between background of Ongoing App Ops chip and content -->
-    <dimen name="ongoing_appops_chip_bg_padding">0dp</dimen>
-    <!-- Margin between icons of Ongoing App Ops chip when QQS-->
-    <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen>
-    <!-- Margin between icons of Ongoing App Ops chip when QS-->
-    <dimen name="ongoing_appops_chip_icon_margin_expanded">8dp</dimen>
-    <!-- Icon size of Ongoing App Ops chip -->
-    <dimen name="ongoing_appops_chip_icon_size">18dp</dimen>
-    <!-- Radius of Ongoing App Ops chip corners -->
-    <dimen name="ongoing_appops_chip_bg_corner_radius">4dp</dimen>
     <!-- Text size for Ongoing App Ops dialog title -->
     <dimen name="ongoing_appops_dialog_title_size">20sp</dimen>
     <!-- Text size for Ongoing App Ops dialog items -->
     <dimen name="ongoing_appops_dialog_item_size">16sp</dimen>
+    <!-- Side margins around the Ongoing App Ops chip-->
+    <dimen name="ongoing_appops_chip_margin">0dp</dimen>
+    <!-- Height of the Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_height">32dp</dimen>
+    <!-- Start and End padding for Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_text_padding">8dp</dimen>
+    <!-- Padding between background of Ongoing App Ops chip and content -->
+    <dimen name="ongoing_appops_chip_bg_padding">0dp</dimen>
+    <!-- Side padding between background of Ongoing App Ops chip and content -->
+    <dimen name="ongoing_appops_chip_side_padding">8dp</dimen>
+    <!-- Margin between icons of Ongoing App Ops chip when QQS-->
+    <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen>
+    <!-- Margin between icons of Ongoing App Ops chip when QS-->
+    <dimen name="ongoing_appops_chip_icon_margin_expanded">2dp</dimen>
+    <!-- Icon size of Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_icon_size">@*android:dimen/status_bar_icon_size</dimen>
+    <!-- Radius of Ongoing App Ops chip corners -->
+    <dimen name="ongoing_appops_chip_bg_corner_radius">16dp</dimen>
+    <!-- Size of text of Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_text_size">12sp</dimen>
+    <!-- Margin between items in Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_items_margin">8dp</dimen>
 
     <!-- How much a bubble is elevated -->
     <dimen name="bubble_elevation">8dp</dimen>
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/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 2ff98ba..37abab9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -16,19 +16,32 @@
 
 package com.android.systemui.shared.recents;
 
+import android.graphics.Region;
+import android.os.Bundle;
 import android.view.MotionEvent;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 
 oneway interface IOverviewProxy {
-    void onBind(in ISystemUiProxy sysUiProxy);
+
+    void onActiveNavBarRegionChanges(in Region activeRegion) = 11;
+
+    void onInitialize(in Bundle params) = 12;
+
+
+    /**
+     * @deprecated
+     */
+    void onBind(in ISystemUiProxy sysUiProxy) = 0;
 
     /**
      * Called once immediately prior to the first onMotionEvent() call, providing a hint to the
      * target the initial source of the subsequent motion events.
      *
      * @param downHitTarget is one of the {@link NavigationBarCompat.HitTarget}s
+     *
+     * @deprecated
      */
-    void onPreMotionEvent(int downHitTarget);
+    void onPreMotionEvent(int downHitTarget) = 1;
 
     /**
      * Proxies motion events from the nav bar in SystemUI to the OverviewProxyService. The sender
@@ -38,40 +51,48 @@
      * Quick scrub: DOWN, (MOVE/POINTER_DOWN/POINTER_UP)*, SCRUB_START, SCRUB_PROGRESS*, SCRUB_END
      *
      * Once quick scrub is sent, then no further motion events will be provided.
+     *
+     * @deprecated
      */
-    void onMotionEvent(in MotionEvent event);
+    void onMotionEvent(in MotionEvent event) = 2;
 
     /**
      * Sent when the user starts to actively scrub the nav bar to switch tasks. Once this event is
      * sent the caller will stop sending any motion events and will no longer preemptively cancel
      * any recents animations started as a part of the motion event handling.
+     *
+     * @deprecated
      */
-    void onQuickScrubStart();
+    void onQuickScrubStart() = 3;
 
     /**
      * Sent when the user stops actively scrubbing the nav bar to switch tasks.
+     *
+     * @deprecated
      */
-    void onQuickScrubEnd();
+    void onQuickScrubEnd() = 4;
 
     /**
      * Sent for each movement over the nav bar while the user is scrubbing it to switch tasks.
+     *
+     * @deprecated
      */
-    void onQuickScrubProgress(float progress);
+    void onQuickScrubProgress(float progress) = 5;
 
     /**
      * Sent when overview button is pressed to toggle show/hide of overview.
      */
-    void onOverviewToggle();
+    void onOverviewToggle() = 6;
 
     /**
      * Sent when overview is to be shown.
      */
-    void onOverviewShown(boolean triggeredFromAltTab);
+    void onOverviewShown(boolean triggeredFromAltTab) = 7;
 
     /**
      * Sent when overview is to be hidden.
      */
-    void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
+    void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) = 8;
 
     /**
      * Sent when a user swipes up over the navigation bar to launch overview. Swipe up is determined
@@ -83,11 +104,13 @@
      * visible, this event will still be sent if user swipes up). When this signal is sent,
      * navigation bar will not handle any gestures such as quick scrub and the home button will
      * cancel (long) press.
+     *
+     * @deprecated
      */
-    void onQuickStep(in MotionEvent event);
+    void onQuickStep(in MotionEvent event) = 9;
 
     /**
      * Sent when there was an action on one of the onboarding tips view.
      */
-    void onTip(int actionType, int viewType);
+    void onTip(int actionType, int viewType) = 10;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
index f7ccb81..804f4f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shared.system;
 
+import android.os.Bundle;
 import android.os.Looper;
 import android.util.Pair;
 import android.view.BatchedInputEventReceiver;
@@ -53,6 +54,16 @@
     }
 
     /**
+     * Creates a dispatcher from the extras received as part on onInitialize
+     */
+    public static InputEventReceiver fromBundle(Bundle params, String key,
+            Looper looper, Choreographer choreographer, InputEventListener listener) {
+
+        InputChannel channel = params.getParcelable(key);
+        return new InputEventReceiver(channel, looper, choreographer, listener);
+    }
+
+    /**
      * @see BatchedInputEventReceiver
      */
     public static class InputEventReceiver {
@@ -90,7 +101,7 @@
         private final InputChannel mInputChannel;
         private final InputEventSender mSender;
 
-        private InputEventDispatcher(InputChannel inputChannel, Looper looper) {
+        public InputEventDispatcher(InputChannel inputChannel, Looper looper) {
             mInputChannel = inputChannel;
             mSender = new InputEventSender(inputChannel, looper) { };
         }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index 69aea2c..b363b4a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -17,31 +17,15 @@
 package com.android.systemui.shared.system;
 
 import android.annotation.IntDef;
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-public class NavigationBarCompat {
-    /**
-     * Touch slopes and thresholds for quick step operations. Drag slop is the point where the
-     * home button press/long press over are ignored and will start to drag when exceeded and the
-     * touch slop is when the respected operation will occur when exceeded. Touch slop must be
-     * larger than the drag slop.
-     */
-    public static int getQuickStepDragSlopPx() {
-        return convertDpToPixel(10);
-    }
+/**
+ * TODO: Remove this class
+ */
+public class NavigationBarCompat extends QuickStepContract {
 
-    public static int getQuickStepTouchSlopPx() {
-        return convertDpToPixel(24);
-    }
-
-    public static int getQuickScrubTouchSlopPx() {
-        return convertDpToPixel(24);
-    }
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({HIT_TARGET_NONE, HIT_TARGET_BACK, HIT_TARGET_HOME, HIT_TARGET_OVERVIEW})
@@ -75,8 +59,4 @@
      * Interaction type: show/hide the overview button while this service is connected to launcher
      */
     public static final int FLAG_SHOW_OVERVIEW_BUTTON = 0x4;
-
-    private static int convertDpToPixel(float dp){
-        return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
-    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
new file mode 100644
index 0000000..6d7abd0
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.system;
+
+import android.content.res.Resources;
+
+/**
+ * Various shared constants between Launcher and SysUI as part of quickstep
+ */
+public class QuickStepContract {
+
+    public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy";
+    public static final String KEY_EXTRA_INPUT_CHANNEL = "extra_input_channel";
+    public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius";
+    public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners";
+
+    /**
+     * Touch slopes and thresholds for quick step operations. Drag slop is the point where the
+     * home button press/long press over are ignored and will start to drag when exceeded and the
+     * touch slop is when the respected operation will occur when exceeded. Touch slop must be
+     * larger than the drag slop.
+     */
+    public static int getQuickStepDragSlopPx() {
+        return convertDpToPixel(10);
+    }
+
+    public static int getQuickStepTouchSlopPx() {
+        return convertDpToPixel(24);
+    }
+
+    public static int getQuickScrubTouchSlopPx() {
+        return convertDpToPixel(24);
+    }
+
+    private static int convertDpToPixel(float dp) {
+        return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
+    }
+}
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/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f277c43..3ac7fd4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -724,6 +724,7 @@
     }
 
     private void handleFaceAuthFailed() {
+        setFaceRunningState(BIOMETRIC_STATE_STOPPED);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
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..6bb4fb5 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;
@@ -40,8 +41,8 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
+import com.android.systemui.statusbar.NotificationMediaManager;
 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;
@@ -141,7 +142,7 @@
             StatusBar statusBar, StatusBarStateController statusBarStateController,
             NotificationListener listener) {
         return new NotificationIconAreaController(context, statusBar, statusBarStateController,
-                listener);
+                listener, Dependency.get(NotificationMediaManager.class));
     }
 
     public KeyguardIndicationController createKeyguardIndicationController(Context context,
@@ -153,15 +154,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 +220,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/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index ecbf024..15dc43f 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -16,12 +16,13 @@
 
 import android.content.Context
 import android.util.AttributeSet
-import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.TextView
+import com.android.systemui.Dependency
 import com.android.systemui.R
+import com.android.systemui.statusbar.policy.KeyguardMonitor
 
 class OngoingPrivacyChip @JvmOverloads constructor(
     context: Context,
@@ -38,10 +39,12 @@
             context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
     private val iconColor = context.resources.getColor(
             R.color.status_bar_clock_color, context.theme)
+    private val sidePadding =
+            context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
     private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg)
     private lateinit var text: TextView
     private lateinit var iconsContainer: LinearLayout
-    private lateinit var inUseText: TextView
+    private lateinit var back: LinearLayout
     var expanded = false
         set(value) {
             if (value != field) {
@@ -49,6 +52,8 @@
                 updateView()
             }
         }
+    @Suppress("DEPRECATION")
+    private val keyguardMonitor = Dependency.get(KeyguardMonitor::class.java)
     var builder = PrivacyDialogBuilder(context, emptyList<PrivacyItem>())
     var privacyList = emptyList<PrivacyItem>()
         set(value) {
@@ -60,15 +65,15 @@
     override fun onFinishInflate() {
         super.onFinishInflate()
 
-        inUseText = findViewById(R.id.in_use_text)
+        back = findViewById(R.id.background)
         text = findViewById(R.id.text_container)
         iconsContainer = findViewById(R.id.icons_container)
     }
 
     // Should only be called if the builder icons or app changed
     private fun updateView() {
-        inUseText.visibility = if (expanded) View.GONE else View.VISIBLE
-        background = if (expanded) backgroundDrawable else null
+        back.background = if (expanded) backgroundDrawable else null
+        back.setPaddingRelative(0, 0, if (expanded) sidePadding else 0, 0)
         fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) {
             iconsContainer.removeAllViews()
             dialogBuilder.generateIcons().forEachIndexed { i, it ->
@@ -90,16 +95,7 @@
         if (!privacyList.isEmpty()) {
             generateContentDescription()
             setIcons(builder, iconsContainer)
-            text.visibility = if (builder.types.size == 1 && expanded) VISIBLE else GONE
-            if (builder.types.size == 1 && expanded) {
-                if (builder.app != null) {
-                    text.setText(builder.app?.applicationName)
-                } else {
-                    text.text = context.resources.getQuantityString(
-                            R.plurals.ongoing_privacy_chip_multiple_apps,
-                            builder.appsAndTypes.size, builder.appsAndTypes.size)
-                }
-            }
+            setApplicationText()
         } else {
             text.visibility = GONE
             iconsContainer.removeAllViews()
@@ -107,13 +103,28 @@
         requestLayout()
     }
 
+    private fun setApplicationText() {
+        text.visibility = if (builder.types.size == 1 && expanded) VISIBLE else GONE
+        if (builder.types.size == 1 && expanded) {
+            if (builder.app != null && !amISecure()) {
+                text.setText(builder.app?.applicationName)
+            } else {
+                text.text = context.resources.getQuantityString(
+                        R.plurals.ongoing_privacy_chip_multiple_apps,
+                        builder.appsAndTypes.size, builder.appsAndTypes.size)
+            }
+        }
+    }
+
+    private fun amISecure() = keyguardMonitor.isShowing && keyguardMonitor.isSecure
+
     private fun generateContentDescription() {
         val typesText = builder.joinTypes()
         if (builder.types.size > 1) {
             contentDescription = context.getString(
                     R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
         } else {
-            if (builder.app != null) {
+            if (builder.app != null && !amISecure()) {
                 contentDescription =
                         context.getString(R.string.ongoing_privacy_chip_content_single_app,
                                 builder.app?.applicationName, typesText)
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index cff7fe4..75b8a05 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -36,8 +36,8 @@
 import java.util.concurrent.TimeUnit
 
 class OngoingPrivacyDialog constructor(
-    val context: Context,
-    val dialogBuilder: PrivacyDialogBuilder
+    private val context: Context,
+    private val dialogBuilder: PrivacyDialogBuilder
 ) {
 
     private val iconSize = context.resources.getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
index 9c1076a..bbea6b2 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
@@ -18,7 +18,7 @@
 import android.graphics.drawable.Drawable
 import com.android.systemui.R
 
-class PrivacyDialogBuilder(val context: Context, itemsList: List<PrivacyItem>) {
+class PrivacyDialogBuilder(private val context: Context, itemsList: List<PrivacyItem>) {
 
     val appsAndTypes: List<Pair<PrivacyApplication, List<PrivacyType>>>
     val types: List<PrivacyType>
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 f1c3bf2..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(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/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index ee9255c..6a8c19a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -48,7 +48,6 @@
 import android.view.DisplayCutout;
 import android.view.View;
 import android.view.WindowInsets;
-import android.view.WindowManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -82,6 +81,7 @@
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
@@ -201,6 +201,8 @@
         mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
         mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
         StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
+        // Ignore privacy icons because they show in the space above QQS
+        iconContainer.addIgnoredSlots(getIgnoredIconSlots());
         iconContainer.setShouldRestrictIcons(false);
         mIconManager = new TintedIconManager(iconContainer);
 
@@ -242,6 +244,18 @@
         updateShowPercent();
     }
 
+    private List<String> getIgnoredIconSlots() {
+        ArrayList<String> ignored = new ArrayList<>();
+        ignored.add(mContext.getResources().getString(
+                com.android.internal.R.string.status_bar_camera));
+        ignored.add(mContext.getResources().getString(
+                com.android.internal.R.string.status_bar_microphone));
+        ignored.add(mContext.getResources().getString(
+                com.android.internal.R.string.status_bar_location));
+
+        return ignored;
+    }
+
     private void updateStatusText() {
         boolean changed = updateRingerStatus() || updateAlarmStatus();
 
@@ -373,15 +387,6 @@
 
         setLayoutParams(lp);
 
-        if (mPrivacyChip != null) {
-            MarginLayoutParams lm = (MarginLayoutParams) mPrivacyChip.getLayoutParams();
-            int sideMargins = lm.leftMargin;
-            int topBottomMargins = resources.getDimensionPixelSize(
-                    R.dimen.ongoing_appops_top_chip_margin);
-            lm.setMargins(sideMargins, topBottomMargins, sideMargins, topBottomMargins);
-            mPrivacyChip.setLayoutParams(lm);
-        }
-
         updateStatusIconAlphaAnimator();
         updateHeaderTextContainerAlphaAnimator();
         updatePrivacyChipAlphaAnimator();
@@ -553,12 +558,10 @@
             Handler mUiHandler = new Handler(Looper.getMainLooper());
             mUiHandler.post(() -> {
                 Dialog mDialog = new OngoingPrivacyDialog(mContext, builder).createDialog();
-                mDialog.getWindow().setType(
-                        WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
-                SystemUIDialog.setShowForAllUsers(mDialog, true);
+                SystemUIDialog.setShowForAllUsers(mDialog, false);
                 SystemUIDialog.registerDismissListener(mDialog);
                 SystemUIDialog.setWindowOnTop(mDialog);
-                mUiHandler.post(() -> mDialog.show());
+                mActivityStarter.postQSRunnableDismissingKeyguard(() -> mDialog.show());
                 mHost.collapsePanels();
             });
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index c474faf..83c4cfc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -24,6 +24,10 @@
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
 import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_CHANNEL;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -32,7 +36,9 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -41,6 +47,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
+import android.view.InputChannel;
 import android.view.MotionEvent;
 
 import com.android.internal.policy.ScreenDecorationsUtils;
@@ -51,6 +58,7 @@
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputChannelCompat.InputEventDispatcher;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.CallbackController;
@@ -93,6 +101,8 @@
     private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
     private final Intent mQuickStepIntent;
 
+    private Region mActiveNavBarRegion;
+
     private IOverviewProxy mOverviewProxy;
     private int mConnectionBackoffAttempts;
     private @InteractionType int mInteractionFlags;
@@ -103,6 +113,8 @@
     private float mWindowCornerRadius;
     private boolean mSupportsRoundedCornersOnWindows;
 
+    private InputEventDispatcher mInputEventDispatcher;
+
     private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
 
         public void startScreenPinning(int taskId) {
@@ -309,6 +321,20 @@
                 mCurrentBoundedUserId = -1;
                 Log.e(TAG_OPS, "Failed to call onBind()", e);
             }
+
+            Bundle params = new Bundle();
+            params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder());
+            params.putParcelable(KEY_EXTRA_INPUT_CHANNEL, createNewInputDispatcher());
+            params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius);
+            params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows);
+            try {
+                mOverviewProxy.onInitialize(params);
+            } catch (RemoteException e) {
+                // Ignore error until the migration is complete.
+                Log.e(TAG_OPS, "Failed to call onBind()", e);
+            }
+            dispatchNavButtonBounds();
+
             notifyConnectionChanged();
         }
 
@@ -317,6 +343,7 @@
             Log.w(TAG_OPS, "Null binding of '" + name + "', try reconnecting");
             mCurrentBoundedUserId = -1;
             retryConnectionWithBackoff();
+            disposeInputDispatcher();
         }
 
         @Override
@@ -324,15 +351,32 @@
             Log.w(TAG_OPS, "Binding died of '" + name + "', try reconnecting");
             mCurrentBoundedUserId = -1;
             retryConnectionWithBackoff();
+            disposeInputDispatcher();
         }
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
             // Do nothing
             mCurrentBoundedUserId = -1;
+            disposeInputDispatcher();
         }
     };
 
+    private void disposeInputDispatcher() {
+        if (mInputEventDispatcher != null) {
+            mInputEventDispatcher.dispose();
+            mInputEventDispatcher = null;
+        }
+    }
+
+    private InputChannel createNewInputDispatcher() {
+        disposeInputDispatcher();
+
+        InputChannel[] channels = InputChannel.openInputChannelPair("overview-proxy-service");
+        mInputEventDispatcher = new InputEventDispatcher(channels[0], Looper.getMainLooper());
+        return channels[1];
+    }
+
     private final DeviceProvisionedListener mDeviceProvisionedCallback =
                 new DeviceProvisionedListener() {
             @Override
@@ -382,6 +426,24 @@
         }
     }
 
+    /**
+     * Sets the navbar region which can receive touch inputs
+     */
+    public void onActiveNavBarRegionChanges(Region activeRegion) {
+        mActiveNavBarRegion = activeRegion;
+        dispatchNavButtonBounds();
+    }
+
+    private void dispatchNavButtonBounds() {
+        if (mOverviewProxy != null && mActiveNavBarRegion != null) {
+            try {
+                mOverviewProxy.onActiveNavBarRegionChanges(mActiveNavBarRegion);
+            } catch (RemoteException e) {
+                Log.e(TAG_OPS, "Failed to call onActiveNavBarRegionChanges()", e);
+            }
+        }
+    }
+
     public float getBackButtonAlpha() {
         return mBackButtonAlpha;
     }
@@ -477,6 +539,10 @@
         return mOverviewProxy;
     }
 
+    public InputEventDispatcher getInputEventDispatcher() {
+        return mInputEventDispatcher;
+    }
+
     public int getInteractionFlags() {
         return mInteractionFlags;
     }
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 839b06c..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.
@@ -47,7 +47,11 @@
     }
 
     /**
-     * Called when a notification is updated, before any filtering of notifications have occurred.
+     * Called when a notification is about to be updated. Notification- and ranking-derived fields
+     * on the entry have already been updated but the following have not yet occurred:
+     * (a) View binding (i.e. the associated view has not yet been updated / inflation has not yet
+     *      been kicked off.
+     * (b) Notification filtering
      */
     default void onPreEntryUpdated(NotificationEntry entry) {
     }
@@ -61,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 81d0e25..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";
@@ -230,7 +230,6 @@
                 }
             }
         }
-        entry.setLowPriorityStateUpdated(false);
     }
 
     @Override
@@ -346,8 +345,7 @@
 
         Dependency.get(LeakDetector.class).trackInstance(entry);
         // Construct the expanded view.
-        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
-                mNotificationData.get(entry.key) != null);
+        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification));
 
         abortExistingInflation(key);
 
@@ -384,13 +382,11 @@
 
         mNotificationData.update(entry, ranking, notification);
 
-        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
-                mNotificationData.get(entry.key) != null);
-
         for (NotificationEntryListener listener : mNotificationEntryListeners) {
             listener.onPreEntryUpdated(entry);
         }
 
+        getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification));
         updateNotifications();
 
         if (DEBUG) {
@@ -422,6 +418,7 @@
         }
     }
 
+    @Override
     public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
         List<NotificationEntry> entries = new ArrayList<>();
         entries.addAll(mNotificationData.getActiveNotifications());
@@ -447,8 +444,7 @@
                     entry,
                     oldImportances.get(entry.key),
                     oldAdjustments.get(entry.key),
-                    NotificationUiAdjustment.extractFromNotificationEntry(entry),
-                    mNotificationData.get(entry.key) != null);
+                    NotificationUiAdjustment.extractFromNotificationEntry(entry));
         }
 
         updateNotifications();
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 0b8596f..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;
@@ -124,8 +124,7 @@
      */
     public void inflateViews(
             NotificationEntry entry,
-            Runnable onDismissRunnable,
-            boolean isUpdate)
+            Runnable onDismissRunnable)
             throws InflationException {
         ViewGroup parent = mListContainer.getViewParentForNotification(entry);
         PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
@@ -135,13 +134,13 @@
         if (entry.rowExists()) {
             entry.updateIcons(mContext, sbn);
             entry.reset();
-            updateNotification(entry, pmUser, sbn, entry.getRow(), isUpdate);
+            updateNotification(entry, pmUser, sbn, entry.getRow());
         } else {
             entry.createIcons(mContext, sbn);
             new RowInflaterTask().inflate(mContext, parent, entry,
                     row -> {
                         bindRow(entry, pmUser, sbn, row, onDismissRunnable);
-                        updateNotification(entry, pmUser, sbn, row, isUpdate);
+                        updateNotification(entry, pmUser, sbn, row);
                     });
         }
     }
@@ -197,15 +196,14 @@
             NotificationEntry entry,
             @Nullable Integer oldImportance,
             NotificationUiAdjustment oldAdjustment,
-            NotificationUiAdjustment newAdjustment,
-            boolean isUpdate) {
+            NotificationUiAdjustment newAdjustment) {
         if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
             if (entry.rowExists()) {
                 entry.reset();
                 PackageManager pmUser = StatusBar.getPackageManagerForUser(
                         mContext,
                         entry.notification.getUser().getIdentifier());
-                updateNotification(entry, pmUser, entry.notification, entry.getRow(), isUpdate);
+                updateNotification(entry, pmUser, entry.notification, entry.getRow());
             } else {
                 // Once the RowInflaterTask is done, it will pick up the updated entry, so
                 // no-op here.
@@ -224,12 +222,8 @@
             NotificationEntry entry,
             PackageManager pmUser,
             StatusBarNotification sbn,
-            ExpandableNotificationRow row,
-            boolean isUpdate) {
-        boolean isLowPriority = entry.ambient;
-        boolean wasLowPriority = row.isLowPriority();
-        row.setIsLowPriority(isLowPriority);
-        row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
+            ExpandableNotificationRow row) {
+        row.setIsLowPriority(entry.ambient);
         // bind the click event to the content area
         checkNotNull(mNotificationClicker).register(row, sbn);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index c886685..396a3a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -45,7 +45,7 @@
     private boolean mReorderingAllowed;
     private VisibilityLocationProvider mVisibilityLocationProvider;
     private ArraySet<View> mAllowedReorderViews = new ArraySet<>();
-    private ArraySet<View> mLowPriorityReorderingViews = new ArraySet<>();
+    private ArraySet<NotificationEntry> mLowPriorityReorderingViews = new ArraySet<>();
     private ArraySet<View> mAddedChildren = new ArraySet<>();
     private boolean mPulsing;
 
@@ -53,14 +53,21 @@
     public VisualStabilityManager(NotificationEntryManager notificationEntryManager) {
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
-            public void onEntryReinflated(NotificationEntry entry) {
-                if (entry.hasLowPriorityStateUpdated()) {
-                    onLowPriorityUpdated(entry);
-                    if (mPresenter != null) {
-                        mPresenter.updateNotificationViews();
-                    }
+            public void onPreEntryUpdated(NotificationEntry entry) {
+                final boolean mAmbientStateHasChanged =
+                        entry.ambient != entry.getRow().isLowPriority();
+                if (mAmbientStateHasChanged) {
+                    mLowPriorityReorderingViews.add(entry);
                 }
             }
+
+            @Override
+            public void onPostEntryUpdated(NotificationEntry entry) {
+                // This line is technically not required as we'll get called as the hierarchy
+                // manager will call onReorderingFinished() immediately before this.
+                // TODO: Find a way to make this relationship more explicit
+                mLowPriorityReorderingViews.remove(entry);
+            }
         });
     }
 
@@ -142,7 +149,7 @@
         if (mAddedChildren.contains(row)) {
             return true;
         }
-        if (mLowPriorityReorderingViews.contains(row)) {
+        if (mLowPriorityReorderingViews.contains(row.getEntry())) {
             return true;
         }
         if (mAllowedReorderViews.contains(row)
@@ -172,10 +179,6 @@
         }
     }
 
-    private void onLowPriorityUpdated(NotificationEntry entry) {
-        mLowPriorityReorderingViews.add(entry.getRow());
-    }
-
     /**
      * Notify the visual stability manager that a new view was added and should be allowed to
      * reorder next time.
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 f74de5b..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);
     }
 
@@ -622,10 +622,6 @@
         return null;
     }
 
-    public boolean hasLowPriorityStateUpdated() {
-        return row != null && row.hasLowPriorityStateUpdated();
-    }
-
     public void removeRow() {
         if (row != null) row.setRemoved();
     }
@@ -650,10 +646,6 @@
         return parent == null;
     }
 
-    public void setLowPriorityStateUpdated(boolean updated) {
-        if (row != null) row.setLowPriorityStateUpdated(updated);
-    }
-
     /**
      * @return Can the underlying notification be cleared? This can be different from whether the
      *         notification can be dismissed in case notifications are sensitive on the lockscreen.
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 b8e33a8..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;
@@ -124,6 +124,7 @@
     public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
     private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
     private boolean mUpdateBackgroundOnUpdate;
+    private boolean mNotificationTranslationFinished = false;
 
     /**
      * Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -133,8 +134,7 @@
     }
 
     private LayoutListener mLayoutListener;
-    private boolean mLowPriorityStateUpdated;
-    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();
     }
@@ -1453,6 +1454,10 @@
         return mIsBlockingHelperShowing;
     }
 
+    public boolean isBlockingHelperShowingAndTranslationFinished() {
+        return mIsBlockingHelperShowing && mNotificationTranslationFinished;
+    }
+
     public void setOnDismissRunnable(Runnable onDismissRunnable) {
         mOnDismissRunnable = onDismissRunnable;
     }
@@ -1577,15 +1582,6 @@
         }
     }
 
-
-    public void setLowPriorityStateUpdated(boolean lowPriorityStateUpdated) {
-        mLowPriorityStateUpdated = lowPriorityStateUpdated;
-    }
-
-    public boolean hasLowPriorityStateUpdated() {
-        return mLowPriorityStateUpdated;
-    }
-
     public boolean isLowPriority() {
         return mIsLowPriority;
     }
@@ -1620,7 +1616,7 @@
     }
 
     @VisibleForTesting
-    public NotificationInflater getNotificationInflater() {
+    public NotificationContentInflater getNotificationInflater() {
         return mNotificationInflater;
     }
 
@@ -1635,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());
@@ -1851,7 +1847,6 @@
     }
 
     void onGutsOpened() {
-        resetTranslation();
         updateContentAccessibilityImportanceForGuts(false /* isEnabled */);
     }
 
@@ -1905,11 +1900,10 @@
 
     @Override
     public void setTranslation(float translationX) {
-        if (areGutsExposed()) {
-            // Don't translate if guts are showing.
+        if (isBlockingHelperShowingAndTranslationFinished()) {
+            mGuts.setTranslationX(translationX);
             return;
-        }
-        if (!mShouldTranslateContents) {
+        } else if (!mShouldTranslateContents) {
             setTranslationX(translationX);
         } else if (mTranslateableViews != null) {
             // Translate the group of views
@@ -1925,6 +1919,7 @@
             // positioning, so we can use the scrollX instead.
             getEntry().expandedIcon.setScrollX((int) -translationX);
         }
+
         if (mMenuRow.getMenuView() != null) {
             mMenuRow.onParentTranslationUpdate(translationX);
         }
@@ -1936,6 +1931,10 @@
             return getTranslationX();
         }
 
+        if (isBlockingHelperShowingAndCanTranslate()) {
+            return mGuts.getTranslationX();
+        }
+
         if (mTranslateableViews != null && mTranslateableViews.size() > 0) {
             // All of the views in the list should have same translation, just use first one.
             return mTranslateableViews.get(0).getTranslationX();
@@ -1944,15 +1943,16 @@
         return 0;
     }
 
+    private boolean isBlockingHelperShowingAndCanTranslate() {
+        return areGutsExposed() && mIsBlockingHelperShowing && mNotificationTranslationFinished;
+    }
+
     public Animator getTranslateViewAnimator(final float leftTarget,
             AnimatorUpdateListener listener) {
         if (mTranslateAnim != null) {
             mTranslateAnim.cancel();
         }
-        if (areGutsExposed()) {
-            // No translation if guts are exposed.
-            return null;
-        }
+
         final ObjectAnimator translateAnim = ObjectAnimator.ofFloat(this, TRANSLATE_CONTENT,
                 leftTarget);
         if (listener != null) {
@@ -1968,6 +1968,9 @@
 
             @Override
             public void onAnimationEnd(Animator anim) {
+                if (mIsBlockingHelperShowing) {
+                    mNotificationTranslationFinished = true;
+                }
                 if (!cancelled && leftTarget == 0) {
                     mMenuRow.resetMenu();
                     mTranslateAnim = null;
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/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 2a88080..7882fd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -195,6 +195,9 @@
             return false;
         }
         ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+        if (row.isBlockingHelperShowingAndTranslationFinished()) {
+            return true;
+        }
         if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
             return false;
         }
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..64209a7 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) == Gravity.LEFT);
         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,81 @@
         }
     }
 
+    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) {
+            float calculatedLegProgress = MathUtils.constrainedMap(
+                    0f, POINTEDNESS_BEFORE_SNAP_RATIO,
+                    mGestureLength * START_POINTING_RATIO, mGestureLength,
+                    dist);
+
+            // Blend animated value with drag calculated value, allow the gesture to continue
+            // while the animation is playing with jump cuts in the animation.
+            setLegProgress(MathUtils.lerp(calculatedLegProgress, mLegProgress, mDragProgress));
+
+            if (mGestureDetected) {
+                mGestureDetected = false;
+
+                mLegAnimator.setFloatValues(POINTEDNESS_BEFORE_SNAP_RATIO);
+                mLegAnimator.start();
+            }
+        } 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 651670c..8152206 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -45,6 +45,8 @@
 import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Region.Op;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -128,8 +130,8 @@
     private Rect mBackButtonBounds = new Rect();
     private Rect mRecentsButtonBounds = new Rect();
     private Rect mRotationButtonBounds = new Rect();
+    private final Region mActiveRegion = new Region();
     private int[] mTmpPosition = new int[2];
-    private Rect mTmpRect = new Rect();
 
     private KeyButtonDrawable mBackIcon;
     private KeyButtonDrawable mHomeDefaultIcon;
@@ -954,17 +956,22 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        updateButtonLocationOnScreen(getBackButton(), mBackButtonBounds);
-        updateButtonLocationOnScreen(getHomeButton(), mHomeButtonBounds);
-        updateButtonLocationOnScreen(getRecentsButton(), mRecentsButtonBounds);
-        updateButtonLocationOnScreen(getRotateSuggestionButton(), mRotationButtonBounds);
+
+        mActiveRegion.setEmpty();
+        updateButtonLocation(getBackButton(), mBackButtonBounds, true);
+        updateButtonLocation(getHomeButton(), mHomeButtonBounds, false);
+        updateButtonLocation(getRecentsButton(), mRecentsButtonBounds, false);
+        updateButtonLocation(getRotateSuggestionButton(), mRotationButtonBounds, true);
+        // TODO: Handle button visibility changes
+        mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
         if (mGestureHelper != null) {
             mGestureHelper.onLayout(changed, left, top, right, bottom);
         }
         mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
     }
 
-    private void updateButtonLocationOnScreen(ButtonDispatcher button, Rect buttonBounds) {
+    private void updateButtonLocation(ButtonDispatcher button, Rect buttonBounds,
+            boolean isActive) {
         View view = button.getCurrentView();
         if (view == null) {
             buttonBounds.setEmpty();
@@ -975,6 +982,14 @@
         final float posY = view.getTranslationY();
         view.setTranslationX(0);
         view.setTranslationY(0);
+
+        if (isActive) {
+            view.getLocationOnScreen(mTmpPosition);
+            buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
+                    mTmpPosition[0] + view.getMeasuredWidth(),
+                    mTmpPosition[1] + view.getMeasuredHeight());
+            mActiveRegion.op(buttonBounds, Op.UNION);
+        }
         view.getLocationInWindow(mTmpPosition);
         buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
                 mTmpPosition[0] + view.getMeasuredWidth(),
@@ -1212,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());
@@ -1237,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/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 62f85fe..99269cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -24,6 +24,7 @@
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -47,6 +48,7 @@
     private final NotificationEntryManager mEntryManager;
     private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons;
     private final StatusBarStateController mStatusBarStateController;
+    private final NotificationMediaManager mMediaManager;
     @VisibleForTesting
     final NotificationListener.NotificationSettingsListener mSettingsListener =
             new NotificationListener.NotificationSettingsListener() {
@@ -93,13 +95,15 @@
 
     public NotificationIconAreaController(Context context, StatusBar statusBar,
             StatusBarStateController statusBarStateController,
-            NotificationListener notificationListener) {
+            NotificationListener notificationListener,
+            NotificationMediaManager notificationMediaManager) {
         mStatusBar = statusBar;
         mContrastColorUtil = ContrastColorUtil.getInstance(context);
         mContext = context;
         mEntryManager = Dependency.get(NotificationEntryManager.class);
         mStatusBarStateController = statusBarStateController;
         mStatusBarStateController.addCallback(this);
+        mMediaManager = notificationMediaManager;
         notificationListener.addNotificationSettingsListener(mSettingsListener);
 
         initializeNotificationAreaViews(context);
@@ -192,10 +196,13 @@
 
     protected boolean shouldShowNotificationIcon(NotificationEntry entry,
             boolean showAmbient, boolean showLowPriority, boolean hideDismissed,
-            boolean hideRepliedMessages) {
+            boolean hideRepliedMessages, boolean hideCurrentMedia) {
         if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
             return false;
         }
+        if (hideCurrentMedia && entry.key.equals(mMediaManager.getMediaNotificationKey())) {
+            return false;
+        }
         if (!showLowPriority && !entry.isHighPriority()) {
             return false;
         }
@@ -235,14 +242,16 @@
     private void updateShelfIcons() {
         updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
                 true /* showAmbient */, !mFullyDark /* showLowPriority */,
-                false /* hideDismissed */, mFullyDark /* hideRepliedMessages */);
+                false /* hideDismissed */, mFullyDark /* hideRepliedMessages */,
+                mFullyDark /* hideCurrentMedia */);
     }
 
     public void updateStatusBarIcons() {
         updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
                 false /* showAmbient */, mShowLowPriority /* showLowPriority */,
                 true /* hideDismissed */,
-                true /* hideRepliedMessages */);
+                true /* hideRepliedMessages */,
+                false /* hideCurrentMedia */);
     }
 
     @VisibleForTesting
@@ -261,7 +270,7 @@
      */
     private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function,
             NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority,
-            boolean hideDismissed, boolean hideRepliedMessages) {
+            boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia) {
         ArrayList<StatusBarIconView> toShow = new ArrayList<>(
                 mNotificationScrollLayout.getChildCount());
 
@@ -271,7 +280,7 @@
             if (view instanceof ExpandableNotificationRow) {
                 NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry();
                 if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed,
-                        hideRepliedMessages)) {
+                        hideRepliedMessages, hideCurrentMedia)) {
                     toShow.add(function.apply(ent));
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 069703e..f4fa1e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -590,11 +590,6 @@
                     mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
             PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
                     mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
-            // Move big clock up while pulling up the bouncer
-            PropertyAnimator.setProperty(mBigClockContainer, AnimatableProperty.Y,
-                    MathUtils.lerp(-mBigClockContainer.getHeight(), 0,
-                          Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(getExpandedFraction())),
-                    CLOCK_ANIMATION_PROPERTIES, animateClock);
             updateClock();
             stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
         }
@@ -1334,8 +1329,7 @@
         }
     };
 
-    private void setKeyguardBottomAreaVisibility(int statusBarState,
-            boolean goingToFullShade) {
+    private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) {
         mKeyguardBottomArea.animate().cancel();
         if (goingToFullShade) {
             mKeyguardBottomArea.animate()
@@ -1438,6 +1432,7 @@
         if (mBarState == StatusBarState.SHADE_LOCKED
                 || mBarState == StatusBarState.KEYGUARD) {
             updateKeyguardBottomAreaAlpha();
+            updateBigClockAlpha();
         }
         if (mBarState == StatusBarState.SHADE && mQsExpanded
                 && !mStackScrollerOverscrolling && mQsScrimEnabled) {
@@ -1883,6 +1878,19 @@
         }
     }
 
+    /**
+     * Custom clock fades away when user drags up to unlock or pulls down quick settings.
+     *
+     * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See
+     * {@link updateKeyguardBottomAreaAlpha}.
+     */
+    private void updateBigClockAlpha() {
+        float expansionAlpha = MathUtils.map(isUnlockHintRunning()
+                ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, getExpandedFraction());
+        float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
+        mBigClockContainer.setAlpha(alpha);
+    }
+
     private float getNotificationsTopY() {
         if (mNotificationStackScroller.getNotGoneChildCount() == 0) {
             return getExpandedHeight();
@@ -2597,6 +2605,7 @@
         }
         mNotificationStackScroller.setExpandedHeight(expandedHeight);
         updateKeyguardBottomAreaAlpha();
+        updateBigClockAlpha();
         updateStatusBarIcons();
     }
 
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/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 84f1cef..73ab527 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -56,6 +56,7 @@
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.system.InputChannelCompat.InputEventDispatcher;
 import com.android.systemui.shared.system.NavigationBarCompat;
 
 import java.io.PrintWriter;
@@ -676,8 +677,13 @@
     }
 
     private boolean proxyMotionEvents(MotionEvent event) {
-        final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
         event.transform(mTransformGlobalMatrix);
+        InputEventDispatcher dispatcher = mOverviewEventSender.getInputEventDispatcher();
+        if (dispatcher != null) {
+            dispatcher.dispatch(event);
+        }
+
+        final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
         try {
             if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
                 overviewProxy.onPreMotionEvent(mNavigationBarView.getDownHitTarget());
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 8495709..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);
@@ -1550,12 +1542,12 @@
 
     @Override
     public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
-        mEntryManager.updateNotificationRanking(null /* rankingMap */);
+        mEntryManager.updateNotifications();
     }
 
     @Override
     public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
-        mEntryManager.updateNotificationRanking(null);
+        mEntryManager.updateNotifications();
         if (isAmbient) {
             mDozeServiceHost.fireNotificationPulse();
         } else if (!mAmbientPulseManager.hasNotifications()) {
@@ -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/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 6495910..6e36c01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.notification.stack.ViewState;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A container for Status bar system icons. Limits the number of system icons and handles overflow
@@ -67,6 +68,8 @@
     private ArrayList<StatusIconState> mLayoutStates = new ArrayList<>();
     // So we can count and measure properly
     private ArrayList<View> mMeasureViews = new ArrayList<>();
+    // Any ignored icon will never be added as a child
+    private ArrayList<String> mIgnoredSlots = new ArrayList<>();
 
     public StatusIconContainer(Context context) {
         this(context, null);
@@ -146,7 +149,8 @@
         // Collect all of the views which want to be laid out
         for (int i = 0; i < count; i++) {
             StatusIconDisplayable icon = (StatusIconDisplayable) getChildAt(i);
-            if (icon.isIconVisible() && !icon.isIconBlocked()) {
+            if (icon.isIconVisible() && !icon.isIconBlocked()
+                    && !mIgnoredSlots.contains(icon.getSlot())) {
                 mMeasureViews.add((View) icon);
             }
         }
@@ -205,6 +209,47 @@
     }
 
     /**
+     * Add a name of an icon slot to be ignored. It will not show up nor be measured
+     * @param slotName name of the icon as it exists in
+     * frameworks/base/core/res/res/values/config.xml
+     */
+    public void addIgnoredSlot(String slotName) {
+        addIgnoredSlotInternal(slotName);
+        requestLayout();
+    }
+
+    /**
+     * Add a list of slots to be ignored
+     * @param slots names of the icons to ignore
+     */
+    public void addIgnoredSlots(List<String> slots) {
+        for (String slot : slots) {
+            addIgnoredSlotInternal(slot);
+        }
+
+        requestLayout();
+    }
+
+    private void addIgnoredSlotInternal(String slotName) {
+        if (!mIgnoredSlots.contains(slotName)) {
+            mIgnoredSlots.add(slotName);
+        }
+    }
+
+    /**
+     * Remove a slot from the list of ignored icon slots. It will then be shown when set to visible
+     * by the {@link StatusBarIconController}.
+     * @param slotName name of the icon slot to remove from the ignored list
+     */
+    public void removeIgnoredSlot(String slotName) {
+        if (mIgnoredSlots.contains(slotName)) {
+            mIgnoredSlots.remove(slotName);
+        }
+
+        requestLayout();
+    }
+
+    /**
      * Layout is happening from end -> start
      */
     private void calculateIconTranslations() {
@@ -223,7 +268,8 @@
             StatusIconDisplayable iconView = (StatusIconDisplayable) child;
             StatusIconState childState = getViewStateFromChild(child);
 
-            if (!iconView.isIconVisible() || iconView.isIconBlocked()) {
+            if (!iconView.isIconVisible() || iconView.isIconBlocked()
+                    || mIgnoredSlots.contains(iconView.getSlot())) {
                 childState.visibleState = STATE_HIDDEN;
                 if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") not visible");
                 continue;
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/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
index 608dd8b..120d0b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
@@ -29,6 +29,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationMediaManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -47,6 +48,8 @@
     StatusBar mStatusBar;
     @Mock
     StatusBarStateController mStatusBarStateController;
+    @Mock
+    private NotificationMediaManager mMediaManager;
     private NotificationIconAreaController mController;
 
     @Before
@@ -54,7 +57,7 @@
         MockitoAnnotations.initMocks(this);
 
         mController = new NotificationIconAreaController(mContext, mStatusBar,
-                mStatusBarStateController, mListener);
+                mStatusBarStateController, mListener, mMediaManager);
     }
 
     @Test
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/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 4afbc64..4bd50ec 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -45,6 +45,7 @@
 import android.util.LocalLog;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
+import android.view.contentcapture.ContentCaptureHelper;
 import android.view.contentcapture.ContentCaptureManager;
 import android.view.contentcapture.IContentCaptureManager;
 import android.view.contentcapture.UserDataRemovalRequest;
@@ -79,7 +80,8 @@
 
     private final LocalService mLocalService = new LocalService();
 
-    private final LocalLog mRequestsHistory = new LocalLog(20);
+    @Nullable
+    final LocalLog mRequestsHistory;
 
     @GuardedBy("mLock")
     private ActivityManagerInternal mAm;
@@ -105,15 +107,19 @@
                 UserManager.DISALLOW_CONTENT_CAPTURE);
         DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
                 ActivityThread.currentApplication().getMainExecutor(),
-                (namespace, key, value) -> {
-                    if (!ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED
-                            .equals(key)) {
-                        Slog.i(mTag, "Ignoring change on " + key);
-                        return;
-                    }
-                    setDisabledByDeviceConfig(value);
-                });
-        setDisabledByDeviceConfig();
+                (namespace, key, value) -> onDeviceConfigChange(key, value));
+        setLoggingLevelFromDeviceConfig();
+        setDisabledFromDeviceConfig();
+
+        final int loggingSize = ContentCaptureHelper.getIntDeviceConfigProperty(
+                ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20);
+        if (loggingSize > 0) {
+            if (debug) Slog.d(mTag, "log history size: " + loggingSize);
+            mRequestsHistory = new LocalLog(loggingSize);
+        } else {
+            if (debug) Slog.d(mTag, "disabled log history because size is " + loggingSize);
+            mRequestsHistory = null;
+        }
 
         // Sets which services are disabled
         final UserManager um = getContext().getSystemService(UserManager.class);
@@ -213,7 +219,33 @@
         return false;
     }
 
-    private void setDisabledByDeviceConfig() {
+    private void onDeviceConfigChange(@NonNull String key, @Nullable String value) {
+        switch (key) {
+            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED:
+                setDisabledByDeviceConfig(value);
+                return;
+            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL:
+                setLoggingLevelFromDeviceConfig();
+                return;
+            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE:
+            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY:
+            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE:
+            case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY:
+                // TODO(b/123096662): implement it
+                Slog.d(mTag, "changes on " + key + " not supported yet");
+                return;
+            default:
+                Slog.i(mTag, "Ignoring change on " + key);
+        }
+    }
+
+    private void setLoggingLevelFromDeviceConfig() {
+        ContentCaptureHelper.setLoggingLevel();
+        verbose = ContentCaptureHelper.sVerbose;
+        debug = ContentCaptureHelper.sDebug;
+    }
+
+    private void setDisabledFromDeviceConfig() {
         final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
                 ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED);
         setDisabledByDeviceConfig(value);
@@ -327,13 +359,6 @@
         }
     }
 
-    /**
-     * Logs a request so it's dumped later...
-     */
-    void logRequestLocked(@NonNull String historyItem) {
-        mRequestsHistory.log(historyItem);
-    }
-
     private ActivityManagerInternal getAmInternal() {
         synchronized (mLock) {
             if (mAm == null) {
@@ -527,9 +552,13 @@
             synchronized (mLock) {
                 dumpLocked("", pw);
             }
-            if (showHistory) {
-                pw.println(); pw.println("Requests history:"); pw.println();
+            pw.print("Requests history: ");
+            if (mRequestsHistory == null) {
+                pw.println("disabled by device config");
+            } else if (showHistory) {
+                pw.println();
                 mRequestsHistory.reverseDump(fd, pw, args);
+                pw.println();
             }
         }
 
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 7102b82..7150264 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -187,13 +187,15 @@
         final ComponentName componentName = activityPresentationInfo.componentName;
         final ComponentName serviceComponentName = getServiceComponentName();
         final boolean enabled = isEnabledLocked();
-        final String historyItem =
-                "id=" + sessionId + " uid=" + uid
-                + " a=" + ComponentName.flattenToShortString(componentName)
-                + " t=" + taskId + " d=" + displayId
-                + " s=" + ComponentName.flattenToShortString(serviceComponentName)
-                + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)");
-        mMaster.logRequestLocked(historyItem);
+        if (mMaster.mRequestsHistory != null) {
+            final String historyItem =
+                    "id=" + sessionId + " uid=" + uid
+                    + " a=" + ComponentName.flattenToShortString(componentName)
+                    + " t=" + taskId + " d=" + displayId
+                    + " s=" + ComponentName.flattenToShortString(serviceComponentName)
+                    + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)");
+            mMaster.mRequestsHistory.log(historyItem);
+        }
 
         if (!enabled) {
             // TODO: it would be better to split in differet reasons, like
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index 3c52e17..4094843 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -20,6 +20,7 @@
 import android.os.IBinder;
 import android.service.contentcapture.ContentCaptureService;
 import android.service.contentcapture.SnapshotData;
+import android.util.LocalLog;
 import android.util.Slog;
 import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.ContentCaptureSessionId;
@@ -37,6 +38,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;
 
     /**
@@ -83,7 +87,10 @@
      */
     @GuardedBy("mLock")
     public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) {
-        mService.getMaster().logRequestLocked("snapshot: id=" + mId);
+        final LocalLog logHistory = mService.getMaster().mRequestsHistory;
+        if (logHistory != null) {
+            logHistory.log("snapshot: id=" + mId);
+        }
 
         mRemoteService.onActivitySnapshotRequest(mId, snapshotData);
     }
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..775e4c8 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<>();
 
@@ -68,7 +68,7 @@
      * Subclasses of ExtconUEventObserver should override this method to handle UEvents.
      *
      * @param extconInfo that matches the {@code DEVPATH} of {@code event}
-     * @param event the event
+     * @param event      the event
      */
     protected abstract void onUEvent(ExtconInfo extconInfo, UEvent event);
 
@@ -91,6 +91,9 @@
 
         /** Returns a new list of all external connections whose name matches {@code regex}. */
         public static List<ExtconInfo> getExtconInfos(@Nullable String regex) {
+            if (!extconExists()) {
+                return new ArrayList<>(0);  // Always return a new list.
+            }
             Pattern p = regex == null ? null : Pattern.compile(regex);
             File file = new File("/sys/class/extcon");
             File[] files = file.listFiles();
@@ -159,6 +162,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/FgThread.java b/services/core/java/com/android/server/FgThread.java
index fe30057..5d0e308 100644
--- a/services/core/java/com/android/server/FgThread.java
+++ b/services/core/java/com/android/server/FgThread.java
@@ -17,9 +17,12 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Shared singleton foreground thread for the system.  This is a thread for regular
  * foreground service operations, which shouldn't be blocked by anything running in
@@ -34,6 +37,7 @@
 
     private static FgThread sInstance;
     private static Handler sHandler;
+    private static HandlerExecutor sHandlerExecutor;
 
     private FgThread() {
         super("android.fg", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
@@ -48,6 +52,7 @@
             looper.setSlowLogThresholdMs(
                     SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -64,4 +69,11 @@
             return sHandler;
         }
     }
+
+    public static Executor getExecutor() {
+        synchronized (FgThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/IoThread.java b/services/core/java/com/android/server/IoThread.java
index bfe825a..21fd29c 100644
--- a/services/core/java/com/android/server/IoThread.java
+++ b/services/core/java/com/android/server/IoThread.java
@@ -17,8 +17,11 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Shared singleton I/O thread for the system.  This is a thread for non-background
  * service operations that can potential block briefly on network IO operations
@@ -27,6 +30,7 @@
 public final class IoThread extends ServiceThread {
     private static IoThread sInstance;
     private static Handler sHandler;
+    private static HandlerExecutor sHandlerExecutor;
 
     private IoThread() {
         super("android.io", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
@@ -38,6 +42,7 @@
             sInstance.start();
             sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -54,4 +59,11 @@
             return sHandler;
         }
     }
+
+    public static Executor getExecutor() {
+        synchronized (IoThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 3479e18..5989a46 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);
@@ -2165,6 +2136,13 @@
         }
     }
 
+    @Override
+    public String[] getIgnoreSettingsWhitelist() {
+        synchronized (mLock) {
+            return mIgnoreSettingsPackageWhitelist.toArray(new String[0]);
+        }
+    }
+
     @GuardedBy("mLock")
     private boolean isThrottlingExemptLocked(CallerIdentity callerIdentity) {
         if (callerIdentity.mUid == Process.SYSTEM_UID) {
@@ -2710,77 +2688,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 +2775,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 {
@@ -2816,8 +2801,7 @@
         } catch (RemoteException e) {
             // if the remote process registering the listener is already dead, just swallow the
             // exception and return
-            Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.",
-                    e);
+            Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.", e);
             return false;
         }
     }
@@ -2830,8 +2814,7 @@
         } catch (NoSuchElementException e) {
             // if the death callback isn't connected (it should be...), log error,
             // swallow the exception and return
-            Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.",
-                    e);
+            Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.", e);
             return false;
         }
     }
@@ -2863,52 +2846,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/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 6b57fcd..710a0ba3 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -30,11 +30,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.BatteryManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
@@ -354,8 +356,12 @@
             try {
                 synchronized (mLock) {
                     if (mNightMode != mode) {
-                        Settings.Secure.putIntForUser(getContext().getContentResolver(),
-                                Settings.Secure.UI_NIGHT_MODE, mode, user);
+                        // Only persist setting if not transient night mode or not in car mode
+                        if (!shouldTransientNightWhenInCarMode() || !mCarModeEnabled) {
+                            Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                                    Settings.Secure.UI_NIGHT_MODE, mode, user);
+                        }
+
                         mNightMode = mode;
                         updateLocked(0, 0);
                     }
@@ -438,9 +444,39 @@
         }
     }
 
+    // Night mode settings in car mode are only persisted below Q.
+    // When targeting Q, changes are not saved and night mode will be re-read
+    // from settings when exiting car mode.
+    private boolean shouldTransientNightWhenInCarMode() {
+        int uid = Binder.getCallingUid();
+        PackageManager packageManager = getContext().getPackageManager();
+        String[] packagesForUid = packageManager.getPackagesForUid(uid);
+        if (packagesForUid == null || packagesForUid.length == 0) {
+            return false;
+        }
+
+        try {
+            ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser(
+                    packagesForUid[0], 0, uid);
+
+            return appInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
+        } catch (PackageManager.NameNotFoundException ignored) {
+        }
+
+        return false;
+    }
+
     void setCarModeLocked(boolean enabled, int flags) {
         if (mCarModeEnabled != enabled) {
             mCarModeEnabled = enabled;
+
+            // When transient night mode and exiting car mode, restore night mode from settings
+            if (shouldTransientNightWhenInCarMode() && !mCarModeEnabled) {
+                Context context = getContext();
+                updateNightModeFromSettings(context,
+                        context.getResources(),
+                        UserHandle.getCallingUserId());
+            }
         }
         mCarModeEnableFlags = flags;
     }
@@ -498,7 +534,9 @@
             uiMode |= mNightMode << 4;
         }
 
-        if (mPowerSave) {
+        // Override night mode in power save mode if not transient night mode or not in car mode
+        boolean shouldOverrideNight = !mCarModeEnabled || !shouldTransientNightWhenInCarMode();
+        if (mPowerSave && shouldOverrideNight) {
             uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
             uiMode |= Configuration.UI_MODE_NIGHT_YES;
         }
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/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index f01305e..0d49e4c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1681,7 +1681,8 @@
     public void killAppZygoteIfNeededLocked(AppZygote appZygote) {
         final ApplicationInfo appInfo = appZygote.getAppInfo();
         ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
-        if (zygoteProcesses.size() == 0) { // Only remove if no longer in use now
+        if (zygoteProcesses != null && zygoteProcesses.size() == 0) {
+            // Only remove if no longer in use now
             mAppZygotes.remove(appInfo.processName, appInfo.uid);
             mAppZygoteProcesses.remove(appZygote);
             mAppIsolatedUidRangeAllocator.freeUidRangeLocked(appInfo);
@@ -1703,6 +1704,7 @@
             ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
             zygoteProcesses.remove(app);
             if (zygoteProcesses.size() == 0) {
+                mService.mHandler.removeMessages(KILL_APP_ZYGOTE_MSG);
                 Message msg = mService.mHandler.obtainMessage(KILL_APP_ZYGOTE_MSG);
                 msg.obj = appZygote;
                 mService.mHandler.sendMessageDelayed(msg, KILL_APP_ZYGOTE_DELAY_MS);
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..3663518 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(
+                BackgroundThread.getExecutor(), mRoleHolderChangedListener, UserHandle.ALL);
     }
 
     @Override
@@ -118,12 +118,9 @@
         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)) {
+            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..47c9b86 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -104,8 +104,7 @@
 
     @Override
     public void onSwitchUser(int userId) {
-        cancelAndUnbindLocked(peekUserStateLocked(userId),
-                AttentionService.ATTENTION_FAILURE_UNKNOWN);
+        cancelAndUnbindLocked(peekUserStateLocked(userId));
     }
 
     /** Resolves and sets up the attention service if it had not been done yet. */
@@ -152,7 +151,8 @@
         }
 
         synchronized (mLock) {
-            unbindAfterTimeoutLocked();
+            final long now = SystemClock.uptimeMillis();
+            freeIfInactiveLocked();
 
             final UserState userState = getOrCreateCurrentUserStateLocked();
             // lazily start the service, which should be very lightweight to start
@@ -172,7 +172,7 @@
                 try {
                     // throttle frequent requests
                     final AttentionCheckCache attentionCheckCache = userState.mAttentionCheckCache;
-                    if (attentionCheckCache != null && SystemClock.uptimeMillis()
+                    if (attentionCheckCache != null && now
                             < attentionCheckCache.mLastComputed + STALE_AFTER_MILLIS) {
                         callback.onSuccess(requestCode, attentionCheckCache.mResult,
                                 attentionCheckCache.mTimestamp);
@@ -190,6 +190,7 @@
                                 userState.mAttentionCheckCache = new AttentionCheckCache(
                                         SystemClock.uptimeMillis(), result,
                                         timestamp);
+                                userState.mCurrentAttentionCheckIsFulfilled = true;
                             }
                             StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
                                     result);
@@ -198,14 +199,10 @@
                         @Override
                         public void onFailure(int requestCode, int error) {
                             callback.onFailure(requestCode, error);
+                            userState.mCurrentAttentionCheckIsFulfilled = true;
                             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");
@@ -219,7 +216,10 @@
     /** Cancels the specified attention check. */
     public void cancelAttentionCheck(int requestCode) {
         synchronized (mLock) {
-            final UserState userState = getOrCreateCurrentUserStateLocked();
+            final UserState userState = peekCurrentUserStateLocked();
+            if (userState == null) {
+                return;
+            }
             if (userState.mService == null) {
                 if (userState.mPendingAttentionCheck != null
                         && userState.mPendingAttentionCheck.mRequestCode == requestCode) {
@@ -236,8 +236,12 @@
     }
 
     @GuardedBy("mLock")
-    private void unbindAfterTimeoutLocked() {
-        mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CONNECTION_EXPIRED,
+    private void freeIfInactiveLocked() {
+        // If we are called here, it means someone used the API again - reset the timer then.
+        mAttentionHandler.removeMessages(AttentionHandler.CHECK_CONNECTION_EXPIRATION);
+
+        // Schedule resources cleanup if no one calls the API again.
+        mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CHECK_CONNECTION_EXPIRATION,
                 CONNECTION_TTL_MILLIS);
     }
 
@@ -264,12 +268,14 @@
     }
 
     @GuardedBy("mLock")
-    UserState peekCurrentUserStateLocked() {
+    @Nullable
+    private UserState peekCurrentUserStateLocked() {
         return peekUserStateLocked(ActivityManager.getCurrentUser());
     }
 
     @GuardedBy("mLock")
-    UserState peekUserStateLocked(int userId) {
+    @Nullable
+    private UserState peekUserStateLocked(int userId) {
         return mUserStates.get(userId);
     }
 
@@ -406,6 +412,8 @@
         @GuardedBy("mLock")
         int mCurrentAttentionCheckRequestCode;
         @GuardedBy("mLock")
+        boolean mCurrentAttentionCheckIsFulfilled;
+        @GuardedBy("mLock")
         PendingAttentionCheck mPendingAttentionCheck;
 
         @GuardedBy("mLock")
@@ -501,7 +509,7 @@
     }
 
     private class AttentionHandler extends Handler {
-        private static final int CONNECTION_EXPIRED = 1;
+        private static final int CHECK_CONNECTION_EXPIRATION = 1;
         private static final int ATTENTION_CHECK_TIMEOUT = 2;
 
         AttentionHandler() {
@@ -511,19 +519,26 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 // Do not occupy resources when not in use - unbind proactively.
-                case CONNECTION_EXPIRED: {
+                case CHECK_CONNECTION_EXPIRATION: {
                     for (int i = 0; i < mUserStates.size(); i++) {
-                        cancelAndUnbindLocked(mUserStates.valueAt(i),
-                                AttentionService.ATTENTION_FAILURE_UNKNOWN);
+                        cancelAndUnbindLocked(mUserStates.valueAt(i));
                     }
-
                 }
                 break;
 
                 // Callee is no longer interested in the attention check result - cancel.
                 case ATTENTION_CHECK_TIMEOUT: {
-                    cancelAndUnbindLocked(peekCurrentUserStateLocked(),
-                            AttentionService.ATTENTION_FAILURE_TIMED_OUT);
+                    synchronized (mLock) {
+                        final UserState userState = peekCurrentUserStateLocked();
+                        if (userState != null) {
+                            // If not called back already.
+                            if (!userState.mCurrentAttentionCheckIsFulfilled) {
+                                cancel(userState,
+                                        AttentionService.ATTENTION_FAILURE_TIMED_OUT);
+                            }
+
+                        }
+                    }
                 }
                 break;
 
@@ -533,25 +548,29 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void cancelAndUnbindLocked(UserState userState,
-            @AttentionFailureCodes int failureCode) {
-        synchronized (mLock) {
-            if (userState != null && userState.mService != null) {
-                try {
-                    userState.mService.cancelAttentionCheck(
-                            userState.mCurrentAttentionCheckRequestCode);
-                } catch (RemoteException e) {
-                    Slog.e(LOG_TAG, "Unable to cancel attention check");
-                }
-
-                if (userState.mPendingAttentionCheck != null) {
-                    userState.mPendingAttentionCheck.cancel(failureCode);
-                }
-                mContext.unbindService(userState.mConnection);
-                userState.mConnection.cleanupService();
-                mUserStates.remove(userState.mUserId);
+    private void cancel(UserState userState, @AttentionFailureCodes int failureCode) {
+        if (userState != null && userState.mService != null) {
+            try {
+                userState.mService.cancelAttentionCheck(
+                        userState.mCurrentAttentionCheckRequestCode);
+            } catch (RemoteException e) {
+                Slog.e(LOG_TAG, "Unable to cancel attention check");
             }
+
+            if (userState.mPendingAttentionCheck != null) {
+                userState.mPendingAttentionCheck.cancel(failureCode);
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void cancelAndUnbindLocked(UserState userState) {
+        synchronized (mLock) {
+            cancel(userState, AttentionService.ATTENTION_FAILURE_UNKNOWN);
+
+            mContext.unbindService(userState.mConnection);
+            userState.mConnection.cleanupService();
+            mUserStates.remove(userState.mUserId);
         }
     }
 
@@ -563,8 +582,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
-                cancelAndUnbindLocked(peekCurrentUserStateLocked(),
-                        AttentionService.ATTENTION_FAILURE_UNKNOWN);
+                cancelAndUnbindLocked(peekCurrentUserStateLocked());
             }
         }
     }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 6df60d6..9af57da 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -280,9 +280,9 @@
                     AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
             sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
                     AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource);
-            // Un-mute ringtone stream volume
-            mAudioService.setUpdateRingerModeServiceInt();
         }
+        // Un-mute ringtone stream volume
+        mAudioService.postUpdateRingerModeServiceInt();
     }
 
     /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a6643d4..d902201 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -248,6 +248,7 @@
     private static final int MSG_NOTIFY_VOL_EVENT = 22;
     private static final int MSG_DISPATCH_AUDIO_SERVER_STATE = 23;
     private static final int MSG_ENABLE_SURROUND_FORMATS = 24;
+    private static final int MSG_UPDATE_RINGER_MODE = 25;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -753,6 +754,7 @@
         intentFilter.addAction(Intent.ACTION_USER_FOREGROUND);
         intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
         intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        intentFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
 
         intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
         mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
@@ -2720,7 +2722,11 @@
         }
     }
 
-    /*package*/ void setUpdateRingerModeServiceInt() {
+    /*package*/ void postUpdateRingerModeServiceInt() {
+        sendMsg(mAudioHandler, MSG_UPDATE_RINGER_MODE, SENDMSG_QUEUE, 0, 0, null, 0);
+    }
+
+    private void onUpdateRingerModeServiceInt() {
         setRingerModeInt(getRingerModeInternal(), false);
     }
 
@@ -3217,6 +3223,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("/")
@@ -4944,6 +4965,10 @@
                 case MSG_ENABLE_SURROUND_FORMATS:
                     onEnableSurroundFormats((ArrayList<Integer>) msg.obj);
                     break;
+
+                case MSG_UPDATE_RINGER_MODE:
+                    onUpdateRingerModeServiceInt();
+                    break;
             }
         }
     }
@@ -5159,6 +5184,20 @@
             } else if (action.equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) ||
                     action.equals(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION)) {
                 handleAudioEffectBroadcast(context, intent);
+            } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
+                final int[] suspendedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
+                final String[] suspendedPackages =
+                        intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                if (suspendedPackages == null || suspendedUids == null
+                        || suspendedPackages.length != suspendedUids.length) {
+                    return;
+                }
+                for (int i = 0; i < suspendedUids.length; i++) {
+                    if (!TextUtils.isEmpty(suspendedPackages[i])) {
+                        mMediaFocusControl.noFocusForSuspendedApp(
+                                suspendedPackages[i], suspendedUids[i]);
+                    }
+                }
             }
         }
     } // end class AudioServiceBroadcastReceiver
@@ -5323,6 +5362,11 @@
             }
         }
 
+        if (callingPackageName == null || clientId == null || aa == null) {
+            Log.e(TAG, "Invalid null parameter to request audio focus");
+            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+        }
+
         return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
                 clientId, callingPackageName, flags, sdk,
                 forceFocusDuckingForAccessibility(aa, durationHint, Binder.getCallingUid()));
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index 99f0840..db55138 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -45,8 +45,8 @@
     private AudioFocusDeathHandler mDeathHandler; // may be null
     private IAudioFocusDispatcher mFocusDispatcher; // may be null
     private final IBinder mSourceRef; // may be null
-    private final String mClientId;
-    private final String mPackageName;
+    private final @NonNull String mClientId;
+    private final @NonNull String mPackageName;
     private final int mCallingUid;
     private final MediaFocusControl mFocusController; // never null
     private final int mSdkTarget;
@@ -72,7 +72,7 @@
     /**
      * the audio attributes associated with the focus request
      */
-    private final AudioAttributes mAttributes;
+    private final @NonNull AudioAttributes mAttributes;
 
     /**
      * Class constructor
@@ -87,9 +87,10 @@
      * @param uid
      * @param ctlr cannot be null
      */
-    FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags,
-            IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
-            String pn, int uid, @NonNull MediaFocusControl ctlr, int sdk) {
+    FocusRequester(@NonNull AudioAttributes aa, int focusRequest, int grantFlags,
+            IAudioFocusDispatcher afl, IBinder source, @NonNull String id,
+            AudioFocusDeathHandler hdlr, @NonNull String pn, int uid,
+            @NonNull MediaFocusControl ctlr, int sdk) {
         mAttributes = aa;
         mFocusDispatcher = afl;
         mSourceRef = source;
@@ -124,11 +125,7 @@
     }
 
     boolean hasSameClient(String otherClient) {
-        try {
-            return mClientId.compareTo(otherClient) == 0;
-        } catch (NullPointerException e) {
-            return false;
-        }
+        return mClientId.compareTo(otherClient) == 0;
     }
 
     boolean isLockedFocusOwner() {
@@ -143,12 +140,8 @@
         return (mFocusDispatcher != null) && mFocusDispatcher.equals(fd);
     }
 
-    boolean hasSamePackage(String pack) {
-        try {
-            return mPackageName.compareTo(pack) == 0;
-        } catch (NullPointerException e) {
-            return false;
-        }
+    boolean hasSamePackage(@NonNull String pack) {
+        return mPackageName.compareTo(pack) == 0;
     }
 
     boolean hasSameUid(int uid) {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index d023bd7..b4bbbc7 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -24,7 +24,6 @@
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.media.IAudioFocusDispatcher;
-import android.media.audiopolicy.AudioPolicy;
 import android.media.audiopolicy.IAudioPolicyCallback;
 import android.os.Binder;
 import android.os.Build;
@@ -35,6 +34,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
+import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
@@ -44,7 +44,6 @@
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.Stack;
-import java.text.DateFormat;
 
 /**
  * @hide
@@ -138,6 +137,30 @@
     private static final AudioEventLogger mEventLogger = new AudioEventLogger(50,
             "focus commands as seen by MediaFocusControl");
 
+    /*package*/ void noFocusForSuspendedApp(@NonNull String packageName, int uid) {
+        synchronized (mAudioFocusLock) {
+            final Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
+            List<String> clientsToRemove = new ArrayList<>();
+            while (stackIterator.hasNext()) {
+                final FocusRequester focusOwner = stackIterator.next();
+                if (focusOwner.hasSameUid(uid) && focusOwner.hasSamePackage(packageName)) {
+                    clientsToRemove.add(focusOwner.getClientId());
+                    mEventLogger.log((new AudioEventLogger.StringEvent(
+                            "focus owner:" + focusOwner.getClientId()
+                                    + " in uid:" + uid + " pack: " + packageName
+                                    + " getting AUDIOFOCUS_LOSS due to app suspension"))
+                            .printLog(TAG));
+                    // make the suspended app lose focus through its focus listener (if any)
+                    focusOwner.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
+                }
+            }
+            for (String clientToRemove : clientsToRemove) {
+                // update the stack but don't signal the change.
+                removeFocusStackEntry(clientToRemove, false, true);
+            }
+        }
+    }
+
     /**
      * Discard the current audio focus owner.
      * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
@@ -688,9 +711,9 @@
     }
 
     /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */
-    protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
-            IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
-            int sdk, boolean forceDuck) {
+    protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb,
+            IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName,
+            int flags, int sdk, boolean forceDuck) {
         mEventLogger.log((new AudioEventLogger.StringEvent(
                 "requestAudioFocus() from uid/pid " + Binder.getCallingUid()
                     + "/" + Binder.getCallingPid()
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 8995068..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,75 +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 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 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);
+        @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;
-                            }
-                        }
-                    });
+                // 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 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 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 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();
+                }
+            });
+        }
+    };
 
     /**
      * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
@@ -647,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);
+        }
     };
 
 
@@ -670,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;
     }
@@ -693,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;
@@ -761,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
@@ -780,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
@@ -790,6 +856,11 @@
     }
 
     @Override
+    protected List<Face> getEnrolledTemplates(int userId) {
+        return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+    }
+
+    @Override
     protected void notifyClientActiveCallbacks(boolean isActive) {
         // noop for Face.
     }
@@ -799,6 +870,11 @@
         return BiometricsProtoEnums.MODALITY_FACE;
     }
 
+    @Override
+    protected int getLockoutMode() {
+        return mCurrentUserLockoutMode;
+    }
+
     /** Gets the face daemon */
     private synchronized IBiometricsFace getFaceDaemon() {
         if (mDaemon == null) {
@@ -828,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);
@@ -865,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/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 45f169c..7dd3b36 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -129,240 +129,9 @@
     private final NightDisplayTintController mNightDisplayTintController =
             new NightDisplayTintController();
 
-    private final TintController mDisplayWhiteBalanceTintController = new TintController() {
-        // Three chromaticity coordinates per color: X, Y, and Z
-        private final int NUM_VALUES_PER_PRIMARY = 3;
-        // Four colors: red, green, blue, and white
-        private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;
-
-        private final Object mLock = new Object();
-        private int mTemperatureMin;
-        private int mTemperatureMax;
-        private int mTemperatureDefault;
-        private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
-        private ColorSpace.Rgb mDisplayColorSpaceRGB;
-        private float[] mChromaticAdaptationMatrix;
-        private int mCurrentColorTemperature;
-        private float[] mCurrentColorTemperatureXYZ;
-        private boolean mSetUp = false;
-        private float[] mMatrixDisplayWhiteBalance = new float[16];
-        private Boolean mIsAvailable;
-
-        @Override
-        public void setUp(Context context, boolean needsLinear) {
-            mSetUp = false;
-            final Resources res = context.getResources();
-
-            ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl();
-            if (displayColorSpaceRGB == null) {
-                Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res");
-                displayColorSpaceRGB = getDisplayColorSpaceFromResources(res);
-                if (displayColorSpaceRGB == null) {
-                    Slog.e(TAG, "Failed to get display color space from resources");
-                    return;
-                }
-            }
-
-            final String[] nominalWhiteValues = res.getStringArray(
-                    R.array.config_displayWhiteBalanceDisplayNominalWhite);
-            float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
-            for (int i = 0; i < nominalWhiteValues.length; i++) {
-                displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
-            }
-
-            final int colorTemperatureMin = res.getInteger(
-                    R.integer.config_displayWhiteBalanceColorTemperatureMin);
-            if (colorTemperatureMin <= 0) {
-                Slog.e(TAG, "Display white balance minimum temperature must be greater than 0");
-                return;
-            }
-
-            final int colorTemperatureMax = res.getInteger(
-                    R.integer.config_displayWhiteBalanceColorTemperatureMax);
-            if (colorTemperatureMax < colorTemperatureMin) {
-                Slog.e(TAG, "Display white balance max temp must be greater or equal to min");
-                return;
-            }
-
-            final int colorTemperature = res.getInteger(
-                    R.integer.config_displayWhiteBalanceColorTemperatureDefault);
-
-            synchronized (mLock) {
-                mDisplayColorSpaceRGB = displayColorSpaceRGB;
-                mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
-                mTemperatureMin = colorTemperatureMin;
-                mTemperatureMax = colorTemperatureMax;
-                mTemperatureDefault = colorTemperature;
-                mSetUp = true;
-            }
-
-            setMatrix(mTemperatureDefault);
-        }
-
-        @Override
-        public float[] getMatrix() {
-            return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
-        }
-
-        @Override
-        public void setMatrix(int cct) {
-            if (!mSetUp) {
-                Slog.w(TAG, "Can't set display white balance temperature: uninitialized");
-                return;
-            }
-
-            if (cct < mTemperatureMin) {
-                Slog.w(TAG, "Requested display color temperature is below allowed minimum");
-                cct = mTemperatureMin;
-            } else if (cct > mTemperatureMax) {
-                Slog.w(TAG, "Requested display color temperature is above allowed maximum");
-                cct = mTemperatureMax;
-            }
-
-            Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
-
-            synchronized (mLock) {
-                mCurrentColorTemperature = cct;
-
-                // Adapt the display's nominal white point to match the requested CCT value
-                mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
-
-                mChromaticAdaptationMatrix =
-                        ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
-                                mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
-
-                // Convert the adaptation matrix to RGB space
-                float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
-                        mDisplayColorSpaceRGB.getTransform());
-                result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
-
-                // Normalize the transform matrix to peak white value in RGB space
-                final float adaptedMaxR = result[0] + result[3] + result[6];
-                final float adaptedMaxG = result[1] + result[4] + result[7];
-                final float adaptedMaxB = result[2] + result[5] + result[8];
-                final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
-                for (int i = 0; i < result.length; i++) {
-                    result[i] /= denum;
-                }
-
-                Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
-                java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
-                java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
-                java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
-            }
-        }
-
-        @Override
-        public int getLevel() {
-            return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
-        }
-
-        @Override
-        public boolean isAvailable(Context context) {
-            if (mIsAvailable == null) {
-                mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context);
-            }
-            return mIsAvailable;
-        }
-
-        /**
-         * Format a given matrix into a string.
-         *
-         * @param matrix the matrix to format
-         * @param cols number of columns in the matrix
-         */
-        private String matrixToString(float[] matrix, int cols) {
-            if (matrix == null || cols <= 0) {
-                Slog.e(TAG, "Invalid arguments when formatting matrix to string");
-                return "";
-            }
-
-            StringBuilder sb = new StringBuilder("");
-            for (int i = 0; i < matrix.length; i++) {
-                if (i % cols == 0) {
-                    sb.append("\n    ");
-                }
-                sb.append(String.format("%9.6f ", matrix[i]));
-            }
-            return sb.toString();
-        }
-
-        @Override
-        public void dump(PrintWriter pw) {
-            synchronized (mLock) {
-                pw.println("  mSetUp = " + mSetUp);
-                if (!mSetUp) {
-                    return;
-                }
-
-                pw.println("  isActivated = " + isActivated());
-                pw.println("  mTemperatureMin = " + mTemperatureMin);
-                pw.println("  mTemperatureMax = " + mTemperatureMax);
-                pw.println("  mTemperatureDefault = " + mTemperatureDefault);
-                pw.println("  mCurrentColorTemperature = " + mCurrentColorTemperature);
-                pw.println("  mCurrentColorTemperatureXYZ = " +
-                        matrixToString(mCurrentColorTemperatureXYZ, 3));
-                pw.println("  mDisplayColorSpaceRGB RGB-to-XYZ = " +
-                        matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
-                pw.println("  mChromaticAdaptationMatrix = " +
-                        matrixToString(mChromaticAdaptationMatrix, 3));
-                pw.println("  mDisplayColorSpaceRGB XYZ-to-RGB = " +
-                        matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
-                pw.println("  mMatrixDisplayWhiteBalance = " +
-                        matrixToString(mMatrixDisplayWhiteBalance, 4));
-            }
-        }
-
-        private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
-            return new ColorSpace.Rgb(
-                "Display Color Space",
-                redGreenBlueXYZ,
-                whiteXYZ,
-                2.2f // gamma, unused for display white balance
-            );
-        }
-
-        private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
-            final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
-            if (displayToken == null) {
-                return null;
-            }
-
-            DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken);
-            if (primaries == null || primaries.red == null || primaries.green == null ||
-                primaries.blue == null || primaries.white == null) {
-                return null;
-            }
-
-            return makeRgbColorSpaceFromXYZ(
-                    new float[] {
-                        primaries.red.X, primaries.red.Y, primaries.red.Z,
-                        primaries.green.X, primaries.green.Y, primaries.green.Z,
-                        primaries.blue.X, primaries.blue.Y, primaries.blue.Z,
-                    },
-                    new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z }
-                    );
-        }
-
-        private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) {
-            final String[] displayPrimariesValues = res.getStringArray(
-                    R.array.config_displayWhiteBalanceDisplayPrimaries);
-            float[] displayRedGreenBlueXYZ =
-                    new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
-            float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
-
-            for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
-                displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
-            }
-
-            for (int i = 0; i < displayWhiteXYZ.length; i++) {
-                displayWhiteXYZ[i] = Float.parseFloat(
-                        displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
-            }
-
-            return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ);
-        }
-    };
+    @VisibleForTesting
+    final DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController =
+            new DisplayWhiteBalanceTintController();
 
     private final TintController mGlobalSaturationTintController = new TintController() {
 
@@ -860,7 +629,8 @@
         return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
     }
 
-    private void updateDisplayWhiteBalanceStatus() {
+    @VisibleForTesting
+    void updateDisplayWhiteBalanceStatus() {
         boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
         mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() &&
                 !mNightDisplayTintController.isActivated() &&
@@ -1101,6 +871,7 @@
         pw.println("Display white balance:");
         if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
             pw.println("    Activated: " + mDisplayWhiteBalanceTintController.isActivated());
+            mDisplayWhiteBalanceTintController.dump(pw);
         } else {
             pw.println("    Not available");
         }
@@ -1533,6 +1304,244 @@
         }
     }
 
+    final class DisplayWhiteBalanceTintController extends TintController {
+        // Three chromaticity coordinates per color: X, Y, and Z
+        private final int NUM_VALUES_PER_PRIMARY = 3;
+        // Four colors: red, green, blue, and white
+        private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;
+
+        private final Object mLock = new Object();
+        @VisibleForTesting
+        int mTemperatureMin;
+        @VisibleForTesting
+        int mTemperatureMax;
+        private int mTemperatureDefault;
+        private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+        @VisibleForTesting
+        ColorSpace.Rgb mDisplayColorSpaceRGB;
+        private float[] mChromaticAdaptationMatrix;
+        @VisibleForTesting
+        int mCurrentColorTemperature;
+        private float[] mCurrentColorTemperatureXYZ;
+        private boolean mSetUp = false;
+        private float[] mMatrixDisplayWhiteBalance = new float[16];
+        private Boolean mIsAvailable;
+
+        @Override
+        public void setUp(Context context, boolean needsLinear) {
+            mSetUp = false;
+            final Resources res = context.getResources();
+
+            ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl();
+            if (displayColorSpaceRGB == null) {
+                Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res");
+                displayColorSpaceRGB = getDisplayColorSpaceFromResources(res);
+                if (displayColorSpaceRGB == null) {
+                    Slog.e(TAG, "Failed to get display color space from resources");
+                    return;
+                }
+            }
+
+            final String[] nominalWhiteValues = res.getStringArray(
+                    R.array.config_displayWhiteBalanceDisplayNominalWhite);
+            float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+            for (int i = 0; i < nominalWhiteValues.length; i++) {
+                displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
+            }
+
+            final int colorTemperatureMin = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureMin);
+            if (colorTemperatureMin <= 0) {
+                Slog.e(TAG, "Display white balance minimum temperature must be greater than 0");
+                return;
+            }
+
+            final int colorTemperatureMax = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureMax);
+            if (colorTemperatureMax < colorTemperatureMin) {
+                Slog.e(TAG, "Display white balance max temp must be greater or equal to min");
+                return;
+            }
+
+            final int colorTemperature = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureDefault);
+
+            synchronized (mLock) {
+                mDisplayColorSpaceRGB = displayColorSpaceRGB;
+                mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
+                mTemperatureMin = colorTemperatureMin;
+                mTemperatureMax = colorTemperatureMax;
+                mTemperatureDefault = colorTemperature;
+                mSetUp = true;
+            }
+
+            setMatrix(mTemperatureDefault);
+        }
+
+        @Override
+        public float[] getMatrix() {
+            return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
+        }
+
+        @Override
+        public void setMatrix(int cct) {
+            if (!mSetUp) {
+                Slog.w(TAG, "Can't set display white balance temperature: uninitialized");
+                return;
+            }
+
+            if (cct < mTemperatureMin) {
+                Slog.w(TAG, "Requested display color temperature is below allowed minimum");
+                cct = mTemperatureMin;
+            } else if (cct > mTemperatureMax) {
+                Slog.w(TAG, "Requested display color temperature is above allowed maximum");
+                cct = mTemperatureMax;
+            }
+
+            Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
+
+            synchronized (mLock) {
+                mCurrentColorTemperature = cct;
+
+                // Adapt the display's nominal white point to match the requested CCT value
+                mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
+
+                mChromaticAdaptationMatrix =
+                        ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
+                                mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
+
+                // Convert the adaptation matrix to RGB space
+                float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
+                        mDisplayColorSpaceRGB.getTransform());
+                result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
+
+                // Normalize the transform matrix to peak white value in RGB space
+                final float adaptedMaxR = result[0] + result[3] + result[6];
+                final float adaptedMaxG = result[1] + result[4] + result[7];
+                final float adaptedMaxB = result[2] + result[5] + result[8];
+                final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
+                for (int i = 0; i < result.length; i++) {
+                    result[i] /= denum;
+                }
+
+                Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
+                java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
+                java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
+                java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
+            }
+        }
+
+        @Override
+        public int getLevel() {
+            return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
+        }
+
+        @Override
+        public boolean isAvailable(Context context) {
+            if (mIsAvailable == null) {
+                mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context);
+            }
+            return mIsAvailable;
+        }
+
+        /**
+         * Format a given matrix into a string.
+         *
+         * @param matrix the matrix to format
+         * @param cols number of columns in the matrix
+         */
+        private String matrixToString(float[] matrix, int cols) {
+            if (matrix == null || cols <= 0) {
+                Slog.e(TAG, "Invalid arguments when formatting matrix to string");
+                return "";
+            }
+
+            StringBuilder sb = new StringBuilder("");
+            for (int i = 0; i < matrix.length; i++) {
+                if (i % cols == 0) {
+                    sb.append("\n      ");
+                }
+                sb.append(String.format("%9.6f ", matrix[i]));
+            }
+            return sb.toString();
+        }
+
+        @Override
+        public void dump(PrintWriter pw) {
+            synchronized (mLock) {
+                pw.println("    mSetUp = " + mSetUp);
+                if (!mSetUp) {
+                    return;
+                }
+
+                pw.println("    mTemperatureMin = " + mTemperatureMin);
+                pw.println("    mTemperatureMax = " + mTemperatureMax);
+                pw.println("    mTemperatureDefault = " + mTemperatureDefault);
+                pw.println("    mCurrentColorTemperature = " + mCurrentColorTemperature);
+                pw.println("    mCurrentColorTemperatureXYZ = " +
+                        matrixToString(mCurrentColorTemperatureXYZ, 3));
+                pw.println("    mDisplayColorSpaceRGB RGB-to-XYZ = " +
+                        matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
+                pw.println("    mChromaticAdaptationMatrix = " +
+                        matrixToString(mChromaticAdaptationMatrix, 3));
+                pw.println("    mDisplayColorSpaceRGB XYZ-to-RGB = " +
+                        matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
+                pw.println("    mMatrixDisplayWhiteBalance = " +
+                        matrixToString(mMatrixDisplayWhiteBalance, 4));
+            }
+        }
+
+        private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
+            return new ColorSpace.Rgb(
+                "Display Color Space",
+                redGreenBlueXYZ,
+                whiteXYZ,
+                2.2f // gamma, unused for display white balance
+            );
+        }
+
+        private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
+            final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
+            if (displayToken == null) {
+                return null;
+            }
+
+            DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken);
+            if (primaries == null || primaries.red == null || primaries.green == null ||
+                primaries.blue == null || primaries.white == null) {
+                return null;
+            }
+
+            return makeRgbColorSpaceFromXYZ(
+                    new float[] {
+                        primaries.red.X, primaries.red.Y, primaries.red.Z,
+                        primaries.green.X, primaries.green.Y, primaries.green.Z,
+                        primaries.blue.X, primaries.blue.Y, primaries.blue.Z,
+                    },
+                    new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z }
+                    );
+        }
+
+        private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) {
+            final String[] displayPrimariesValues = res.getStringArray(
+                    R.array.config_displayWhiteBalanceDisplayPrimaries);
+            float[] displayRedGreenBlueXYZ =
+                    new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
+            float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+
+            for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
+                displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
+            }
+
+            for (int i = 0; i < displayWhiteXYZ.length; i++) {
+                displayWhiteXYZ[i] = Float.parseFloat(
+                        displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
+            }
+
+            return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ);
+        }
+    };
+
     /**
      * Local service that allows color transforms to be enabled from other system services.
      */
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 6ee5665..e9ae516 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -19,6 +19,7 @@
 import android.graphics.Rect;
 import android.hardware.display.DisplayViewport;
 import android.os.IBinder;
+import android.view.DisplayAddress;
 import android.view.Surface;
 import android.view.SurfaceControl;
 
@@ -225,8 +226,12 @@
         viewport.deviceHeight = isRotated ? info.width : info.height;
 
         viewport.uniqueId = info.uniqueId;
-        // TODO(b/112898898) Use an actual port here.
-        viewport.physicalPort = null;
+
+        if (info.address instanceof DisplayAddress.Physical) {
+            viewport.physicalPort = ((DisplayAddress.Physical) info.address).getPort();
+        } else {
+            viewport.physicalPort = null;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index ab64f61..729ea17 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -19,6 +19,7 @@
 import android.hardware.display.DisplayViewport;
 import android.util.DisplayMetrics;
 import android.view.Display;
+import android.view.DisplayAddress;
 import android.view.DisplayCutout;
 import android.view.Surface;
 
@@ -274,7 +275,7 @@
      * Display address, or null if none.
      * Interpretation varies by display type.
      */
-    public String address;
+    public DisplayAddress address;
 
     /**
      * Display state.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 80ea1da..b99ba7e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1336,7 +1336,7 @@
                 && !TextUtils.isEmpty(info.uniqueId)) {
             viewportType = VIEWPORT_VIRTUAL;
         } else {
-            Slog.wtf(TAG, "Unable to populate viewport for display device: " + info);
+            Slog.i(TAG, "Display " + info + " does not support input device matching.");
             return;
         }
 
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/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 28f21f63..4891947 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -31,6 +31,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
+import android.view.DisplayAddress;
 import android.view.DisplayCutout;
 import android.view.DisplayEventReceiver;
 import android.view.Surface;
@@ -382,6 +383,7 @@
                 mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos;
                 mInfo.state = mState;
                 mInfo.uniqueId = getUniqueId();
+                mInfo.address = DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
 
                 // Assume that all built-in displays that have secure output (eg. HDCP) also
                 // support compositing from gralloc protected buffers.
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index e8d6ad4..9e4c1cb 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -16,9 +16,6 @@
 
 package com.android.server.display;
 
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.IndentingPrintWriter;
-
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -35,9 +32,13 @@
 import android.os.UserHandle;
 import android.util.Slog;
 import android.view.Display;
+import android.view.DisplayAddress;
 import android.view.Surface;
 import android.view.SurfaceControl;
 
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -581,7 +582,7 @@
         private final int mHeight;
         private final float mRefreshRate;
         private final int mFlags;
-        private final String mAddress;
+        private final DisplayAddress mAddress;
         private final Display.Mode mMode;
 
         private Surface mSurface;
@@ -596,7 +597,7 @@
             mHeight = height;
             mRefreshRate = refreshRate;
             mFlags = flags;
-            mAddress = address;
+            mAddress = DisplayAddress.fromMacAddress(address);
             mSurface = surface;
             mMode = createMode(width, height, refreshRate);
         }
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..243b6fe 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -373,6 +373,7 @@
     private final NtpTimeHelper mNtpTimeHelper;
     private final GnssBatchingProvider mGnssBatchingProvider;
     private final GnssGeofenceProvider mGnssGeofenceProvider;
+    // Available only on GNSS HAL 2.0 implementations and later.
     private GnssVisibilityControl mGnssVisibilityControl;
 
     // Handler for processing events
@@ -463,8 +464,8 @@
         }
     };
 
-    // TODO(b/119326010): replace OnSubscriptionsChangedListener with
-    // ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED broadcast reseiver.
+    // TODO: replace OnSubscriptionsChangedListener with ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED
+    //       broadcast receiver.
     private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
             new OnSubscriptionsChangedListener() {
                 @Override
@@ -676,8 +677,7 @@
         mNtpTimeHelper.onNetworkAvailable();
         if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
             if (mSupportsXtra) {
-                // Download only if supported, (prevents an unneccesary on-boot
-                // download)
+                // Download only if supported, (prevents an unnecessary on-boot download)
                 xtraDownloadRequest();
             }
         }
@@ -764,7 +764,7 @@
 
     /** Returns true if the location request is too frequent. */
     private boolean isRequestLocationRateLimited() {
-        // TODO(b/73198123): implement exponential backoff.
+        // TODO: implement exponential backoff.
         return false;
     }
 
@@ -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/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index c3f25bf..20f872a 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -24,10 +24,13 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.location.LocationManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.StatsLog;
@@ -70,7 +73,7 @@
     private final Handler mHandler;
     private final Context mContext;
 
-    private boolean mIsMasterLocationSettingsEnabled = true;
+    private boolean mIsDeviceLocationSettingsEnabled;
 
     // Number of non-framework location access proxy apps is expected to be small (< 5).
     private static final int HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7;
@@ -88,13 +91,9 @@
         mAppOps = mContext.getSystemService(AppOpsManager.class);
         mPackageManager = mContext.getPackageManager();
 
-        // Set to empty proxy app list initially until the configuration properties are loaded.
-        updateNfwLocationAccessProxyAppsInGnssHal();
-
-        // Listen for proxy app package installation, removal events.
-        listenForProxyAppsPackageUpdates();
-
-        // TODO(b/122855984): Handle global location settings on/off.
+        // Complete initialization as the first event to run in mHandler thread. After that,
+        // all object state read/update events run in the mHandler thread.
+        runOnHandler(this::handleInitialize);
     }
 
     void updateProxyApps(List<String> nfwLocationAccessProxyApps) {
@@ -105,10 +104,6 @@
         runOnHandler(() -> handleUpdateProxyApps(nfwLocationAccessProxyApps));
     }
 
-    void masterLocationSettingsUpdated(boolean enabled) {
-        runOnHandler(() -> handleMasterLocationSettingsUpdated(enabled));
-    }
-
     void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
             String otherProtocolStackName, byte requestor, String requestorId, byte responseType,
             boolean inEmergencyMode, boolean isCachedLocation) {
@@ -117,7 +112,19 @@
                         requestor, requestorId, responseType, inEmergencyMode, isCachedLocation)));
     }
 
+    private void handleInitialize() {
+        disableNfwLocationAccess(); // Disable until config properties are loaded.
+        listenForProxyAppsPackageUpdates();
+        listenForDeviceLocationSettingsUpdate();
+        mIsDeviceLocationSettingsEnabled = getDeviceLocationSettings();
+    }
+
+    private boolean getDeviceLocationSettings() {
+        return mContext.getSystemService(LocationManager.class).isLocationEnabled();
+    }
+
     private void listenForProxyAppsPackageUpdates() {
+        // Listen for proxy apps package installation, removal events.
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -143,11 +150,22 @@
         }, UserHandle.ALL, intentFilter, null, mHandler);
     }
 
+    private void listenForDeviceLocationSettingsUpdate() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE),
+                true,
+                new ContentObserver(mHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        handleDeviceLocationSettingsUpdated();
+                    }
+                }, UserHandle.USER_ALL);
+    }
+
     private void handleProxyAppPackageUpdate(String pkgName, String action) {
         final Boolean locationPermission = mProxyAppToLocationPermissions.get(pkgName);
-        // pkgName is not one of the proxy apps in our list.
         if (locationPermission == null) {
-            return;
+            return; // ignore, pkgName is not one of the proxy apps in our list.
         }
 
         Log.i(TAG, "Proxy app " + pkgName + " package changed: " + action);
@@ -197,15 +215,33 @@
                 return true;
             }
         }
-
         return false;
     }
 
-    private void handleMasterLocationSettingsUpdated(boolean enabled) {
-        mIsMasterLocationSettingsEnabled = enabled;
-        Log.i(TAG, "Master location settings switch changed to "
-                + (enabled ? "enabled" : "disabled"));
-        updateNfwLocationAccessProxyAppsInGnssHal();
+    private void handleDeviceLocationSettingsUpdated() {
+        final boolean enabled = getDeviceLocationSettings();
+        Log.i(TAG, "Device location settings enabled: " + enabled);
+
+        if (mIsDeviceLocationSettingsEnabled == enabled) {
+            return;
+        }
+
+        mIsDeviceLocationSettingsEnabled = enabled;
+        if (!mIsDeviceLocationSettingsEnabled) {
+            disableNfwLocationAccess();
+            return;
+        }
+
+        // When device location settings was disabled, we already set the proxy app list
+        // to empty in GNSS HAL. Update only if the proxy app list is not empty.
+        String[] locationPermissionEnabledProxyApps = getLocationPermissionEnabledProxyApps();
+        if (locationPermissionEnabledProxyApps.length != 0) {
+            setNfwLocationAccessProxyAppsInGnssHal(locationPermissionEnabledProxyApps);
+        }
+    }
+
+    private void disableNfwLocationAccess() {
+        setNfwLocationAccessProxyAppsInGnssHal(NO_LOCATION_ENABLED_PROXY_APPS);
     }
 
     // Represents NfwNotification structure in IGnssVisibilityControlCallback.hal
@@ -316,8 +352,7 @@
             return mPackageManager.getApplicationInfo(pkgName, 0).uid;
         } catch (PackageManager.NameNotFoundException e) {
             if (DEBUG) {
-                Log.d(TAG, "Non-framework location access proxy app "
-                        + pkgName + " is not found.");
+                Log.d(TAG, "Non-framework location access proxy app " + pkgName + " is not found.");
             }
             return null;
         }
@@ -329,8 +364,14 @@
     }
 
     private void updateNfwLocationAccessProxyAppsInGnssHal() {
-        final String[] locationPermissionEnabledProxyApps = shouldDisableNfwLocationAccess()
-                ? NO_LOCATION_ENABLED_PROXY_APPS : getLocationPermissionEnabledProxyApps();
+        if (!mIsDeviceLocationSettingsEnabled) {
+            return; // Keep non-framework location access disabled.
+        }
+        setNfwLocationAccessProxyAppsInGnssHal(getLocationPermissionEnabledProxyApps());
+    }
+
+    private void setNfwLocationAccessProxyAppsInGnssHal(
+            String[] locationPermissionEnabledProxyApps) {
         final String proxyAppsStr = Arrays.toString(locationPermissionEnabledProxyApps);
         Log.i(TAG, "Updating non-framework location access proxy apps in the GNSS HAL to: "
                 + proxyAppsStr);
@@ -341,12 +382,8 @@
         }
     }
 
-    private boolean shouldDisableNfwLocationAccess() {
-        return !mIsMasterLocationSettingsEnabled;
-    }
-
     private String[] getLocationPermissionEnabledProxyApps() {
-        // Get a count of proxy apps with location permission enabled to array creation size.
+        // Get a count of proxy apps with location permission enabled for array creation size.
         int countLocationPermissionEnabledProxyApps = 0;
         for (Boolean hasLocationPermissionEnabled : mProxyAppToLocationPermissions.values()) {
             if (hasLocationPermissionEnabled) {
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/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index e479a15..d0c59c1 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -320,6 +320,11 @@
     private final class PackageReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
+            final String action = intent.getAction();
+            if (action == null) {
+                Slog.e(TAG, "Cannot handle package broadcast with null action");
+                return;
+            }
             final Uri data = intent.getData();
             if (data == null) {
                 Slog.e(TAG, "Cannot handle package broadcast with null data");
@@ -337,7 +342,7 @@
                 userIds = new int[] { UserHandle.getUserId(extraUid) };
             }
 
-            switch (intent.getAction()) {
+            switch (action) {
                 case ACTION_PACKAGE_ADDED:
                     if (replacing) {
                         onPackageUpgraded(packageName, userIds);
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 33b8641..640b155 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -19,6 +19,9 @@
 per-file CompilerStats.java = agampe@google.com
 per-file CompilerStats.java = calin@google.com
 per-file CompilerStats.java = ngeoffray@google.com
+per-file DynamicCodeLoggingService.java = agampe@google.com
+per-file DynamicCodeLoggingService.java = calin@google.com
+per-file DynamicCodeLoggingService.java = ngeoffray@google.com
 per-file InstructionSets.java = agampe@google.com
 per-file InstructionSets.java = calin@google.com
 per-file InstructionSets.java = ngeoffray@google.com
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 8608349..f5d88e3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -84,7 +84,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.IoThread;
 import com.android.server.LocalServices;
-import com.android.server.pm.permission.PermissionManagerInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 import libcore.io.IoUtils;
 
@@ -131,7 +131,7 @@
     private final Context mContext;
     private final PackageManagerService mPm;
     private final StagingManager mStagingManager;
-    private final PermissionManagerInternal mPermissionManager;
+    private final PermissionManagerServiceInternal mPermissionManager;
 
     private AppOpsManager mAppOps;
 
@@ -189,7 +189,7 @@
     public PackageInstallerService(Context context, PackageManagerService pm, ApexManager am) {
         mContext = context;
         mPm = pm;
-        mPermissionManager = LocalServices.getService(PermissionManagerInternal.class);
+        mPermissionManager = LocalServices.getService(PermissionManagerServiceInternal.class);
 
         mInstallThread = new HandlerThread(TAG);
         mInstallThread.start();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 400443a..b1c186e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -238,7 +238,6 @@
 import android.os.storage.StorageManagerInternal;
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
-import android.permission.PermissionControllerManager;
 import android.provider.DeviceConfig;
 import android.provider.MediaStore;
 import android.provider.Settings.Global;
@@ -291,7 +290,6 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
 import com.android.server.AttributeCache;
 import com.android.server.DeviceIdleController;
 import com.android.server.EventLogTags;
@@ -315,9 +313,9 @@
 import com.android.server.pm.permission.BasePermission;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
-import com.android.server.pm.permission.PermissionManagerInternal;
-import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionsState;
 import com.android.server.security.VerityUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -371,7 +369,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -445,8 +442,6 @@
     private static final boolean ENABLE_FREE_CACHE_V2 =
             SystemProperties.getBoolean("fw.free_cache_v2", true);
 
-    private static final long BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60);
-
     private static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts";
 
     private static final int RADIO_UID = Process.PHONE_UID;
@@ -940,7 +935,7 @@
 
     // TODO remove this and go through mPermissonManager directly
     final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
-    private final PermissionManagerInternal mPermissionManager;
+    private final PermissionManagerServiceInternal mPermissionManager;
 
     private final ComponentResolver mComponentResolver;
     // List of packages names to keep cached, even if they are uninstalled for all users
@@ -985,6 +980,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 +1347,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;
@@ -1937,9 +1935,14 @@
                             }
                         }
 
-                        // We may also need to apply pending (restored) runtime
-                        // permission grants within these users.
-                        mSettings.applyPendingPermissionGrantsLPw(packageName, userId);
+                        // We may also need to apply pending (restored) runtime permission grants
+                        // within these users.
+                        mPermissionManager.restoreDelayedRuntimePermissions(packageName,
+                                UserHandle.of(userId));
+
+                        // Persistent preferred activity might have came into effect due to this
+                        // install.
+                        updateDefaultHomeLPw(userId);
                     }
                 }
             }
@@ -3306,7 +3309,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 +12807,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
@@ -18817,7 +18821,7 @@
             // permission as requiring a review as this is the initial state.
             int flags = 0;
             if (ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) {
-                flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+                flags |= FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
             }
             if (permissionsState.updatePermissionFlags(bp, userId, userSettableMask, flags)) {
                 if (hasInstallState) {
@@ -19076,6 +19080,7 @@
             pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
             scheduleWritePackageRestrictionsLocked(userId);
             postPreferredActivityChangedBroadcast(userId);
+            updateDefaultHomeLPw(userId);
         }
     }
 
@@ -19226,6 +19231,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 +19266,9 @@
                     pir.removeFilter(pa);
                 }
                 changed = true;
+                if (!skipUpdateDefaultHome) {
+                    updateDefaultHomeLPw(thisUserId);
+                }
             }
         }
         if (changed) {
@@ -19313,8 +19328,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 +19399,7 @@
                     new PersistentPreferredActivity(filter, activity));
             scheduleWritePackageRestrictionsLocked(userId);
             postPreferredActivityChangedBroadcast(userId);
+            updateDefaultHomeLPw(userId);
         }
     }
 
@@ -19426,6 +19443,7 @@
             if (changed) {
                 scheduleWritePackageRestrictionsLocked(userId);
                 postPreferredActivityChangedBroadcast(userId);
+                updateDefaultHomeLPw(userId);
             }
         }
     }
@@ -19513,6 +19531,7 @@
                     (readParser, readUserId) -> {
                         synchronized (mPackages) {
                             mSettings.readPreferredActivitiesLPw(readParser, readUserId);
+                            updateDefaultHomeLPw(readUserId);
                         }
                     });
         } catch (Exception e) {
@@ -19568,8 +19587,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) {
@@ -19633,139 +19661,6 @@
     }
 
     @Override
-    public byte[] getPermissionGrantBackup(int userId) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Only the system may call getPermissionGrantBackup()");
-        }
-
-        AtomicReference<byte[]> backup = new AtomicReference<>();
-        mContext.getSystemService(PermissionControllerManager.class).getRuntimePermissionBackup(
-                UserHandle.of(userId), mContext.getMainExecutor(), (b) -> {
-                    synchronized (backup) {
-                        backup.set(b);
-                        backup.notifyAll();
-                    }
-                });
-
-        long start = System.currentTimeMillis();
-        synchronized (backup) {
-            while (backup.get() == null) {
-                long timeLeft = start + BACKUP_TIMEOUT_MILLIS - System.currentTimeMillis();
-                if (timeLeft <= 0) {
-                    return null;
-                }
-
-                try {
-                    backup.wait(timeLeft);
-                } catch (InterruptedException ignored) {
-                    return null;
-                }
-            }
-        }
-
-        return backup.get();
-    }
-
-    @Override
-    public void restorePermissionGrants(byte[] backup, int userId) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Only the system may call restorePermissionGrants()");
-        }
-
-        try {
-            final XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
-            restoreFromXml(parser, userId, TAG_PERMISSION_BACKUP,
-                    (parser1, userId1) -> {
-                        synchronized (mPackages) {
-                            processRestoredPermissionGrantsLPr(parser1, userId1);
-                        }
-                    });
-        } catch (Exception e) {
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
-            }
-        }
-    }
-
-    @GuardedBy("mPackages")
-    private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId)
-            throws XmlPullParserException, IOException {
-        String pkgName = null;
-        int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-
-            final String tagName = parser.getName();
-            if (tagName.equals(TAG_GRANT)) {
-                pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
-                if (DEBUG_BACKUP) {
-                    Slog.v(TAG, "+++ Restoring grants for package " + pkgName);
-                }
-            } else if (tagName.equals(TAG_PERMISSION)) {
-
-                final boolean isGranted = "true".equals(parser.getAttributeValue(null, ATTR_IS_GRANTED));
-                final String permName = parser.getAttributeValue(null, ATTR_PERMISSION_NAME);
-
-                int newFlagSet = 0;
-                if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) {
-                    newFlagSet |= FLAG_PERMISSION_USER_SET;
-                }
-                if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) {
-                    newFlagSet |= FLAG_PERMISSION_USER_FIXED;
-                }
-                if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) {
-                    newFlagSet |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-                }
-                if (DEBUG_BACKUP) {
-                    Slog.v(TAG, "  + Restoring grant:"
-                            + " pkg=" + pkgName
-                            + " perm=" + permName
-                            + " granted=" + isGranted
-                            + " bits=0x" + Integer.toHexString(newFlagSet));
-                }
-                final PackageSetting ps = mSettings.mPackages.get(pkgName);
-                if (ps != null) {
-                    // Already installed so we apply the grant immediately
-                    if (DEBUG_BACKUP) {
-                        Slog.v(TAG, "        + already installed; applying");
-                    }
-                    PermissionsState perms = ps.getPermissionsState();
-                    BasePermission bp =
-                            (BasePermission) mPermissionManager.getPermissionTEMP(permName);
-                    if (bp != null) {
-                        if (isGranted) {
-                            perms.grantRuntimePermission(bp, userId);
-                        }
-                        if (newFlagSet != 0) {
-                            perms.updatePermissionFlags(
-                                    bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet);
-                        }
-                    }
-                } else {
-                    // Need to wait for post-restore install to apply the grant
-                    if (DEBUG_BACKUP) {
-                        Slog.v(TAG, "        - not yet installed; saving for later");
-                    }
-                    mSettings.processRestoredPermissionGrantLPr(pkgName, permName,
-                            isGranted, newFlagSet, userId);
-                }
-            } else {
-                PackageManagerService.reportSettingsProblem(Log.WARN,
-                        "Unknown element under <" + TAG_PERMISSION_BACKUP + ">: " + tagName);
-                XmlUtils.skipCurrentTag(parser);
-            }
-        }
-
-        scheduleWriteSettingsLocked();
-        mSettings.writeRuntimePermissionsForUserLPr(userId, false);
-    }
-
-    @Override
     public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
             int sourceUserId, int targetUserId, int flags) {
         mContext.enforceCallingOrSelfPermission(
@@ -19927,19 +19822,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
@@ -21213,10 +21148,6 @@
                 }
             }
 
-            if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS) && packageName == null) {
-                mSettings.dumpRestoredPermissionGrantsLPr(pw, dumpState);
-            }
-
             if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
                 // XXX should handle packageName != null by dumping only install data that
                 // the given package is involved with.
@@ -23829,6 +23760,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/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 975ffb2..92fe377 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -19,10 +19,6 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
@@ -249,23 +245,6 @@
     private static final String ATTR_SDK_VERSION = "sdkVersion";
     private static final String ATTR_DATABASE_VERSION = "databaseVersion";
 
-    // Bookkeeping for restored permission grants
-    private static final String TAG_RESTORED_RUNTIME_PERMISSIONS = "restored-perms";
-    // package name: ATTR_PACKAGE_NAME
-    private static final String TAG_PERMISSION_ENTRY = "perm";
-    // permission name: ATTR_NAME
-    // permission granted (boolean): ATTR_GRANTED
-    private static final String ATTR_USER_SET = "set";
-    private static final String ATTR_USER_FIXED = "fixed";
-    private static final String ATTR_REVOKE_ON_UPGRADE = "rou";
-    private static final String ATTR_REVOKE_WHEN_REQUESTED = "rwr";
-
-    // Flag mask of restored permission grants that are applied at install time
-    private static final int USER_RUNTIME_GRANT_MASK =
-            FLAG_PERMISSION_USER_SET
-            | FLAG_PERMISSION_USER_FIXED
-            | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-
     private final Object mLock;
 
     private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
@@ -303,26 +282,6 @@
         int[] excludedUserIds;
     }
 
-    // Bookkeeping for restored user permission grants
-    final class RestoredPermissionGrant {
-        String permissionName;
-        boolean granted;
-        int grantBits;
-
-        RestoredPermissionGrant(String name, boolean isGranted, int theGrantBits) {
-            permissionName = name;
-            granted = isGranted;
-            grantBits = theGrantBits;
-        }
-    }
-
-    // This would be more compact as a flat array of restored grants or something, but we
-    // may have quite a few, especially during early device lifetime, and avoiding all those
-    // linear lookups will be important.
-    private final SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>>
-            mRestoredUserGrants =
-                new SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>>();
-
     private static int mFirstAvailableUid = 0;
 
     /** Map from volume UUID to {@link VersionInfo} */
@@ -461,43 +420,6 @@
         return mRenamedPackages.put(pkgName, origPkgName);
     }
 
-    void applyPendingPermissionGrantsLPw(String packageName, int userId) {
-        ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage =
-                mRestoredUserGrants.get(userId);
-        if (grantsByPackage == null || grantsByPackage.size() == 0) {
-            return;
-        }
-
-        ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(packageName);
-        if (grants == null || grants.size() == 0) {
-            return;
-        }
-
-        final PackageSetting ps = mPackages.get(packageName);
-        if (ps == null) {
-            Slog.e(TAG, "Can't find supposedly installed package " + packageName);
-            return;
-        }
-        final PermissionsState perms = ps.getPermissionsState();
-
-        for (RestoredPermissionGrant grant : grants) {
-            BasePermission bp = mPermissions.getPermission(grant.permissionName);
-            if (bp != null) {
-                if (grant.granted) {
-                    perms.grantRuntimePermission(bp, userId);
-                }
-                perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, grant.grantBits);
-            }
-        }
-
-        // And remove it from the pending-grant bookkeeping
-        grantsByPackage.remove(packageName);
-        if (grantsByPackage.size() < 1) {
-            mRestoredUserGrants.remove(userId);
-        }
-        writeRuntimePermissionsForUserLPr(userId, false);
-    }
-
     public boolean canPropagatePermissionToInstantApp(String permName) {
         return mPermissions.canPropagatePermissionToInstantApp(permName);
     }
@@ -1982,13 +1904,6 @@
         }
     }
 
-    // Specifically for backup/restore
-    public void processRestoredPermissionGrantLPr(String pkgName, String permission,
-            boolean isGranted, int restoredFlagSet, int userId) {
-        mRuntimePermissionsPersistence.rememberRestoredUserGrantLPr(
-                pkgName, permission, isGranted, restoredFlagSet, userId);
-    }
-
     void writeDefaultAppsLPr(XmlSerializer serializer, int userId)
             throws IllegalArgumentException, IllegalStateException, IOException {
         serializer.startTag(null, TAG_DEFAULT_APPS);
@@ -5014,51 +4929,6 @@
         pw.print(mReadMessages.toString());
     }
 
-    void dumpRestoredPermissionGrantsLPr(PrintWriter pw, DumpState dumpState) {
-        if (mRestoredUserGrants.size() > 0) {
-            pw.println();
-            pw.println("Restored (pending) permission grants:");
-            for (int userIndex = 0; userIndex < mRestoredUserGrants.size(); userIndex++) {
-                ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage =
-                        mRestoredUserGrants.valueAt(userIndex);
-                if (grantsByPackage != null && grantsByPackage.size() > 0) {
-                    final int userId = mRestoredUserGrants.keyAt(userIndex);
-                    pw.print("  User "); pw.println(userId);
-
-                    for (int pkgIndex = 0; pkgIndex < grantsByPackage.size(); pkgIndex++) {
-                        ArraySet<RestoredPermissionGrant> grants = grantsByPackage.valueAt(pkgIndex);
-                        if (grants != null && grants.size() > 0) {
-                            final String pkgName = grantsByPackage.keyAt(pkgIndex);
-                            pw.print("    "); pw.print(pkgName); pw.println(" :");
-
-                            for (RestoredPermissionGrant g : grants) {
-                                pw.print("      ");
-                                pw.print(g.permissionName);
-                                if (g.granted) {
-                                    pw.print(" GRANTED");
-                                }
-                                if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) {
-                                    pw.print(" user_set");
-                                }
-                                if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) {
-                                    pw.print(" user_fixed");
-                                }
-                                if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
-                                    pw.print(" revoke_on_upgrade");
-                                }
-                                if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
-                                    pw.print(" revoke_when_requested");
-                                }
-                                pw.println();
-                            }
-                        }
-                    }
-                }
-            }
-            pw.println();
-        }
-    }
-
     private static void dumpSplitNames(PrintWriter pw, PackageParser.Package pkg) {
         if (pkg == null) {
             pw.print("unknown");
@@ -5328,55 +5198,6 @@
 
                 serializer.endTag(null, TAG_RUNTIME_PERMISSIONS);
 
-                // Now any restored permission grants that are waiting for the apps
-                // in question to be installed.  These are stored as per-package
-                // TAG_RESTORED_RUNTIME_PERMISSIONS blocks, each containing some
-                // number of individual permission grant entities.
-                if (mRestoredUserGrants.get(userId) != null) {
-                    ArrayMap<String, ArraySet<RestoredPermissionGrant>> restoredGrants =
-                            mRestoredUserGrants.get(userId);
-                    if (restoredGrants != null) {
-                        final int pkgCount = restoredGrants.size();
-                        for (int i = 0; i < pkgCount; i++) {
-                            final ArraySet<RestoredPermissionGrant> pkgGrants =
-                                    restoredGrants.valueAt(i);
-                            if (pkgGrants != null && pkgGrants.size() > 0) {
-                                final String pkgName = restoredGrants.keyAt(i);
-                                serializer.startTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
-                                serializer.attribute(null, ATTR_PACKAGE_NAME, pkgName);
-
-                                final int N = pkgGrants.size();
-                                for (int z = 0; z < N; z++) {
-                                    RestoredPermissionGrant g = pkgGrants.valueAt(z);
-                                    serializer.startTag(null, TAG_PERMISSION_ENTRY);
-                                    serializer.attribute(null, ATTR_NAME, g.permissionName);
-
-                                    if (g.granted) {
-                                        serializer.attribute(null, ATTR_GRANTED, "true");
-                                    }
-
-                                    if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) {
-                                        serializer.attribute(null, ATTR_USER_SET, "true");
-                                    }
-                                    if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) {
-                                        serializer.attribute(null, ATTR_USER_FIXED, "true");
-                                    }
-                                    if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
-                                        serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
-                                    }
-                                    if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED)
-                                            != 0) {
-                                        serializer.attribute(null, ATTR_REVOKE_WHEN_REQUESTED,
-                                                "true");
-                                    }
-                                    serializer.endTag(null, TAG_PERMISSION_ENTRY);
-                                }
-                                serializer.endTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
-                            }
-                        }
-                    }
-                }
-
                 serializer.endDocument();
                 destination.finishWrite(out);
 
@@ -5455,29 +5276,6 @@
             }
         }
 
-        // Backup/restore support
-
-        public void rememberRestoredUserGrantLPr(String pkgName, String permission,
-                boolean isGranted, int restoredFlagSet, int userId) {
-            // This change will be remembered at write-settings time
-            ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage =
-                    mRestoredUserGrants.get(userId);
-            if (grantsByPackage == null) {
-                grantsByPackage = new ArrayMap<String, ArraySet<RestoredPermissionGrant>>();
-                mRestoredUserGrants.put(userId, grantsByPackage);
-            }
-
-            ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(pkgName);
-            if (grants == null) {
-                grants = new ArraySet<RestoredPermissionGrant>();
-                grantsByPackage.put(pkgName, grants);
-            }
-
-            RestoredPermissionGrant grant = new RestoredPermissionGrant(permission,
-                    isGranted, restoredFlagSet);
-            grants.add(grant);
-        }
-
         // Private internals
 
         @GuardedBy("Settings.this.mLock")
@@ -5520,50 +5318,6 @@
                         }
                         parsePermissionsLPr(parser, sus.getPermissionsState(), userId);
                     } break;
-
-                    case TAG_RESTORED_RUNTIME_PERMISSIONS: {
-                        final String pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
-                        parseRestoredRuntimePermissionsLPr(parser, pkgName, userId);
-                    } break;
-                }
-            }
-        }
-
-        private void parseRestoredRuntimePermissionsLPr(XmlPullParser parser,
-                final String pkgName, final int userId) throws IOException, XmlPullParserException {
-            final int outerDepth = parser.getDepth();
-            int type;
-            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                    continue;
-                }
-
-                switch (parser.getName()) {
-                    case TAG_PERMISSION_ENTRY: {
-                        final String permName = parser.getAttributeValue(null, ATTR_NAME);
-                        final boolean isGranted = "true".equals(
-                                parser.getAttributeValue(null, ATTR_GRANTED));
-
-                        int permBits = 0;
-                        if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) {
-                            permBits |= FLAG_PERMISSION_USER_SET;
-                        }
-                        if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) {
-                            permBits |= FLAG_PERMISSION_USER_FIXED;
-                        }
-                        if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) {
-                            permBits |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-                        }
-                        if ("true".equals(parser.getAttributeValue(null,
-                                ATTR_REVOKE_WHEN_REQUESTED))) {
-                            permBits |= FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
-                        }
-
-                        if (isGranted || permBits != 0) {
-                            rememberRestoredUserGrantLPr(pkgName, permName, isGranted, permBits, userId);
-                        }
-                    } break;
                 }
             }
         }
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/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 38940d6..f56b984 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -30,6 +30,7 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
@@ -43,6 +44,9 @@
 import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS;
 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -69,7 +73,9 @@
 import android.os.UserManagerInternal;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
+import android.permission.PermissionControllerManager;
 import android.permission.PermissionManager;
+import android.permission.PermissionManagerInternal;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -77,6 +83,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
@@ -91,9 +98,8 @@
 import com.android.server.pm.PackageSetting;
 import com.android.server.pm.SharedUserSetting;
 import com.android.server.pm.UserManagerService;
-import com.android.server.pm.permission.DefaultPermissionGrantPolicy
-        .DefaultPermissionGrantedCallback;
-import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionsState.PermissionState;
 
 import libcore.util.EmptyArray;
@@ -106,6 +112,10 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Manages all permissions and handles permissions related tasks.
@@ -122,6 +132,8 @@
     /** Permission grant: grant as runtime a permission that was granted as an install time one. */
     private static final int GRANT_UPGRADE = 4;
 
+    private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60);
+
     /** Cap the size of permission trees that 3rd party apps can define; in characters of text */
     private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768;
     /** Empty array to avoid allocations */
@@ -146,6 +158,9 @@
     /** Internal connection to the user manager */
     private final UserManagerInternal mUserManagerInt;
 
+    /** Permission controller: User space permission management */
+    private PermissionControllerManager mPermissionControllerManager;
+
     /** Default permission policy to provide proper behaviour out-of-the-box */
     private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
 
@@ -180,6 +195,16 @@
     @GuardedBy("mLock")
     private ArrayMap<String, List<String>> mBackgroundPermissions;
 
+    /**
+     * A permission backup might contain apps that are not installed. In this case we delay the
+     * restoration until the app is installed.
+     *
+     * <p>This array ({@code userId -> noDelayedBackupLeft}) is {@code true} for all the users where
+     * there is <u>no more</u> delayed backup left.
+     */
+    @GuardedBy("mLock")
+    private final SparseBooleanArray mHasNoDelayedPermBackup = new SparseBooleanArray();
+
     PermissionManagerService(Context context,
             @Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
             @NonNull Object externalLock) {
@@ -218,29 +243,31 @@
             }
         }
 
-        LocalServices.addService(
-                PermissionManagerInternal.class, new PermissionManagerInternalImpl());
+        PermissionManagerServiceInternalImpl localService =
+                new PermissionManagerServiceInternalImpl();
+        LocalServices.addService(PermissionManagerServiceInternal.class, localService);
+        LocalServices.addService(PermissionManagerInternal.class, localService);
     }
 
     /**
      * Creates and returns an initialized, internal service for use by other components.
      * <p>
      * The object returned is identical to the one returned by the LocalServices class using:
-     * {@code LocalServices.getService(PermissionManagerInternal.class);}
+     * {@code LocalServices.getService(PermissionManagerServiceInternal.class);}
      * <p>
      * NOTE: The external lock is temporary and should be removed. This needs to be a
      * lock created by the permission manager itself.
      */
-    public static PermissionManagerInternal create(Context context,
+    public static PermissionManagerServiceInternal create(Context context,
             @Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
             @NonNull Object externalLock) {
-        final PermissionManagerInternal permMgrInt =
-                LocalServices.getService(PermissionManagerInternal.class);
+        final PermissionManagerServiceInternal permMgrInt =
+                LocalServices.getService(PermissionManagerServiceInternal.class);
         if (permMgrInt != null) {
             return permMgrInt;
         }
         new PermissionManagerService(context, defaultGrantCallback, externalLock);
-        return LocalServices.getService(PermissionManagerInternal.class);
+        return LocalServices.getService(PermissionManagerServiceInternal.class);
     }
 
     @Nullable BasePermission getPermission(String permName) {
@@ -332,6 +359,74 @@
     }
 
     /**
+     * Get the state of the runtime permissions as xml file.
+     *
+     * <p>Can not be called on main thread.
+     *
+     * @param user The user the data should be extracted for
+     *
+     * @return The state as a xml file
+     */
+    private @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) {
+        CompletableFuture<byte[]> backup = new CompletableFuture<>();
+        mPermissionControllerManager.getRuntimePermissionBackup(user, mContext.getMainExecutor(),
+                backup::complete);
+
+        try {
+            return backup.get(BACKUP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException | ExecutionException  | TimeoutException e) {
+            Slog.e(TAG, "Cannot create permission backup for " + user, e);
+            return null;
+        }
+    }
+
+    /**
+     * Restore a permission state previously backed up via {@link #backupRuntimePermissions}.
+     *
+     * <p>If not all state can be restored, the un-appliable state will be delayed and can be
+     * applied via {@link #restoreDelayedRuntimePermissions}.
+     *
+     * @param backup The state as an xml file
+     * @param user The user the data should be restored for
+     */
+    private void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) {
+        synchronized (mLock) {
+            mHasNoDelayedPermBackup.delete(user.getIdentifier());
+            mPermissionControllerManager.restoreRuntimePermissionBackup(backup, user);
+        }
+    }
+
+    /**
+     * Try to apply permission backup that was previously not applied.
+     *
+     * <p>Can not be called on main thread.
+     *
+     * @param packageName The package that is newly installed
+     * @param user The user the package is installed for
+     *
+     * @see #restoreRuntimePermissions
+     */
+    private void restoreDelayedRuntimePermissions(@NonNull String packageName,
+            @NonNull UserHandle user) {
+        synchronized (mLock) {
+            if (mHasNoDelayedPermBackup.get(user.getIdentifier(), false)) {
+                return;
+            }
+
+            mPermissionControllerManager.restoreDelayedRuntimePermissionBackup(packageName, user,
+                    mContext.getMainExecutor(), (hasMoreBackup) -> {
+                        if (hasMoreBackup) {
+                            return;
+                        }
+
+                        synchronized (mLock) {
+                            mHasNoDelayedPermBackup.put(user.getIdentifier(), true);
+                        }
+                    });
+        }
+    }
+
+    /**
      * Returns {@code true} if the permission can be implied from another granted permission.
      * <p>Some permissions, such as ACCESS_FINE_LOCATION, imply other permissions,
      * such as ACCESS_COURSE_LOCATION. If the caller holds an umbrella permission, give
@@ -741,7 +836,6 @@
         if (ps == null) {
             return;
         }
-        final boolean isLegacySystemApp = mPackageManagerInt.isLegacySystemApp(pkg);
 
         final PermissionsState permissionsState = ps.getPermissionsState();
         PermissionsState origPermissions = permissionsState;
@@ -828,17 +922,9 @@
                     // For all apps normal permissions are install time ones.
                     grant = GRANT_INSTALL;
                 } else if (bp.isRuntime()) {
-                    // If a permission review is required for legacy apps we represent
-                    // their permissions as always granted runtime ones since we need
-                    // to keep the review required permission flag per user while an
-                    // install permission's state is shared across all users.
                     if (origPermissions.hasInstallPermission(bp.getName())) {
-                        // For legacy apps that became modern, install becomes runtime.
-                        grant = GRANT_UPGRADE;
-                    } else if (isLegacySystemApp) {
-                        // For legacy system apps, install becomes runtime.
-                        // We cannot check hasInstallPermission() for system apps since those
-                        // permissions were granted implicitly and not persisted pre-M.
+                        // Before Q we represented some runtime permissions as install permissions,
+                        // in Q we cannot do this anymore. Hence upgrade them all.
                         grant = GRANT_UPGRADE;
                     } else {
                         // For modern apps keep runtime permissions unchanged.
@@ -891,110 +977,111 @@
                             }
                             // Grant an install permission.
                             if (permissionsState.grantInstallPermission(bp) !=
-                                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                    PERMISSION_OPERATION_FAILURE) {
                                 changedInstallPermission = true;
                             }
                         } break;
 
                         case GRANT_RUNTIME: {
-                            // Grant previously granted runtime permissions.
-                            for (int userId : UserManagerService.getInstance().getUserIds()) {
-                                final PermissionState permissionState = origPermissions
+                            for (int userId : currentUserIds) {
+                                PermissionState permState = origPermissions
                                         .getRuntimePermissionState(perm, userId);
-                                int flags = permissionState != null
-                                        ? permissionState.getFlags() : 0;
-                                if (origPermissions.hasRuntimePermission(perm, userId)) {
-                                    // Don't propagate the permission in a permission review
-                                    // mode if the former was revoked, i.e. marked to not
-                                    // propagate on upgrade. Note that in a permission review
-                                    // mode install permissions are represented as constantly
-                                    // granted runtime ones since we need to keep a per user
-                                    // state associated with the permission. Also the revoke
-                                    // on upgrade flag is no longer applicable and is reset.
-                                    final boolean revokeOnUpgrade = (flags & PackageManager
-                                            .FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
-                                    if (revokeOnUpgrade) {
-                                        flags &= ~PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-                                        // Since we changed the flags, we have to write.
-                                        updatedUserIds = ArrayUtils.appendInt(
-                                                updatedUserIds, userId);
+                                int flags = permState != null ? permState.getFlags() : 0;
+
+                                boolean wasChanged = false;
+
+                                if (appSupportsRuntimePermissions) {
+                                    // Remove review flag as it is not necessary anymore
+                                    if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                                        flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
+                                        wasChanged = true;
                                     }
-                                    if (!revokeOnUpgrade) {
-                                        if (permissionsState.grantRuntimePermission(bp, userId) ==
-                                                PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                            // If we cannot put the permission as it was,
-                                            // we have to write.
-                                            updatedUserIds = ArrayUtils.appendInt(
-                                                    updatedUserIds, userId);
+
+                                    if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
+                                        flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+                                        wasChanged = true;
+                                    } else {
+                                        if (permState != null && permState.isGranted()) {
+                                            if (permissionsState.grantRuntimePermission(bp, userId)
+                                                    == PERMISSION_OPERATION_FAILURE) {
+                                                wasChanged = true;
+                                            }
+                                        }
+                                    }
+                                } else {
+                                    if (permState == null) {
+                                        // New permission
+                                        if (PLATFORM_PACKAGE_NAME.equals(
+                                                bp.getSourcePackageName())) {
+                                            if (!bp.isRemoved()) {
+                                                flags |= FLAG_PERMISSION_REVIEW_REQUIRED
+                                                        | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+                                                wasChanged = true;
+                                            }
                                         }
                                     }
 
-                                    // If the app supports runtime permissions no need for a review.
-                                    if (appSupportsRuntimePermissions
-                                            && (flags & PackageManager
-                                                    .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
-                                        flags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
-                                        // Since we changed the flags, we have to write.
-                                        updatedUserIds = ArrayUtils.appendInt(
-                                                updatedUserIds, userId);
-                                    }
-                                } else if (!appSupportsRuntimePermissions) {
-                                    // For legacy apps that need a permission review, every new
-                                    // runtime permission is granted but it is pending a review.
-                                    // We also need to review only platform defined runtime
-                                    // permissions as these are the only ones the platform knows
-                                    // how to disable the API to simulate revocation as legacy
-                                    // apps don't expect to run with revoked permissions.
-                                    if (PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName())) {
-                                        if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0
-                                                && !bp.isRemoved()) {
-                                            flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
-                                            // We changed the flags, hence have to write.
-                                            updatedUserIds = ArrayUtils.appendInt(
-                                                    updatedUserIds, userId);
-                                        }
-                                    }
                                     if (permissionsState.grantRuntimePermission(bp, userId)
-                                            != PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                        // We changed the permission, hence have to write.
-                                        updatedUserIds = ArrayUtils.appendInt(
-                                                updatedUserIds, userId);
+                                            != PERMISSION_OPERATION_FAILURE) {
+                                        wasChanged = true;
                                     }
                                 }
-                                // Propagate the permission flags.
+
+                                if (wasChanged) {
+                                    updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+                                }
+
                                 permissionsState.updatePermissionFlags(bp, userId, flags, flags);
                             }
                         } break;
 
                         case GRANT_UPGRADE: {
-                            // Grant runtime permissions for a previously held install permission.
-                            final PermissionState permissionState = origPermissions
+                            // Upgrade from Pre-Q to Q permission model. Make all permissions
+                            // runtime
+                            PermissionState permState = origPermissions
                                     .getInstallPermissionState(perm);
-                            final int flags =
-                                    (permissionState != null) ? permissionState.getFlags() : 0;
+                            int flags = (permState != null) ? permState.getFlags() : 0;
 
+                            // Remove install permission
                             if (origPermissions.revokeInstallPermission(bp)
-                                    != PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                // We will be transferring the permission flags, so clear them.
+                                    != PERMISSION_OPERATION_FAILURE) {
                                 origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL,
                                         PackageManager.MASK_PERMISSION_FLAGS, 0);
                                 changedInstallPermission = true;
                             }
 
-                            // If the permission is not to be promoted to runtime we ignore it and
-                            // also its other flags as they are not applicable to install permissions.
-                            if ((flags & PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE) == 0) {
-                                for (int userId : currentUserIds) {
+                            for (int userId : currentUserIds) {
+                                boolean wasChanged = false;
+
+                                if (appSupportsRuntimePermissions) {
+                                    // Remove review flag as it is not necessary anymore
+                                    if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                                        flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
+                                        wasChanged = true;
+                                    }
+
+                                    if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
+                                        flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+                                        wasChanged = true;
+                                    } else {
+                                        if (permissionsState.grantRuntimePermission(bp, userId) !=
+                                                PERMISSION_OPERATION_FAILURE) {
+                                             wasChanged = true;
+                                        }
+                                    }
+                                } else {
                                     if (permissionsState.grantRuntimePermission(bp, userId) !=
-                                            PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                        // Transfer the permission flags.
-                                        permissionsState.updatePermissionFlags(bp, userId,
-                                                flags, flags);
-                                        // If we granted the permission, we have to write.
-                                        updatedUserIds = ArrayUtils.appendInt(
-                                                updatedUserIds, userId);
+                                            PERMISSION_OPERATION_FAILURE) {
+                                        flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+                                        wasChanged = true;
                                     }
                                 }
+
+                                if (wasChanged) {
+                                    updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+                                }
+
+                                permissionsState.updatePermissionFlags(bp, userId, flags, flags);
                             }
                         } break;
 
@@ -1011,7 +1098,7 @@
                     }
                 } else {
                     if (permissionsState.revokeInstallPermission(bp) !=
-                            PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                            PERMISSION_OPERATION_FAILURE) {
                         // Also drop the permission flags.
                         permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
                                 PackageManager.MASK_PERMISSION_FLAGS, 0);
@@ -1094,6 +1181,10 @@
             @NonNull int[] updatedUserIds) {
         AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
 
+        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+            return updatedUserIds;
+        }
+
         String pkgName = pkg.packageName;
 
         int[] users = UserManagerService.getInstance().getUserIds();
@@ -1119,26 +1210,14 @@
                             if ((flags & (FLAG_PERMISSION_GRANTED_BY_DEFAULT
                                     | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED))
                                     == 0) {
-                                if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
-                                    if (permissionToOpCode(permission) != OP_NONE) {
-                                        setAppOpMode(permission, pkg, userId, MODE_IGNORED);
+                                int revokeResult = ps.revokeRuntimePermission(bp, userId);
+                                if (revokeResult
+                                        != PERMISSION_OPERATION_FAILURE) {
 
-                                        if (DEBUG_PERMISSIONS) {
-                                            Slog.i(TAG, "Revoking app-op "
-                                                    + permissionToOp(permission) + " for " + pkgName
-                                                    + " as it is now requested");
-                                        }
-                                    }
-                                } else {
-                                    int revokeResult = ps.revokeRuntimePermission(bp, userId);
-                                    if (revokeResult
-                                            != PermissionsState.PERMISSION_OPERATION_FAILURE) {
-
-                                        if (DEBUG_PERMISSIONS) {
-                                            Slog.i(TAG, "Revoking runtime permission " + permission
-                                                    + " for " + pkgName
-                                                    + " as it is now requested");
-                                        }
+                                    if (DEBUG_PERMISSIONS) {
+                                        Slog.i(TAG, "Revoking runtime permission " + permission
+                                                + " for " + pkgName
+                                                + " as it is now requested");
                                     }
                                 }
 
@@ -1925,7 +2004,7 @@
             // Development permissions must be handled specially, since they are not
             // normal runtime permissions.  For now they apply to all users.
             if (permissionsState.grantInstallPermission(bp) !=
-                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                    PERMISSION_OPERATION_FAILURE) {
                 if (callback != null) {
                     callback.onInstallPermissionGranted();
                 }
@@ -1945,7 +2024,7 @@
 
         final int result = permissionsState.grantRuntimePermission(bp, userId);
         switch (result) {
-            case PermissionsState.PERMISSION_OPERATION_FAILURE: {
+            case PERMISSION_OPERATION_FAILURE: {
                 return;
             }
 
@@ -2045,7 +2124,7 @@
             // Development permissions must be handled specially, since they are not
             // normal runtime permissions.  For now they apply to all users.
             if (permissionsState.revokeInstallPermission(bp) !=
-                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                    PERMISSION_OPERATION_FAILURE) {
                 if (callback != null) {
                     callback.onInstallPermissionRevoked();
                 }
@@ -2054,7 +2133,7 @@
         }
 
         if (permissionsState.revokeRuntimePermission(bp, userId) ==
-                PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                PERMISSION_OPERATION_FAILURE) {
             return;
         }
 
@@ -2522,6 +2601,8 @@
             throw new IllegalStateException("Signature|privileged permissions not in "
                     + "privapp-permissions whitelist: " + mPrivappPermissionsViolations);
         }
+
+        mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
     }
 
     private static String getVolumeUuidForPackage(PackageParser.Package pkg) {
@@ -2574,7 +2655,7 @@
         return mBackgroundPermissions;
     }
 
-    private class PermissionManagerInternalImpl extends PermissionManagerInternal {
+    private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal {
         @Override
         public void systemReady() {
             PermissionManagerService.this.systemReady();
@@ -2737,5 +2818,21 @@
                 return mSettings.getPermissionLocked(permName);
             }
         }
+
+        @Override
+        public @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) {
+            return PermissionManagerService.this.backupRuntimePermissions(user);
+        }
+
+        @Override
+        public void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) {
+            PermissionManagerService.this.restoreRuntimePermissions(backup, user);
+        }
+
+        @Override
+        public void restoreDelayedRuntimePermissions(@NonNull String packageName,
+                @NonNull UserHandle user) {
+            PermissionManagerService.this.restoreDelayedRuntimePermissions(packageName, user);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
similarity index 96%
rename from services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
rename to services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index f4979746..1dd2408 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -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.
@@ -18,19 +18,22 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.PackageManager.PermissionInfoFlags;
 import android.content.pm.PackageParser;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
-import android.content.pm.PackageManager.PermissionInfoFlags;
+import android.permission.PermissionManagerInternal;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
 /**
- * Internal interfaces to be used by other components within the system server.
+ * Internal interfaces services.
+ *
+ * TODO: Should be merged into PermissionManagerInternal, but currently uses internal classes.
  */
-public abstract class PermissionManagerInternal {
+public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal {
     /**
      * Callbacks invoked when interesting actions have been taken on a permission.
      * <p>
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
index 0892b32..2280d3f 100644
--- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -16,6 +16,9 @@
                 },
                 {
                     "include-filter": "android.permission.cts.SplitPermissionTest"
+                },
+                {
+                    "include-filter": "android.permission.cts.PermissionFlagsTest"
                 }
             ]
         },
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..4815e5c 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -85,7 +85,9 @@
 import android.os.Temperature;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.DiskInfo;
 import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
 import android.telephony.ModemActivityInfo;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
@@ -1942,6 +1944,41 @@
         }
     }
 
+    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);
+    }
+
+    private void pullSDCardInfo(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        StorageManager storageManager = mContext.getSystemService(StorageManager.class);
+        if (storageManager != null) {
+            List<VolumeInfo> volumes = storageManager.getVolumes();
+            for (VolumeInfo vol : volumes) {
+                final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
+                final DiskInfo diskInfo = vol.getDisk();
+                if (diskInfo != null && diskInfo.isSd()) {
+                    if (envState.equals(Environment.MEDIA_MOUNTED)) {
+                        StatsLogEventWrapper e =
+                                new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+                        e.writeInt(vol.getType() + 1);
+                        e.writeLong(diskInfo.size);
+                        pulledData.add(e);
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Pulls various data.
      */
@@ -2130,6 +2167,14 @@
                 pullDangerousPermissionState(elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.TIME_ZONE_DATA_INFO: {
+                pullTimeZoneDataInfo(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
+            case StatsLog.SDCARD_INFO: {
+                pullSDCardInfo(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/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index 7a3f030..f581bc0 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -44,7 +44,7 @@
 import com.android.server.SystemService;
 import com.android.server.pm.UserManagerService;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
-import com.android.server.pm.permission.PermissionManagerInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 /**
  * Starts the telecom component by binding to its ITelecomService implementation. Telecom is setup
@@ -133,7 +133,7 @@
     }
 
     private DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() {
-        return LocalServices.getService(PermissionManagerInternal.class)
+        return LocalServices.getService(PermissionManagerServiceInternal.class)
                 .getDefaultPermissionGrantPolicy();
     }
 
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 989706f..7beee0e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -447,7 +447,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.
@@ -4299,9 +4300,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);
         }
@@ -4897,7 +4901,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(),
@@ -4917,6 +4922,7 @@
                         Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + height);
                         displayContent.updateBaseDisplayMetrics(width, height,
                                 displayContent.mBaseDisplayDensity);
+                        changed = true;
                     }
                 } catch (NumberFormatException ex) {
                 }
@@ -4925,17 +4931,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
@@ -5105,11 +5114,7 @@
 
     @Override
     public void startWindowTrace(){
-        try {
-            mWindowTracing.startTrace(null /* printwriter */);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        mWindowTracing.startTrace(null /* printwriter */);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 48cd6b6..3430987 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2466,7 +2466,7 @@
     /** @return false if this window desires touch events. */
     boolean cantReceiveTouchInput() {
         return mAppToken != null && mAppToken.getTask() != null
-                && mAppToken.getTask().mStack.shouldIgnoreInput();
+                && (mAppToken.getTask().mStack.shouldIgnoreInput() || mAppToken.hiddenRequested);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
index e4461ea..2ce6e6c 100644
--- a/services/core/java/com/android/server/wm/WindowTraceBuffer.java
+++ b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
@@ -20,7 +20,6 @@
 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
 
-import android.os.Trace;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -36,24 +35,30 @@
 /**
  * Buffer used for window tracing.
  */
-abstract class WindowTraceBuffer {
+class WindowTraceBuffer {
     private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
 
-    final Object mBufferLock = new Object();
-    final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
-    final File mTraceFile;
-    int mBufferSize;
-    private final int mBufferCapacity;
+    private final Object mBufferLock = new Object();
 
-    WindowTraceBuffer(int size, File traceFile) throws IOException {
-        mBufferCapacity = size;
-        mTraceFile = traceFile;
+    private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
+    private int mBufferUsedSize;
+    private int mBufferCapacity;
 
-        initTraceFile();
+    WindowTraceBuffer(int bufferCapacity) {
+        mBufferCapacity = bufferCapacity;
+        resetBuffer();
     }
 
     int getAvailableSpace() {
-        return mBufferCapacity - mBufferSize;
+        return mBufferCapacity - mBufferUsedSize;
+    }
+
+    int size() {
+        return mBuffer.size();
+    }
+
+    void setCapacity(int capacity) {
+        mBufferCapacity = capacity;
     }
 
     /**
@@ -70,42 +75,37 @@
                     + mBufferCapacity + " Object size: " + protoLength);
         }
         synchronized (mBufferLock) {
-            boolean canAdd = canAdd(protoLength);
-            if (canAdd) {
-                mBuffer.add(proto);
-                mBufferSize += protoLength;
-            }
+            discardOldest(protoLength);
+            mBuffer.add(proto);
+            mBufferUsedSize += protoLength;
             mBufferLock.notify();
         }
     }
 
-    /**
-     * Stops the buffer execution and flush all buffer content to the disk.
-     *
-     * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile}
-     */
-    void dump() throws IOException, InterruptedException {
-        try {
-            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFile");
-            writeTraceToFile();
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
-        }
-    }
-
-    @VisibleForTesting
     boolean contains(byte[] other) {
         return mBuffer.stream()
                 .anyMatch(p -> Arrays.equals(p.getBytes(), other));
     }
 
-    private void initTraceFile() throws IOException {
-        mTraceFile.delete();
-        try (OutputStream os = new FileOutputStream(mTraceFile)) {
-            mTraceFile.setReadable(true, false);
-            ProtoOutputStream proto = new ProtoOutputStream(os);
-            proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
-            proto.flush();
+    /**
+     * Writes the trace buffer to disk.
+     */
+    void writeTraceToFile(File traceFile) throws IOException {
+        synchronized (mBufferLock) {
+            traceFile.delete();
+            traceFile.setReadable(true, false);
+            try (OutputStream os = new FileOutputStream(traceFile)) {
+                ProtoOutputStream proto = new ProtoOutputStream();
+                proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+                os.write(proto.getBytes());
+                while (!mBuffer.isEmpty()) {
+                    proto = mBuffer.poll();
+                    mBufferUsedSize -= proto.getRawSize();
+                    byte[] protoBytes = proto.getBytes();
+                    os.write(protoBytes);
+                }
+                os.flush();
+            }
         }
     }
 
@@ -114,59 +114,48 @@
      * smaller than the overall buffer size.
      *
      * @param protoLength byte array representation of the Proto object to add
-     * @return {@code true} if the element can be added to the buffer or not
      */
-    abstract boolean canAdd(int protoLength);
+    private void discardOldest(int protoLength) {
+        long availableSpace = getAvailableSpace();
+
+        while (availableSpace < protoLength) {
+
+            ProtoOutputStream item = mBuffer.poll();
+            if (item == null) {
+                throw new IllegalStateException("No element to discard from buffer");
+            }
+            mBufferUsedSize -= item.getRawSize();
+            availableSpace = getAvailableSpace();
+        }
+    }
 
     /**
-     * Flush all buffer content to the disk.
-     *
-     * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile}
+     * Removes all elements form the buffer
      */
-    abstract void writeTraceToFile() throws IOException, InterruptedException;
-
-    /**
-     * Builder for a {@code WindowTraceBuffer} which creates a {@link WindowTraceRingBuffer} for
-     * continuous mode or a {@link WindowTraceQueueBuffer} otherwise
-     */
-    static class Builder {
-        private boolean mContinuous;
-        private File mTraceFile;
-        private int mBufferCapacity;
-
-        Builder setContinuousMode(boolean continuous) {
-            mContinuous = continuous;
-            return this;
+    void resetBuffer() {
+        synchronized (mBufferLock) {
+            mBuffer.clear();
+            mBufferUsedSize = 0;
         }
+    }
 
-        Builder setTraceFile(File traceFile) {
-            mTraceFile = traceFile;
-            return this;
-        }
+    @VisibleForTesting
+    int getBufferSize() {
+        return mBufferUsedSize;
+    }
 
-        Builder setBufferCapacity(int size) {
-            mBufferCapacity = size;
-            return this;
-        }
-
-        File getFile() {
-            return mTraceFile;
-        }
-
-        WindowTraceBuffer build() throws IOException {
-            if (mBufferCapacity <= 0) {
-                throw new IllegalStateException("Buffer capacity must be greater than 0.");
-            }
-
-            if (mTraceFile == null) {
-                throw new IllegalArgumentException("A valid trace file must be specified.");
-            }
-
-            if (mContinuous) {
-                return new WindowTraceRingBuffer(mBufferCapacity, mTraceFile);
-            } else {
-                return new WindowTraceQueueBuffer(mBufferCapacity, mTraceFile);
-            }
+    String getStatus() {
+        synchronized (mBufferLock) {
+            return "Buffer size: "
+                    + mBufferCapacity
+                    + " bytes"
+                    + "\n"
+                    + "Buffer usage: "
+                    + mBufferUsedSize
+                    + " bytes"
+                    + "\n"
+                    + "Elements in the buffer: "
+                    + mBuffer.size();
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java b/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java
deleted file mode 100644
index 5888b7a..0000000
--- a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.os.Build.IS_USER;
-
-import android.util.Log;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * A buffer structure backed by a {@link java.util.concurrent.BlockingQueue} to store the first
- * {@code #size size} bytes of window trace elements.
- * Once the buffer is full it will no longer accepts new elements.
- */
-class WindowTraceQueueBuffer extends WindowTraceBuffer {
-    private static final String TAG = "WindowTracing";
-
-    private Thread mConsumerThread;
-    private boolean mCancel;
-
-    @VisibleForTesting
-    WindowTraceQueueBuffer(int size, File traceFile, boolean startConsumerThread)
-            throws IOException {
-        super(size, traceFile);
-        if (startConsumerThread) {
-            initializeConsumerThread();
-        }
-    }
-
-    WindowTraceQueueBuffer(int size, File traceFile) throws IOException {
-        this(size, traceFile, !IS_USER);
-    }
-
-    private void initializeConsumerThread() {
-        mCancel = false;
-        mConsumerThread = new Thread(() -> {
-            try {
-                loop();
-            } catch (InterruptedException e) {
-                Log.i(TAG, "Interrupting trace consumer thread");
-            } catch (IOException e) {
-                Log.e(TAG, "Failed to execute trace consumer thread", e);
-            }
-        }, "window_tracing");
-        mConsumerThread.start();
-    }
-
-    private void loop() throws IOException, InterruptedException {
-        while (!mCancel) {
-            ProtoOutputStream proto;
-            synchronized (mBufferLock) {
-                mBufferLock.wait();
-                proto = mBuffer.poll();
-                if (proto != null) {
-                    mBufferSize -= proto.getRawSize();
-                }
-            }
-            if (proto != null) {
-                try (OutputStream os = new FileOutputStream(mTraceFile, true)) {
-                    byte[] protoBytes = proto.getBytes();
-                    os.write(protoBytes);
-                }
-            }
-        }
-    }
-
-    @Override
-    boolean canAdd(int protoLength) {
-        long availableSpace = getAvailableSpace();
-        return availableSpace >= protoLength;
-    }
-
-    @Override
-    void writeTraceToFile() throws InterruptedException {
-        synchronized (mBufferLock) {
-            mCancel = true;
-            mBufferLock.notify();
-        }
-        if (mConsumerThread != null) {
-            mConsumerThread.join();
-            mConsumerThread = null;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java b/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java
deleted file mode 100644
index 77d30be..0000000
--- a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.util.proto.ProtoOutputStream;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * A ring buffer to store the {@code #size size} bytes of window trace data.
- * The buffer operates on a trace entry level, that is, if the new trace data is larger than the
- * available buffer space, the buffer will discard as many full trace entries as necessary to fit
- * the new trace.
- */
-class WindowTraceRingBuffer extends WindowTraceBuffer {
-    WindowTraceRingBuffer(int size, File traceFile) throws IOException {
-        super(size, traceFile);
-    }
-
-    @Override
-    boolean canAdd(int protoLength) {
-        long availableSpace = getAvailableSpace();
-
-        while (availableSpace < protoLength) {
-            discardOldest();
-            availableSpace = getAvailableSpace();
-        }
-
-        return true;
-    }
-
-    @Override
-    void writeTraceToFile() throws IOException {
-        synchronized (mBufferLock) {
-            try (OutputStream os = new FileOutputStream(mTraceFile, true)) {
-                while (!mBuffer.isEmpty()) {
-                    ProtoOutputStream proto = mBuffer.poll();
-                    mBufferSize -= proto.getRawSize();
-                    byte[] protoBytes = proto.getBytes();
-                    os.write(protoBytes);
-                }
-            }
-        }
-    }
-
-    private void discardOldest() {
-        ProtoOutputStream item = mBuffer.poll();
-        if (item == null) {
-            throw new IllegalStateException("No element to discard from buffer");
-        }
-        mBufferSize -= item.getRawSize();
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index abc474d..0ce215c 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -31,8 +31,6 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Choreographer;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -47,139 +45,191 @@
      * Maximum buffer size, currently defined as 512 KB
      * Size was experimentally defined to fit between 100 to 150 elements.
      */
-    private static final int WINDOW_TRACE_BUFFER_SIZE = 512 * 1024;
+    private static final int BUFFER_CAPACITY_CRITICAL = 512 * 1024;
+    private static final int BUFFER_CAPACITY_TRIM = 2048 * 1024;
+    private static final int BUFFER_CAPACITY_ALL = 4096 * 1024;
+    private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace.pb";
     private static final String TAG = "WindowTracing";
 
     private final WindowManagerService mService;
     private final Choreographer mChoreographer;
     private final WindowManagerGlobalLock mGlobalLock;
 
-    private final Object mLock = new Object();
-    private final WindowTraceBuffer.Builder mBufferBuilder;
+    private final Object mEnabledLock = new Object();
+    private final File mTraceFile;
+    private final WindowTraceBuffer mBuffer;
+    private final Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
+            log("onFrame" /* where */);
 
-    private WindowTraceBuffer mTraceBuffer;
-
-    private @WindowTraceLogLevel int mWindowTraceLogLevel = WindowTraceLogLevel.TRIM;
-    private boolean mContinuousMode;
+    private @WindowTraceLogLevel int mLogLevel = WindowTraceLogLevel.TRIM;
+    private boolean mLogOnFrame = false;
     private boolean mEnabled;
     private volatile boolean mEnabledLockFree;
     private boolean mScheduled;
-    private Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
-            log("onFrame" /* where */);
 
-    private WindowTracing(File file, WindowManagerService service, Choreographer choreographer) {
-        this(file, service, choreographer, service.mGlobalLock);
+    static WindowTracing createDefaultAndStartLooper(WindowManagerService service,
+            Choreographer choreographer) {
+        File file = new File(TRACE_FILENAME);
+        return new WindowTracing(file, service, choreographer, BUFFER_CAPACITY_TRIM);
     }
 
-    @VisibleForTesting
-    WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
-            WindowManagerGlobalLock globalLock) {
-        mBufferBuilder = new WindowTraceBuffer.Builder()
-                .setTraceFile(file)
-                .setBufferCapacity(WINDOW_TRACE_BUFFER_SIZE);
+    private WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
+            int bufferCapacity) {
+        this(file, service, choreographer, service.mGlobalLock, bufferCapacity);
+    }
 
+    WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
+            WindowManagerGlobalLock globalLock, int bufferCapacity) {
         mChoreographer = choreographer;
         mService = service;
         mGlobalLock = globalLock;
+        mTraceFile = file;
+        mBuffer = new WindowTraceBuffer(bufferCapacity);
+        setLogLevel(WindowTraceLogLevel.TRIM, null /* pw */);
     }
 
-    void startTrace(@Nullable PrintWriter pw) throws IOException {
+    void startTrace(@Nullable PrintWriter pw) {
         if (IS_USER) {
             logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
             return;
         }
-        synchronized (mLock) {
-            logAndPrintln(pw, "Start tracing to " + mBufferBuilder.getFile() + ".");
-            if (mTraceBuffer != null) {
-                writeTraceToFileLocked();
-            }
-            mTraceBuffer = mBufferBuilder
-                    .setContinuousMode(mContinuousMode)
-                    .build();
+        synchronized (mEnabledLock) {
+            logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
+            mBuffer.resetBuffer();
             mEnabled = mEnabledLockFree = true;
         }
     }
 
-    private void logAndPrintln(@Nullable PrintWriter pw, String msg) {
-        Log.i(TAG, msg);
-        if (pw != null) {
-            pw.println(msg);
-            pw.flush();
-        }
-    }
-
     void stopTrace(@Nullable PrintWriter pw) {
         if (IS_USER) {
             logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
             return;
         }
-        synchronized (mLock) {
-            logAndPrintln(pw, "Stop tracing to " + mBufferBuilder.getFile()
-                    + ". Waiting for traces to flush.");
+        synchronized (mEnabledLock) {
+            logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
             mEnabled = mEnabledLockFree = false;
 
-            synchronized (mLock) {
-                if (mEnabled) {
-                    logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
-                    throw new IllegalStateException("tracing enabled while waiting for flush.");
-                }
-                writeTraceToFileLocked();
-                mTraceBuffer = null;
+            if (mEnabled) {
+                logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
+                throw new IllegalStateException("tracing enabled while waiting for flush.");
             }
-            logAndPrintln(pw, "Trace written to " + mBufferBuilder.getFile() + ".");
+            writeTraceToFileLocked();
+            logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
         }
     }
 
-    @VisibleForTesting
-    void setContinuousMode(boolean continuous, PrintWriter pw) {
-        logAndPrintln(pw, "Setting window tracing continuous mode to " + continuous);
+    private void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
+        logAndPrintln(pw, "Setting window tracing log level to " + logLevel);
+        mLogLevel = logLevel;
 
-        if (mEnabled) {
-            logAndPrintln(pw, "Trace is currently active, change will take effect once the "
-                    + "trace is restarted.");
+        switch (logLevel) {
+            case WindowTraceLogLevel.ALL: {
+                setBufferCapacity(BUFFER_CAPACITY_ALL, pw);
+                break;
+            }
+            case WindowTraceLogLevel.TRIM: {
+                setBufferCapacity(BUFFER_CAPACITY_TRIM, pw);
+                break;
+            }
+            case WindowTraceLogLevel.CRITICAL: {
+                setBufferCapacity(BUFFER_CAPACITY_CRITICAL, pw);
+                break;
+            }
         }
-        mContinuousMode = continuous;
-        mWindowTraceLogLevel = (continuous) ? WindowTraceLogLevel.CRITICAL :
-                WindowTraceLogLevel.TRIM;
+    }
+
+    private void setLogFrequency(boolean onFrame, PrintWriter pw) {
+        logAndPrintln(pw, "Setting window tracing log frequency to "
+                + ((onFrame) ? "frame" : "transaction"));
+        mLogOnFrame = onFrame;
+    }
+
+    private void setBufferCapacity(int capacity, PrintWriter pw) {
+        logAndPrintln(pw, "Setting window tracing buffer capacity to " + capacity + "bytes");
+        mBuffer.setCapacity(capacity);
     }
 
     boolean isEnabled() {
         return mEnabledLockFree;
     }
 
-    static WindowTracing createDefaultAndStartLooper(WindowManagerService service,
-            Choreographer choreographer) {
-        File file = new File("/data/misc/wmtrace/wm_trace.pb");
-        return new WindowTracing(file, service, choreographer);
-    }
-
     int onShellCommand(ShellCommand shell) {
         PrintWriter pw = shell.getOutPrintWriter();
-        try {
-            String cmd = shell.getNextArgRequired();
-            switch (cmd) {
-                case "start":
-                    startTrace(pw);
-                    return 0;
-                case "stop":
-                    stopTrace(pw);
-                    return 0;
-                case "continuous":
-                    setContinuousMode(Boolean.valueOf(shell.getNextArgRequired()), pw);
-                    return 0;
-                default:
-                    pw.println("Unknown command: " + cmd);
-                    return -1;
-            }
-        } catch (IOException e) {
-            logAndPrintln(pw, e.toString());
-            throw new RuntimeException(e);
+        String cmd = shell.getNextArgRequired();
+        switch (cmd) {
+            case "start":
+                startTrace(pw);
+                return 0;
+            case "stop":
+                stopTrace(pw);
+                return 0;
+            case "status":
+                logAndPrintln(pw, getStatus());
+                return 0;
+            case "frame":
+                setLogFrequency(true /* onFrame */, pw);
+                mBuffer.resetBuffer();
+                return 0;
+            case "transaction":
+                setLogFrequency(false /* onFrame */, pw);
+                mBuffer.resetBuffer();
+                return 0;
+            case "level":
+                String logLevelStr = shell.getNextArgRequired().toLowerCase();
+                switch (logLevelStr) {
+                    case "all": {
+                        setLogLevel(WindowTraceLogLevel.ALL, pw);
+                        break;
+                    }
+                    case "trim": {
+                        setLogLevel(WindowTraceLogLevel.TRIM, pw);
+                        break;
+                    }
+                    case "critical": {
+                        setLogLevel(WindowTraceLogLevel.CRITICAL, pw);
+                        break;
+                    }
+                    default: {
+                        setLogLevel(WindowTraceLogLevel.TRIM, pw);
+                        break;
+                    }
+                }
+                mBuffer.resetBuffer();
+                return 0;
+            case "size":
+                setBufferCapacity(Integer.parseInt(shell.getNextArgRequired()) * 1024, pw);
+                mBuffer.resetBuffer();
+                return 0;
+            default:
+                pw.println("Unknown command: " + cmd);
+                pw.println("Window manager trace options:");
+                pw.println("  start: Start logging");
+                pw.println("  stop: Stop logging");
+                pw.println("  frame: Log trace once per frame");
+                pw.println("  transaction: Log each transaction");
+                pw.println("  size: Set the maximum log size (in KB)");
+                pw.println("  level [lvl]: Set the log level between");
+                pw.println("    lvl may be one of:");
+                pw.println("      critical: Only visible windows with reduced information");
+                pw.println("      trim: All windows with reduced");
+                pw.println("      all: All window and information");
+                return -1;
         }
     }
 
+    private String getStatus() {
+        return "Status: "
+                + ((isEnabled()) ? "Enabled" : "Disabled")
+                + "\n"
+                + "Log level: "
+                + mLogLevel
+                + "\n"
+                + mBuffer.getStatus();
+    }
+
     /**
      * If tracing is enabled, log the current state or schedule the next frame to be logged,
-     * according to {@link #mContinuousMode}.
+     * according to {@link #mLogOnFrame}.
      *
      * @param where Logging point descriptor
      */
@@ -188,7 +238,7 @@
             return;
         }
 
-        if (mContinuousMode) {
+        if (mLogOnFrame) {
             schedule();
         } else {
             log(where);
@@ -215,25 +265,24 @@
     private void log(String where) {
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
         try {
-            synchronized (mGlobalLock) {
-                ProtoOutputStream os = new ProtoOutputStream();
-                long tokenOuter = os.start(ENTRY);
-                os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
-                os.write(WHERE, where);
+            ProtoOutputStream os = new ProtoOutputStream();
+            long tokenOuter = os.start(ENTRY);
+            os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+            os.write(WHERE, where);
 
+            long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
+            synchronized (mGlobalLock) {
                 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToProtoLocked");
                 try {
-                    long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
-                    mService.writeToProtoLocked(os, mWindowTraceLogLevel);
-                    os.end(tokenInner);
+                    mService.writeToProtoLocked(os, mLogLevel);
                 } finally {
                     Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                 }
-                os.end(tokenOuter);
-                mTraceBuffer.add(os);
-
-                mScheduled = false;
             }
+            os.end(tokenInner);
+            os.end(tokenOuter);
+            mBuffer.add(os);
+            mScheduled = false;
         } catch (Exception e) {
             Log.wtf(TAG, "Exception while tracing state", e);
         } finally {
@@ -242,31 +291,37 @@
     }
 
     /**
-     * Writes the trace buffer to disk. This method has no internal synchronization and should be
-     * externally synchronized
-     */
-    private void writeTraceToFileLocked() {
-        if (mTraceBuffer == null) {
-            return;
-        }
-
-        try {
-            mTraceBuffer.dump();
-        } catch (IOException e) {
-            Log.e(TAG, "Unable to write buffer to file", e);
-        } catch (InterruptedException e) {
-            Log.e(TAG, "Unable to interrupt window tracing file write thread", e);
-        }
-    }
-
-    /**
-     * Writes the trace buffer to disk and clones it into a new file for the bugreport.
+     * Writes the trace buffer to new file for the bugreport.
+     *
      * This method is synchronized with {@code #startTrace(PrintWriter)} and
      * {@link #stopTrace(PrintWriter)}.
      */
     void writeTraceToFile() {
-        synchronized (mLock) {
+        synchronized (mEnabledLock) {
             writeTraceToFileLocked();
         }
     }
-}
+
+    private void logAndPrintln(@Nullable PrintWriter pw, String msg) {
+        Log.i(TAG, msg);
+        if (pw != null) {
+            pw.println(msg);
+            pw.flush();
+        }
+    }
+
+    /**
+     * Writes the trace buffer to disk. This method has no internal synchronization and should be
+     * externally synchronized
+     */
+    private void writeTraceToFileLocked() {
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFileLocked");
+            mBuffer.writeTraceToFile(mTraceFile);
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to write buffer to file", e);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+        }
+    }
+}
\ No newline at end of file
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/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
index de5dd17..d5cfab9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
@@ -16,7 +16,7 @@
 
 package com.android.server.devicepolicy;
 
-import android.app.admin.DevicePolicyManager.InstallUpdateCallback;
+import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback;
 import android.app.admin.StartInstallingUpdateCallback;
 import android.content.Context;
 import android.os.ParcelFileDescriptor;
@@ -62,41 +62,43 @@
 
     private static Map<Integer, Integer> buildErrorCodesMap() {
         Map<Integer, Integer> map = new HashMap<>();
-        map.put(UpdateEngine.ErrorCodeConstants.ERROR, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+        map.put(
+                UpdateEngine.ErrorCodeConstants.ERROR,
+                InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
         map.put(
                 DOWNLOAD_STATE_INITIALIZATION_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
+                InstallSystemUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
         map.put(
                 UpdateEngine.ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
+                InstallSystemUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
 
         // Error constants corresponding to errors related to bad update file.
         map.put(
                 UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
         map.put(
                 UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
         map.put(
                 UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
         map.put(
                 UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID);
 
         // Error constants corresponding to errors related to devices bad state.
         map.put(
                 UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
         map.put(
                 UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
         map.put(
                 UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
-                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
         map.put(
                 UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
-                InstallUpdateCallback.UPDATE_ERROR_UNKNOWN);
+                InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN);
 
         return map;
     }
@@ -153,12 +155,13 @@
         } catch (ZipException e) {
             Log.w(UpdateInstaller.TAG, e);
             notifyCallbackOnError(
-                    InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+                    InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
                     Log.getStackTraceString(e));
         } catch (IOException e) {
             Log.w(UpdateInstaller.TAG, e);
             notifyCallbackOnError(
-                    InstallUpdateCallback.UPDATE_ERROR_UNKNOWN, Log.getStackTraceString(e));
+                    InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                    Log.getStackTraceString(e));
         }
     }
 
@@ -185,7 +188,7 @@
         if (mSizeForUpdate == -1) {
             Log.w(UpdateInstaller.TAG, "Failed to find payload entry in the given package.");
             notifyCallbackOnError(
-                    InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+                    InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
                     "Failed to find payload entry in the given package.");
             return;
         }
@@ -210,7 +213,7 @@
                 if (entry.getMethod() != ZipEntry.STORED) {
                     Log.w(UpdateInstaller.TAG, "Invalid compression method.");
                     notifyCallbackOnError(
-                            InstallUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
+                            InstallSystemUpdateCallback.UPDATE_ERROR_UPDATE_FILE_INVALID,
                             "Invalid compression method.");
                     return false;
                 }
@@ -263,7 +266,7 @@
             } else {
                 mUpdateInstaller.notifyCallbackOnError(
                         errorCodesMap.getOrDefault(
-                                errorCode, InstallUpdateCallback.UPDATE_ERROR_UNKNOWN),
+                                errorCode, InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN),
                         errorStringsMap.getOrDefault(errorCode, UNKNOWN_ERROR + errorCode));
             }
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
index 5f1e926..582306c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NonAbUpdateInstaller.java
@@ -16,7 +16,7 @@
 
 package com.android.server.devicepolicy;
 
-import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback;
 import android.app.admin.StartInstallingUpdateCallback;
 import android.content.Context;
 import android.os.ParcelFileDescriptor;
@@ -45,7 +45,7 @@
         } catch (IOException e) {
             Log.w(TAG, "IO error while trying to install non AB update.", e);
             notifyCallbackOnError(
-                    DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                    InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN,
                     Log.getStackTraceString(e));
         }
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
index cf68ccf..7148ed4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
@@ -18,7 +18,7 @@
 
 import android.annotation.Nullable;
 import android.app.admin.DevicePolicyEventLogger;
-import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback;
 import android.app.admin.StartInstallingUpdateCallback;
 import android.content.Context;
 import android.content.Intent;
@@ -66,7 +66,7 @@
         mCopiedUpdateFile = null;
         if (!isBatteryLevelSufficient()) {
             notifyCallbackOnError(
-                    DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_BATTERY_LOW,
+                    InstallSystemUpdateCallback.UPDATE_ERROR_BATTERY_LOW,
                     "The battery level must be above "
                             + mConstants.BATTERY_THRESHOLD_NOT_CHARGING + " while not charging or"
                             + "above " + mConstants.BATTERY_THRESHOLD_CHARGING + " while charging");
@@ -76,7 +76,7 @@
             mCopiedUpdateFile = copyUpdateFileToDataOtaPackageDir();
             if (mCopiedUpdateFile == null) {
                 notifyCallbackOnError(
-                        DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                        InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN,
                         "Error while copying file.");
                 return;
             }
@@ -111,7 +111,7 @@
         } catch (IOException e) {
             Log.w(TAG, "Failed to copy update file to OTA directory", e);
             notifyCallbackOnError(
-                    DevicePolicyManager.InstallUpdateCallback.UPDATE_ERROR_UNKNOWN,
+                    InstallSystemUpdateCallback.UPDATE_ERROR_UNKNOWN,
                     Log.getStackTraceString(e));
             return null;
         }
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..8ad4d76 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -1,6 +1,10 @@
 java_library_static {
     name: "services.net",
     srcs: ["java/**/*.java"],
+    static_libs: [
+        "netd_aidl_interface-java",
+        "networkstack-aidl-interfaces-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/core/java/android/net/dhcp/DhcpServerCallbacks.java b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java
similarity index 100%
rename from core/java/android/net/dhcp/DhcpServerCallbacks.java
rename to services/net/java/android/net/dhcp/DhcpServerCallbacks.java
diff --git a/core/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java
similarity index 100%
rename from core/java/android/net/ip/IpClientCallbacks.java
rename to services/net/java/android/net/ip/IpClientCallbacks.java
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/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java
similarity index 100%
rename from services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java
rename to services/robotests/backup/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java
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/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
index 0355e84..5cb6cbb 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
@@ -556,16 +556,6 @@
     }
 
     @Override
-    public byte[] getPermissionGrantBackup(int userId) throws RemoteException {
-        return new byte[0];
-    }
-
-    @Override
-    public void restorePermissionGrants(byte[] backup, int userId) throws RemoteException {
-
-    }
-
-    @Override
     public ComponentName getHomeActivities(List<ResolveInfo> outHomeCandidates)
         throws RemoteException {
         return null;
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/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
index 5900fc5..01759d2 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
@@ -98,6 +98,8 @@
 
         mColorDisplayService = new ColorDisplayService(mContext);
         mBinderService = mColorDisplayService.new BinderService();
+        LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
+                        mColorDisplayService.new ColorDisplayServiceInternal());
     }
 
     @After
@@ -110,6 +112,8 @@
 
         mUserId = UserHandle.USER_NULL;
         mContext = null;
+
+        LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
     }
 
     @AfterClass
@@ -979,6 +983,99 @@
         assertActiveColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
     }
 
+    @Test
+    public void displayWhiteBalance_enable() {
+        setWhiteBalance(true /* Enable DWB Setting */);
+        setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+        mBinderService.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
+        startService();
+        assertDwbActive(true);
+    }
+
+    @Test
+    public void displayWhiteBalance_disableAfterNightDisplayEnable() {
+        setWhiteBalance(true /* Enable DWB Setting */);
+
+        startService();
+        /* Enable nightlight */
+        setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+        setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+        /* Since we are using FakeSettingsProvider which could not trigger observer change,
+         * force an update here.*/
+        mColorDisplayService.updateDisplayWhiteBalanceStatus();
+        assertDwbActive(false);
+    }
+
+    @Test
+    public void displayWhiteBalance_enableAfterNightDisplayDisable() {
+        setWhiteBalance(true /* Enable DWB Setting */);
+        startService();
+        /* Enable nightlight */
+        setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+        setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+        mColorDisplayService.updateDisplayWhiteBalanceStatus();
+        assertDwbActive(false);
+
+        /* Disable nightlight */
+        setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+        mColorDisplayService.updateDisplayWhiteBalanceStatus();
+        assertDwbActive(true);
+    }
+
+    @Test
+    public void displayWhiteBalance_enableAfterLinearColorMode() {
+        setWhiteBalance(true /* Enable DWB Setting */);
+        setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+        startService();
+        mBinderService.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
+
+        mColorDisplayService.updateDisplayWhiteBalanceStatus();
+        assertDwbActive(true);
+    }
+
+    @Test
+    public void displayWhiteBalance_setTemperatureOverMax() {
+        int max = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMax;
+
+        ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService(
+                        ColorDisplayService.ColorDisplayServiceInternal.class);
+        cdsInternal.setDisplayWhiteBalanceColorTemperature(max+1);
+
+        assertWithMessage("Unexpected temperature set")
+                .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
+                .isEqualTo(max);
+    }
+
+    @Test
+    public void displayWhiteBalance_setTemperatureBelowMin() {
+        int min = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMin;
+
+        ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService(
+                        ColorDisplayService.ColorDisplayServiceInternal.class);
+        cdsInternal.setDisplayWhiteBalanceColorTemperature(min - 1);
+
+        assertWithMessage("Unexpected temperature set")
+                .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
+                .isEqualTo(min);
+    }
+
+    @Test
+    public void displayWhiteBalance_setValidTemperature() {
+        int min = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMin;
+        int max = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMax;
+        int valToSet = (min + max) / 2;
+
+        ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService(
+                        ColorDisplayService.ColorDisplayServiceInternal.class);
+        cdsInternal.setDisplayWhiteBalanceColorTemperature(valToSet);
+
+        assertWithMessage("Unexpected temperature set")
+                .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
+                .isEqualTo(valToSet);
+    }
+
     /**
      * Configures Night display to use a custom schedule.
      *
@@ -1041,6 +1138,16 @@
     }
 
     /**
+     * Configures the Display White Balance setting state.
+     *
+     * @param state {@code true} if display white balance should be enabled
+     */
+    private void setWhiteBalance(boolean state) {
+        Secure.putIntForUser(mContext.getContentResolver(),
+                Secure.DISPLAY_WHITE_BALANCE_ENABLED, state ? 1 : 0, mUserId);
+    }
+
+    /**
      * Configures color mode.
      */
     private void setColorMode(int colorMode) {
@@ -1111,6 +1218,17 @@
     }
 
     /**
+     * Convenience method for asserting that the DWB active status matches expectation.
+     *
+     * @param enabled the expected active status.
+     */
+    private void assertDwbActive(boolean enabled) {
+        assertWithMessage("Incorrect Display White Balance state")
+                .that(mColorDisplayService.mDisplayWhiteBalanceTintController.isActivated())
+                .isEqualTo(enabled);
+    }
+
+    /**
      * Convenience for making a {@link LocalTime} instance with an offset relative to now.
      *
      * @param offsetMinutes the offset relative to now (in minutes)
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 6d28ed1..50734ef 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -55,8 +55,8 @@
 
 import com.android.internal.os.AtomicFile;
 import com.android.server.LocalServices;
-import com.android.server.pm.permission.PermissionManagerInternal;
 import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 import org.junit.After;
 import org.junit.Before;
@@ -88,7 +88,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -103,7 +104,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -120,7 +122,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -143,7 +146,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -313,7 +317,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -507,7 +512,8 @@
     public void testUpdatePackageSetting03() {
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         final Settings testSettings01 =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         final SharedUserSetting testUserSetting01 = createSharedUserSetting(
@@ -625,7 +631,8 @@
     public void testCreateNewSetting03() {
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         final Settings testSettings01 =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         final SharedUserSetting testUserSetting01 = createSharedUserSetting(
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/WindowTraceBufferTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
index 2b8e307..b299f0d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
@@ -36,11 +36,10 @@
 import org.junit.Test;
 
 import java.io.File;
-import java.io.IOException;
 
 
 /**
- * Test class for {@link WindowTraceBuffer} and {@link WindowTraceQueueBuffer}.
+ * Test class for {@link WindowTraceBuffer}.
  *
  * Build/Install/Run:
  *  atest WmTests:WindowTraceBufferTest
@@ -49,12 +48,15 @@
 @Presubmit
 public class WindowTraceBufferTest {
     private File mFile;
+    private WindowTraceBuffer mBuffer;
 
     @Before
     public void setUp() throws Exception {
         final Context testContext = getInstrumentation().getContext();
         mFile = testContext.getFileStreamPath("tracing_test.dat");
         mFile.delete();
+
+        mBuffer = new WindowTraceBuffer(10);
     }
 
     @After
@@ -63,145 +65,112 @@
     }
 
     @Test
-    public void testTraceQueueBuffer_addItem() throws Exception {
-        ProtoOutputStream toWrite1 = getDummy(1);
-        ProtoOutputStream toWrite2 = getDummy(2);
-        ProtoOutputStream toWrite3 = getDummy(3);
-        final int objectSize = toWrite1.getRawSize();
-        final int bufferCapacity = objectSize * 2;
-
-        final WindowTraceBuffer buffer = buildQueueBuffer(bufferCapacity);
-
-        buffer.add(toWrite1);
-        byte[] toWrite1Bytes = toWrite1.getBytes();
-        assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
-
-        buffer.add(toWrite2);
-        byte[] toWrite2Bytes = toWrite2.getBytes();
-        assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
-        assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
-
-        buffer.add(toWrite3);
-        byte[] toWrite3Bytes = toWrite3.getBytes();
-        assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
-        assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
-        assertTrue("Third element should not be in the list",
-                !buffer.contains(toWrite3Bytes));
-
-        assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2);
-        assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity),
-                buffer.mBufferSize, bufferCapacity);
-        assertEquals("Buffer is full, available space should be 0",
-                buffer.getAvailableSpace(), 0);
-    }
-
-    @Test
-    public void testTraceRingBuffer_addItem() throws Exception {
+    public void test_addItem() {
         ProtoOutputStream toWrite = getDummy(1);
         final int objectSize = toWrite.getRawSize();
+        mBuffer.setCapacity(objectSize);
+        mBuffer.resetBuffer();
 
-        final WindowTraceBuffer buffer = buildRingBuffer(objectSize);
+        Preconditions.checkArgument(mBuffer.size() == 0);
 
-        Preconditions.checkArgument(buffer.mBuffer.isEmpty());
+        mBuffer.add(toWrite);
 
-        buffer.add(toWrite);
-
-        assertEquals("Item was not added to the buffer", buffer.mBuffer.size(), 1);
+        assertEquals("Item was not added to the buffer", 1, mBuffer.size());
         assertEquals("Total buffer getSize differs from inserted object",
-                buffer.mBufferSize, objectSize);
-        assertEquals("Available buffer space does not match used one",
-                buffer.getAvailableSpace(), 0);
+                mBuffer.getBufferSize(), objectSize);
+        assertEquals("Available buffer space does not match used one", 0,
+                mBuffer.getAvailableSpace());
     }
 
     @Test
-    public void testTraceRingBuffer_addItemMustOverwriteOne() throws Exception {
+    public void test_addItemMustOverwriteOne() {
         ProtoOutputStream toWrite1 = getDummy(1);
         ProtoOutputStream toWrite2 = getDummy(2);
         ProtoOutputStream toWrite3 = getDummy(3);
         final int objectSize = toWrite1.getRawSize();
-
         final int bufferCapacity = objectSize * 2 + 1;
-        final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity);
+        mBuffer.setCapacity(bufferCapacity);
+        mBuffer.resetBuffer();
 
-        buffer.add(toWrite1);
+        mBuffer.add(toWrite1);
         byte[] toWrite1Bytes = toWrite1.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
+                mBuffer.contains(toWrite1Bytes));
 
-        buffer.add(toWrite2);
+        mBuffer.add(toWrite2);
         byte[] toWrite2Bytes = toWrite2.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
+                mBuffer.contains(toWrite1Bytes));
         assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
+                mBuffer.contains(toWrite2Bytes));
 
-        buffer.add(toWrite3);
+        mBuffer.add(toWrite3);
         byte[] toWrite3Bytes = toWrite3.getBytes();
         assertTrue("First element should not be in the list",
-                !buffer.contains(toWrite1Bytes));
+                !mBuffer.contains(toWrite1Bytes));
         assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
+                mBuffer.contains(toWrite2Bytes));
         assertTrue("Third element should be in the list",
-                buffer.contains(toWrite3Bytes));
-        assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2);
+                mBuffer.contains(toWrite3Bytes));
+        assertEquals("Buffer should have 2 elements", 2, mBuffer.size());
         assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity),
-                buffer.mBufferSize, bufferCapacity - 1);
-        assertEquals(" Buffer is full, available space should be 0",
-                buffer.getAvailableSpace(), 1);
+                mBuffer.getBufferSize(), bufferCapacity - 1);
+        assertEquals(" Buffer is full, available space should be 0", 1,
+                mBuffer.getAvailableSpace());
     }
 
     @Test
-    public void testTraceRingBuffer_addItemMustOverwriteMultiple() throws Exception {
+    public void test_addItemMustOverwriteMultiple() {
         ProtoOutputStream toWriteSmall1 = getDummy(1);
         ProtoOutputStream toWriteSmall2 = getDummy(2);
         final int objectSize = toWriteSmall1.getRawSize();
-
         final int bufferCapacity = objectSize * 2;
-        final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity);
+        mBuffer.setCapacity(bufferCapacity);
+        mBuffer.resetBuffer();
 
         ProtoOutputStream toWriteBig = new ProtoOutputStream();
         toWriteBig.write(MAGIC_NUMBER, 1);
         toWriteBig.write(MAGIC_NUMBER, 2);
 
-        buffer.add(toWriteSmall1);
+        mBuffer.add(toWriteSmall1);
         byte[] toWriteSmall1Bytes = toWriteSmall1.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWriteSmall1Bytes));
+                mBuffer.contains(toWriteSmall1Bytes));
 
-        buffer.add(toWriteSmall2);
+        mBuffer.add(toWriteSmall2);
         byte[] toWriteSmall2Bytes = toWriteSmall2.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWriteSmall1Bytes));
+                mBuffer.contains(toWriteSmall1Bytes));
         assertTrue("Second element should be in the list",
-                buffer.contains(toWriteSmall2Bytes));
+                mBuffer.contains(toWriteSmall2Bytes));
 
-        buffer.add(toWriteBig);
+        mBuffer.add(toWriteBig);
         byte[] toWriteBigBytes = toWriteBig.getBytes();
         assertTrue("Third element should overwrite all others",
-                !buffer.contains(toWriteSmall1Bytes));
+                !mBuffer.contains(toWriteSmall1Bytes));
         assertTrue("Third element should overwrite all others",
-                !buffer.contains(toWriteSmall2Bytes));
+                !mBuffer.contains(toWriteSmall2Bytes));
         assertTrue("Third element should overwrite all others",
-                buffer.contains(toWriteBigBytes));
+                mBuffer.contains(toWriteBigBytes));
 
-        assertEquals(" Buffer should have only 1 big element", buffer.mBuffer.size(), 1);
+        assertEquals(" Buffer should have only 1 big element", 1, mBuffer.size());
         assertEquals(String.format(" Buffer is full, used space should be %d", bufferCapacity),
-                buffer.mBufferSize, bufferCapacity);
-        assertEquals(" Buffer is full, available space should be 0",
-                buffer.getAvailableSpace(), 0);
+                mBuffer.getBufferSize(), bufferCapacity);
+        assertEquals(" Buffer is full, available space should be 0", 0,
+                mBuffer.getAvailableSpace());
     }
 
-    private WindowTraceBuffer buildRingBuffer(int capacity) throws IOException {
-        return new WindowTraceBuffer.Builder()
-                .setContinuousMode(true)
-                .setBufferCapacity(capacity)
-                .setTraceFile(mFile)
-                .build();
+    @Test
+    public void test_startResetsBuffer() {
+        ProtoOutputStream toWrite = getDummy(1);
+        mBuffer.resetBuffer();
+        Preconditions.checkArgument(mBuffer.size() == 0);
+
+        mBuffer.add(toWrite);
+        assertEquals("Item was not added to the buffer", 1, mBuffer.size());
+        mBuffer.resetBuffer();
+        assertEquals("Buffer should be empty after reset", 0, mBuffer.size());
+        assertEquals("Buffer size should be 0 after reset", 0, mBuffer.getBufferSize());
     }
 
     private ProtoOutputStream getDummy(int value) {
@@ -212,7 +181,4 @@
         return toWrite;
     }
 
-    private WindowTraceBuffer buildQueueBuffer(int size) throws IOException {
-        return new WindowTraceQueueBuffer(size, mFile, false);
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
index 3c6e240..8358fdd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
@@ -88,8 +88,7 @@
         mFile.delete();
 
         mWindowTracing = new WindowTracing(mFile, mWmMock, mChoreographer,
-                new WindowManagerGlobalLock());
-        mWindowTracing.setContinuousMode(false /* continuous */, null /* pw */);
+                new WindowManagerGlobalLock(), 1024);
     }
 
     @After
@@ -103,13 +102,13 @@
     }
 
     @Test
-    public void isEnabled_returnsTrueAfterStart() throws Exception {
+    public void isEnabled_returnsTrueAfterStart() {
         mWindowTracing.startTrace(mock(PrintWriter.class));
         assertTrue(mWindowTracing.isEnabled());
     }
 
     @Test
-    public void isEnabled_returnsFalseAfterStop() throws Exception {
+    public void isEnabled_returnsFalseAfterStop() {
         mWindowTracing.startTrace(mock(PrintWriter.class));
         mWindowTracing.stopTrace(mock(PrintWriter.class));
         assertFalse(mWindowTracing.isEnabled());
@@ -133,6 +132,8 @@
         mWindowTracing.startTrace(mock(PrintWriter.class));
         mWindowTracing.stopTrace(mock(PrintWriter.class));
 
+        assertTrue("Trace file should exist", mFile.exists());
+
         byte[] header = new byte[MAGIC_HEADER.length];
         try (InputStream is = new FileInputStream(mFile)) {
             assertEquals(MAGIC_HEADER.length, is.read(header));
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/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index f1ddfe4..8feed7f 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -15,7 +15,6 @@
  */
 package com.android.server.usage;
 
-import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED;
 import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
 import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
 import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
@@ -302,27 +301,6 @@
                 UsageStats usageStats = packageStats.valueAt(i);
                 usageStats.update(null, timeStamp, eventType, instanceId);
             }
-        } else if (eventType == ACTIVITY_DESTROYED) {
-            UsageStats usageStats = packageStats.get(packageName);
-            if (usageStats != null) {
-                // If previous event is not ACTIVITY_STOPPED, convert ACTIVITY_DESTROYED
-                // to ACTIVITY_STOPPED and add to event list.
-                // Otherwise do not add anything to event list. (Because we want to save space
-                // and we do not want a ACTIVITY_STOPPED followed by
-                // ACTIVITY_DESTROYED in event list).
-                final int index = usageStats.mActivities.indexOfKey(instanceId);
-                if (index >= 0) {
-                    final int type = usageStats.mActivities.valueAt(index);
-                    if (type != ACTIVITY_STOPPED) {
-                        Event event = new Event(ACTIVITY_STOPPED, timeStamp);
-                        event.mPackage = packageName;
-                        event.mClass = className;
-                        event.mInstanceId = instanceId;
-                        addEvent(event);
-                    }
-                }
-                usageStats.update(className, timeStamp, ACTIVITY_DESTROYED, instanceId);
-            }
         } else {
             UsageStats usageStats = getOrCreateUsageStats(packageName);
             usageStats.update(className, timeStamp, eventType, instanceId);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index af5278f..ebb0210 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -145,8 +145,16 @@
     AppTimeLimitController mAppTimeLimit;
 
     final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray();
-    final SparseArray<String> mVisibleActivities = new SparseArray();
+    final SparseArray<ActivityData> mVisibleActivities = new SparseArray();
 
+    private static class ActivityData {
+        private final String mTaskRootPackage;
+        private final String mTaskRootClass;
+        private ActivityData(String taskRootPackage, String taskRootClass) {
+            mTaskRootPackage = taskRootPackage;
+            mTaskRootClass = taskRootClass;
+        }
+    }
 
     private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener =
             new UsageStatsManagerInternal.AppIdleStateChangeListener() {
@@ -464,47 +472,57 @@
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             convertToSystemTimeLocked(event);
 
-            if (event.getPackageName() != null
-                    && mPackageManagerInternal.isPackageEphemeral(userId, event.getPackageName())) {
+            if (event.mPackage != null
+                    && mPackageManagerInternal.isPackageEphemeral(userId, event.mPackage)) {
                 event.mFlags |= Event.FLAG_IS_PACKAGE_INSTANT_APP;
             }
 
-            final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            service.reportEvent(event);
-
-            mAppStandby.reportEvent(event, elapsedRealtime, userId);
-
-            String packageName;
-
-            switch(mUsageSource) {
-                case USAGE_SOURCE_CURRENT_ACTIVITY:
-                    packageName = event.getPackageName();
-                    break;
-                case USAGE_SOURCE_TASK_ROOT_ACTIVITY:
-                default:
-                    packageName = event.getTaskRootPackageName();
-                    if (packageName == null) {
-                        packageName = event.getPackageName();
-                    }
-                    break;
-            }
-
             switch (event.mEventType) {
                 case Event.ACTIVITY_RESUMED:
-                    synchronized (mVisibleActivities) {
-                        // check if this activity has already been resumed
-                        if (mVisibleActivities.get(event.mInstanceId) != null) break;
-                        mVisibleActivities.put(event.mInstanceId, event.getClassName());
-                        try {
-                            mAppTimeLimit.noteUsageStart(packageName, userId);
-                        } catch (IllegalArgumentException iae) {
-                            Slog.e(TAG, "Failed to note usage start", iae);
+                    // check if this activity has already been resumed
+                    if (mVisibleActivities.get(event.mInstanceId) != null) break;
+                    mVisibleActivities.put(event.mInstanceId,
+                            new ActivityData(event.mTaskRootPackage, event.mTaskRootClass));
+                    try {
+                        switch(mUsageSource) {
+                            case USAGE_SOURCE_CURRENT_ACTIVITY:
+                                mAppTimeLimit.noteUsageStart(event.mPackage, userId);
+                                break;
+                            case USAGE_SOURCE_TASK_ROOT_ACTIVITY:
+                            default:
+                                mAppTimeLimit.noteUsageStart(event.mTaskRootPackage, userId);
+                                break;
+                        }
+                    } catch (IllegalArgumentException iae) {
+                        Slog.e(TAG, "Failed to note usage start", iae);
+                    }
+                    break;
+                case Event.ACTIVITY_PAUSED:
+                    if (event.mTaskRootPackage == null) {
+                        // Task Root info is missing. Repair the event based on previous data
+                        final ActivityData prevData = mVisibleActivities.get(event.mInstanceId);
+                        if (prevData == null) {
+                            Slog.w(TAG, "Unexpected activity event reported! (" + event.mPackage
+                                    + "/" + event.mClass + " event : " + event.mEventType
+                                    + " instanceId : " + event.mInstanceId + ")");
+                        } else {
+                            event.mTaskRootPackage = prevData.mTaskRootPackage;
+                            event.mTaskRootClass = prevData.mTaskRootClass;
                         }
                     }
                     break;
-                case Event.ACTIVITY_STOPPED:
                 case Event.ACTIVITY_DESTROYED:
+                    // Treat activity destroys like activity stops.
+                    event.mEventType = Event.ACTIVITY_STOPPED;
+                    // Fallthrough
+                case Event.ACTIVITY_STOPPED:
+                    final ActivityData prevData =
+                            mVisibleActivities.removeReturnOld(event.mInstanceId);
+                    if (prevData == null) {
+                        // The activity stop was already handled.
+                        return;
+                    }
+
                     ArraySet<String> tokens;
                     synchronized (mUsageReporters) {
                         tokens = mUsageReporters.removeReturnOld(event.mInstanceId);
@@ -517,7 +535,7 @@
                                 final String token = tokens.valueAt(i);
                                 try {
                                     mAppTimeLimit.noteUsageStop(
-                                            buildFullToken(event.getPackageName(), token), userId);
+                                            buildFullToken(event.mPackage, token), userId);
                                 } catch (IllegalArgumentException iae) {
                                     Slog.w(TAG, "Failed to stop usage for during reporter death: "
                                             + iae);
@@ -525,18 +543,32 @@
                             }
                         }
                     }
-
-                    synchronized (mVisibleActivities) {
-                        if (mVisibleActivities.removeReturnOld(event.mInstanceId) != null) {
-                            try {
-                                mAppTimeLimit.noteUsageStop(packageName, userId);
-                            } catch (IllegalArgumentException iae) {
-                                Slog.w(TAG, "Failed to note usage stop", iae);
-                            }
+                    if (event.mTaskRootPackage == null) {
+                        // Task Root info is missing. Repair the event based on previous data
+                        event.mTaskRootPackage = prevData.mTaskRootPackage;
+                        event.mTaskRootClass = prevData.mTaskRootClass;
+                    }
+                    try {
+                        switch(mUsageSource) {
+                            case USAGE_SOURCE_CURRENT_ACTIVITY:
+                                mAppTimeLimit.noteUsageStop(event.mPackage, userId);
+                                break;
+                            case USAGE_SOURCE_TASK_ROOT_ACTIVITY:
+                            default:
+                                mAppTimeLimit.noteUsageStop(event.mTaskRootPackage, userId);
+                                break;
                         }
+                    } catch (IllegalArgumentException iae) {
+                        Slog.w(TAG, "Failed to note usage stop", iae);
                     }
                     break;
             }
+
+            final UserUsageStatsService service =
+                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+            service.reportEvent(event);
+
+            mAppStandby.reportEvent(event, elapsedRealtime, userId);
         }
     }
 
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/Connection.java b/telecomm/java/android/telecom/Connection.java
index bd0d4ae..ae12a17 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -623,7 +623,7 @@
             "android.telecom.event.HANDOVER_FAILED";
 
     /**
-     * Connection extra key used to store SIP invite fields for an incoming call for IMS calls
+     * String Connection extra key used to store SIP invite fields for an incoming call for IMS call
      */
     public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
 
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/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index a1c32b5..2462bee 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2123,6 +2123,11 @@
      * @hide - not meant for public use
      */
     public interface RcsColumns {
+        // TODO(sahinc): Turn this to true once the schema finalizes, so that people can update
+        //  their messaging databases. NOTE: move the switch/case update in MmsSmsDatabaseHelper to
+        //  the latest version of the database before turning this flag to true.
+        boolean IS_RCS_TABLE_SCHEMA_CODE_COMPLETE = false;
+
         /**
          * The authority for the content provider
          */
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 6690bd0..ced4f4a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -229,10 +229,19 @@
     public static final int SRVCC_STATE_HANDOVER_CANCELED  = 3;
 
     /**
-     * An invalid UICC card identifier. See {@link #getCardIdForDefaultEuicc()} and
-     * {@link UiccCardInfo#getCardId()}.
+     * A UICC card identifier used if the device does not support the operation.
+     * For example, {@link #getCardIdForDefaultEuicc()} returns this value if the device has no
+     * eUICC, or the eUICC cannot be read.
      */
-    public static final int INVALID_CARD_ID = -1;
+    public static final int UNSUPPORTED_CARD_ID = -1;
+
+    /**
+     * A UICC card identifier used before the UICC card is loaded. See
+     * {@link #getCardIdForDefaultEuicc()} and {@link UiccCardInfo#getCardId()}.
+     * <p>
+     * Note that once the UICC card is loaded, the card ID may become {@link #UNSUPPORTED_CARD_ID}.
+     */
+    public static final int UNINITIALIZED_CARD_ID = -2;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -1689,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();
@@ -3178,24 +3184,25 @@
     }
 
     /**
-     * Get the card ID of the default eUICC card. If there is no eUICC, returns
-     * {@link #INVALID_CARD_ID}.
+     * Get the card ID of the default eUICC card. If the eUICCs have not yet been loaded, returns
+     * {@link #UNINITIALIZED_CARD_ID}. If there is no eUICC or the device does not support card IDs
+     * for eUICCs, returns {@link #UNSUPPORTED_CARD_ID}.
      *
      * <p>The card ID is a unique identifier associated with a UICC or eUICC card. Card IDs are
      * unique to a device, and always refer to the same UICC or eUICC card unless the device goes
      * through a factory reset.
      *
-     * @return card ID of the default eUICC card.
+     * @return card ID of the default eUICC card, if loaded.
      */
     public int getCardIdForDefaultEuicc() {
         try {
             ITelephony telephony = getITelephony();
             if (telephony == null) {
-                return INVALID_CARD_ID;
+                return UNINITIALIZED_CARD_ID;
             }
             return telephony.getCardIdForDefaultEuicc(mSubId, mContext.getOpPackageName());
         } catch (RemoteException e) {
-            return INVALID_CARD_ID;
+            return UNINITIALIZED_CARD_ID;
         }
     }
 
@@ -4941,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();
@@ -5018,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 {
@@ -5057,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) {
@@ -6647,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
@@ -6658,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);
@@ -6682,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
@@ -6693,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) {
@@ -6702,7 +6717,8 @@
                 mTelephonyScanManager = new TelephonyScanManager();
             }
         }
-        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback);
+        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback,
+                getOpPackageName());
     }
 
     /**
@@ -6712,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);
@@ -8713,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/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 1bbf9f4..ad34349 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -460,7 +460,7 @@
      */
     @Nullable
     public String getEid() {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             return null;
         }
         try {
@@ -483,7 +483,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public int getOtaStatus() {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             return EUICC_OTA_STATUS_UNAVAILABLE;
         }
         try {
@@ -518,7 +518,7 @@
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void downloadSubscription(DownloadableSubscription subscription,
             boolean switchAfterDownload, PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -580,7 +580,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             PendingIntent callbackIntent =
                     resolutionIntent.getParcelableExtra(
                             EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT);
@@ -617,7 +617,7 @@
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDownloadableSubscriptionMetadata(
             DownloadableSubscription subscription, PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -647,7 +647,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -666,7 +666,7 @@
      */
     @Nullable
     public EuiccInfo getEuiccInfo() {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             return null;
         }
         try {
@@ -691,7 +691,7 @@
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -731,7 +731,7 @@
      */
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -757,7 +757,7 @@
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void updateSubscriptionNickname(
             int subscriptionId, String nickname, PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -781,7 +781,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void eraseSubscriptions(PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -811,7 +811,7 @@
      * @hide
      */
     public void retainSubscriptionsForFactoryReset(PendingIntent callbackIntent) {
-        if (!refreshCardIdIfInvalid()) {
+        if (!refreshCardIdIfUninitialized()) {
             sendUnavailableError(callbackIntent);
             return;
         }
@@ -822,16 +822,24 @@
         }
     }
 
-    private boolean refreshCardIdIfInvalid() {
-        if (!isEnabled()) {
-            return false;
-        }
-        // Refresh mCardId if it's invalid.
-        if (mCardId == TelephonyManager.INVALID_CARD_ID) {
+    /**
+     * Refreshes the cardId if its uninitialized, and returns whether we should continue the
+     * operation.
+     * <p>
+     * Note that after a successful refresh, the mCardId may be TelephonyManager.UNSUPPORTED_CARD_ID
+     * on older HALs. For backwards compatability, we continue to the LPA and let it decide which
+     * card to use.
+     */
+    private boolean refreshCardIdIfUninitialized() {
+        // Refresh mCardId if its UNINITIALIZED_CARD_ID
+        if (mCardId == TelephonyManager.UNINITIALIZED_CARD_ID) {
             TelephonyManager tm = (TelephonyManager)
                     mContext.getSystemService(Context.TELEPHONY_SERVICE);
             mCardId = tm.getCardIdForDefaultEuicc();
         }
+        if (mCardId == TelephonyManager.UNINITIALIZED_CARD_ID) {
+            return false;
+        }
         return true;
     }
 
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 59167b7..d5c7079 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -266,6 +266,11 @@
     public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
     public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
     public static final String EXTRA_IS_CALL_PULL = "CallPull";
+
+    /**
+     * String extra property
+     *  Containing fields from the SIP INVITE message for an IMS call
+     */
     public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS =
                                   "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
 
@@ -350,6 +355,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 +797,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 +869,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 +938,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/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index ab10800..0cb8f22 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -9,10 +9,12 @@
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method public String[] getNamesForUids(int[]);
     method public String getPermissionControllerPackageName();
+    method public int getPermissionFlags(String, String, android.os.UserHandle);
     method @NonNull public String getServicesSystemSharedLibraryPackageName();
     method @NonNull public String getSharedSystemSharedLibraryPackageName();
     method public void grantRuntimePermission(String, String, android.os.UserHandle);
     method public void revokeRuntimePermission(String, String, android.os.UserHandle);
+    method public void updatePermissionFlags(String, String, int, int, android.os.UserHandle);
   }
 
 }
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/Debug.cpp b/tools/aapt2/Debug.cpp
index 5f664f5..9832485 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -435,7 +435,7 @@
   const size_t NS = pool->size();
   for (size_t s=0; s<NS; s++) {
     String8 str = pool->string8ObjectAt(s);
-    printer->Print(StringPrintf("String #%zd: %s\n", s, str.string()));
+    printer->Print(StringPrintf("String #%zd : %s\n", s, str.string()));
   }
 }
 
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 2f8ca2d..c188782 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -1084,7 +1084,7 @@
   // Create a overlayable entry grouping that represents this <overlayable>
   auto overlayable = std::make_shared<Overlayable>(
       overlayable_name.value(), (overlayable_actor) ? overlayable_actor.value() : "",
-      out_resource->source);
+      source_);
 
   bool error = false;
   std::string comment;
@@ -1113,6 +1113,13 @@
     const std::string& element_name = parser->element_name();
     const std::string& element_namespace = parser->element_namespace();
     if (element_namespace.empty() && element_name == "item") {
+      if (current_policies == OverlayableItem::Policy::kNone) {
+        diag_->Error(DiagMessage(element_source)
+                         << "<item> within an <overlayable> must be inside a <policy> block");
+        error = true;
+        continue;
+      }
+
       // Items specify the name and type of resource that should be overlayable
       Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
       if (!item_name) {
@@ -1169,6 +1176,8 @@
             current_policies |= OverlayableItem::Policy::kSystem;
           } else if (trimmed_part == "vendor") {
             current_policies |= OverlayableItem::Policy::kVendor;
+          } else if (trimmed_part == "signature") {
+            current_policies |= OverlayableItem::Policy::kSignature;
           } else {
             diag_->Error(DiagMessage(element_source)
                          << "<policy> has unsupported type '" << trimmed_part << "'");
@@ -1176,6 +1185,11 @@
             continue;
           }
         }
+      } else {
+        diag_->Error(DiagMessage(element_source)
+                         << "<policy> must have a 'type' attribute");
+        error = true;
+        continue;
       }
     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
       diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> "
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 827c7de..25b76b0 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -894,8 +894,10 @@
 TEST_F(ResourceParserTest, ParseOverlayable) {
   std::string input = R"(
       <overlayable name="Name" actor="overlay://theme">
-        <item type="string" name="foo" />
-        <item type="drawable" name="bar" />
+          <policy type="signature">
+            <item type="string" name="foo" />
+            <item type="drawable" name="bar" />
+          </policy>
       </overlayable>)";
   ASSERT_TRUE(TestParse(input));
 
@@ -906,7 +908,7 @@
   OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
-  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
 
   search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
   ASSERT_TRUE(search_result);
@@ -915,7 +917,7 @@
   result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
-  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayableRequiresName) {
@@ -931,7 +933,6 @@
 TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
   std::string input = R"(
       <overlayable name="Name">
-        <item type="string" name="foo" />
         <policy type="product">
           <item type="string" name="bar" />
         </policy>
@@ -944,23 +945,18 @@
         <policy type="public">
           <item type="string" name="faz" />
         </policy>
+        <policy type="signature">
+          <item type="string" name="foz" />
+        </policy>
       </overlayable>)";
   ASSERT_TRUE(TestParse(input));
 
-  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
+  auto search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
   OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
-  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
-
-  search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
-  ASSERT_TRUE(search_result);
-  ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable_item);
-  result_overlayable_item = search_result.value().entry->overlayable_item.value();
-  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
@@ -986,6 +982,30 @@
   result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/foz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableNoPolicyError) {
+  std::string input = R"(
+      <overlayable name="Name">
+        <item type="string" name="foo" />
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable name="Name">
+        <policy>
+          <item name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 7ca99ea..32dfd26 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -92,6 +92,9 @@
 
     // The resource can be overlaid by any overlay on the product partition.
     kProduct = 0x08,
+
+    // The resource can be overlaid by any overlay signed with the same signature as its actor.
+    kSignature = 0x010,
   };
 
   std::shared_ptr<Overlayable> overlayable;
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index ab4805f..0032960 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -727,7 +727,7 @@
           // This must be a FileReference.
           std::unique_ptr<FileReference> file_ref =
               util::make_unique<FileReference>(dst_pool->MakeRef(
-                  str, StringPool::Context(StringPool::Context::kHighPriority, config), data));
+                  str, StringPool::Context(StringPool::Context::kHighPriority, config)));
           if (type == ResourceType::kRaw) {
             file_ref->type = ResourceFile::Type::kUnknown;
           } else if (util::EndsWith(*file_ref->path, ".xml")) {
@@ -739,7 +739,7 @@
         }
 
         // There are no styles associated with this string, so treat it as a simple string.
-        return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config), data));
+        return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config)));
       }
     } break;
 
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 73b568e..a2fd7c6 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -138,10 +138,10 @@
 
 // Represents a set of overlayable resources.
 message Overlayable {
-  // The name of the <overlyabale>.
+  // The name of the <overlayable>.
   string name = 1;
 
-  // The location of the <overlyabale> declaration in the source.
+  // The location of the <overlayable> declaration in the source.
   Source source = 2;
 
   // The component responsible for enabling and disabling overlays targeting this <overlayable>.
@@ -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/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index a8c2666..8eabd32 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -165,13 +165,12 @@
   return MakeRefImpl(str, Context{}, true);
 }
 
-StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context,
-                                    Maybe<size_t> index) {
-  return MakeRefImpl(str, context, true, index);
+StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context) {
+  return MakeRefImpl(str, context, true);
 }
 
 StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, const Context& context,
-                                        bool unique, Maybe<size_t> index) {
+                                        bool unique) {
   if (unique) {
     auto range = indexed_strings_.equal_range(str);
     for (auto iter = range.first; iter != range.second; ++iter) {
@@ -181,26 +180,15 @@
     }
   }
 
-  const size_t size = strings_.size();
-  // Insert the string at the end of the string vector if no index is specified
-  const size_t insertion_index = index ? index.value() : size;
-
   std::unique_ptr<Entry> entry(new Entry());
   entry->value = str.to_string();
   entry->context = context;
-  entry->index_ = insertion_index;
+  entry->index_ = strings_.size();
   entry->ref_ = 0;
   entry->pool_ = this;
 
   Entry* borrow = entry.get();
-  if (insertion_index == size) {
-    strings_.emplace_back(std::move(entry));
-  } else {
-    // Allocate enough space for the string at the index
-    strings_.resize(std::max(insertion_index + 1, size));
-    strings_[insertion_index] = std::move(entry);
-  }
-
+  strings_.emplace_back(std::move(entry));
   indexed_strings_.insert(std::make_pair(StringPiece(borrow->value), borrow));
   return Ref(borrow);
 }
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 115d5d3..1006ca9 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -166,8 +166,7 @@
 
   // Adds a string to the pool, unless it already exists, with a context object that can be used
   // when sorting the string pool. Returns a reference to the string in the pool.
-  Ref MakeRef(const android::StringPiece& str, const Context& context,
-              Maybe<size_t> index = {});
+  Ref MakeRef(const android::StringPiece& str, const Context& context);
 
   // Adds a string from another string pool. Returns a reference to the string in the string pool.
   Ref MakeRef(const Ref& ref);
@@ -211,8 +210,7 @@
 
   static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8, IDiagnostics* diag);
 
-  Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique,
-                  Maybe<size_t> index = {});
+  Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique);
   void ReAssignIndices();
 
   std::vector<std::unique_ptr<Entry>> strings_;
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 648be7d..9a7238b 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -84,24 +84,6 @@
   EXPECT_THAT(ref_c.index(), Eq(2u));
 }
 
-TEST(StringPoolTest, AssignStringIndex) {
-  StringPool pool;
-
-  StringPool::Ref ref_a = pool.MakeRef("0", StringPool::Context{}, 0u);
-  StringPool::Ref ref_b = pool.MakeRef("1", StringPool::Context{}, 1u);
-  StringPool::Ref ref_c = pool.MakeRef("5", StringPool::Context{}, 5u);
-  StringPool::Ref ref_d = pool.MakeRef("2", StringPool::Context{}, 2u);
-  StringPool::Ref ref_e = pool.MakeRef("4", StringPool::Context{}, 4u);
-  StringPool::Ref ref_f = pool.MakeRef("3", StringPool::Context{}, 3u);
-
-  EXPECT_THAT(ref_a.index(), Eq(0u));
-  EXPECT_THAT(ref_b.index(), Eq(1u));
-  EXPECT_THAT(ref_d.index(), Eq(2u));
-  EXPECT_THAT(ref_f.index(), Eq(3u));
-  EXPECT_THAT(ref_e.index(), Eq(4u));
-  EXPECT_THAT(ref_c.index(), Eq(5u));
-}
-
 TEST(StringPoolTest, PruneStringsWithNoReferences) {
   StringPool pool;
 
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 7a74ba9..0cf86cc 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -43,7 +43,8 @@
 
 class IApkSerializer {
  public:
-  IApkSerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {}
+  IApkSerializer(IAaptContext* context, const Source& source) : context_(context),
+                                                                source_(source) {}
 
   virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
                             IArchiveWriter* writer, uint32_t compression_flags) = 0;
@@ -167,7 +168,7 @@
       std::unique_ptr<io::IData> data = file->file->OpenAsData();
       if (!data) {
         context_->GetDiagnostics()->Error(DiagMessage(source_)
-                                         << "failed to open file " << *file->path);
+                                          << "failed to open file " << *file->path);
         return false;
       }
 
@@ -175,7 +176,7 @@
       std::unique_ptr<xml::XmlResource> xml = xml::Inflate(data->data(), data->size(), &error);
       if (xml == nullptr) {
         context_->GetDiagnostics()->Error(DiagMessage(source_) << "failed to parse binary XML: "
-                                          << error);
+                                                               << error);
         return false;
       }
 
@@ -256,9 +257,6 @@
 int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer,
             ApkFormat output_format, TableFlattenerOptions table_flattener_options,
             XmlFlattenerOptions xml_flattener_options) {
-  // Do not change the ordering of strings in the values string pool
-  table_flattener_options.sort_stringpool_entries = false;
-
   unique_ptr<IApkSerializer> serializer;
   if (output_format == ApkFormat::kBinary) {
     serializer.reset(new BinaryApkSerializer(context, apk->GetSource(), table_flattener_options,
@@ -274,7 +272,7 @@
   io::IFile* manifest = apk->GetFileCollection()->FindFile(kAndroidManifestPath);
   if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/,
                                 output_writer, (manifest != nullptr && manifest->WasCompressed())
-                                ? ArchiveEntry::kCompress : 0u)) {
+                                               ? ArchiveEntry::kCompress : 0u)) {
     context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
                                      << "failed to serialize AndroidManifest.xml");
     return 1;
@@ -303,8 +301,7 @@
               if (files_written.insert(*file->path).second) {
                 if (!serializer->SerializeFile(file, output_writer)) {
                   context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                                       << "failed to serialize file "
-                                                       << *file->path);
+                                                   << "failed to serialize file " << *file->path);
                   return 1;
                 }
               }
@@ -338,7 +335,7 @@
 
     if (!io::CopyFileToArchivePreserveCompression(context, file, path, output_writer)) {
       context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                       << "failed to copy file " << path);
+                                           << "failed to copy file " << path);
       return 1;
     }
   }
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 8463046..4961aa5 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -400,7 +400,8 @@
 
 static bool IsVectorElement(const std::string& name) {
   return name == "vector" || name == "animated-vector" || name == "pathInterpolator" ||
-         name == "objectAnimator" || name == "gradient" || name == "animated-selector";
+         name == "objectAnimator" || name == "gradient" || name == "animated-selector" ||
+         name == "set";
 }
 
 template <typename T>
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 40aaa05..59eb9ec 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -401,7 +401,6 @@
     if (entry->flags & ResTable_entry::FLAG_PUBLIC) {
       Visibility visibility;
       visibility.level = Visibility::Level::kPublic;
-      visibility.source = source_.WithLine(0);
       if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) {
         return false;
       }
@@ -448,7 +447,6 @@
                                                       arraysize(header->name)));
   overlayable->actor = util::Utf16ToUtf8(strcpy16_dtoh((const char16_t*)header->actor,
                                                        arraysize(header->name)));
-  overlayable->source = source_.WithLine(0);
 
   ResChunkPullParser parser(GetChunkData(chunk),
                             GetChunkDataLen(chunk));
@@ -473,6 +471,10 @@
           & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
         policies |= OverlayableItem::Policy::kProduct;
       }
+      if (policy_header->policy_flags
+          & ResTable_overlayable_policy_header::POLICY_SIGNATURE) {
+        policies |= OverlayableItem::Policy::kSignature;
+      }
 
       const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
           ((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize));
@@ -491,7 +493,6 @@
         }
 
         OverlayableItem overlayable_item(overlayable);
-        overlayable_item.source = source_.WithLine(0);
         overlayable_item.policies = policies;
         if (!table_->SetOverlayable(iter->second, overlayable_item, diag_)) {
           return false;
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 9d341cc..d677317 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -274,7 +274,9 @@
       FlattenLibrarySpec(buffer);
     }
 
-    FlattenOverlayable(buffer);
+    if (!FlattenOverlayable(buffer)) {
+      return false;
+    }
 
     pkg_writer.Finish();
     return true;
@@ -468,23 +470,29 @@
           overlayable_chunk = &chunk;
         }
 
+        if (item.policies == 0) {
+          context_->GetDiagnostics()->Error(DiagMessage(item.overlayable->source)
+                                                << "overlayable "
+                                                << entry->name
+                                                << " does not specify policy");
+          return false;
+        }
+
         uint32_t policy_flags = 0;
-        if (item.policies == OverlayableItem::Policy::kNone) {
-          // Encode overlayable entries defined without a policy as publicly overlayable
+        if (item.policies & OverlayableItem::Policy::kPublic) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
-        } else {
-          if (item.policies & OverlayableItem::Policy::kPublic) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
-          }
-          if (item.policies & OverlayableItem::Policy::kSystem) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
-          }
-          if (item.policies & OverlayableItem::Policy::kVendor) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
-          }
-          if (item.policies & OverlayableItem::Policy::kProduct) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
-          }
+        }
+        if (item.policies & OverlayableItem::Policy::kSystem) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
+        }
+        if (item.policies & OverlayableItem::Policy::kVendor) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
+        }
+        if (item.policies & OverlayableItem::Policy::kProduct) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
+        }
+        if (item.policies & OverlayableItem::Policy::kSignature) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_SIGNATURE;
         }
 
         auto policy = overlayable_chunk->policy_ids.find(policy_flags);
@@ -702,17 +710,15 @@
 }  // namespace
 
 bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
-  if (options_.sort_stringpool_entries) {
-    // We must do this before writing the resources, since the string pool IDs may change.
-    table->string_pool.Prune();
-    table->string_pool.Sort([](const StringPool::Context &a, const StringPool::Context &b) -> int {
-      int diff = util::compare(a.priority, b.priority);
-      if (diff == 0) {
-        diff = a.config.compare(b.config);
-      }
-      return diff;
-    });
-  }
+  // We must do this before writing the resources, since the string pool IDs may change.
+  table->string_pool.Prune();
+  table->string_pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int {
+    int diff = util::compare(a.priority, b.priority);
+    if (diff == 0) {
+      diff = a.config.compare(b.config);
+    }
+    return diff;
+  });
 
   // Write the ResTable header.
   ChunkWriter table_writer(buffer_);
diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
index 71330e3..73c1729 100644
--- a/tools/aapt2/format/binary/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -44,9 +44,6 @@
   // Set of whitelisted resource names to avoid altering in key stringpool
   std::set<std::string> whitelisted_resources;
 
-  // When true, sort the entries in the values string pool by priority and configuration.
-  bool sort_stringpool_entries = true;
-
   // Map from original resource paths to shortened resource paths.
   std::map<std::string, std::string> shortened_path_map;
 };
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index ddc1173..4c5dbec 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -671,9 +671,6 @@
   overlayable_item_two.policies |= OverlayableItem::Policy::kSystem;
   overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
 
-  std::string name_three = "com.app.test:integer/overlayable_three_item";
-  OverlayableItem overlayable_item_three(overlayable);
-
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
@@ -683,8 +680,6 @@
           .SetOverlayable(name_one, overlayable_item_one)
           .AddSimple(name_two, ResourceId(0x7f020002))
           .SetOverlayable(name_two, overlayable_item_two)
-          .AddSimple(name_three, ResourceId(0x7f020003))
-          .SetOverlayable(name_three, overlayable_item_three)
           .Build();
 
   ResourceTable output_table;
@@ -713,16 +708,6 @@
   EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
                                        | OverlayableItem::Policy::kProduct
                                        | OverlayableItem::Policy::kVendor);
-
-  search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
-  ASSERT_TRUE(search_result);
-  ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable_item);
-  overlayable_item = search_result.value().entry->overlayable_item.value();
-  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
-  EXPECT_EQ(overlayable_item.overlayable->name, "TestName");
-  EXPECT_EQ(overlayable_item.overlayable->actor, "overlay://theme");
-  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
 }
 
 TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
@@ -745,6 +730,8 @@
 
   std::string name_three = "com.app.test:integer/overlayable_three";
   OverlayableItem overlayable_item_three(group_one);
+  overlayable_item_three.policies |= OverlayableItem::Policy::kSignature;
+
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
@@ -793,7 +780,22 @@
   result_overlayable = search_result.value().entry->overlayable_item.value();
   EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
   EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
-  EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic);
+  EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSignature);
+}
+
+TEST_F(TableFlattenerTest, FlattenOverlayableNoPolicyFails) {
+  auto group = std::make_shared<Overlayable>("TestName", "overlay://theme");
+  std::string name_zero = "com.app.test:integer/overlayable_zero";
+  OverlayableItem overlayable_item_zero(group);
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.test", 0x7f)
+          .AddSimple(name_zero, ResourceId(0x7f020000))
+          .SetOverlayable(name_zero, overlayable_item_zero)
+          .Build();
+  ResourceTable output_table;
+  ASSERT_FALSE(Flatten(context_.get(), {}, table.get(), &output_table));
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index aff1b39..06f1bf7 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -390,6 +390,9 @@
       case pb::OverlayableItem::PRODUCT:
         out_overlayable->policies |= OverlayableItem::Policy::kProduct;
         break;
+      case pb::OverlayableItem::SIGNATURE:
+        out_overlayable->policies |= OverlayableItem::Policy::kSignature;
+        break;
       default:
         *out_error = "unknown overlayable policy";
         return false;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index b549e23..eb2b1a2 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -309,6 +309,9 @@
   if (overlayable_item.policies & OverlayableItem::Policy::kVendor) {
     pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
   }
+  if (overlayable_item.policies & OverlayableItem::Policy::kSignature) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::SIGNATURE);
+  }
 
   SerializeSourceToPb(overlayable_item.source, source_pool,
                       pb_overlayable_item->mutable_source());
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index cce3939..d369ac4 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -526,6 +526,10 @@
       "FontPack", "overlay://theme"));
   overlayable_item_baz.policies |= OverlayableItem::Policy::kPublic;
 
+  OverlayableItem overlayable_item_boz(std::make_shared<Overlayable>(
+      "IconPack", "overlay://theme"));
+  overlayable_item_boz.policies |= OverlayableItem::Policy::kSignature;
+
   OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>(
       "Other", "overlay://customization"));
   overlayable_item_biz.comment ="comment";
@@ -536,6 +540,7 @@
           .SetOverlayable("com.app.a:bool/foo", overlayable_item_foo)
           .SetOverlayable("com.app.a:bool/bar", overlayable_item_bar)
           .SetOverlayable("com.app.a:bool/baz", overlayable_item_baz)
+          .SetOverlayable("com.app.a:bool/boz", overlayable_item_boz)
           .SetOverlayable("com.app.a:bool/biz", overlayable_item_biz)
           .AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
           .Build();
@@ -576,6 +581,14 @@
   EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
   EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
 
+  search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/boz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("IconPack"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
   ASSERT_TRUE(search_result);
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
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;
     }