Merge "Using proc state to determine foreground status."
diff --git a/api/current.txt b/api/current.txt
index 18f150f..e2ea649 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5245,8 +5245,8 @@
     method public android.app.Notification clone();
     method public int describeContents();
     method public boolean getAllowSystemGeneratedContextualActions();
-    method public android.app.PendingIntent getAppOverlayIntent();
     method public int getBadgeIconType();
+    method public android.app.Notification.BubbleMetadata getBubbleMetadata();
     method public java.lang.String getChannelId();
     method public java.lang.String getGroup();
     method public int getGroupAlertBehavior();
@@ -5459,6 +5459,25 @@
     method public android.app.Notification.BigTextStyle setSummaryText(java.lang.CharSequence);
   }
 
+  public static final class Notification.BubbleMetadata implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getDesiredHeight();
+    method public android.graphics.drawable.Icon getIcon();
+    method public android.app.PendingIntent getIntent();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.Notification.BubbleMetadata> CREATOR;
+  }
+
+  public static class Notification.BubbleMetadata.Builder {
+    ctor public Notification.BubbleMetadata.Builder();
+    method public android.app.Notification.BubbleMetadata build();
+    method public android.app.Notification.BubbleMetadata.Builder setDesiredHeight(int);
+    method public android.app.Notification.BubbleMetadata.Builder setIcon(android.graphics.drawable.Icon);
+    method public android.app.Notification.BubbleMetadata.Builder setIntent(android.app.PendingIntent);
+    method public android.app.Notification.BubbleMetadata.Builder setTitle(java.lang.CharSequence);
+  }
+
   public static class Notification.Builder {
     ctor public Notification.Builder(android.content.Context, java.lang.String);
     ctor public deprecated Notification.Builder(android.content.Context);
@@ -5478,9 +5497,9 @@
     method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
     method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
     method public android.app.Notification.Builder setAllowSystemGeneratedContextualActions(boolean);
-    method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent);
     method public android.app.Notification.Builder setAutoCancel(boolean);
     method public android.app.Notification.Builder setBadgeIconType(int);
+    method public android.app.Notification.Builder setBubbleMetadata(android.app.Notification.BubbleMetadata);
     method public android.app.Notification.Builder setCategory(java.lang.String);
     method public android.app.Notification.Builder setChannelId(java.lang.String);
     method public android.app.Notification.Builder setChronometerCountDown(boolean);
@@ -5695,8 +5714,8 @@
 
   public final class NotificationChannel implements android.os.Parcelable {
     ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
+    method public boolean canBubble();
     method public boolean canBypassDnd();
-    method public boolean canOverlayApps();
     method public boolean canShowBadge();
     method public int describeContents();
     method public void enableLights(boolean);
@@ -5712,7 +5731,7 @@
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
     method public boolean hasUserSetImportance();
-    method public void setAllowAppOverlay(boolean);
+    method public void setAllowBubbles(boolean);
     method public void setBypassDnd(boolean);
     method public void setDescription(java.lang.String);
     method public void setGroup(java.lang.String);
@@ -5746,7 +5765,7 @@
 
   public class NotificationManager {
     method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
-    method public boolean areAppOverlaysAllowed();
+    method public boolean areBubblesAllowed();
     method public boolean areNotificationsEnabled();
     method public boolean canNotifyAsPackage(java.lang.String);
     method public void cancel(int);
@@ -25457,6 +25476,7 @@
     method public java.lang.Object setAuxEffectSendLevel(float);
     method public java.lang.Object setDataSource(android.media.DataSourceDesc);
     method public java.lang.Object setDisplay(android.view.SurfaceHolder);
+    method public void setDrmEventCallback(java.util.concurrent.Executor, android.media.MediaPlayer2.DrmEventCallback);
     method public java.lang.Object setNextDataSource(android.media.DataSourceDesc);
     method public java.lang.Object setNextDataSources(java.util.List<android.media.DataSourceDesc>);
     method public java.lang.Object setPlaybackParams(android.media.PlaybackParams);
@@ -25535,6 +25555,33 @@
     field public static final int SEEK_PREVIOUS_SYNC = 0; // 0x0
   }
 
+  public static class MediaPlayer2.DrmEventCallback {
+    ctor public MediaPlayer2.DrmEventCallback();
+    method public void onDrmConfig(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaDrm);
+    method public android.media.MediaPlayer2.DrmPreparationInfo onDrmInfo(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaPlayer2.DrmInfo);
+    method public byte[] onDrmKeyRequest(android.media.MediaPlayer2, android.media.DataSourceDesc, android.media.MediaDrm.KeyRequest);
+    method public void onDrmPrepared(android.media.MediaPlayer2, android.media.DataSourceDesc, int, byte[]);
+  }
+
+  public static final class MediaPlayer2.DrmInfo {
+    method public java.util.Map<java.util.UUID, byte[]> getPssh();
+    method public java.util.List<java.util.UUID> getSupportedSchemes();
+  }
+
+  public static final class MediaPlayer2.DrmPreparationInfo {
+  }
+
+  public static final class MediaPlayer2.DrmPreparationInfo.Builder {
+    ctor public MediaPlayer2.DrmPreparationInfo.Builder();
+    method public android.media.MediaPlayer2.DrmPreparationInfo build();
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setInitData(byte[]);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setKeySetId(byte[]);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setKeyType(int);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setMimeType(java.lang.String);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setOptionalParameters(java.util.Map<java.lang.String, java.lang.String>);
+    method public android.media.MediaPlayer2.DrmPreparationInfo.Builder setUuid(java.util.UUID);
+  }
+
   public static class MediaPlayer2.EventCallback {
     ctor public MediaPlayer2.EventCallback();
     method public void onCallCompleted(android.media.MediaPlayer2, android.media.DataSourceDesc, int, int);
@@ -25562,6 +25609,10 @@
     field public static final java.lang.String WIDTH = "android.media.mediaplayer.width";
   }
 
+  public static final class MediaPlayer2.NoDrmSchemeException extends android.media.MediaDrmException {
+    ctor public MediaPlayer2.NoDrmSchemeException(java.lang.String);
+  }
+
   public static class MediaPlayer2.TrackInfo {
     method public android.media.MediaFormat getFormat();
     method public java.lang.String getLanguage();
diff --git a/api/system-current.txt b/api/system-current.txt
index bac7cda..276be9d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4863,6 +4863,8 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean);
     method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
+    method public abstract void onGetRuntimePermissionsBackup(android.os.UserHandle, java.io.OutputStream);
+    method public abstract java.util.List<android.permission.RuntimePermissionUsageInfo> onPermissionUsageResult(boolean, long);
     method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
     method public abstract java.util.Map<java.lang.String, java.util.List<java.lang.String>> onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.lang.String);
     field public static final java.lang.String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
@@ -4891,11 +4893,12 @@
   public final class RuntimePermissionUsageInfo implements android.os.Parcelable {
     ctor public RuntimePermissionUsageInfo(java.lang.CharSequence, int);
     method public int describeContents();
-    method public java.lang.CharSequence getName();
     method public int getAppAccessCount();
+    method public java.lang.CharSequence getName();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionUsageInfo> CREATOR;
   }
+
 }
 
 package android.permissionpresenterservice {
@@ -7251,7 +7254,6 @@
     method public void callSessionInviteParticipantsRequestDelivered();
     method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionMayHandover(int, int);
-    method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile);
     method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase);
     method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
@@ -7262,6 +7264,7 @@
     method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile);
     method public void callSessionResumed(android.telephony.ims.ImsCallProfile);
+    method public void callSessionRttAudioIndicatorChanged(android.telephony.ims.ImsStreamMediaProfile);
     method public void callSessionRttMessageReceived(java.lang.String);
     method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
     method public void callSessionRttModifyResponseReceived(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index fb50fa19..71f02b9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -945,6 +945,11 @@
     field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
   }
 
+  public final class MessageQueue {
+    method public int postSyncBarrier();
+    method public void removeSyncBarrier(int);
+  }
+
   public final class NativeHandle implements java.io.Closeable {
     ctor public NativeHandle();
     ctor public NativeHandle(java.io.FileDescriptor, boolean);
@@ -957,11 +962,6 @@
     method public boolean hasSingleFileDescriptor();
   }
 
-  public final class MessageQueue {
-    method public int postSyncBarrier();
-    method public void removeSyncBarrier(int);
-  }
-
   public final class PowerManager {
     method public int getPowerSaveMode();
     method public boolean setDynamicPowerSavings(boolean, int);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 0fa7cff..f4a1715 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -182,6 +182,7 @@
         DataStallEvent data_stall_event = 121;
         RescuePartyResetReported rescue_party_reset_reported = 122;
         SignedConfigReported signed_config_reported = 123;
+        GnssNiEventReported gnss_ni_event_reported = 124;
     }
 
     // Pulled events will start at field 10000.
@@ -3902,3 +3903,63 @@
     // Which key was used to verify the config.
     optional Key verified_with = 5;
 }
+
+/*
+ * Logs GNSS Network-Initiated (NI) location events.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/location/GnssLocationProvider.java
+ */
+message GnssNiEventReported {
+    // The type of GnssNiEvent.
+    enum EventType {
+        UNKNOWN = 0;
+        NI_REQUEST = 1;
+        NI_RESPONSE = 2;
+    }
+    optional EventType event_type = 1;
+
+    // An ID generated by HAL to associate NI notifications and UI responses.
+    optional int32 notification_id = 2;
+
+    // A type which distinguishes different categories of NI request, such as VOICE, UMTS_SUPL etc.
+    optional int32 ni_type = 3;
+
+    // NI requires notification.
+    optional bool need_notify = 4;
+
+    // NI requires verification.
+    optional bool need_verify = 5;
+
+    // NI requires privacy override, no notification/minimal trace.
+    optional bool privacy_override = 6;
+
+    // Timeout period to wait for user response. Set to 0 for no timeout limit. Specified in
+    // seconds.
+    optional int32 timeout = 7;
+
+    // Default response when timeout.
+    optional int32 default_response = 8;
+
+    // String representing the requester of the network inititated location request.
+    optional string requestor_id = 9;
+
+    // Notification message text string representing the service(for eg. SUPL-service) who sent the
+    // network initiated location request.
+    optional string text = 10;
+
+    // requestorId decoding scheme.
+    optional int32 requestor_id_encoding = 11;
+
+    // Notification message text decoding scheme.
+    optional int32 text_encoding = 12;
+
+    // True if SUPL ES is enabled.
+    optional bool is_supl_es_enabled = 13;
+
+    // True if GNSS location is enabled.
+    optional bool is_location_enabled = 14;
+
+    // GNSS NI responses which define the response in NI structures.
+    optional int32 user_response = 15;
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 98c5a0fb..d374f1c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.os.Process.myUid;
 
 import static java.lang.Character.MIN_VALUE;
 
@@ -1025,7 +1026,7 @@
      */
     @Nullable private ContentCaptureManager getContentCaptureManager() {
         // ContextCapture disabled for system apps
-        if (getApplicationInfo().isSystemApp()) return null;
+        if (!UserHandle.isApp(myUid())) return null;
         if (mContentCaptureManager == null) {
             mContentCaptureManager = getSystemService(ContentCaptureManager.class);
         }
@@ -1048,9 +1049,8 @@
 
     private void notifyContentCaptureManagerIfNeeded(@ContentCaptureNotificationType int type) {
         final ContentCaptureManager cm = getContentCaptureManager();
-        if (cm == null) {
-            return;
-        }
+        if (cm == null) return;
+
         switch (type) {
             case CONTENT_CAPTURE_START:
                 //TODO(b/111276913): decide whether the InteractionSessionId should be
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 57132a7..ab8f234 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -16,6 +16,10 @@
 
 package android.app;
 
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+
 import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager.StackInfo;
@@ -32,7 +36,6 @@
 import android.view.IWindowManager;
 import android.view.InputDevice;
 import android.view.MotionEvent;
-import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
 import android.view.SurfaceSession;
@@ -82,7 +85,6 @@
     private boolean mOpened; // Protected by mGuard.
 
     private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
-    private Surface mTmpSurface = new Surface();
 
     /** The ActivityView is only allowed to contain one task. */
     private final boolean mSingleTaskInstance;
@@ -319,20 +321,20 @@
     private class SurfaceCallback implements SurfaceHolder.Callback {
         @Override
         public void surfaceCreated(SurfaceHolder surfaceHolder) {
-            mTmpSurface = new Surface();
             if (mVirtualDisplay == null) {
                 initVirtualDisplay(new SurfaceSession());
                 if (mVirtualDisplay != null && mActivityViewCallback != null) {
                     mActivityViewCallback.onActivityViewReady(ActivityView.this);
                 }
             } else {
-                // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
-                // whether it has a surface. Setting a fake surface here so DisplayManager will
-                // consider this display on.
-                mVirtualDisplay.setSurface(mTmpSurface);
                 mTmpTransaction.reparent(mRootSurfaceControl,
                         mSurfaceView.getSurfaceControl().getHandle()).apply();
             }
+
+            if (mVirtualDisplay != null) {
+                mVirtualDisplay.setDisplayState(true);
+            }
+
             updateLocation();
         }
 
@@ -346,10 +348,8 @@
 
         @Override
         public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
-            mTmpSurface.release();
-            mTmpSurface = null;
             if (mVirtualDisplay != null) {
-                mVirtualDisplay.setSurface(null);
+                mVirtualDisplay.setDisplayState(false);
             }
             cleanTapExcludeRegion();
         }
@@ -370,15 +370,11 @@
         final int height = mSurfaceView.getHeight();
         final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
 
-        // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
-        // whether it has a surface. Setting a fake surface here so DisplayManager will consider
-        // this display on.
         mVirtualDisplay = displayManager.createVirtualDisplay(
-                DISPLAY_NAME + "@" + System.identityHashCode(this),
-                width, height, getBaseDisplayDensity(), mTmpSurface,
-                DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
-                        | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
-                        | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL);
+                DISPLAY_NAME + "@" + System.identityHashCode(this), width, height,
+                getBaseDisplayDensity(), null,
+                VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+                        | VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL);
         if (mVirtualDisplay == null) {
             Log.e(TAG, "Failed to initialize ActivityView");
             return;
@@ -443,11 +439,6 @@
             displayReleased = false;
         }
 
-        if (mTmpSurface != null) {
-            mTmpSurface.release();
-            mTmpSurface = null;
-        }
-
         if (displayReleased && mActivityViewCallback != null) {
             mActivityViewCallback.onActivityViewDestroyed(this);
         }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 163be8e..199c133 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -65,9 +65,9 @@
     boolean areNotificationsEnabled(String pkg);
     int getPackageImportance(String pkg);
 
-    void setAppOverlaysAllowed(String pkg, int uid, boolean allowed);
-    boolean areAppOverlaysAllowed(String pkg);
-    boolean areAppOverlaysAllowedForPackage(String pkg, int uid);
+    void setBubblesAllowed(String pkg, int uid, boolean allowed);
+    boolean areBubblesAllowed(String pkg);
+    boolean areBubblesAllowedForPackage(String pkg, int uid);
 
     void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
     void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b657a91..72819cb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1276,7 +1276,7 @@
     private String mShortcutId;
     private CharSequence mSettingsText;
 
-    private PendingIntent mAppOverlayIntent;
+    private BubbleMetadata mBubbleMetadata;
 
     /** @hide */
     @IntDef(prefix = { "GROUP_ALERT_" }, value = {
@@ -2278,7 +2278,7 @@
 
         mGroupAlertBehavior = parcel.readInt();
         if (parcel.readInt() != 0) {
-            mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel);
+            mBubbleMetadata = BubbleMetadata.CREATOR.createFromParcel(parcel);
         }
 
         mAllowSystemGeneratedContextualActions = parcel.readBoolean();
@@ -2396,7 +2396,7 @@
         that.mBadgeIcon = this.mBadgeIcon;
         that.mSettingsText = this.mSettingsText;
         that.mGroupAlertBehavior = this.mGroupAlertBehavior;
-        that.mAppOverlayIntent = this.mAppOverlayIntent;
+        that.mBubbleMetadata = this.mBubbleMetadata;
         that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions;
 
         if (!heavy) {
@@ -2719,9 +2719,9 @@
 
         parcel.writeInt(mGroupAlertBehavior);
 
-        if (mAppOverlayIntent != null) {
+        if (mBubbleMetadata != null) {
             parcel.writeInt(1);
-            mAppOverlayIntent.writeToParcel(parcel, 0);
+            mBubbleMetadata.writeToParcel(parcel, 0);
         } else {
             parcel.writeInt(0);
         }
@@ -3141,11 +3141,11 @@
     }
 
     /**
-     * Returns the intent that will be used to display app content in a floating window over the
-     * existing foreground activity.
+     * Returns the bubble metadata that will be used to display app content in a floating window
+     * over the existing foreground activity.
      */
-    public PendingIntent getAppOverlayIntent() {
-        return mAppOverlayIntent;
+    public BubbleMetadata getBubbleMetadata() {
+        return mBubbleMetadata;
     }
 
     /**
@@ -3508,19 +3508,18 @@
         }
 
         /**
-         * Sets the intent that will be used to display app content in a floating window
-         * over the existing foreground activity.
+         * Sets the {@link BubbleMetadata} that will be used to display app content in a floating
+         * window over the existing foreground activity.
          *
-         * <p>This intent will be ignored unless this notification is posted to a channel that
-         * allows {@link NotificationChannel#canOverlayApps() app overlays}.</p>
+         * <p>This data will be ignored unless the notification is posted to a channel that
+         * allows {@link NotificationChannel#canBubble() bubbles}.</p>
          *
-         * <p>Notifications with a valid and allowed app overlay intent will be displayed as
-         * floating windows outside of the notification shade on unlocked devices. When a user
-         * interacts with one of these windows, this app overlay intent will be invoked and
-         * displayed.</p>
+         * <b>Notifications with a valid and allowed bubble metadata will display in collapsed state
+         * outside of the notification shade on unlocked devices. When a user interacts with the
+         * collapsed state, the bubble intent will be invoked and displayed.</b>
          */
-        public Builder setAppOverlayIntent(PendingIntent intent) {
-            mN.mAppOverlayIntent = intent;
+        public Builder setBubbleMetadata(BubbleMetadata data) {
+            mN.mBubbleMetadata = data;
             return this;
         }
 
@@ -8422,6 +8421,186 @@
         }
     }
 
+    /**
+     * Encapsulates the information needed to display a notification as a bubble.
+     *
+     * <p>A bubble is used to display app content in a floating window over the existing
+     * foreground activity. A bubble has a collapsed state represented by an icon,
+     * {@link BubbleMetadata.Builder#setIcon(Icon)} and an expanded state which is populated
+     * via {@link BubbleMetadata.Builder#setIntent(PendingIntent)}.</p>
+     *
+     * <b>Notifications with a valid and allowed bubble will display in collapsed state
+     * outside of the notification shade on unlocked devices. When a user interacts with the
+     * collapsed bubble, the bubble intent will be invoked and displayed.</b>
+     *
+     * @see Notification.Builder#setBubbleMetadata(BubbleMetadata)
+     */
+    public static final class BubbleMetadata implements Parcelable {
+
+        private PendingIntent mPendingIntent;
+        private CharSequence mTitle;
+        private Icon mIcon;
+        private int mDesiredHeight;
+
+        private BubbleMetadata(PendingIntent intent, CharSequence title, Icon icon, int height) {
+            mPendingIntent = intent;
+            mTitle = title;
+            mIcon = icon;
+            mDesiredHeight = height;
+        }
+
+        private BubbleMetadata(Parcel in) {
+            mPendingIntent = PendingIntent.CREATOR.createFromParcel(in);
+            mTitle = in.readCharSequence();
+            mIcon = Icon.CREATOR.createFromParcel(in);
+            mDesiredHeight = in.readInt();
+        }
+
+        /**
+         * @return the pending intent used to populate the floating window for this bubble.
+         */
+        public PendingIntent getIntent() {
+            return mPendingIntent;
+        }
+
+        /**
+         * @return the title that will appear along with the app content defined by
+         * {@link #getIntent()} for this bubble.
+         */
+        public CharSequence getTitle() {
+            return mTitle;
+        }
+
+        /**
+         * @return the icon that will be displayed for this bubble when it is collapsed.
+         */
+        public Icon getIcon() {
+            return mIcon;
+        }
+
+        /**
+         * @return the ideal height for the floating window that app content defined by
+         * {@link #getIntent()} for this bubble.
+         */
+        public int getDesiredHeight() {
+            return mDesiredHeight;
+        }
+
+        public static final Parcelable.Creator<BubbleMetadata> CREATOR =
+                new Parcelable.Creator<BubbleMetadata>() {
+
+                    @Override
+                    public BubbleMetadata createFromParcel(Parcel source) {
+                        return new BubbleMetadata(source);
+                    }
+
+                    @Override
+                    public BubbleMetadata[] newArray(int size) {
+                        return new BubbleMetadata[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            mPendingIntent.writeToParcel(out, 0);
+            out.writeCharSequence(mTitle);
+            mIcon.writeToParcel(out, 0);
+            out.writeInt(mDesiredHeight);
+        }
+
+        /**
+         * Builder to construct a {@link BubbleMetadata} object.
+         */
+        public static class Builder {
+
+            private PendingIntent mPendingIntent;
+            private CharSequence mTitle;
+            private Icon mIcon;
+            private int mDesiredHeight;
+
+            /**
+             * Constructs a new builder object.
+             */
+            public Builder() {
+            }
+
+            /**
+             * Sets the intent that will be used when the bubble is expanded. This will display the
+             * app content in a floating window over the existing foreground activity.
+             */
+            public BubbleMetadata.Builder setIntent(PendingIntent intent) {
+                if (intent == null) {
+                    throw new IllegalArgumentException("Bubble requires non-null pending intent");
+                }
+                mPendingIntent = intent;
+                return this;
+            }
+
+            /**
+             * Sets the title that will appear along with the app content for this bubble.
+             *
+             * <p>A title is required and should expect to fit on a single line and make sense when
+             * shown with the content defined by {@link #setIntent(PendingIntent)}.</p>
+             */
+            public BubbleMetadata.Builder setTitle(CharSequence title) {
+                if (TextUtils.isEmpty(title)) {
+                    throw new IllegalArgumentException("Bubbles require non-null or empty title");
+                }
+                mTitle = title;
+                return this;
+            }
+
+            /**
+             * Sets the icon that will represent the bubble when it is collapsed.
+             *
+             * <p>An icon is required and should be representative of the content within the bubble.
+             * If your app produces multiple bubbles, the image should be unique for each of them.
+             * </p>
+             */
+            public BubbleMetadata.Builder setIcon(Icon icon) {
+                if (icon == null) {
+                    throw new IllegalArgumentException("Bubbles require non-null icon");
+                }
+                mIcon = icon;
+                return this;
+            }
+
+            /**
+             * Sets the desired height for the app content defined by
+             * {@link #setIntent(PendingIntent)}, this height may not be respected if there is not
+             * enough space on the screen or if the provided height is too small to be useful.
+             */
+            public BubbleMetadata.Builder setDesiredHeight(int height) {
+                mDesiredHeight = Math.max(height, 0);
+                return this;
+            }
+
+            /**
+             * Creates the {@link BubbleMetadata} defined by this builder.
+             * <p>Will throw {@link IllegalStateException} if required fields have not been set
+             * on this builder.</p>
+             */
+            public BubbleMetadata build() {
+                if (mPendingIntent == null) {
+                    throw new IllegalStateException("Must supply pending intent to bubble");
+                }
+                if (TextUtils.isEmpty(mTitle)) {
+                    throw new IllegalStateException("Must supply a title for the bubble");
+                }
+                if (mIcon == null) {
+                    throw new IllegalStateException("Must supply an icon for the bubble");
+                }
+                return new BubbleMetadata(mPendingIntent, mTitle, mIcon, mDesiredHeight);
+            }
+        }
+    }
+
+
     // When adding a new Style subclass here, don't forget to update
     // Builder.getNotificationStyleClass.
 
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 950e9aa..e95d62f 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -85,7 +85,7 @@
     private static final String ATT_FG_SERVICE_SHOWN = "fgservice";
     private static final String ATT_GROUP = "group";
     private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
-    private static final String ATT_ALLOW_APP_OVERLAY = "app_overlay";
+    private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
     private static final String DELIMITER = ",";
 
     /**
@@ -121,7 +121,7 @@
     /**
      * @hide
      */
-    public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000100;
+    public static final int USER_LOCKED_ALLOW_BUBBLE = 0x00000100;
 
     /**
      * @hide
@@ -134,7 +134,7 @@
             USER_LOCKED_VIBRATION,
             USER_LOCKED_SOUND,
             USER_LOCKED_SHOW_BADGE,
-            USER_LOCKED_ALLOW_APP_OVERLAY
+            USER_LOCKED_ALLOW_BUBBLE
     };
 
     private static final int DEFAULT_LIGHT_COLOR = 0;
@@ -144,7 +144,7 @@
             NotificationManager.IMPORTANCE_UNSPECIFIED;
     private static final boolean DEFAULT_DELETED = false;
     private static final boolean DEFAULT_SHOW_BADGE = true;
-    private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
+    private static final boolean DEFAULT_ALLOW_BUBBLE = true;
 
     @UnsupportedAppUsage
     private final String mId;
@@ -168,7 +168,7 @@
     private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
     // If this is a blockable system notification channel.
     private boolean mBlockableSystem = false;
-    private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY;
+    private boolean mAllowBubbles = DEFAULT_ALLOW_BUBBLE;
     private boolean mImportanceLockedByOEM;
 
     /**
@@ -231,7 +231,7 @@
         mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null;
         mLightColor = in.readInt();
         mBlockableSystem = in.readBoolean();
-        mAllowAppOverlay = in.readBoolean();
+        mAllowBubbles = in.readBoolean();
         mImportanceLockedByOEM = in.readBoolean();
     }
 
@@ -285,7 +285,7 @@
         }
         dest.writeInt(mLightColor);
         dest.writeBoolean(mBlockableSystem);
-        dest.writeBoolean(mAllowAppOverlay);
+        dest.writeBoolean(mAllowBubbles);
         dest.writeBoolean(mImportanceLockedByOEM);
     }
 
@@ -480,7 +480,7 @@
 
     /**
      * Sets whether notifications posted to this channel can appear outside of the notification
-     * shade, floating over other apps' content.
+     * shade, floating over other apps' content as a bubble.
      *
      * <p>This value will be ignored for channels that aren't allowed to pop on screen (that is,
      * channels whose {@link #getImportance() importance} is <
@@ -488,10 +488,10 @@
      *
      * <p>Only modifiable before the channel is submitted to
      *      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.</p>
-     * @see Notification#getAppOverlayIntent()
+     * @see Notification#getBubbleMetadata()
      */
-    public void setAllowAppOverlay(boolean allowAppOverlay) {
-        mAllowAppOverlay = allowAppOverlay;
+    public void setAllowBubbles(boolean allowBubbles) {
+        mAllowBubbles = allowBubbles;
     }
 
     /**
@@ -610,16 +610,16 @@
      * Returns whether notifications posted to this channel can display outside of the notification
      * shade, in a floating window on top of other apps.
      */
-    public boolean canOverlayApps() {
-        return isAppOverlayAllowed() && getImportance() >= IMPORTANCE_HIGH;
+    public boolean canBubble() {
+        return isBubbleAllowed() && getImportance() >= IMPORTANCE_HIGH;
     }
 
     /**
-     * Like {@link #canOverlayApps()}, but only checks the permission, not the importance.
+     * Like {@link #canBubble()}, but only checks the permission, not the importance.
      * @hide
      */
-    public boolean isAppOverlayAllowed() {
-        return mAllowAppOverlay;
+    public boolean isBubbleAllowed() {
+        return mAllowBubbles;
     }
 
     /**
@@ -719,7 +719,7 @@
         lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
         setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false));
         setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
-        setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
+        setAllowBubbles(safeBool(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
     }
 
     @Nullable
@@ -838,8 +838,8 @@
         if (isBlockableSystem()) {
             out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockableSystem()));
         }
-        if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) {
-            out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps()));
+        if (canBubble() != DEFAULT_ALLOW_BUBBLE) {
+            out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(canBubble()));
         }
 
         out.endTag(null, TAG_CHANNEL);
@@ -883,7 +883,7 @@
         record.put(ATT_DELETED, Boolean.toString(isDeleted()));
         record.put(ATT_GROUP, getGroup());
         record.put(ATT_BLOCKABLE_SYSTEM, isBlockableSystem());
-        record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps());
+        record.put(ATT_ALLOW_BUBBLE, canBubble());
         return record;
     }
 
@@ -983,7 +983,7 @@
                 && mShowBadge == that.mShowBadge
                 && isDeleted() == that.isDeleted()
                 && isBlockableSystem() == that.isBlockableSystem()
-                && mAllowAppOverlay == that.mAllowAppOverlay
+                && mAllowBubbles == that.mAllowBubbles
                 && Objects.equals(getId(), that.getId())
                 && Objects.equals(getName(), that.getName())
                 && Objects.equals(mDesc, that.mDesc)
@@ -1000,7 +1000,7 @@
                 getLockscreenVisibility(), getSound(), mLights, getLightColor(),
                 getUserLockedFields(),
                 isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
-                getAudioAttributes(), isBlockableSystem(), mAllowAppOverlay,
+                getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
                 mImportanceLockedByOEM);
         result = 31 * result + Arrays.hashCode(mVibration);
         return result;
@@ -1028,7 +1028,7 @@
                 + ", mGroup='" + mGroup + '\''
                 + ", mAudioAttributes=" + mAudioAttributes
                 + ", mBlockableSystem=" + mBlockableSystem
-                + ", mAllowAppOverlay=" + mAllowAppOverlay
+                + ", mAllowBubbles=" + mAllowBubbles
                 + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
                 + '}';
         pw.println(prefix + output);
@@ -1055,7 +1055,7 @@
                 + ", mGroup='" + mGroup + '\''
                 + ", mAudioAttributes=" + mAudioAttributes
                 + ", mBlockableSystem=" + mBlockableSystem
-                + ", mAllowAppOverlay=" + mAllowAppOverlay
+                + ", mAllowBubbles=" + mAllowBubbles
                 + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
                 + '}';
     }
@@ -1090,7 +1090,7 @@
             mAudioAttributes.writeToProto(proto, NotificationChannelProto.AUDIO_ATTRIBUTES);
         }
         proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem);
-        proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowAppOverlay);
+        proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowBubbles);
 
         proto.end(token);
     }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index aad3253..43614fe 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1080,14 +1080,14 @@
      * notification shade, floating over other apps' content.
      *
      * <p>This value will be ignored for notifications that are posted to channels that do not
-     * allow app overlays ({@link NotificationChannel#canOverlayApps()}.
+     * allow bubbles ({@link NotificationChannel#canBubble()}.
      *
-     * @see Notification#getAppOverlayIntent()
+     * @see Notification#getBubbleMetadata()
      */
-    public boolean areAppOverlaysAllowed() {
+    public boolean areBubblesAllowed() {
         INotificationManager service = getService();
         try {
-            return service.areAppOverlaysAllowed(mContext.getPackageName());
+            return service.areBubblesAllowed(mContext.getPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 636a70f..b845673 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5555,26 +5555,24 @@
     }
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract void addPackageToPreferred(String packageName);
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract void removePackageFromPreferred(String packageName);
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
-     *
      * Retrieve the list of all currently configured preferred packages. The
      * first package on the list is the most preferred, the last is the least
      * preferred.
@@ -5582,15 +5580,16 @@
      * @param flags Additional option flags to modify the data returned.
      * @return A List of PackageInfo objects, one for each preferred
      *         application, in order of preference.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags);
 
     /**
-     * @deprecated This is a protected API that should not have been available
-     * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this cannot be directly modified.
-     *
      * Add a new preferred activity mapping to the system.  This will be used
      * to automatically select the given activity component when
      * {@link Context#startActivity(Intent) Context.startActivity()} finds
@@ -5604,20 +5603,26 @@
      * this preference was made.
      * @param activity The component name of the activity that is to be
      * preferred.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract void addPreferredActivity(IntentFilter filter, int match,
             ComponentName[] set, ComponentName activity);
 
     /**
-     * @deprecated This is a protected API that should not have been available
-     * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this cannot be directly modified.
-     *
      * Same as {@link #addPreferredActivity(IntentFilter, int,
             ComponentName[], ComponentName)}, but with a specific userId to apply the preference
             to.
      * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     @UnsupportedAppUsage
@@ -5627,10 +5632,6 @@
     }
 
     /**
-     * @deprecated This is a protected API that should not have been available
-     * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this cannot be directly modified.
-     *
      * Replaces an existing preferred activity mapping to the system, and if that were not present
      * adds a new preferred activity.  This will be used
      * to automatically select the given activity component when
@@ -5645,7 +5646,13 @@
      * this preference was made.
      * @param activity The component name of the activity that is to be
      * preferred.
+     *
      * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     @UnsupportedAppUsage
@@ -5653,10 +5660,6 @@
             ComponentName[] set, ComponentName activity);
 
     /**
-     * @deprecated This is a protected API that should not have been available
-     * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this cannot be directly modified.
-     *
      * Replaces an existing preferred activity mapping to the system, and if that were not present
      * adds a new preferred activity.  This will be used to automatically select the given activity
      * component when {@link Context#startActivity(Intent) Context.startActivity()} finds multiple
@@ -5671,6 +5674,11 @@
      * @param activity The component name of the activity that is to be preferred.
      *
      * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     @SystemApi
@@ -5681,6 +5689,11 @@
 
     /**
      * @hide
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     @UnsupportedAppUsage
@@ -5690,10 +5703,6 @@
     }
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
-     *
      * Remove all preferred activity mappings, previously added with
      * {@link #addPreferredActivity}, from the
      * system whose activities are implemented in the given package name.
@@ -5701,15 +5710,16 @@
      *
      * @param packageName The name of the package whose preferred activity
      * mappings are to be removed.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract void clearPackagePreferredActivities(String packageName);
 
     /**
-     * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superseded
-     * by (and conflicts with) the modern activity-based preferences.
-     *
      * Retrieve all preferred activities, previously added with
      * {@link #addPreferredActivity}, that are
      * currently registered with the system.
@@ -5725,6 +5735,11 @@
      * @return Returns the total number of registered preferred activities
      * (the number of distinct IntentFilter records, not the number of unique
      * activity components) that were found.
+     *
+     * @deprecated This function no longer does anything. It is the platform's
+     * responsibility to assign preferred activities and this cannot be modified
+     * directly. To determine the activities resolved by the platform, use
+     * {@link #resolveActivity} or {@link #queryIntentActivities}.
      */
     @Deprecated
     public abstract int getPreferredActivities(@NonNull List<IntentFilter> outFilters,
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0fc50c6..1ac0ab6 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2656,6 +2656,23 @@
     }
 
     /**
+     * Matches a given {@code targetCode} against a set of release codeNames. Target codes can
+     * either be of the form {@code [codename]}" (e.g {@code "Q"}) or of the form
+     * {@code [codename].[fingerprint]} (e.g {@code "Q.cafebc561"}).
+     */
+    private static boolean matchTargetCode(@NonNull String[] codeNames,
+            @NonNull String targetCode) {
+        final String targetCodeName;
+        final int targetCodeIdx = targetCode.indexOf('.');
+        if (targetCodeIdx == -1) {
+            targetCodeName = targetCode;
+        } else {
+            targetCodeName = targetCode.substring(0, targetCodeIdx);
+        }
+        return ArrayUtils.contains(codeNames, targetCodeName);
+    }
+
+    /**
      * Computes the targetSdkVersion to use at runtime. If the package is not
      * compatible with this platform, populates {@code outError[0]} with an
      * error message.
@@ -2698,7 +2715,7 @@
 
         // If it's a pre-release SDK and the codename matches this platform, it
         // definitely targets this SDK.
-        if (ArrayUtils.contains(platformSdkCodenames, targetCode) || forceCurrentDev) {
+        if (matchTargetCode(platformSdkCodenames, targetCode) || forceCurrentDev) {
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
@@ -2831,7 +2848,7 @@
 
         // If it's a pre-release SDK and the codename matches this platform, we
         // definitely meet the minimum SDK requirement.
-        if (ArrayUtils.contains(platformSdkCodenames, minCode)) {
+        if (matchTargetCode(platformSdkCodenames, minCode)) {
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index cda8498..7e45441 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -436,6 +436,7 @@
     public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
         try {
             mDm.setVirtualDisplaySurface(token, surface);
+            setVirtualDisplayState(token, surface != null);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
@@ -458,6 +459,14 @@
         }
     }
 
+    void setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn) {
+        try {
+            mDm.setVirtualDisplayState(token, isOn);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Gets the stable device display size, in pixels.
      */
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 2d81cdf..aae8afb 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -82,6 +82,9 @@
     // No permissions required but must be same Uid as the creator.
     void releaseVirtualDisplay(in IVirtualDisplayCallback token);
 
+    // No permissions required but must be same Uid as the creator.
+    void setVirtualDisplayState(in IVirtualDisplayCallback token, boolean isOn);
+
     // Get a stable metric for the device's display size. No permissions required.
     Point getStableDisplaySize();
 
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index d354666..bf62c95 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -104,6 +104,18 @@
         }
     }
 
+    /**
+     * Sets the on/off state for a virtual display.
+     *
+     * @param isOn Whether the display should be on or off.
+     * @hide
+     */
+    public void setDisplayState(boolean isOn) {
+        if (mToken != null) {
+            mGlobal.setVirtualDisplayState(mToken, isOn);
+        }
+    }
+
     @Override
     public String toString() {
         return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken
diff --git a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
index d382eb9..bdd5ab6 100644
--- a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
+++ b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
@@ -15,10 +15,12 @@
  */
 package android.hardware.hdmi;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -56,6 +58,21 @@
         mHandler = handler == null ? new Handler(Looper.getMainLooper()) : handler;
     }
 
+    /**
+     * Callback interface used to get the set System Audio Mode result.
+     *
+     * @hide
+     */
+    // TODO(b/110094868): unhide and add @SystemApi for Q
+    public interface SetSystemAudioModeCallback {
+        /**
+         * Called when the input was changed.
+         *
+         * @param result the result of the set System Audio Mode
+         */
+        void onComplete(int result);
+    }
+
     /** @hide */
     // TODO(b/110094868): unhide and add @SystemApi for Q
     @Override
@@ -117,4 +134,34 @@
             mPendingReportAudioStatus = true;
         }
     }
+
+    /**
+     * Set System Audio Mode on/off with audio system device.
+     *
+     * @param state true to set System Audio Mode on. False to set off.
+     * @param callback callback offer the setting result.
+     *
+     * @hide
+     */
+    // TODO(b/110094868): unhide and add @SystemApi for Q
+    public void setSystemAudioMode(boolean state, @NonNull SetSystemAudioModeCallback callback) {
+        // TODO(amyjojo): implement this when needed.
+    }
+
+    /**
+     * When device is switching to an audio only source, this method is called to broadcast
+     * a setSystemAudioMode on message to the HDMI CEC system without querying Active Source or
+     * TV supporting System Audio Control or not. This is to get volume control passthrough
+     * from STB even if TV does not support it.
+     *
+     * @hide
+     */
+    // TODO(b/110094868): unhide and add @SystemApi for Q
+    public void setSystemAudioModeOnForAudioOnlySource() {
+        try {
+            mService.setSystemAudioModeOnForAudioOnlySource();
+        } catch (RemoteException e) {
+            Log.d(TAG, "Failed to set System Audio Mode on for Audio Only source");
+        }
+    }
 }
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index f5d288e..be8009e 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -432,6 +432,19 @@
     }
 
     /**
+     * Get the physical address of the device.
+     *
+     * @hide
+     */
+    public int getPhysicalAddress() {
+        try {
+            return mService.getPhysicalAddress();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Listener used to get hotplug event from HDMI port.
      */
     public interface HotplugEventListener {
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 2b8d00b..66bb084 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -50,6 +50,7 @@
     List<HdmiPortInfo> getPortInfo();
     boolean canChangeSystemAudioMode();
     boolean getSystemAudioMode();
+    int getPhysicalAddress();
     void setSystemAudioMode(boolean enabled, IHdmiControlCallback callback);
     void addSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
     void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
@@ -73,4 +74,5 @@
     void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener);
     void setStandbyMode(boolean isStandbyModeOn);
     void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
+    void setSystemAudioModeOnForAudioOnlySource();
 }
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 7a7bd83..249b622 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -18,6 +18,8 @@
 
 import android.os.RemoteCallback;
 import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
 
 /**
  * Interface for system apps to communication with the permission controller.
@@ -27,6 +29,7 @@
 oneway interface IPermissionController {
     void revokeRuntimePermissions(in Bundle request, boolean doDryRun, int reason,
             String callerPackageName, in RemoteCallback callback);
+    void getRuntimePermissionBackup(in UserHandle user, in ParcelFileDescriptor pipe);
     void getAppPermissions(String packageName, in RemoteCallback callback);
     void revokeRuntimePermission(String packageName, String permissionName);
     void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted,
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 0865b62..bfcca7c 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -36,10 +36,12 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -51,6 +53,11 @@
 import com.android.internal.infra.AbstractRemoteService;
 import com.android.internal.util.Preconditions;
 
+import libcore.io.IoUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -58,6 +65,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Interface for communicating with the permission controller.
@@ -118,6 +126,20 @@
     }
 
     /**
+     * Callback for delivering the result of {@link #getRuntimePermissionBackup}.
+     *
+     * @hide
+     */
+    public interface OnGetRuntimePermissionBackupCallback {
+        /**
+         * The result for {@link #getRuntimePermissionBackup}.
+         *
+         * @param backup The backup file
+         */
+        void onGetRuntimePermissionsBackup(@NonNull byte[] backup);
+    }
+
+    /**
      * Callback for delivering the result of {@link #getAppPermissions}.
      *
      * @hide
@@ -219,6 +241,26 @@
     }
 
     /**
+     * Create a backup of the runtime permissions.
+     *
+     * @param user The user to be backed up
+     * @param executor Executor on which to invoke the callback
+     * @param callback Callback to receive the result
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
+    public void getRuntimePermissionBackup(@NonNull UserHandle user,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnGetRuntimePermissionBackupCallback callback) {
+        checkNotNull(executor);
+        checkNotNull(callback);
+
+        sRemoteService.scheduleRequest(new PendingGetRuntimePermissionBackup(sRemoteService,
+                user, executor, callback));
+    }
+
+    /**
      * Gets the runtime permissions for an app.
      *
      * @param packageName The package for which to query.
@@ -355,6 +397,89 @@
     }
 
     /**
+     * Task to read a large amount of data from a remote service.
+     */
+    private static class FileReaderTask<Callback extends Consumer<byte[]>>
+            extends AsyncTask<Void, Void, byte[]> {
+        private ParcelFileDescriptor mLocalPipe;
+        private ParcelFileDescriptor mRemotePipe;
+
+        private final @NonNull Callback mCallback;
+
+        FileReaderTask(@NonNull Callback callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        protected void onPreExecute() {
+            ParcelFileDescriptor[] pipe;
+            try {
+                pipe = ParcelFileDescriptor.createPipe();
+            } catch (IOException e) {
+                Log.e(TAG, "Could not create pipe needed to get runtime permission backup", e);
+                return;
+            }
+
+            mLocalPipe = pipe[0];
+            mRemotePipe = pipe[1];
+        }
+
+        /**
+         * Get the file descriptor the remote service should write the data to.
+         *
+         * <p>Needs to be closed <u>locally</u> before the FileReader can finish.
+         *
+         * @return The file the data should be written to
+         */
+        ParcelFileDescriptor getRemotePipe() {
+            return mRemotePipe;
+        }
+
+        @Override
+        protected byte[] doInBackground(Void... ignored) {
+            ByteArrayOutputStream combinedBuffer = new ByteArrayOutputStream();
+
+            try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(mLocalPipe)) {
+                byte[] buffer = new byte[16 * 1024];
+
+                while (!isCancelled()) {
+                    int numRead = in.read(buffer);
+                    if (numRead == -1) {
+                        break;
+                    }
+
+                    combinedBuffer.write(buffer, 0, numRead);
+                }
+            } catch (IOException | NullPointerException e) {
+                Log.e(TAG, "Error reading runtime permission backup", e);
+                combinedBuffer.reset();
+            }
+
+            return combinedBuffer.toByteArray();
+        }
+
+        /**
+         * Interrupt the reading of the data.
+         *
+         * <p>Needs to be called when canceling this task as it might be hung.
+         */
+        void interruptRead() {
+            IoUtils.closeQuietly(mLocalPipe);
+        }
+
+        @Override
+        protected void onCancelled() {
+            onPostExecute(new byte[]{});
+        }
+
+        @Override
+        protected void onPostExecute(byte[] backup) {
+            IoUtils.closeQuietly(mLocalPipe);
+            mCallback.accept(backup);
+        }
+    }
+
+    /**
      * Request for {@link #revokeRuntimePermissions}
      */
     private static final class PendingRevokeRuntimePermissionRequest extends
@@ -441,6 +566,68 @@
     }
 
     /**
+     * Request for {@link #getRuntimePermissionBackup}
+     */
+    private static final class PendingGetRuntimePermissionBackup extends
+            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController>
+            implements Consumer<byte[]> {
+        private final @NonNull FileReaderTask<PendingGetRuntimePermissionBackup> mBackupReader;
+        private final @NonNull Executor mExecutor;
+        private final @NonNull OnGetRuntimePermissionBackupCallback mCallback;
+        private final @NonNull UserHandle mUser;
+
+        private PendingGetRuntimePermissionBackup(@NonNull RemoteService service,
+                @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor,
+                @NonNull OnGetRuntimePermissionBackupCallback callback) {
+            super(service);
+
+            mUser = user;
+            mExecutor = executor;
+            mCallback = callback;
+
+            mBackupReader = new FileReaderTask<>(this);
+        }
+
+        @Override
+        protected void onTimeout(RemoteService remoteService) {
+            mBackupReader.cancel(true);
+            mBackupReader.interruptRead();
+        }
+
+        @Override
+        public void run() {
+            mBackupReader.execute();
+
+            ParcelFileDescriptor remotePipe = mBackupReader.getRemotePipe();
+            try {
+                getService().getServiceInterface().getRuntimePermissionBackup(mUser, remotePipe);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error getting runtime permission backup", e);
+            } finally {
+                // Remote pipe end is duped by binder call. Local copy is not needed anymore
+                IoUtils.closeQuietly(remotePipe);
+            }
+        }
+
+        /**
+         * Called when the {@link #mBackupReader} finished reading the file.
+         *
+         * @param backup The data read
+         */
+        @Override
+        public void accept(byte[] backup) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onGetRuntimePermissionsBackup(backup));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+
+            finish();
+        }
+    }
+
+    /**
      * Request for {@link #getAppPermissions}
      */
     private static final class PendingGetAppPermissionRequest extends
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 75d61e6..10e8c8d 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -33,11 +33,16 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallback;
+import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 
+import java.io.IOException;
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -51,6 +56,7 @@
  */
 @SystemApi
 public abstract class PermissionControllerService extends Service {
+    private static final String LOG_TAG = PermissionControllerService.class.getSimpleName();
 
     /**
      * The {@link Intent} action that must be declared as handled by a service
@@ -83,6 +89,15 @@
             @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName);
 
     /**
+     * Create a backup of the runtime permissions.
+     *
+     * @param user The user to back up
+     * @param out The stream to write the backup to
+     */
+    public abstract void onGetRuntimePermissionsBackup(@NonNull UserHandle user,
+            @NonNull OutputStream out);
+
+    /**
      * Gets the runtime permissions for an app.
      *
      * @param packageName The package for which to query.
@@ -163,6 +178,18 @@
             }
 
             @Override
+            public void getRuntimePermissionBackup(UserHandle user, ParcelFileDescriptor pipe) {
+                checkNotNull(user);
+                checkNotNull(pipe);
+
+                enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+
+                mHandler.sendMessage(obtainMessage(
+                        PermissionControllerService::getRuntimePermissionsBackup,
+                        PermissionControllerService.this, user, pipe));
+            }
+
+            @Override
             public void getAppPermissions(String packageName, RemoteCallback callback) {
                 checkNotNull(packageName, "packageName");
                 checkNotNull(callback, "callback");
@@ -237,6 +264,15 @@
         callback.sendResult(result);
     }
 
+    private void getRuntimePermissionsBackup(@NonNull UserHandle user,
+            @NonNull ParcelFileDescriptor outFile) {
+        try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(outFile)) {
+            onGetRuntimePermissionsBackup(user, out);
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Could not open pipe to write backup tp", e);
+        }
+    }
+
     private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) {
         List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
         if (permissions != null && !permissions.isEmpty()) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 64aa062..39c4266 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7407,6 +7407,22 @@
         private static final Validator DOZE_WAKE_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
+         * Gesture that skips media.
+         * @hide
+         */
+        public static final String SKIP_GESTURE = "skip_gesture";
+
+        private static final Validator SKIP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
+         * Gesture that silences sound (alarms, notification, calls).
+         * @hide
+         */
+        public static final String SILENCE_GESTURE = "silence_gesture";
+
+        private static final Validator SILENCE_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
          * The current night mode that has been selected by the user.  Owned
          * and controlled by UiModeManagerService.  Constants are as per
          * UiModeManager.
@@ -8483,6 +8499,8 @@
             NOTIFICATION_NEW_INTERRUPTION_MODEL,
             TRUST_AGENTS_EXTEND_UNLOCK,
             LOCK_SCREEN_WHEN_TRUST_LOST,
+            SKIP_GESTURE,
+            SILENCE_GESTURE,
         };
 
         /**
@@ -8650,6 +8668,8 @@
             VALIDATORS.put(TRUST_AGENTS_EXTEND_UNLOCK, TRUST_AGENTS_EXTEND_UNLOCK_VALIDATOR);
             VALIDATORS.put(LOCK_SCREEN_CUSTOM_CLOCK_FACE, LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR);
             VALIDATORS.put(LOCK_SCREEN_WHEN_TRUST_LOST, LOCK_SCREEN_WHEN_TRUST_LOST_VALIDATOR);
+            VALIDATORS.put(SKIP_GESTURE, SKIP_GESTURE_VALIDATOR);
+            VALIDATORS.put(SILENCE_GESTURE, SILENCE_GESTURE_VALIDATOR);
         }
 
         /**
@@ -9361,6 +9381,15 @@
                 "hdmi_system_audio_control_enabled";
 
         /**
+         * Whether HDMI Routing Control feature is enabled. If enabled, the switch device will
+         * route to the correct input source on receiving Routing Control related messages. If
+         * disabled, you can only switch the input via controls on this device.
+         * @hide
+         */
+        public static final String HDMI_CEC_SWITCH_ENABLED =
+                "hdmi_cec_switch_enabled";
+
+        /**
          * Whether TV will automatically turn on upon reception of the CEC command
          * &lt;Text View On&gt; or &lt;Image View On&gt;. (0 = false, 1 = true)
          *
@@ -11062,6 +11091,31 @@
         /** {@hide} */
         public static final String
                 BLUETOOTH_HEARING_AID_PRIORITY_PREFIX = "bluetooth_hearing_aid_priority_";
+        /**
+         * Enable/disable radio bug detection
+         *
+         * {@hide}
+         */
+        public static final String
+                ENABLE_RADIO_BUG_DETECTION = "enable_radio_bug_detection";
+
+        /**
+         * Count threshold of RIL wakelock timeout for radio bug detection
+         *
+         * {@hide}
+         */
+        public static final String
+                RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD =
+                "radio_bug_wakelock_timeout_count_threshold";
+
+        /**
+         * Count threshold of RIL system error for radio bug detection
+         *
+         * {@hide}
+         */
+        public static final String
+                RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD =
+                "radio_bug_system_error_count_threshold";
 
         /**
          * Activity manager specific settings.
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 0116961..aaba85b 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -42,6 +42,7 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAugmentedAutofillManagerClient;
+import android.view.autofill.IAutofillWindowPresenter;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -95,10 +96,10 @@
         }
 
         @Override
-        public void onDestroyFillWindowRequest(int sessionId) {
+        public void onDestroyAllFillWindowsRequest() {
             mHandler.sendMessage(
-                    obtainMessage(AugmentedAutofillService::handleOnDestroyFillWindowRequest,
-                            AugmentedAutofillService.this, sessionId));
+                    obtainMessage(AugmentedAutofillService::handleOnDestroyAllFillWindowsRequest,
+                            AugmentedAutofillService.this));
         }
     };
 
@@ -185,18 +186,21 @@
                 new FillCallback(proxy));
     }
 
-    private void handleOnDestroyFillWindowRequest(@NonNull int sessionId) {
-        AutofillProxy proxy = null;
+    private void handleOnDestroyAllFillWindowsRequest() {
         if (mAutofillProxies != null) {
-            proxy = mAutofillProxies.get(sessionId);
+            final int size = mAutofillProxies.size();
+            for (int i = 0; i < size; i++) {
+                final int sessionId = mAutofillProxies.keyAt(i);
+                final AutofillProxy proxy = mAutofillProxies.valueAt(i);
+                if (proxy == null) {
+                    // TODO(b/111330312): this might be fine, in which case we should logv it
+                    Log.w(TAG, "No proxy for session " + sessionId);
+                    return;
+                }
+                proxy.destroy();
+            }
+            mAutofillProxies.clear();
         }
-        if (proxy == null) {
-            // TODO(b/111330312): this might be fine, in which case we should logv it
-            Log.w(TAG, "No proxy for session " + sessionId);
-            return;
-        }
-        proxy.destroy();
-        mAutofillProxies.remove(sessionId);
     }
 
     private void handleOnUnbind() {
@@ -350,6 +354,16 @@
             }
         }
 
+        public void requestShowFillUi(int width, int height, Rect anchorBounds,
+                IAutofillWindowPresenter presenter) throws RemoteException {
+            mClient.requestShowFillUi(mSessionId, mFocusedId, width, height, anchorBounds,
+                    presenter);
+        }
+
+        public void requestHideFillUi() throws RemoteException {
+            mClient.requestHideFillUi(mSessionId, mFocusedId);
+        }
+
         private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue) {
             synchronized (mLock) {
                 // TODO(b/111330312): should we close the popupwindow if the focused id changed?
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 33b88e42..51b0f01 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -16,22 +16,25 @@
 package android.service.autofill.augmented;
 
 import static android.service.autofill.augmented.AugmentedAutofillService.DEBUG;
+import static android.service.autofill.augmented.AugmentedAutofillService.VERBOSE;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.app.Dialog;
 import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.service.autofill.augmented.PresentationParams.Area;
 import android.util.Log;
-import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
 import android.view.WindowManager;
+import android.view.autofill.IAutofillWindowPresenter;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
@@ -71,7 +74,7 @@
     /** Indicates the data being shown is a physical address */
     public static final long FLAG_METADATA_ADDRESS = 0x1;
 
-    // TODO(b/111330312): add moar flags
+    // TODO(b/111330312): add more flags
 
     /** @hide */
     @LongDef(prefix = { "FLAG" }, value = {
@@ -83,8 +86,17 @@
     private final Object mLock = new Object();
     private final CloseGuard mCloseGuard = CloseGuard.get();
 
+    private final @NonNull Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
+    private final @NonNull FillWindowPresenter mFillWindowPresenter = new FillWindowPresenter();
+
     @GuardedBy("mLock")
-    private Dialog mDialog;
+    private WindowManager mWm;
+    @GuardedBy("mLock")
+    private View mFillView;
+    @GuardedBy("mLock")
+    private boolean mShowing;
+    @GuardedBy("mLock")
+    private Rect mBounds;
 
     @GuardedBy("mLock")
     private boolean mDestroyed;
@@ -140,51 +152,28 @@
             // window instead of destroying. In fact, it might be better to allocate a full window
             // initially, which is transparent (and let touches get through) everywhere but in the
             // rect boundaries.
-            destroy();
 
             // TODO(b/111330312): make sure all touch events are handled, window is always closed,
             // etc.
 
-            mDialog = new Dialog(rootView.getContext()) {
-                @Override
-                public boolean onTouchEvent(MotionEvent event) {
-                    if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
-                        FillWindow.this.destroy();
+            mWm = rootView.getContext().getSystemService(WindowManager.class);
+            mFillView = rootView;
+            // Listen to the touch outside to destroy the window when typing is detected.
+            mFillView.setOnTouchListener(
+                    (view, motionEvent) -> {
+                        if (motionEvent.getAction() == MotionEvent.ACTION_OUTSIDE) {
+                            if (VERBOSE) Log.v(TAG, "Outside touch detected, hiding the window");
+                            hide();
+                        }
+                        return false;
                     }
-                    return false;
-                }
-            };
-            mCloseGuard.open("destroy");
-            final Window window = mDialog.getWindow();
-            window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
-            // Makes sure touch outside the dialog is received by the window behind the dialog.
-            window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
-            // Makes sure the touch outside the dialog is received by the dialog to dismiss it.
-            window.addFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
-            // Makes sure keyboard shows up.
-            window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
-
-            final int height = rect.bottom - rect.top;
-            final int width = rect.right - rect.left;
-            final WindowManager.LayoutParams windowParams = window.getAttributes();
-            windowParams.gravity = Gravity.TOP | Gravity.LEFT;
-            windowParams.y = rect.top + height;
-            windowParams.height = height;
-            windowParams.x = rect.left;
-            windowParams.width = width;
-
-            window.setAttributes(windowParams);
-            window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-            window.setBackgroundDrawableResource(android.R.color.transparent);
-
-            mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
-            final ViewGroup.LayoutParams diagParams = new ViewGroup.LayoutParams(width, height);
-            mDialog.setContentView(rootView, diagParams);
-
+            );
+            mShowing = false;
+            mBounds = new Rect(area.getBounds());
             if (DEBUG) {
                 Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView);
             }
-
+            mDestroyed = false;
             mProxy.setFillWindow(this);
             return true;
         }
@@ -194,36 +183,87 @@
     void show() {
         // TODO(b/111330312): check if updated first / throw exception
         if (DEBUG) Log.d(TAG, "show()");
-
         synchronized (mLock) {
             checkNotDestroyedLocked();
-            if (mDialog == null) {
+            if (mWm == null || mFillView == null) {
                 throw new IllegalStateException("update() not called yet, or already destroyed()");
             }
-
-            mDialog.show();
             if (mProxy != null) {
+                try {
+                    mProxy.requestShowFillUi(mBounds.right - mBounds.left,
+                            mBounds.bottom - mBounds.top,
+                            /*anchorBounds=*/ null, mFillWindowPresenter);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Error requesting to show fill window", e);
+                }
                 mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN);
             }
         }
     }
 
     /**
+     * Hides the window.
+     *
+     * <p>The window is not destroyed and can be shown again
+     */
+    private void hide() {
+        if (DEBUG) Log.d(TAG, "hide()");
+        synchronized (mLock) {
+            checkNotDestroyedLocked();
+            if (mWm == null || mFillView == null) {
+                throw new IllegalStateException("update() not called yet, or already destroyed()");
+            }
+            if (mProxy != null && mShowing) {
+                try {
+                    mProxy.requestHideFillUi();
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Error requesting to hide fill window", e);
+                }
+            }
+        }
+    }
+
+    private void handleShow(WindowManager.LayoutParams p) {
+        if (DEBUG) Log.d(TAG, "handleShow()");
+        synchronized (mLock) {
+            if (mWm != null && mFillView != null) {
+                p.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+                if (!mShowing) {
+                    mWm.addView(mFillView, p);
+                    mShowing = true;
+                } else {
+                    mWm.updateViewLayout(mFillView, p);
+                }
+            }
+        }
+    }
+
+    private void handleHide() {
+        if (DEBUG) Log.d(TAG, "handleHide()");
+        synchronized (mLock) {
+            if (mWm != null && mFillView != null && mShowing) {
+                mWm.removeView(mFillView);
+                mShowing = false;
+            }
+        }
+    }
+
+    /**
      * Destroys the window.
      *
      * <p>Once destroyed, this window cannot be used anymore
      */
     public void destroy() {
-        if (DEBUG) Log.d(TAG, "destroy(): mDestroyed=" + mDestroyed + " mDialog=" + mDialog);
-
-        synchronized (this) {
-            if (mDestroyed || mDialog == null) return;
-
-            mDialog.dismiss();
-            mDialog = null;
-            if (mProxy != null) {
-                mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
-            }
+        if (DEBUG) {
+            Log.d(TAG,
+                    "destroy(): mDestroyed=" + mDestroyed + " mShowing=" + mShowing + " mFillView="
+                            + mFillView);
+        }
+        synchronized (mLock) {
+            if (mDestroyed) return;
+            hide();
+            mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
+            mDestroyed = true;
             mCloseGuard.close();
         }
     }
@@ -250,11 +290,15 @@
     public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
         synchronized (this) {
             pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
-            if (mDialog != null) {
-                pw.print(prefix); pw.print("dialog: ");
-                pw.println(mDialog.isShowing() ? "shown" : "hidden");
-                pw.print(prefix); pw.print("window: ");
-                pw.println(mDialog.getWindow().getAttributes());
+            if (mFillView != null) {
+                pw.print(prefix); pw.print("fill window: ");
+                pw.println(mShowing ? "shown" : "hidden");
+                pw.print(prefix); pw.print("fill view: ");
+                pw.println(mFillView);
+                pw.print(prefix); pw.print("mBounds: ");
+                pw.println(mBounds);
+                pw.print(prefix); pw.print("mWm: ");
+                pw.println(mWm);
             }
         }
     }
@@ -264,4 +308,19 @@
     public void close() throws Exception {
         destroy();
     }
+
+    private final class FillWindowPresenter extends IAutofillWindowPresenter.Stub {
+        @Override
+        public void show(WindowManager.LayoutParams p, Rect transitionEpicenter,
+                boolean fitsSystemWindows, int layoutDirection) {
+            if (DEBUG) Log.d(TAG, "FillWindowPresenter.show()");
+            mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleShow, FillWindow.this, p));
+        }
+
+        @Override
+        public void hide(Rect transitionEpicenter) {
+            if (DEBUG) Log.d(TAG, "FillWindowPresenter.hide()");
+            mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleHide, FillWindow.this));
+        }
+    }
 }
diff --git a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
index b3ac2da1..fb6912a 100644
--- a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
+++ b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
@@ -36,5 +36,5 @@
                        in ComponentName activityComponent, in AutofillId focusedId,
                        in AutofillValue focusedValue, long requestTime, in IFillCallback callback);
 
-    void onDestroyFillWindowRequest(int sessionId);
+    void onDestroyAllFillWindowsRequest();
 }
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index e5e028d..1eaa3c5 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -323,15 +323,21 @@
         mSessionUids.put(sessionId, uid);
         onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
 
-        final int flags = context.getFlags();
-        if ((flags & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0) {
-            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_BY_FLAG_SECURE,
-                    mClientInterface.asBinder());
-            return;
+        final int clientFlags = context.getFlags();
+        int stateFlags = 0;
+        if ((clientFlags & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0) {
+            stateFlags |= ContentCaptureSession.STATE_FLAG_SECURE;
         }
+        if ((clientFlags & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0) {
+            stateFlags |= ContentCaptureSession.STATE_BY_APP;
+        }
+        if (stateFlags == 0) {
+            stateFlags = ContentCaptureSession.STATE_ACTIVE;
+        } else {
+            stateFlags |= ContentCaptureSession.STATE_DISABLED;
 
-        setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE,
-                mClientInterface.asBinder());
+        }
+        setClientState(clientReceiver, stateFlags, mClientInterface.asBinder());
     }
 
     private void handleSendEvents(int uid,
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 93941d0..888a4c5 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -2997,5 +2997,23 @@
                 afm.post(() -> afm.autofill(sessionId, ids, values));
             }
         }
+
+        @Override
+        public void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
+                Rect anchorBounds, IAutofillWindowPresenter presenter) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.requestShowFillUi(sessionId, id, width, height, anchorBounds,
+                        presenter));
+            }
+        }
+
+        @Override
+        public void requestHideFillUi(int sessionId, AutofillId id) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.requestHideFillUi(id, false));
+            }
+        }
     }
 }
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
index 67cd0bf..140507c 100644
--- a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -21,6 +21,7 @@
 import android.graphics.Rect;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
+import android.view.autofill.IAutofillWindowPresenter;
 
 /**
  * Object running in the application process and responsible to provide the functionalities
@@ -29,6 +30,24 @@
  * @hide
  */
 interface IAugmentedAutofillManagerClient {
-   Rect getViewCoordinates(in AutofillId id);
-   void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+    /**
+      * Gets the coordinates of the input field view.
+      */
+    Rect getViewCoordinates(in AutofillId id);
+
+    /**
+     * Autofills the activity with the contents of the values.
+     */
+    void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values);
+
+    /**
+      * Requests showing the fill UI.
+      */
+    void requestShowFillUi(int sessionId, in AutofillId id, int width, int height,
+            in Rect anchorBounds, in IAutofillWindowPresenter presenter);
+
+    /**
+      * Requests hiding the fill UI.
+      */
+    void requestHideFillUi(int sessionId, in AutofillId id);
 }
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index ff45efd..81b2e01 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -29,12 +29,12 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.SyncResultReceiver;
 
 import java.io.PrintWriter;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /*
  * NOTE: all methods in this class should return right away, or do the real work in a handler
@@ -62,8 +62,10 @@
     static final boolean VERBOSE = false;
     static final boolean DEBUG = true; // STOPSHIP if not set to false
 
-    @NonNull
-    private final AtomicBoolean mDisabled = new AtomicBoolean();
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private boolean mDisabled;
 
     @NonNull
     private final Context mContext;
@@ -71,11 +73,16 @@
     @Nullable
     private final IContentCaptureManager mService;
 
+    // Flags used for starting session.
+    @GuardedBy("mLock")
+    private int mFlags;
+
     // TODO(b/119220549): use UI Thread directly (as calls are one-way) or a shared thread / handler
     // held at the Application level
     @NonNull
     private final Handler mHandler;
 
+    @GuardedBy("mLock")
     private MainContentCaptureSession mMainSession;
 
     /** @hide */
@@ -114,20 +121,25 @@
     @NonNull
     @UiThread
     public MainContentCaptureSession getMainContentCaptureSession() {
-        if (mMainSession == null) {
-            mMainSession = new MainContentCaptureSession(mContext, mHandler, mService,
-                    mDisabled);
-            if (VERBOSE) {
-                Log.v(TAG, "getDefaultContentCaptureSession(): created " + mMainSession);
+        synchronized (mLock) {
+            if (mMainSession == null) {
+                mMainSession = new MainContentCaptureSession(mContext, mHandler, mService,
+                        mDisabled);
+                if (VERBOSE) {
+                    Log.v(TAG, "getDefaultContentCaptureSession(): created " + mMainSession);
+                }
             }
+            return mMainSession;
         }
-        return mMainSession;
     }
 
     /** @hide */
     public void onActivityStarted(@NonNull IBinder applicationToken,
             @NonNull ComponentName activityComponent, int flags) {
-        getMainContentCaptureSession().start(applicationToken, activityComponent, flags);
+        synchronized (mLock) {
+            mFlags |= flags;
+            getMainContentCaptureSession().start(applicationToken, activityComponent, mFlags);
+        }
     }
 
     /** @hide */
@@ -173,7 +185,9 @@
      * Checks whether content capture is enabled for this activity.
      */
     public boolean isContentCaptureEnabled() {
-        return mService != null && !mDisabled.get();
+        synchronized (mLock) {
+            return mService != null && !mDisabled;
+        }
     }
 
     /**
@@ -183,7 +197,9 @@
      * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
      */
     public void setContentCaptureEnabled(boolean enabled) {
-        //TODO(b/111276913): implement (need to finish / disable all sessions)
+        synchronized (mLock) {
+            mFlags |= enabled ? 0 : ContentCaptureContext.FLAG_DISABLED_BY_APP;
+        }
     }
 
     /**
@@ -198,20 +214,22 @@
 
     /** @hide */
     public void dump(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.println("ContentCaptureManager");
-
-        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled.get());
-        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);
-        }
-        if (mMainSession != null) {
-            final String prefix2 = prefix + "  ";
-            pw.print(prefix); pw.println("Main session:");
-            mMainSession.dump(prefix2, pw);
-        } else {
-            pw.print(prefix); pw.println("No sessions");
+        synchronized (mLock) {
+            pw.print(prefix); pw.println("ContentCaptureManager");
+            pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+            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("Flags: "); pw.println(mFlags);
+            if (mMainSession != null) {
+                final String prefix2 = prefix + "  ";
+                pw.print(prefix); pw.println("Main session:");
+                mMainSession.dump(prefix2, pw);
+            } else {
+                pw.print(prefix); pw.println("No sessions");
+            }
         }
     }
 
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index d9a8416..2123308 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -21,6 +21,7 @@
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.util.DebugUtils;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewStructure;
@@ -56,42 +57,58 @@
      *
      * @hide
      */
-    public static final int STATE_UNKNOWN = 0;
+    // NOTE: not prefixed by STATE_ so it's not printed on getStateAsString()
+    public static final int UNKNWON_STATE = 0x0;
 
     /**
      * Service's startSession() was called, but server didn't confirm it was created yet.
      *
      * @hide
      */
-    public static final int STATE_WAITING_FOR_SERVER = 1;
+    public static final int STATE_WAITING_FOR_SERVER = 0x1;
 
     /**
      * Session is active.
      *
      * @hide
      */
-    public static final int STATE_ACTIVE = 2;
+    public static final int STATE_ACTIVE = 0x2;
 
     /**
      * Session is disabled because there is no service for this user.
      *
      * @hide
      */
-    public static final int STATE_DISABLED_NO_SERVICE = 3;
+    public static final int STATE_DISABLED = 0x4;
 
     /**
      * Session is disabled because its id already existed on server.
      *
      * @hide
      */
-    public static final int STATE_DISABLED_DUPLICATED_ID = 4;
+    public static final int STATE_DUPLICATED_ID = 0x8;
+
+    /**
+     * Session is disabled because service is not set for user.
+     *
+     * @hide
+     */
+    public static final int STATE_NO_SERVICE = 0x10;
 
     /**
      * Session is disabled by FLAG_SECURE
      *
      * @hide
      */
-    public static final int STATE_DISABLED_BY_FLAG_SECURE = 5;
+    public static final int STATE_FLAG_SECURE = 0x20;
+
+    /**
+     * Session is disabled manually by the specific app.
+     *
+     * @hide
+     */
+    public static final int STATE_BY_APP = 0x40;
+
 
     private static final int INITIAL_CHILDREN_CAPACITY = 5;
 
@@ -110,7 +127,7 @@
     @Nullable
     protected final String mId;
 
-    private int mState = STATE_UNKNOWN;
+    private int mState = UNKNWON_STATE;
 
     // Lazily created on demand.
     private ContentCaptureSessionId mContentCaptureSessionId;
@@ -382,21 +399,7 @@
      */
     @NonNull
     protected static String getStateAsString(int state) {
-        switch (state) {
-            case STATE_UNKNOWN:
-                return "UNKNOWN";
-            case STATE_WAITING_FOR_SERVER:
-                return "WAITING_FOR_SERVER";
-            case STATE_ACTIVE:
-                return "ACTIVE";
-            case STATE_DISABLED_NO_SERVICE:
-                return "DISABLED_NO_SERVICE";
-            case STATE_DISABLED_DUPLICATED_ID:
-                return "DISABLED_DUPLICATED_ID";
-            case STATE_DISABLED_BY_FLAG_SECURE:
-                return "DISABLED_FLAG_SECURE";
-            default:
-                return "INVALID:" + state;
-        }
+        return state + " (" + (state == UNKNWON_STATE ? "UNKNOWN"
+                : DebugUtils.flagsToString(ContentCaptureSession.class, "STATE_", state)) + ")";
     }
 }
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index a29aaf0..1d9018c 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -88,6 +88,7 @@
      */
     public static final String EXTRA_BINDER = "binder";
 
+    // TODO(b/111276913): make sure disabled state is in sync with manager's disabled
     @NonNull
     private final AtomicBoolean mDisabled;
 
@@ -113,7 +114,7 @@
     @Nullable
     private DeathRecipient mDirectServiceVulture;
 
-    private int mState = STATE_UNKNOWN;
+    private int mState = UNKNWON_STATE;
 
     @Nullable
     private IBinder mApplicationToken;
@@ -133,11 +134,11 @@
     /** @hide */
     protected MainContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
             @Nullable IContentCaptureManager systemServerInterface,
-            @NonNull AtomicBoolean disabled) {
+            @NonNull boolean disabled) {
         mContext = context;
         mHandler = handler;
         mSystemServerInterface = systemServerInterface;
-        mDisabled = disabled;
+        mDisabled = new AtomicBoolean(disabled);
     }
 
     @Override
@@ -184,7 +185,7 @@
 
     private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName,
             int flags) {
-        if (mState != STATE_UNKNOWN) {
+        if (mState != UNKNWON_STATE) {
             // TODO(b/111276913): revisit this scenario
             Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state "
                     + getStateAsString(mState));
@@ -247,17 +248,14 @@
             }
         }
 
-        // TODO(b/111276913): change the resultCode to use flags so there's just one flag for
-        // disabled stuff
-        if (resultCode == STATE_DISABLED_NO_SERVICE || resultCode == STATE_DISABLED_DUPLICATED_ID
-                || resultCode == STATE_DISABLED_BY_FLAG_SECURE) {
+        if ((mState & STATE_DISABLED) != 0) {
             mDisabled.set(true);
             handleResetSession(/* resetState= */ false);
         } else {
             mDisabled.set(false);
         }
         if (VERBOSE) {
-            Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
+            Log.v(TAG, "handleSessionStarted() result: id=" + mId
                     + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
                     + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size()));
         }
@@ -407,7 +405,7 @@
     // clearings out.
     private void handleResetSession(boolean resetState) {
         if (resetState) {
-            mState = STATE_UNKNOWN;
+            mState = UNKNWON_STATE;
         }
 
         // TODO(b/122454205): must reset children (which currently is owned by superclass)
@@ -496,8 +494,7 @@
         }
         pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get());
         pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
-        pw.print(prefix); pw.print("state: "); pw.print(mState); pw.print(" (");
-        pw.print(getStateAsString(mState)); pw.println(")");
+        pw.print(prefix); pw.print("state: "); pw.println(getStateAsString(mState));
         if (mApplicationToken != null) {
             pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
         }
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 2f2f623..49ca378 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -91,4 +91,10 @@
 
     // OPEN: Settings > Apps & Notifications -> Special app access -> Financial Apps Sms Access
     SETTINGS_FINANCIAL_APPS_SMS_ACCESS = 1597;
+
+    // OPEN: Settings > System > Input & Gesture > Skip songs
+    SETTINGS_GESTURE_SKIP = 1624;
+
+    // OPEN: Settings > System > Input & Gesture > Silence alerts
+    SETTINGS_GESTURE_SILENCE = 1625;
 }
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 0e052fe..9f4345d 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -525,7 +525,10 @@
     }
     optional Zen zen = 71;
 
+    optional SettingProto skip_gesture_enabled = 74 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto silence_gesture_enabled = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 74;
+    // Next tag = 76;
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d40f01c..2e3bd7c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3708,4 +3708,10 @@
     <!-- If device supports corner radius on windows.
          This should be turned off on low-end devices to improve animation performance. -->
     <bool name="config_supportsRoundedCornersOnWindows">true</bool>
+
+    <!-- If the sensor that skips media is available or not. -->
+    <bool name="config_skipSensorAvailable">false</bool>
+
+    <!-- If the sensor that silences alerts is available or not. -->
+    <bool name="config_silenceSensorAvailable">false</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a1ef0d5..daf8b44 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3536,4 +3536,7 @@
   <java-symbol type="string" name="dynamic_mode_notification_title" />
   <java-symbol type="string" name="dynamic_mode_notification_summary" />
   <java-symbol type="drawable" name="ic_battery" />
+
+  <java-symbol type="bool" name="config_skipSensorAvailable" />
+  <java-symbol type="bool" name="config_silenceSensorAvailable" />
 </resources>
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index 267267e..3f91a65 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -51,6 +51,12 @@
     private static final String PRE_RELEASE = "B";
     private static final String NEWER_PRE_RELEASE = "C";
 
+    // Codenames with a fingerprint attached to them. These may only be present in the apps
+    // declared min SDK and not as platform codenames.
+    private static final String OLDER_PRE_RELEASE_WITH_FINGERPRINT = "A.fingerprint";
+    private static final String PRE_RELEASE_WITH_FINGERPRINT = "B.fingerprint";
+    private static final String NEWER_PRE_RELEASE_WITH_FINGERPRINT = "C.fingerprint";
+
     private static final String[] CODENAMES_RELEASED = { /* empty */ };
     private static final String[] CODENAMES_PRE_RELEASE = { PRE_RELEASE };
 
@@ -68,7 +74,7 @@
                 isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE,
                 outError);
 
-        assertEquals(result, expectedMinSdk);
+        assertEquals("Error msg: " + outError[0], expectedMinSdk, result);
 
         if (expectedMinSdk == -1) {
             assertNotNull(outError[0]);
@@ -98,6 +104,7 @@
         // APP: Pre-release API 10
         // DEV: Pre-release API 20
         verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1);
+        verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, false, -1);
 
         // Do allow same pre-release minSdkVersion on pre-release platform,
         // but overwrite the specified version with CUR_DEVELOPMENT.
@@ -105,11 +112,15 @@
         // DEV: Pre-release API 20
         verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false,
                 Build.VERSION_CODES.CUR_DEVELOPMENT);
+        verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, false,
+                Build.VERSION_CODES.CUR_DEVELOPMENT);
+
 
         // Don't allow newer pre-release minSdkVersion on pre-release platform.
         // APP: Pre-release API 30
         // DEV: Pre-release API 20
         verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1);
+        verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false, -1);
     }
 
     @Test
@@ -133,16 +144,20 @@
         // APP: Pre-release API 10
         // DEV: Released API 20
         verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1);
+        verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, true, -1);
 
         // Don't allow same pre-release minSdkVersion on released platform.
         // APP: Pre-release API 20
         // DEV: Released API 20
         verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1);
+        verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, true, -1);
+
 
         // Don't allow newer pre-release minSdkVersion on released platform.
         // APP: Pre-release API 30
         // DEV: Released API 20
         verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1);
+        verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, true, -1);
     }
 
     private void verifyComputeTargetSdkVersion(int targetSdkVersion, String targetSdkCodename,
@@ -189,6 +204,9 @@
         // DEV: Pre-release API 20
         verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, false, -1,
+                false /* forceCurrentDev */);
+
 
         // Do allow same pre-release targetSdkVersion on pre-release platform,
         // but overwrite the specified version with CUR_DEVELOPMENT.
@@ -196,18 +214,26 @@
         // DEV: Pre-release API 20
         verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false,
                 Build.VERSION_CODES.CUR_DEVELOPMENT, false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, false,
+                Build.VERSION_CODES.CUR_DEVELOPMENT, false /* forceCurrentDev */);
+
 
         // Don't allow newer pre-release targetSdkVersion on pre-release platform.
         // APP: Pre-release API 30
         // DEV: Pre-release API 20
         verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false, -1,
+                false /* forceCurrentDev */);
+
 
         // Force newer pre-release targetSdkVersion to current pre-release platform.
         // APP: Pre-release API 30
         // DEV: Pre-release API 20
         verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false,
                 Build.VERSION_CODES.CUR_DEVELOPMENT, true /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, false,
+                Build.VERSION_CODES.CUR_DEVELOPMENT, true /* forceCurrentDev */);
     }
 
     @Test
@@ -235,18 +261,25 @@
         // DEV: Released API 20
         verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE_WITH_FINGERPRINT, true, -1,
+                false /* forceCurrentDev */);
 
         // Don't allow same pre-release targetSdkVersion on released platform.
         // APP: Pre-release API 20
         // DEV: Released API 20
         verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE_WITH_FINGERPRINT, true, -1,
+                false /* forceCurrentDev */);
+
 
         // Don't allow newer pre-release targetSdkVersion on released platform.
         // APP: Pre-release API 30
         // DEV: Released API 20
         verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1,
                 false /* forceCurrentDev */);
+        verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE_WITH_FINGERPRINT, true, -1,
+                false /* forceCurrentDev */);
     }
 
     /**
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 1ed5ce4..df4600e 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -27,7 +27,6 @@
 import static java.lang.reflect.Modifier.isStatic;
 
 import android.platform.test.annotations.Presubmit;
-import android.provider.Settings.Global;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -270,6 +269,7 @@
                     Settings.Global.GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS,
                     Settings.Global.GNSS_SATELLITE_BLACKLIST,
                     Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
+                    Settings.Global.HDMI_CEC_SWITCH_ENABLED,
                     Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
                     Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
                     Settings.Global.HDMI_CONTROL_ENABLED,
@@ -554,8 +554,10 @@
                     Settings.Global.APPOP_HISTORY_PARAMETERS,
                     Settings.Global.APPOP_HISTORY_MODE,
                     Settings.Global.APPOP_HISTORY_INTERVAL_MULTIPLIER,
-                    Settings.Global.APPOP_HISTORY_BASE_INTERVAL_MILLIS);
-
+                    Settings.Global.APPOP_HISTORY_BASE_INTERVAL_MILLIS,
+                    Settings.Global.ENABLE_RADIO_BUG_DETECTION,
+                    Settings.Global.RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD,
+                    Settings.Global.RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD);
     private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
              newHashSet(
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 0dd9d09..64b3ba0 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -318,6 +318,15 @@
             mMaxVolume = maxVolume;
             mIsMute = isMute;
         }
+
+        @Override
+        public void setSystemAudioModeOnForAudioOnlySource() {
+        }
+
+        @Override
+        public int getPhysicalAddress() {
+            return 0x0000;
+        }
     }
 
 }
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 9170d6d..68541b4 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -86,6 +86,26 @@
     mCallbacks.gles.draw(mFunctor, mData, drawInfo);
 }
 
+void WebViewFunctor::initVk(const VkFunctorInitParams& params) {
+    ATRACE_NAME("WebViewFunctor::initVk");
+    if (!mHasContext) {
+        mHasContext = true;
+    } else {
+        return;
+    }
+    mCallbacks.vk.initialize(mFunctor, mData, params);
+}
+
+void WebViewFunctor::drawVk(const VkFunctorDrawParams& params) {
+    ATRACE_NAME("WebViewFunctor::drawVk");
+    mCallbacks.vk.draw(mFunctor, mData, params);
+}
+
+void WebViewFunctor::postDrawVk() {
+    ATRACE_NAME("WebViewFunctor::postDrawVk");
+    mCallbacks.vk.postDraw(mFunctor, mData);
+}
+
 void WebViewFunctor::destroyContext() {
     if (mHasContext) {
         mHasContext = false;
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index 1719ce7..2846cb1 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -42,6 +42,12 @@
 
         void drawGl(const DrawGlInfo& drawInfo) const { mReference.drawGl(drawInfo); }
 
+        void initVk(const VkFunctorInitParams& params) { mReference.initVk(params); }
+
+        void drawVk(const VkFunctorDrawParams& params) { mReference.drawVk(params); }
+
+        void postDrawVk() { mReference.postDrawVk(); }
+
     private:
         friend class WebViewFunctor;
 
@@ -53,6 +59,9 @@
     int id() const { return mFunctor; }
     void sync(const WebViewSyncData& syncData) const;
     void drawGl(const DrawGlInfo& drawInfo);
+    void initVk(const VkFunctorInitParams& params);
+    void drawVk(const VkFunctorDrawParams& params);
+    void postDrawVk();
     void destroyContext();
 
     sp<Handle> createHandle() {
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 6eefed9..d54275f 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -125,8 +125,6 @@
                                              uirenderer::GlFunctorLifecycleListener* listener) {
     FunctorDrawable* functorDrawable;
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
-        // interop is disabled/moved.
         functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(
                 functor, listener, asSkCanvas());
     } else {
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 156f74a..2f8d381 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -17,6 +17,8 @@
 #include "VkFunctorDrawable.h"
 #include <private/hwui/DrawVkInfo.h>
 
+#include "renderthread/VulkanManager.h"
+#include "renderthread/RenderThread.h"
 #include <GrBackendDrawableInfo.h>
 #include <SkImage.h>
 #include <utils/Color.h>
@@ -31,34 +33,58 @@
 namespace uirenderer {
 namespace skiapipeline {
 
-VkFunctorDrawHandler::VkFunctorDrawHandler(Functor* functor) : INHERITED(), mFunctor(functor) {}
+VkFunctorDrawHandler::VkFunctorDrawHandler(sp<WebViewFunctor::Handle> functor_handle,
+                                           const SkMatrix& matrix, const SkIRect& clip,
+                                           const SkImageInfo& image_info)
+        : INHERITED()
+        , mFunctorHandle(functor_handle)
+        , mMatrix(matrix)
+        , mClip(clip)
+        , mImageInfo(image_info) {}
 
 VkFunctorDrawHandler::~VkFunctorDrawHandler() {
-    // TODO(cblume) Fill in the DrawVkInfo parameters.
-    (*mFunctor)(DrawVkInfo::kModePostComposite, nullptr);
+    mFunctorHandle->postDrawVk();
 }
 
 void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) {
     ATRACE_CALL();
+    if (!renderthread::RenderThread::isCurrent())
+        LOG_ALWAYS_FATAL("VkFunctorDrawHandler::draw not called on render thread");
 
     GrVkDrawableInfo vulkan_info;
     if (!info.getVkDrawableInfo(&vulkan_info)) {
         return;
     }
+    renderthread::VulkanManager& vk_manager =
+            renderthread::RenderThread::getInstance().vulkanManager();
+    mFunctorHandle->initVk(vk_manager.getVkFunctorInitParams());
 
-    DrawVkInfo draw_vk_info;
-    // TODO(cblume) Fill in the rest of the parameters and test the actual call.
-    draw_vk_info.isLayer = true;
+    SkMatrix44 mat4(mMatrix);
+    VkFunctorDrawParams params{
+      .width = mImageInfo.width(),
+      .height = mImageInfo.height(),
+      .is_layer = false,  // TODO(boliu): Populate is_layer.
+      .color_space_ptr = mImageInfo.colorSpace(),
+      .clip_left = mClip.fLeft,
+      .clip_top = mClip.fTop,
+      .clip_right = mClip.fRight,
+      .clip_bottom = mClip.fBottom,
+    };
+    mat4.asColMajorf(&params.transform[0]);
+    params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer;
+    params.color_attachment_index = vulkan_info.fColorAttachmentIndex;
+    params.compatible_render_pass = vulkan_info.fCompatibleRenderPass;
+    params.format = vulkan_info.fFormat;
 
-    (*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info);
+    mFunctorHandle->drawVk(params);
+
+    vulkan_info.fDrawBounds->offset.x = mClip.fLeft;
+    vulkan_info.fDrawBounds->offset.y = mClip.fTop;
+    vulkan_info.fDrawBounds->extent.width = mClip.fRight - mClip.fLeft;
+    vulkan_info.fDrawBounds->extent.height = mClip.fBottom - mClip.fTop;
 }
 
 VkFunctorDrawable::~VkFunctorDrawable() {
-    if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
-        if (lp->listener) {
-            lp->listener->onGlFunctorReleased(lp->functor);
-        }
-    }
 }
 
 void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) {
@@ -67,16 +93,17 @@
 }
 
 std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler(
-        GrBackendApi backendApi, const SkMatrix& matrix) {
+        GrBackendApi backendApi, const SkMatrix& matrix, const SkIRect& clip,
+        const SkImageInfo& image_info) {
     if (backendApi != GrBackendApi::kVulkan) {
         return nullptr;
     }
     std::unique_ptr<VkFunctorDrawHandler> draw;
     if (mAnyFunctor.index() == 0) {
-        LOG_ALWAYS_FATAL("Not implemented");
-        return nullptr;
+        return std::make_unique<VkFunctorDrawHandler>(std::get<0>(mAnyFunctor).handle, matrix, clip,
+                                                      image_info);
     } else {
-        return std::make_unique<VkFunctorDrawHandler>(std::get<1>(mAnyFunctor).functor);
+        LOG_ALWAYS_FATAL("Not implemented");
     }
 }
 
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
index d6fefc1..1a53c8f 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -32,15 +32,18 @@
  */
 class VkFunctorDrawHandler : public FunctorDrawable::GpuDrawHandler {
 public:
-    explicit VkFunctorDrawHandler(Functor* functor);
+    VkFunctorDrawHandler(sp<WebViewFunctor::Handle> functor_handle, const SkMatrix& matrix,
+                         const SkIRect& clip, const SkImageInfo& image_info);
     ~VkFunctorDrawHandler() override;
 
     void draw(const GrBackendDrawableInfo& info) override;
 
 private:
     typedef GpuDrawHandler INHERITED;
-
-    Functor* mFunctor;
+    sp<WebViewFunctor::Handle> mFunctorHandle;
+    const SkMatrix mMatrix;
+    const SkIRect mClip;
+    const SkImageInfo mImageInfo;
 };
 
 /**
@@ -57,7 +60,8 @@
     // SkDrawable functions:
     void onDraw(SkCanvas* canvas) override;
     std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(
-            GrBackendApi backendApi, const SkMatrix& matrix) override;
+            GrBackendApi backendApi, const SkMatrix& matrix, const SkIRect& clip,
+            const SkImageInfo& image_info) override;
 };
 
 }  // namespace skiapipeline
diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h
index fd824bd..abc4dbf 100644
--- a/libs/hwui/private/hwui/DrawVkInfo.h
+++ b/libs/hwui/private/hwui/DrawVkInfo.h
@@ -17,80 +17,61 @@
 #ifndef ANDROID_HWUI_DRAW_VK_INFO_H
 #define ANDROID_HWUI_DRAW_VK_INFO_H
 
+#include <SkColorSpace.h>
 #include <vulkan/vulkan.h>
 
 namespace android {
 namespace uirenderer {
 
-/**
- * Structure used by VulkanRenderer::callDrawVKFunction() to pass and receive data from Vulkan
- * functors.
- */
-struct DrawVkInfo {
-    // Input: current width/height of destination surface
-    int width;
-    int height;
+struct VkFunctorInitParams {
+  VkInstance instance;
+  VkPhysicalDevice physical_device;
+  VkDevice device;
+  VkQueue queue;
+  uint32_t graphics_queue_index;
+  uint32_t instance_version;
+  const char* const* enabled_instance_extension_names;
+  uint32_t enabled_instance_extension_names_length;
+  const char* const* enabled_device_extension_names;
+  uint32_t enabled_device_extension_names_length;
+  const VkPhysicalDeviceFeatures2* device_features_2;
+};
 
-    // Input: is the render target an FBO
-    bool isLayer;
+struct VkFunctorDrawParams {
+  // Input: current width/height of destination surface.
+  int width;
+  int height;
 
-    // Input: current transform matrix, in OpenGL format
-    float transform[16];
+  // Input: is the render target a FBO
+  bool is_layer;
 
-    // Input: WebView should do its main compositing draws into this. It cannot do anything that
-    // would require stopping the render pass.
-    VkCommandBuffer secondaryCommandBuffer;
+  // Input: current transform matrix
+  float transform[16];
 
-    // Input: The main color attachment index where secondaryCommandBuffer will eventually be
-    // submitted.
-    uint32_t colorAttachmentIndex;
+  // Input WebView should do its main compositing draws into this. It cannot do
+  // anything that would require stopping the render pass.
+  VkCommandBuffer secondary_command_buffer;
 
-    // Input: A render pass which will be compatible to the one which the secondaryCommandBuffer
-    // will be submitted into.
-    VkRenderPass compatibleRenderPass;
+  // Input: The main color attachment index where secondary_command_buffer will
+  // eventually be submitted.
+  uint32_t color_attachment_index;
 
-    // Input: Format of the destination surface.
-    VkFormat format;
+  // Input: A render pass which will be compatible to the one which the
+  // secondary_command_buffer will be submitted into.
+  VkRenderPass compatible_render_pass;
 
-    // Input: Color space
-    const SkColorSpace* colorSpaceInfo;
+  // Input: Format of the destination surface.
+  VkFormat format;
 
-    // Input: current clip rect
-    int clipLeft;
-    int clipTop;
-    int clipRight;
-    int clipBottom;
+  // Input: Color space.
+  const SkColorSpace* color_space_ptr;
 
-    /**
-     * Values used as the "what" parameter of the functor.
-     */
-    enum Mode {
-        // Called once at WebView start
-        kModeInit,
-        // Called when things need to be re-created
-        kModeReInit,
-        // Notifies the app that the composite functor will be called soon. This allows WebView to
-        // begin work early.
-        kModePreComposite,
-        // Do the actual composite work
-        kModeComposite,
-        // This allows WebView to begin using the previously submitted objects in future work.
-        kModePostComposite,
-        // Invoked every time the UI thread pushes over a frame to the render thread and the owning
-        // view has a dirty display list*. This is a signal to sync any data that needs to be
-        // shared between the UI thread and the render thread. During this time the UI thread is
-        // blocked.
-        kModeSync
-    };
-
-    /**
-     * Values used by Vulkan functors to tell the framework what to do next.
-     */
-    enum Status {
-        // The functor is done
-        kStatusDone = 0x0,
-    };
-};  // struct DrawVkInfo
+  // Input: current clip rect
+  int clip_left;
+  int clip_top;
+  int clip_right;
+  int clip_bottom;
+};
 
 }  // namespace uirenderer
 }  // namespace android
diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h
index da3d06a..96da947 100644
--- a/libs/hwui/private/hwui/WebViewFunctor.h
+++ b/libs/hwui/private/hwui/WebViewFunctor.h
@@ -19,6 +19,7 @@
 
 #include <cutils/compiler.h>
 #include <private/hwui/DrawGlInfo.h>
+#include <private/hwui/DrawVkInfo.h>
 
 namespace android::uirenderer {
 
@@ -52,18 +53,12 @@
             // Called on RenderThread. initialize is guaranteed to happen before this call
             void (*draw)(int functor, void* data, const DrawGlInfo& params);
         } gles;
-        // TODO: VK support. The current DrawVkInfo is monolithic and needs to be split up for
-        // what params are valid on what callbacks
         struct {
             // Called either the first time the functor is used or the first time it's used after
             // a call to onContextDestroyed.
-            // void (*initialize)(int functor, const InitParams& params);
-            // void (*frameStart)(int functor, /* todo: what params are actually needed for this to
-            // be useful? Is this useful? */)
-            // void (*draw)(int functor, const CompositeParams& params /* todo: rename - composite
-            // almost always means something else, and we aren't compositing */);
-            // void (*frameEnd)(int functor, const PostCompositeParams& params /* todo: same as
-            // CompositeParams - rename */);
+            void (*initialize)(int functor, void* data, const VkFunctorInitParams& params);
+            void (*draw)(int functor, void* data, const VkFunctorDrawParams& params);
+            void (*postDraw)(int functor, void*);
         } vk;
     };
 };
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index c06fadd..8bef359 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -171,6 +171,9 @@
     mRenderState = new RenderState(*this);
     mVkManager = new VulkanManager(*this);
     mCacheManager = new CacheManager(mDisplayInfo);
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        mVkManager->initialize();
+    }
 }
 
 void RenderThread::requireGlContext() {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 5272227..1ef83fb 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -47,6 +47,10 @@
 class RenderState;
 class TestUtils;
 
+namespace skiapipeline {
+class VkFunctorDrawHandler;
+}
+
 namespace renderthread {
 
 class CanvasContext;
@@ -124,6 +128,7 @@
     friend class DummyVsyncSource;
     friend class android::uirenderer::TestUtils;
     friend class android::uirenderer::WebViewFunctor;
+    friend class android::uirenderer::skiapipeline::VkFunctorDrawHandler;
 
     RenderThread();
     virtual ~RenderThread();
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index aa7a141..6c540f6 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -34,6 +34,23 @@
 namespace uirenderer {
 namespace renderthread {
 
+static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) {
+    // All Vulkan structs that could be part of the features chain will start with the
+    // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader
+    // so we can get access to the pNext for the next struct.
+    struct CommonVulkanHeader {
+        VkStructureType sType;
+        void*           pNext;
+    };
+
+    void* pNext = features.pNext;
+    while (pNext) {
+        void* current = pNext;
+        pNext = static_cast<CommonVulkanHeader*>(current)->pNext;
+        free(current);
+    }
+}
+
 #define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F)
 #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)
@@ -66,6 +83,11 @@
     mDevice = VK_NULL_HANDLE;
     mPhysicalDevice = VK_NULL_HANDLE;
     mInstance = VK_NULL_HANDLE;
+    mInstanceVersion = 0u;
+    mInstanceExtensions.clear();
+    mDeviceExtensions.clear();
+    free_features_extensions_structs(mPhysicalDeviceFeatures2);
+    mPhysicalDeviceFeatures2 = {};
 }
 
 bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFeatures2& features) {
@@ -81,7 +103,6 @@
         VK_MAKE_VERSION(1, 1, 0),           // apiVersion
     };
 
-    std::vector<const char*> instanceExtensions;
     {
         GET_PROC(EnumerateInstanceExtensionProperties);
 
@@ -99,7 +120,7 @@
         bool hasKHRSurfaceExtension = false;
         bool hasKHRAndroidSurfaceExtension = false;
         for (uint32_t i = 0; i < extensionCount; ++i) {
-            instanceExtensions.push_back(extensions[i].extensionName);
+            mInstanceExtensions.push_back(extensions[i].extensionName);
             if (!strcmp(extensions[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) {
                 hasKHRSurfaceExtension = true;
             }
@@ -120,8 +141,8 @@
         &app_info,                                 // pApplicationInfo
         0,                                         // enabledLayerNameCount
         nullptr,                                   // ppEnabledLayerNames
-        (uint32_t) instanceExtensions.size(),      // enabledExtensionNameCount
-        instanceExtensions.data(),                 // ppEnabledExtensionNames
+        (uint32_t) mInstanceExtensions.size(),     // enabledExtensionNameCount
+        mInstanceExtensions.data(),                // ppEnabledExtensionNames
     };
 
     GET_PROC(CreateInstance);
@@ -201,7 +222,6 @@
     // presentation with any native window. So just use the first one.
     mPresentQueueIndex = 0;
 
-    std::vector<const char*> deviceExtensions;
     {
         uint32_t extensionCount = 0;
         err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount,
@@ -220,7 +240,7 @@
         }
         bool hasKHRSwapchainExtension = false;
         for (uint32_t i = 0; i < extensionCount; ++i) {
-            deviceExtensions.push_back(extensions[i].extensionName);
+            mDeviceExtensions.push_back(extensions[i].extensionName);
             if (!strcmp(extensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
                 hasKHRSwapchainExtension = true;
             }
@@ -237,8 +257,8 @@
         }
         return vkGetInstanceProcAddr(instance, proc_name);
     };
-    grExtensions.init(getProc, mInstance, mPhysicalDevice, instanceExtensions.size(),
-            instanceExtensions.data(), deviceExtensions.size(), deviceExtensions.data());
+    grExtensions.init(getProc, mInstance, mPhysicalDevice, mInstanceExtensions.size(),
+            mInstanceExtensions.data(), mDeviceExtensions.size(), mDeviceExtensions.data());
 
     if (!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
         this->destroy();
@@ -308,8 +328,8 @@
         queueInfo,                               // pQueueCreateInfos
         0,                                       // layerCount
         nullptr,                                 // ppEnabledLayerNames
-        (uint32_t) deviceExtensions.size(),      // extensionCount
-        deviceExtensions.data(),                 // ppEnabledExtensionNames
+        (uint32_t) mDeviceExtensions.size(),     // extensionCount
+        mDeviceExtensions.data(),                // ppEnabledExtensionNames
         nullptr,                                 // ppEnabledFeatures
     };
 
@@ -351,36 +371,17 @@
     return true;
 }
 
-static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) {
-    // All Vulkan structs that could be part of the features chain will start with the
-    // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader
-    // so we can get access to the pNext for the next struct.
-    struct CommonVulkanHeader {
-        VkStructureType sType;
-        void*           pNext;
-    };
-
-    void* pNext = features.pNext;
-    while (pNext) {
-        void* current = pNext;
-        pNext = static_cast<CommonVulkanHeader*>(current)->pNext;
-        free(current);
-    }
-}
-
 void VulkanManager::initialize() {
     if (mDevice != VK_NULL_HANDLE) {
         return;
     }
 
     GET_PROC(EnumerateInstanceVersion);
-    uint32_t instanceVersion = 0;
-    LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion));
-    LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0));
+    LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&mInstanceVersion));
+    LOG_ALWAYS_FATAL_IF(mInstanceVersion < VK_MAKE_VERSION(1, 1, 0));
 
     GrVkExtensions extensions;
-    VkPhysicalDeviceFeatures2 features;
-    LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, features));
+    LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, mPhysicalDeviceFeatures2));
 
     mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
 
@@ -397,9 +398,9 @@
     backendContext.fDevice = mDevice;
     backendContext.fQueue = mGraphicsQueue;
     backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
-    backendContext.fInstanceVersion = instanceVersion;
+    backendContext.fInstanceVersion = mInstanceVersion;
     backendContext.fVkExtensions = &extensions;
-    backendContext.fDeviceFeatures2 = &features;
+    backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
     backendContext.fGetProc = std::move(getProc);
 
     // create the command pool for the command buffers
@@ -433,13 +434,29 @@
     LOG_ALWAYS_FATAL_IF(!grContext.get());
     mRenderThread.setGrContext(grContext);
 
-    free_features_extensions_structs(features);
-
     if (Properties::enablePartialUpdates && Properties::useBufferAge) {
         mSwapBehavior = SwapBehavior::BufferAge;
     }
 }
 
+VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const {
+    return VkFunctorInitParams{
+            .instance = mInstance,
+            .physical_device = mPhysicalDevice,
+            .device = mDevice,
+            .queue = mGraphicsQueue,
+            .graphics_queue_index = mGraphicsQueueIndex,
+            .instance_version = mInstanceVersion,
+            .enabled_instance_extension_names = mInstanceExtensions.data(),
+            .enabled_instance_extension_names_length =
+                    static_cast<uint32_t>(mInstanceExtensions.size()),
+            .enabled_device_extension_names = mDeviceExtensions.data(),
+            .enabled_device_extension_names_length =
+                    static_cast<uint32_t>(mDeviceExtensions.size()),
+            .device_features_2 = &mPhysicalDeviceFeatures2,
+    };
+}
+
 // Returns the next BackbufferInfo to use for the next draw. The function will make sure all
 // previous uses have finished before returning.
 VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurface* surface) {
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 9eb942c..105ee09 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -132,6 +132,9 @@
     // Creates a fence that is signaled, when all the pending Vulkan commands are flushed.
     status_t createReleaseFence(sp<Fence>& nativeFence);
 
+    // Returned pointers are owned by VulkanManager.
+    VkFunctorInitParams getVkFunctorInitParams() const;
+
 private:
     friend class RenderThread;
 
@@ -234,6 +237,12 @@
 
     VkCommandBuffer mDummyCB = VK_NULL_HANDLE;
 
+    // Variables saved to populate VkFunctorInitParams.
+    uint32_t mInstanceVersion = 0u;
+    std::vector<const char*> mInstanceExtensions;
+    std::vector<const char*> mDeviceExtensions;
+    VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures2{};
+
     enum class SwapBehavior {
         Discard,
         BufferAge,
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 793aa27..5516086 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
@@ -816,7 +817,7 @@
      *
      * @return The audio frame size in bytes corresponding to the encoding and the channel mask.
      */
-    public int getFrameSizeInBytes() {
+    public @IntRange(from = 1) int getFrameSizeInBytes() {
         return mFrameSizeInBytes;
     }
 
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index df96994..aa79c41 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -60,6 +60,7 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -67,6 +68,7 @@
 import java.util.Map;
 import java.util.Queue;
 import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
@@ -399,8 +401,7 @@
         clearSourceInfos();
 
         // Modular DRM clean up
-        mOnDrmConfigHelper = null;
-        synchronized (mDrmEventCbLock) {
+        synchronized (mDrmEventCallbackLock) {
             mDrmEventCallbackRecords.clear();
         }
 
@@ -775,7 +776,7 @@
                 }
                 boolean hasError = false;
                 for (DataSourceDesc dsd : dsds) {
-                    if (dsd != null) {
+                    if (dsd == null) {
                         hasError = true;
                         continue;
                     }
@@ -2889,7 +2890,7 @@
     }
 
     private void sendDrmEvent(final DrmEventNotifier notifier) {
-        synchronized (mDrmEventCbLock) {
+        synchronized (mDrmEventCallbackLock) {
             try {
                 for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
                     cb.first.execute(() -> notifier.notify(cb.second));
@@ -2901,12 +2902,28 @@
         }
     }
 
+    private <T> T sendDrmEventWait(final DrmEventNotifier<T> notifier)
+            throws InterruptedException, ExecutionException {
+        synchronized (mDrmEventCallbackLock) {
+            mDrmEventCallbackRecords.get(0);
+            for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+                CompletableFuture<T> ret = new CompletableFuture<>();
+                cb.first.execute(() -> ret.complete(notifier.notifyWait(cb.second)));
+                return ret.get();
+            }
+        }
+        return null;
+    }
+
     private interface EventNotifier {
         void notify(EventCallback callback);
     }
 
-    private interface DrmEventNotifier {
-        void notify(DrmEventCallback callback);
+    private interface DrmEventNotifier<T> {
+        default void notify(DrmEventCallback callback) { }
+        default T notifyWait(DrmEventCallback callback) {
+            return null;
+        }
     }
 
     /* Do not change these values without updating their counterparts
@@ -3351,73 +3368,209 @@
     // Modular DRM begin
 
     /**
-     * Interface definition of a callback to be invoked when the app
-     * can do DRM configuration (get/set properties) before the session
-     * is opened. This facilitates configuration of the properties, like
-     * 'securityLevel', which has to be set after DRM scheme creation but
-     * before the DRM session is opened.
+     * An immutable structure per {@link DataSourceDesc} with settings required to initiate a DRM
+     * protected playback session.
      *
-     * The only allowed DRM calls in this listener are
-     * {@link MediaPlayer2#getDrmPropertyString(DataSourceDesc, String)}
-     * and {@link MediaPlayer2#setDrmPropertyString(DataSourceDesc, String, String)}.
-     * @hide
+     * @see DrmPreparationInfo.Builder
      */
-    public interface OnDrmConfigHelper {
+    public static final class DrmPreparationInfo {
+
         /**
-         * Called to give the app the opportunity to configure DRM before the session is created
-         *
-         * @param mp the {@code MediaPlayer2} associated with this callback
-         * @param dsd the DataSourceDesc of this data source
+         * Mutable builder to create a {@link MediaPlayer2.DrmPreparationInfo} object.
          */
-        public void onDrmConfig(MediaPlayer2 mp, DataSourceDesc dsd);
-    }
+        public static final class Builder {
 
-    /**
-     * Register a callback to be invoked for configuration of the DRM object before
-     * the session is created.
-     * The callback will be invoked synchronously during the execution
-     * of {@link #prepareDrm}.
-     *
-     * @param listener the callback that will be run
-     * @hide
-     */
-    // This is a synchronous call.
-    public void setOnDrmConfigHelper(OnDrmConfigHelper listener) {
-        mOnDrmConfigHelper = listener;
-    }
+            private UUID mUUID;
+            private byte[] mKeySetId;
+            private byte[] mInitData;
+            private String mMimeType;
+            private int mKeyType;
+            private Map<String, String> mOptionalParameters;
 
-    private OnDrmConfigHelper mOnDrmConfigHelper;
+            /**
+             * Set UUID of the crypto scheme selected to decrypt content. An UUID can be retrieved from
+             * the source listening to {@link MediaPlayer2.DrmEventCallback#onDrmInfo}.
+             *
+             * @param uuid of selected crypto scheme
+             * @return this
+             */
+            public Builder setUuid(@NonNull UUID uuid) {
+                this.mUUID = uuid;
+                return this;
+            }
+
+            /**
+             * Set identifier of a persisted offline key obtained from
+             * {@link MediaPlayer2.DrmEventCallback#onDrmPrepared(MediaPlayer2, DataSourceDesc, int, byte[])}.
+             *
+             * A {@code keySetId} can be used to restore persisted offline keys into a new playback
+             * session of a DRM protected data source. When {@code keySetId} is set, {@code initData},
+             * {@code mimeType}, {@code keyType}, {@code optionalParameters} are ignored.
+             *
+             * @param keySetId identifier of a persisted offline key
+             * @return this
+             */
+            public Builder setKeySetId(@Nullable byte[] keySetId) {
+                this.mKeySetId = keySetId;
+                return this;
+            }
+
+            /**
+             * Set container-specific DRM initialization data. Its meaning is interpreted based on
+             * {@code mimeType}. For example, it could contain the content ID, key ID or other data
+             * obtained from the content metadata that is required to generate a
+             * {@link MediaDrm.KeyRequest}.
+             *
+             * @param initData container-specific DRM initialization data
+             * @return this
+             */
+            public Builder setInitData(@Nullable byte[] initData) {
+                this.mInitData = initData;
+                return this;
+            }
+
+            /**
+             * Set mime type of the content
+             *
+             * @param mimeType mime type to the content
+             * @return this
+             */
+            public Builder setMimeType(@Nullable String mimeType) {
+                this.mMimeType = mimeType;
+                return this;
+            }
+
+            /**
+             * Set type of the key request. The request may be to acquire keys
+             * for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content,
+             * {@link MediaDrm#KEY_TYPE_OFFLINE}. Releasing previously acquired keys
+             * ({@link MediaDrm#KEY_TYPE_RELEASE}) is not allowed.
+             *
+             * @param keyType type of the key request
+             * @return this
+             */
+            public Builder setKeyType(@MediaPlayer2.MediaDrmKeyType int keyType) {
+                this.mKeyType = keyType;
+                return this;
+            }
+
+            /**
+             * Set optional parameters to be included in a {@link MediaDrm.KeyRequest} message sent to
+             * the license server.
+             *
+             * @param optionalParameters optional parameters to be included in a key request
+             * @return this
+             */
+            public Builder setOptionalParameters(
+                    @Nullable Map<String, String> optionalParameters) {
+                this.mOptionalParameters = optionalParameters;
+                return this;
+            }
+
+            /**
+             * @return an immutable {@link MediaPlayer2.DrmPreparationInfo} representing the settings of this builder
+             */
+            public MediaPlayer2.DrmPreparationInfo build() {
+                return new MediaPlayer2.DrmPreparationInfo(mUUID, mKeySetId, mInitData, mMimeType, mKeyType,
+                        mOptionalParameters);
+            }
+
+        }
+
+        private final UUID mUUID;
+        private final byte[] mKeySetId;
+        private final byte[] mInitData;
+        private final String mMimeType;
+        private final int mKeyType;
+        private final Map<String, String> mOptionalParameters;
+
+        private DrmPreparationInfo(UUID mUUID, byte[] mKeySetId, byte[] mInitData, String mMimeType,
+                int mKeyType, Map<String, String> optionalParameters) {
+            this.mUUID = mUUID;
+            this.mKeySetId = mKeySetId;
+            this.mInitData = mInitData;
+            this.mMimeType = mMimeType;
+            this.mKeyType = mKeyType;
+            this.mOptionalParameters = optionalParameters;
+        }
+
+    }
 
     /**
      * Interface definition for callbacks to be invoked when the player has the corresponding
      * DRM events.
-     * @hide
      */
     public static class DrmEventCallback {
         /**
-         * Called to indicate DRM info is available
+         * Called to indicate DRM info is available. Return a {@link DrmPreparationInfo} object that
+         * bundles DRM initialization parameters.
          *
          * @param mp the {@code MediaPlayer2} associated with this callback
-         * @param dsd the DataSourceDesc of this data source
-         * @param drmInfo DRM info of the source including PSSH, and subset
-         *                of crypto schemes supported by this device
+         * @param dsd the {@link DataSourceDesc} of this data source
+         * @param drmInfo DRM info of the source including PSSH, and subset of crypto schemes
+         *        supported by this device
+         * @return a {@link DrmPreparationInfo} object to initialize DRM playback, or null to skip
+         *         DRM initialization
          */
-        public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) { }
+        public DrmPreparationInfo onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) {
+            return null;
+        }
 
         /**
-         * Called to notify the client that {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)}
-         * is finished and ready for key request/response.
+         * Called to notify the client that {@code mp} is ready to decrypt DRM protected data source
+         * {@code dsd}
          *
          * @param mp the {@code MediaPlayer2} associated with this callback
-         * @param dsd the DataSourceDesc of this data source
+         * @param dsd the {@link DataSourceDesc} of this data source
          * @param status the result of DRM preparation.
+         * @param keySetId optional identifier that can be used to restore DRM playback initiated
+         *        with a {@link MediaDrm#KEY_TYPE_OFFLINE} key request.
+         *
+         * @see DrmPreparationInfo.Builder#setKeySetId(byte[])
          */
-        public void onDrmPrepared(
-                MediaPlayer2 mp, DataSourceDesc dsd, @PrepareDrmStatusCode int status) { }
+        public void onDrmPrepared(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @PrepareDrmStatusCode int status, @Nullable byte[] keySetId) { }
+
+        /**
+         * Called to give the app the opportunity to configure DRM before the session is created.
+         *
+         * This facilitates configuration of the properties, like 'securityLevel', which
+         * has to be set after DRM scheme creation but before the DRM session is opened.
+         *
+         * The only allowed DRM calls in this listener are
+         * {@link MediaDrm#getPropertyString(String)},
+         * {@link MediaDrm#getPropertyByteArray(String)},
+         * {@link MediaDrm#setPropertyString(String, String)},
+         * {@link MediaDrm#setPropertyByteArray(String, byte[])},
+         * {@link MediaDrm#setOnExpirationUpdateListener},
+         * and {@link MediaDrm#setOnKeyStatusChangeListener}.
+         *
+         * @param mp the {@code MediaPlayer2} associated with this callback
+         * @param dsd the {@link DataSourceDesc} of this data source
+         * @param drm handle to get/set DRM properties and listeners for this data source
+         */
+        public void onDrmConfig(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @NonNull MediaDrm drm) { }
+
+        /**
+         * Called to indicate the DRM session for {@code dsd} is ready for key request/response
+         *
+         * @param mp the {@code MediaPlayer2} associated with this callback
+         * @param dsd the {@link DataSourceDesc} of this data source
+         * @param request a {@link MediaDrm.KeyRequest} prepared using the
+         *        {@link DrmPreparationInfo} returned from
+         *        {@link #onDrmInfo(MediaPlayer2, DataSourceDesc, DrmInfo)}
+         * @return the response to {@code request} (from license server)
+         */
+        public byte[] onDrmKeyRequest(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
+                @NonNull MediaDrm.KeyRequest request) {
+            return null;
+        }
+
     }
 
-    private final Object mDrmEventCbLock = new Object();
-    private ArrayList<Pair<Executor, DrmEventCallback>> mDrmEventCallbackRecords =
+    private final Object mDrmEventCallbackLock = new Object();
+    private List<Pair<Executor, DrmEventCallback>> mDrmEventCallbackRecords =
             new ArrayList<Pair<Executor, DrmEventCallback>>();
 
     /**
@@ -3425,10 +3578,9 @@
      *
      * @param eventCallback the callback that will be run
      * @param executor the executor through which the callback should be invoked
-     * @hide
      */
     // This is a synchronous call.
-    public void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
+    public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull DrmEventCallback eventCallback) {
         if (eventCallback == null) {
             throw new IllegalArgumentException("Illegal null EventCallback");
@@ -3437,8 +3589,9 @@
             throw new IllegalArgumentException(
                     "Illegal null Executor for the EventCallback");
         }
-        synchronized (mDrmEventCbLock) {
-            mDrmEventCallbackRecords.add(new Pair(executor, eventCallback));
+        synchronized (mDrmEventCallbackLock) {
+            mDrmEventCallbackRecords = Collections.singletonList(
+                    new Pair<Executor, DrmEventCallback>(executor, eventCallback));
         }
     }
 
@@ -3450,7 +3603,7 @@
      */
     // This is a synchronous call.
     public void unregisterDrmEventCallback(DrmEventCallback eventCallback) {
-        synchronized (mDrmEventCbLock) {
+        synchronized (mDrmEventCallbackLock) {
             for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
                 if (cb.second == eventCallback) {
                     mDrmEventCallbackRecords.remove(cb);
@@ -3564,7 +3717,7 @@
     /**
      * Prepares the DRM for the given data source
      * <p>
-     * If {@link OnDrmConfigHelper} is registered, it will be called during
+     * If {@link DrmEventCallback} is registered, it will be called during
      * preparation to allow configuration of the DRM properties before opening the
      * DRM session. It should be used only for a series of
      * {@link #getDrmPropertyString(DataSourceDesc, String)} and
@@ -3587,8 +3740,7 @@
      * @param dsd The DRM protected data source
      *
      * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved
-     * from the source through {@link #getDrmInfo(DataSourceDesc)} or registering a
-     * {@link DrmEventCallback#onDrmInfo}.
+     * from the source listening to {@link DrmEventCallback#onDrmInfo}.
      *
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      * @hide
@@ -3661,8 +3813,8 @@
                     sendDrmEvent(new DrmEventNotifier() {
                         @Override
                         public void notify(DrmEventCallback callback) {
-                            callback.onDrmPrepared(
-                                    MediaPlayer2.this, dsd, prepareDrmStatus);
+                            callback.onDrmPrepared(MediaPlayer2.this, dsd, prepareDrmStatus,
+                                    /* TODO: keySetId */ null);
                         }
                     });
 
@@ -3876,7 +4028,6 @@
 
     /**
      * Encapsulates the DRM properties of the source.
-     * @hide
      */
     public static final class DrmInfo {
         private Map<UUID, byte[]> mMapPssh;
@@ -4013,10 +4164,8 @@
     };  // DrmInfo
 
     /**
-     * Thrown when a DRM method is called before preparing a DRM scheme through
-     * {@link MediaPlayer2#prepareDrm(DataSourceDesc, UUID)}.
+     * Thrown when a DRM method is called when there is no active DRM session.
      * Extends MediaDrm.MediaDrmException
-     * @hide
      */
     public static final class NoDrmSchemeException extends MediaDrmException {
         public NoDrmSchemeException(String detailMessage) {
@@ -4291,9 +4440,9 @@
         }
 
         void prepare(UUID uuid) throws UnsupportedSchemeException,
-                ResourceBusyException, NotProvisionedException {
-            final OnDrmConfigHelper onDrmConfigHelper = mOnDrmConfigHelper;
-            Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + onDrmConfigHelper);
+                ResourceBusyException, NotProvisionedException, InterruptedException,
+                ExecutionException {
+            Log.v(TAG, "prepareDrm: uuid: " + uuid);
 
             synchronized (this) {
                 if (mActiveDrmUUID != null) {
@@ -4334,9 +4483,13 @@
             }  // synchronized
 
             // call the callback outside the lock
-            if (onDrmConfigHelper != null)  {
-                onDrmConfigHelper.onDrmConfig(MediaPlayer2.this, mDSD);
-            }
+            sendDrmEventWait(new DrmEventNotifier<Void>() {
+                @Override
+                public Void notifyWait(DrmEventCallback callback) {
+                    callback.onDrmConfig(MediaPlayer2.this, mDSD, mDrmObj);
+                    return null;
+                }
+            });
 
             synchronized (this) {
                 mDrmConfigAllowed = false;
@@ -4506,7 +4659,7 @@
                 @Override
                 public void notify(DrmEventCallback callback) {
                     callback.onDrmPrepared(
-                            MediaPlayer2.this, mDSD, finalStatus);
+                            MediaPlayer2.this, mDSD, finalStatus, /* TODO: keySetId */ null);
                 }
             });
 
diff --git a/native/webview/plat_support/draw_fn.h b/native/webview/plat_support/draw_fn.h
index bb2ee9b..0490e65 100644
--- a/native/webview/plat_support/draw_fn.h
+++ b/native/webview/plat_support/draw_fn.h
@@ -74,7 +74,10 @@
   VkQueue queue;
   uint32_t graphics_queue_index;
   uint32_t instance_version;
-  const char* const* enabled_extension_names;
+  const char* const* enabled_instance_extension_names;
+  uint32_t enabled_instance_extension_names_length;
+  const char* const* enabled_device_extension_names;
+  uint32_t enabled_device_extension_names_length;
   // Only one of device_features and device_features_2 should be non-null.
   // If both are null then no features are enabled.
   VkPhysicalDeviceFeatures* device_features;
@@ -128,15 +131,13 @@
 
 struct AwDrawFn_PostDrawVkParams {
   int version;
-
-  // Input: Fence for the composite command buffer to signal it has finished its
-  // work on the GPU.
-  int fd;
 };
 
 // Called on render thread while UI thread is blocked. Called for both GL and
 // VK.
-typedef void AwDrawFn_OnSync(int functor, void* data, AwDrawFn_OnSyncParams* params);
+typedef void AwDrawFn_OnSync(int functor,
+                             void* data,
+                             AwDrawFn_OnSyncParams* params);
 
 // Called on render thread when either the context is destroyed _or_ when the
 // functor's last reference goes away. Will always be called with an active
@@ -150,17 +151,24 @@
 typedef void AwDrawFn_OnDestroyed(int functor, void* data);
 
 // Only called for GL.
-typedef void AwDrawFn_DrawGL(int functor, void* data, AwDrawFn_DrawGLParams* params);
+typedef void AwDrawFn_DrawGL(int functor,
+                             void* data,
+                             AwDrawFn_DrawGLParams* params);
 
 // Initialize vulkan state. Needs to be called again after any
 // OnContextDestroyed. Only called for Vulkan.
-typedef void AwDrawFn_InitVk(int functor, void* data, AwDrawFn_InitVkParams* params);
+typedef void AwDrawFn_InitVk(int functor,
+                             void* data,
+                             AwDrawFn_InitVkParams* params);
 
 // Only called for Vulkan.
-typedef void AwDrawFn_DrawVk(int functor, void* data, AwDrawFn_DrawVkParams* params);
+typedef void AwDrawFn_DrawVk(int functor,
+                             void* data,
+                             AwDrawFn_DrawVkParams* params);
 
 // Only called for Vulkan.
-typedef void AwDrawFn_PostDrawVk(int functor, void* data,
+typedef void AwDrawFn_PostDrawVk(int functor,
+                                 void* data,
                                  AwDrawFn_PostDrawVkParams* params);
 
 struct AwDrawFnFunctorCallbacks {
@@ -183,7 +191,8 @@
 typedef AwDrawFnRenderMode AwDrawFn_QueryRenderMode(void);
 
 // Create a functor. |functor_callbacks| should be valid until OnDestroyed.
-typedef int AwDrawFn_CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks);
+typedef int AwDrawFn_CreateFunctor(void* data,
+                                   AwDrawFnFunctorCallbacks* functor_callbacks);
 
 // May be called on any thread to signal that the functor should be destroyed.
 // The functor will receive an onDestroyed when the last usage of it is
diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp
index 6c1ceab..b97bbc3 100644
--- a/native/webview/plat_support/draw_functor.cpp
+++ b/native/webview/plat_support/draw_functor.cpp
@@ -74,6 +74,79 @@
   support->callbacks.draw_gl(functor, support->data, &params);
 }
 
+void initializeVk(int functor, void* data,
+                  const uirenderer::VkFunctorInitParams& init_vk_params) {
+  SupportData* support = static_cast<SupportData*>(data);
+  VkPhysicalDeviceFeatures2 device_features_2;
+  if (init_vk_params.device_features_2)
+    device_features_2 = *init_vk_params.device_features_2;
+
+  AwDrawFn_InitVkParams params{
+      .version = kAwDrawFnVersion,
+      .instance = init_vk_params.instance,
+      .physical_device = init_vk_params.physical_device,
+      .device = init_vk_params.device,
+      .queue = init_vk_params.queue,
+      .graphics_queue_index = init_vk_params.graphics_queue_index,
+      .instance_version = init_vk_params.instance_version,
+      .enabled_instance_extension_names =
+          init_vk_params.enabled_instance_extension_names,
+      .enabled_instance_extension_names_length =
+          init_vk_params.enabled_instance_extension_names_length,
+      .enabled_device_extension_names =
+          init_vk_params.enabled_device_extension_names,
+      .enabled_device_extension_names_length =
+          init_vk_params.enabled_device_extension_names_length,
+      .device_features = nullptr,
+      .device_features_2 =
+          init_vk_params.device_features_2 ? &device_features_2 : nullptr,
+  };
+  support->callbacks.init_vk(functor, support->data, &params);
+}
+
+void drawVk(int functor, void* data, const uirenderer::VkFunctorDrawParams& draw_vk_params) {
+  SupportData* support = static_cast<SupportData*>(data);
+  float gabcdef[7];
+  draw_vk_params.color_space_ptr->transferFn(gabcdef);
+  AwDrawFn_DrawVkParams params{
+      .version = kAwDrawFnVersion,
+      .width = draw_vk_params.width,
+      .height = draw_vk_params.height,
+      .is_layer = draw_vk_params.is_layer,
+      .secondary_command_buffer = draw_vk_params.secondary_command_buffer,
+      .color_attachment_index = draw_vk_params.color_attachment_index,
+      .compatible_render_pass = draw_vk_params.compatible_render_pass,
+      .format = draw_vk_params.format,
+      .transfer_function_g = gabcdef[0],
+      .transfer_function_a = gabcdef[1],
+      .transfer_function_b = gabcdef[2],
+      .transfer_function_c = gabcdef[3],
+      .transfer_function_d = gabcdef[4],
+      .transfer_function_e = gabcdef[5],
+      .transfer_function_f = gabcdef[6],
+      .clip_left = draw_vk_params.clip_left,
+      .clip_top = draw_vk_params.clip_top,
+      .clip_right = draw_vk_params.clip_right,
+      .clip_bottom = draw_vk_params.clip_bottom,
+  };
+  COMPILE_ASSERT(sizeof(params.color_space_toXYZD50) == sizeof(skcms_Matrix3x3),
+                 gamut_transform_size_mismatch);
+  draw_vk_params.color_space_ptr->toXYZD50(
+      reinterpret_cast<skcms_Matrix3x3*>(&params.color_space_toXYZD50));
+  COMPILE_ASSERT(NELEM(params.transform) == NELEM(draw_vk_params.transform),
+                 mismatched_transform_matrix_sizes);
+  for (int i = 0; i < NELEM(params.transform); ++i) {
+    params.transform[i] = draw_vk_params.transform[i];
+  }
+  support->callbacks.draw_vk(functor, support->data, &params);
+}
+
+void postDrawVk(int functor, void* data) {
+  SupportData* support = static_cast<SupportData*>(data);
+  AwDrawFn_PostDrawVkParams params{.version = kAwDrawFnVersion};
+  support->callbacks.post_draw_vk(functor, support->data, &params);
+}
+
 int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) {
   static bool callbacks_initialized = false;
   static uirenderer::WebViewFunctorCallbacks webview_functor_callbacks = {
@@ -82,9 +155,19 @@
       .onDestroyed = &onDestroyed,
   };
   if (!callbacks_initialized) {
-    // Under uirenderer::RenderMode::Vulkan, whether gles or vk union should
-    // be populated should match whether the vk-gl interop is used.
-    webview_functor_callbacks.gles.draw = &draw_gl;
+    switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) {
+      case uirenderer::RenderMode::OpenGL_ES:
+        webview_functor_callbacks.gles.draw = &draw_gl;
+        break;
+      case uirenderer::RenderMode::Vulkan:
+        webview_functor_callbacks.vk.initialize = &initializeVk;
+        webview_functor_callbacks.vk.draw = &drawVk;
+        webview_functor_callbacks.vk.postDraw = &postDrawVk;
+        // TODO(boliu): Remove this once SkiaRecordingCanvas::drawWebViewFunctor
+        // no longer uses GL interop.
+        webview_functor_callbacks.gles.draw = &draw_gl;
+        break;
+    }
     callbacks_initialized = true;
   }
   SupportData* support = new SupportData{
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 03c6205..9e89b89 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -99,8 +99,12 @@
     <string name="connected_via_network_scorer_default">Automatically connected via network rating provider</string>
     <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] -->
     <string name="connected_via_passpoint">Connected via %1$s</string>
+    <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] -->
+    <string name="ssid_by_passpoint_provider"><xliff:g id="ssid" example="Cafe Wifi">%1$s</xliff:g> by <xliff:g id="passpointProvider" example="Passpoint Provider">%2$s</xliff:g></string>
     <!-- Status message of Wi-Fi when network has matching passpoint credentials. [CHAR LIMIT=NONE] -->
     <string name="available_via_passpoint">Available via %1$s</string>
+    <!-- Status message of OSU Provider network when not connected. [CHAR LIMIT=NONE] -->
+    <string name="tap_to_set_up">Tap to set up</string>
     <!-- Package name for Settings app-->
     <string name="settings_package" translatable="false">com.android.settings</string>
     <!-- Package name for Certinstaller app-->
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index af5a24f..a97931a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -41,6 +41,7 @@
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiNetworkScoreCache;
+import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -182,6 +183,10 @@
 
     public static final int UNREACHABLE_RSSI = Integer.MIN_VALUE;
 
+    public static final String KEY_PREFIX_AP = "AP:";
+    public static final String KEY_PREFIX_FQDN = "FQDN:";
+    public static final String KEY_PREFIX_OSU = "OSU:";
+
     private final Context mContext;
 
     private String ssid;
@@ -204,9 +209,6 @@
     @Speed private int mSpeed = Speed.NONE;
     private boolean mIsScoredNetworkMetered = false;
 
-    // used to co-relate internal vs returned accesspoint.
-    int mId;
-
     /**
      * Information associated with the {@link PasspointConfiguration}.  Only maintaining
      * the relevant info to preserve spaces.
@@ -215,6 +217,8 @@
     private String mProviderFriendlyName;
 
     private boolean mIsCarrierAp = false;
+
+    private OsuProvider mOsuProvider;
     /**
      * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP.
      */
@@ -280,14 +284,18 @@
         // Calculate required fields
         updateKey();
         updateRssi();
-
-        mId = sLastId.incrementAndGet();
     }
 
+    /**
+     * Creates an AccessPoint with only a WifiConfiguration. This is used for the saved networks
+     * page.
+     *
+     * Passpoint Credential AccessPoints should be created with this.
+     * Make sure to call setScanResults after constructing with this.
+     */
     public AccessPoint(Context context, WifiConfiguration config) {
         mContext = context;
         loadConfig(config);
-        mId = sLastId.incrementAndGet();
     }
 
     /**
@@ -298,7 +306,19 @@
         mContext = context;
         mFqdn = config.getHomeSp().getFqdn();
         mProviderFriendlyName = config.getHomeSp().getFriendlyName();
-        mId = sLastId.incrementAndGet();
+    }
+
+    /**
+     * Initialize an AccessPoint object for a Passpoint OSU Provider.
+     * Make sure to call setScanResults after constructing with this.
+     */
+    public AccessPoint(Context context, OsuProvider provider) {
+        mContext = context;
+        mOsuProvider = provider;
+        mRssi = 1;
+        // TODO: This placeholder SSID is here to avoid null pointer exceptions.
+        ssid = "<OsuProvider AP SSID goes here>";
+        updateKey();
     }
 
     AccessPoint(Context context, Collection<ScanResult> results) {
@@ -324,8 +344,6 @@
         mIsCarrierAp = firstResult.isCarrierAp;
         mCarrierApEapType = firstResult.carrierApEapType;
         mCarrierName = firstResult.carrierName;
-
-        mId = sLastId.incrementAndGet();
     }
 
     @VisibleForTesting void loadConfig(WifiConfiguration config) {
@@ -344,14 +362,19 @@
         StringBuilder builder = new StringBuilder();
 
         if (isPasspoint()) {
-            builder.append(mConfig.FQDN);
-        } else if (TextUtils.isEmpty(getSsidStr())) {
-            builder.append(getBssid());
-        } else {
-            builder.append(getSsidStr());
+            builder.append(KEY_PREFIX_FQDN).append(mConfig.FQDN);
+        } else if (isOsuProvider()) {
+            builder.append(KEY_PREFIX_OSU).append(mOsuProvider.getOsuSsid());
+            builder.append(',').append(mOsuProvider.getServerUri());
+        } else { // Non-Passpoint AP
+            builder.append(KEY_PREFIX_AP);
+            if (TextUtils.isEmpty(getSsidStr())) {
+                builder.append(getBssid());
+            } else {
+                builder.append(getSsidStr());
+            }
+            builder.append(',').append(getSecurity());
         }
-
-        builder.append(',').append(getSecurity());
         mKey = builder.toString();
     }
 
@@ -396,8 +419,8 @@
             return difference;
         }
 
-        // Sort by ssid.
-        difference = getSsidStr().compareToIgnoreCase(other.getSsidStr());
+        // Sort by title.
+        difference = getTitle().compareToIgnoreCase(other.getTitle());
         if (difference != 0) {
             return difference;
         }
@@ -595,6 +618,7 @@
     public static String getKey(ScanResult result) {
         StringBuilder builder = new StringBuilder();
 
+        builder.append(KEY_PREFIX_AP);
         if (TextUtils.isEmpty(result.SSID)) {
             builder.append(result.BSSID);
         } else {
@@ -609,14 +633,17 @@
         StringBuilder builder = new StringBuilder();
 
         if (config.isPasspoint()) {
-            builder.append(config.FQDN);
-        } else if (TextUtils.isEmpty(config.SSID)) {
-            builder.append(config.BSSID);
+            builder.append(KEY_PREFIX_FQDN).append(config.FQDN);
         } else {
-            builder.append(removeDoubleQuotes(config.SSID));
+            builder.append(KEY_PREFIX_AP);
+            if (TextUtils.isEmpty(config.SSID)) {
+                builder.append(config.BSSID);
+            } else {
+                builder.append(removeDoubleQuotes(config.SSID));
+            }
+            builder.append(',').append(getSecurity(config));
         }
 
-        builder.append(',').append(getSecurity(config));
         return builder.toString();
     }
 
@@ -839,91 +866,97 @@
     public String getTitle() {
         if (isPasspoint()) {
             return mConfig.providerFriendlyName;
+        } else if (isOsuProvider()) {
+            return mOsuProvider.getFriendlyName();
         } else {
             return getSsidStr();
         }
     }
 
     public String getSummary() {
-        return getSettingsSummary(mConfig);
+        return getSettingsSummary();
     }
 
     public String getSettingsSummary() {
-        return getSettingsSummary(mConfig);
-    }
-
-    private String getSettingsSummary(WifiConfiguration config) {
         // Update to new summary
         StringBuilder summary = new StringBuilder();
 
-        if (isActive() && config != null && config.isPasspoint()) {
-            // This is the active connection on passpoint
-            summary.append(getSummary(mContext, getDetailedState(),
-                    false, config.providerFriendlyName));
-        } else if (isActive() && config != null && getDetailedState() == DetailedState.CONNECTED
-                && mIsCarrierAp) {
-            summary.append(String.format(mContext.getString(R.string.connected_via_carrier), mCarrierName));
-        } else if (isActive()) {
-            // This is the active connection on non-passpoint network
-            summary.append(getSummary(mContext, getDetailedState(),
-                    mInfo != null && mInfo.isEphemeral()));
-        } else if (config != null && config.isPasspoint()
-                && config.getNetworkSelectionStatus().isNetworkEnabled()) {
-            String format = mContext.getString(R.string.available_via_passpoint);
-            summary.append(String.format(format, config.providerFriendlyName));
-        } else if (config != null && config.hasNoInternetAccess()) {
-            int messageID = config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()
-                    ? R.string.wifi_no_internet_no_reconnect
-                    : R.string.wifi_no_internet;
-            summary.append(mContext.getString(messageID));
-        } else if (config != null && !config.getNetworkSelectionStatus().isNetworkEnabled()) {
-            WifiConfiguration.NetworkSelectionStatus networkStatus =
-                    config.getNetworkSelectionStatus();
-            switch (networkStatus.getNetworkSelectionDisableReason()) {
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE:
-                    summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
-                    break;
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD:
-                    summary.append(mContext.getString(R.string.wifi_check_password_try_again));
-                    break;
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE:
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE:
-                    summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
-                    break;
-                case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION:
-                    summary.append(mContext.getString(R.string.wifi_disabled_generic));
-                    break;
+        if (isActive()) {
+            if (isPasspoint()) {
+                // This is the active connection on passpoint
+                summary.append(getSummary(mContext, ssid, getDetailedState(),
+                        false, mConfig.providerFriendlyName));
+            } else if (mConfig != null && getDetailedState() == DetailedState.CONNECTED
+                    && mIsCarrierAp) {
+                // This is the active connection on a carrier AP
+                summary.append(String.format(mContext.getString(R.string.connected_via_carrier),
+                        mCarrierName));
+            } else {
+                // This is the active connection on non-passpoint network
+                summary.append(getSummary(mContext, getDetailedState(),
+                        mInfo != null && mInfo.isEphemeral()));
             }
-        } else if (config != null && config.getNetworkSelectionStatus().isNotRecommended()) {
-            summary.append(mContext.getString(R.string.wifi_disabled_by_recommendation_provider));
-        } else if (mIsCarrierAp) {
-            summary.append(String.format(mContext.getString(R.string.available_via_carrier), mCarrierName));
-        } else if (!isReachable()) { // Wifi out of range
-            summary.append(mContext.getString(R.string.wifi_not_in_range));
-        } else { // In range, not disabled.
-            if (config != null) { // Is saved network
-                // Last attempt to connect to this failed. Show reason why
-                switch (config.recentFailure.getAssociationStatus()) {
-                    case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
-                        summary.append(mContext.getString(
-                                R.string.wifi_ap_unable_to_handle_new_sta));
+        } else { // not active
+            if (mConfig != null && mConfig.hasNoInternetAccess()) {
+                int messageID = mConfig.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()
+                        ? R.string.wifi_no_internet_no_reconnect
+                        : R.string.wifi_no_internet;
+                summary.append(mContext.getString(messageID));
+            } else if (mConfig != null && !mConfig.getNetworkSelectionStatus().isNetworkEnabled()) {
+                WifiConfiguration.NetworkSelectionStatus networkStatus =
+                        mConfig.getNetworkSelectionStatus();
+                switch (networkStatus.getNetworkSelectionDisableReason()) {
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE:
+                        summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
                         break;
-                    default:
-                        // "Saved"
-                        summary.append(mContext.getString(R.string.wifi_remembered));
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD:
+                        summary.append(mContext.getString(R.string.wifi_check_password_try_again));
                         break;
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE:
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE:
+                        summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
+                        break;
+                    case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION:
+                        summary.append(mContext.getString(R.string.wifi_disabled_generic));
+                        break;
+                }
+            } else if (mConfig != null && mConfig.getNetworkSelectionStatus().isNotRecommended()) {
+                summary.append(mContext.getString(
+                        R.string.wifi_disabled_by_recommendation_provider));
+            } else if (mIsCarrierAp) {
+                summary.append(String.format(mContext.getString(
+                        R.string.available_via_carrier), mCarrierName));
+            } else if (isOsuProvider()) {
+                summary.append(mContext.getString(R.string.tap_to_set_up));
+            } else if (!isReachable()) { // Wifi out of range
+                summary.append(mContext.getString(R.string.wifi_not_in_range));
+            } else { // In range, not disabled.
+                if (mConfig != null) { // Is saved network
+                    // Last attempt to connect to this failed. Show reason why
+                    switch (mConfig.recentFailure.getAssociationStatus()) {
+                        case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
+                            summary.append(mContext.getString(
+                                    R.string.wifi_ap_unable_to_handle_new_sta));
+                            break;
+                        default:
+                            // "Saved"
+                            summary.append(mContext.getString(R.string.wifi_remembered));
+                            break;
+                    }
                 }
             }
         }
 
+
+
         if (isVerboseLoggingEnabled()) {
-            summary.append(WifiUtils.buildLoggingSummary(this, config));
+            summary.append(WifiUtils.buildLoggingSummary(this, mConfig));
         }
 
-        if (config != null && (WifiUtils.isMeteredOverridden(config) || config.meteredHint)) {
+        if (mConfig != null && (WifiUtils.isMeteredOverridden(mConfig) || mConfig.meteredHint)) {
             return mContext.getResources().getString(
                     R.string.preference_summary_default_combination,
-                    WifiUtils.getMeteredLabel(mContext, config),
+                    WifiUtils.getMeteredLabel(mContext, mConfig),
                     summary.toString());
         }
 
@@ -976,6 +1009,13 @@
     }
 
     /**
+     * Return true if this AccessPoint represents an OSU Provider.
+     */
+    public boolean isOsuProvider() {
+        return mOsuProvider != null;
+    }
+
+    /**
      * Return whether the given {@link WifiInfo} is for this access point.
      * If the current AP does not have a network Id then the config is used to
      * match based on SSID and security.
@@ -1065,8 +1105,8 @@
     void setScanResults(Collection<ScanResult> scanResults) {
 
         // Validate scan results are for current AP only by matching SSID/BSSID
-        // Passpoint R1 networks are not bound to a specific SSID/BSSID, so skip this for passpoint.
-        if (!isPasspoint()) {
+        // Passpoint networks are not bound to a specific SSID/BSSID, so skip this for passpoint.
+        if (!isPasspoint() && !isOsuProvider()) {
             String key = getKey();
             for (ScanResult result : scanResults) {
                 String scanResultKey = AccessPoint.getKey(result);
@@ -1119,7 +1159,17 @@
         }
     }
 
-    /** Attempt to update the AccessPoint and return true if an update occurred. */
+    /**
+     * Attempt to update the AccessPoint with the current connection info.
+     * This is used to set an AccessPoint to the active one if the connection info matches, or
+     * conversely to set an AccessPoint to inactive if the connection info does not match. The RSSI
+     * is also updated upon a match. Listeners will be notified if an update occurred.
+     *
+     * This is called in {@link WifiTracker#updateAccessPoints} as well as in callbacks for handling
+     * NETWORK_STATE_CHANGED_ACTION, RSSI_CHANGED_ACTION, and onCapabilitiesChanged in WifiTracker.
+     *
+     * Returns true if an update occurred.
+     */
     public boolean update(
             @Nullable WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) {
 
@@ -1246,11 +1296,11 @@
 
     public static String getSummary(Context context, String ssid, DetailedState state,
             boolean isEphemeral, String passpointProvider) {
-        if (state == DetailedState.CONNECTED && ssid == null) {
-            if (TextUtils.isEmpty(passpointProvider) == false) {
+        if (state == DetailedState.CONNECTED) {
+            if (!TextUtils.isEmpty(passpointProvider)) {
                 // Special case for connected + passpoint networks.
-                String format = context.getString(R.string.connected_via_passpoint);
-                return String.format(format, passpointProvider);
+                String format = context.getString(R.string.ssid_by_passpoint_provider);
+                return String.format(format, ssid, passpointProvider);
             } else if (isEphemeral) {
                 // Special case for connected + ephemeral networks.
                 final NetworkScoreManager networkScoreManager = context.getSystemService(
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 79a7240..6d28891 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -35,6 +35,7 @@
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiNetworkScoreCache;
 import android.net.wifi.WifiNetworkScoreCache.CacheListener;
+import android.net.wifi.hotspot2.OsuProvider;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
@@ -584,7 +585,7 @@
                     Map<Integer, List<ScanResult>>> pairing : passpointConfigsAndScans) {
                 WifiConfiguration config = pairing.first;
 
-                // TODO: Prioritize home networks before roaming networks
+                // TODO(b/118705403): Prioritize home networks before roaming networks
                 List<ScanResult> scanResults = new ArrayList<>();
 
                 List<ScanResult> homeScans =
@@ -618,6 +619,23 @@
                 }
             }
 
+            // Add Passpoint OSU Provider AccessPoints
+            // TODO(b/118705403): filter out OSU Providers which we already have credentials from.
+            Map<OsuProvider, List<ScanResult>> providersAndScans =
+                    mWifiManager.getMatchingOsuProviders(cachedScanResults);
+            for (OsuProvider provider : providersAndScans.keySet()) {
+                AccessPoint accessPointOsu = new AccessPoint(mContext, provider);
+                // TODO(b/118705403): accessPointOsu.setScanResults(Matching ScanResult with best
+                // RSSI)
+                // TODO(b/118705403): Figure out if we would need to update an OSU AP (this will be
+                // used if we need to display it at the top of the picker as the "active" AP).
+                // Otherwise, OSU APs should ignore attempts to update the active connection
+                // info.
+                // accessPointOsu.update(connectionConfig, mLastInfo, mLastNetworkInfo);
+                accessPoints.add(accessPointOsu);
+            }
+
+
             // If there were no scan results, create an AP for the currently connected network (if
             // it exists).
             if (accessPoints.isEmpty() && connectionConfig != null) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index bcf37ff..18bdb20 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2363,6 +2363,14 @@
                 SecureSettingsProto.Zen.SETTINGS_SUGGESTION_VIEWED);
         p.end(zenToken);
 
+        dumpSetting(s, p,
+                Settings.Secure.SKIP_GESTURE,
+                SecureSettingsProto.SKIP_GESTURE_ENABLED);
+
+        dumpSetting(s, p,
+                Settings.Secure.SILENCE_GESTURE,
+                SecureSettingsProto.SILENCE_GESTURE_ENABLED);
+
         // Please insert new settings using the same order as in SecureSettingsProto.
         p.end(token);
 
diff --git a/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml b/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml
new file mode 100644
index 0000000..d3c3a51
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="200dp"
+    android:width="200dp"
+    android:viewportHeight="100"
+    android:viewportWidth="100">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M50.082,14.199m-13.985,0a13.985,13.985 0,1 1,27.97 0a13.985,13.985 0,1 1,-27.97 0"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml b/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml
new file mode 100644
index 0000000..a4417fb
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="200dp"
+    android:width="200dp"
+    android:viewportHeight="100"
+    android:viewportWidth="100" >
+    <path
+        android:fillColor="#000000"
+        android:pathData="M50.082,0.025L50.082,0.025A13.985,15.63 0,0 1,64.067 15.656L64.067,49.029A13.985,15.63 0,0 1,50.082 64.659L50.082,64.659A13.985,15.63 0,0 1,36.097 49.029L36.097,15.656A13.985,15.63 0,0 1,50.082 0.025z"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/layout/bubble_clock.xml b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml
new file mode 100644
index 0000000..0d72657
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml
@@ -0,0 +1,52 @@
+<?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.
+  -->
+<com.android.keyguard.clock.ClockLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+  >
+  <TextClock
+      android:id="@+id/digital_clock"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:letterSpacing="0.03"
+      android:singleLine="true"
+      style="@style/widget_big"
+      android:format12Hour="@string/keyguard_widget_12_hours_format"
+      android:format24Hour="@string/keyguard_widget_24_hours_format"
+  />
+  <com.android.keyguard.clock.ImageClock
+      android:id="@+id/analog_clock"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+  >
+    <ImageView
+        android:id="@+id/minute_hand"
+        android:layout_width="300dp"
+        android:layout_height="300dp"
+        android:src="@drawable/bubble_minute_hand"
+        android:tint="@color/bubbleMinuteHandColor"
+    />
+    <ImageView
+        android:id="@+id/hour_hand"
+        android:layout_width="300dp"
+        android:layout_height="300dp"
+        android:src="@drawable/bubble_hour_hand"
+        android:tint="@color/bubbleHourHandColor"
+    />
+  </com.android.keyguard.clock.ImageClock>
+</com.android.keyguard.clock.ClockLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/digital_clock.xml b/packages/SystemUI/res-keyguard/layout/digital_clock.xml
new file mode 100644
index 0000000..cf4a573
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/digital_clock.xml
@@ -0,0 +1,35 @@
+<?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.
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal"
+    android:layout_alignParentTop="true">
+  <TextClock
+      android:id="@+id/lock_screen_clock"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_gravity="center_horizontal"
+      android:letterSpacing="0.03"
+      android:singleLine="true"
+      style="@style/widget_big"
+      android:format12Hour="@string/keyguard_widget_12_hours_format"
+      android:format24Hour="@string/keyguard_widget_24_hours_format" />
+  />
+</FrameLayout>
+
diff --git a/packages/SystemUI/res-keyguard/values/colors.xml b/packages/SystemUI/res-keyguard/values/colors.xml
new file mode 100644
index 0000000..7a849eb
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/values/colors.xml
@@ -0,0 +1,22 @@
+<?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>
+  <!-- Default color for hour hand of Bubble clock. -->
+  <color name="bubbleHourHandColor">#C97343</color>
+  <!-- Default color for minute hand of Bubble clock. -->
+  <color name="bubbleMinuteHandColor">#F5C983</color>
+</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 523720d5..1a684a0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -323,7 +323,7 @@
                     return null;
                 }
                 // Create our own ClassLoader so we can use our own code as the parent.
-                ClassLoader classLoader = mManager.getClassLoader(info.sourceDir, info.packageName);
+                ClassLoader classLoader = mManager.getClassLoader(info);
                 Context pluginContext = new PluginContextWrapper(
                         mContext.createApplicationContext(info, 0), classLoader);
                 Class<?> pluginClass = Class.forName(cls, true, classLoader);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index da143f9..7139708 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.shared.plugins;
 
+import android.app.LoadedApk;
 import android.app.Notification;
 import android.app.Notification.Action;
 import android.app.NotificationManager;
@@ -44,15 +45,17 @@
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
-import com.android.systemui.shared.plugins.PluginInstanceManager.PluginContextWrapper;
 import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
 
 import dalvik.system.PathClassLoader;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 /**
  * @see Plugin
@@ -117,6 +120,7 @@
         return mPluginEnabler;
     }
 
+    // TODO(mankoff): This appears to be only called from tests. Remove?
     public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
         ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
         if (info == null) {
@@ -282,17 +286,25 @@
         }
     }
 
-    public ClassLoader getClassLoader(String sourceDir, String pkg) {
-        if (!isDebuggable && !mWhitelistedPlugins.contains(pkg)) {
-            Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:" + sourceDir +
-                    ", pkg: " + pkg);
+    /** Returns class loader specific for the given plugin. */
+    public ClassLoader getClassLoader(ApplicationInfo appInfo) {
+        if (!isDebuggable && !mWhitelistedPlugins.contains(appInfo.packageName)) {
+            Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:"
+                    + appInfo.sourceDir + ", pkg: " + appInfo.packageName);
             return null;
         }
-        if (mClassLoaders.containsKey(pkg)) {
-            return mClassLoaders.get(pkg);
+        if (mClassLoaders.containsKey(appInfo.packageName)) {
+            return mClassLoaders.get(appInfo.packageName);
         }
-        ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
-        mClassLoaders.put(pkg, classLoader);
+
+        List<String> zipPaths = new ArrayList<>();
+        List<String> libPaths = new ArrayList<>();
+        LoadedApk.makePaths(null, true, appInfo, zipPaths, libPaths);
+        ClassLoader classLoader = new PathClassLoader(
+                TextUtils.join(File.pathSeparator, zipPaths),
+                TextUtils.join(File.pathSeparator, libPaths),
+                getParentClassLoader());
+        mClassLoaders.put(appInfo.packageName, classLoader);
         return classLoader;
     }
 
@@ -309,11 +321,6 @@
         return mParentClassLoader;
     }
 
-    public Context getContext(ApplicationInfo info, String pkg) throws NameNotFoundException {
-        ClassLoader classLoader = getClassLoader(info.sourceDir, pkg);
-        return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader);
-    }
-
     public <T> boolean dependsOn(Plugin p, Class<T> cls) {
         for (int i = 0; i < mPluginMap.size(); i++) {
             if (mPluginMap.valueAt(i).dependsOn(p, cls)) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 3cfd6a9..0ec0bf0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -1,9 +1,15 @@
 package com.android.keyguard;
 
+import android.content.ContentResolver;
 import android.content.Context;
+import android.database.ContentObserver;
 import android.graphics.Paint;
 import android.graphics.Paint.Style;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
 import android.util.AttributeSet;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -12,6 +18,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.keyguard.clock.BubbleClockController;
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.statusbar.StatusBarState;
@@ -19,13 +26,19 @@
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.ExtensionController.Extension;
 
+import java.util.Objects;
 import java.util.TimeZone;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 /**
  * Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
  */
 public class KeyguardClockSwitch extends RelativeLayout {
+
+    private LayoutInflater mLayoutInflater;
+
+    private final ContentResolver mContentResolver;
     /**
      * Optional/alternative clock injected via plugin.
      */
@@ -79,12 +92,25 @@
                 }
     };
 
+    private final ContentObserver mContentObserver =
+            new ContentObserver(new Handler(Looper.getMainLooper())) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    super.onChange(selfChange);
+                    if (mClockExtension != null) {
+                        mClockExtension.reload();
+                    }
+                }
+    };
+
     public KeyguardClockSwitch(Context context) {
         this(context, null);
     }
 
     public KeyguardClockSwitch(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mLayoutInflater = LayoutInflater.from(context);
+        mContentResolver = context.getContentResolver();
     }
 
     /**
@@ -108,7 +134,22 @@
         mClockExtension = Dependency.get(ExtensionController.class).newExtension(ClockPlugin.class)
                 .withPlugin(ClockPlugin.class)
                 .withCallback(mClockPluginConsumer)
+                // Using withDefault even though this isn't the default as a workaround.
+                // ExtensionBulider 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)))
                 .build();
+        mContentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
+                false, mContentObserver);
         Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
     }
 
@@ -116,6 +157,7 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mClockExtension.destroy();
+        mContentResolver.unregisterContentObserver(mContentObserver);
         Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
     }
 
@@ -269,4 +311,44 @@
     StatusBarStateController.StateListener getStateListener() {
         return mStateListener;
     }
+
+    /**
+     * 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/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
new file mode 100644
index 0000000..db6127f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.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.keyguard.clock;
+
+import android.graphics.Paint.Style;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextClock;
+
+import com.android.keyguard.R;
+import com.android.systemui.plugins.ClockPlugin;
+
+import java.util.TimeZone;
+
+/**
+ * Controller for Bubble clock that can appear on lock screen and AOD.
+ */
+public class BubbleClockController implements ClockPlugin {
+
+    /**
+     * Custom clock shown on AOD screen and behind stack scroller on lock.
+     */
+    private View mView;
+    private TextClock mDigitalClock;
+    private ImageClock mAnalogClock;
+
+    /**
+     * Small clock shown on lock screen above stack scroller.
+     */
+    private View mLockClockContainer;
+    private TextClock mLockClock;
+
+    /**
+     * Controller for transition to dark state.
+     */
+    private CrossFadeDarkController mDarkController;
+
+    private BubbleClockController() { }
+
+    /**
+     * Create a BubbleClockController instance.
+     *
+     * @param layoutInflater Inflater used to inflate custom clock views.
+     */
+    public static BubbleClockController build(LayoutInflater layoutInflater) {
+        BubbleClockController controller = new BubbleClockController();
+        controller.createViews(layoutInflater);
+        return controller;
+    }
+
+    private void createViews(LayoutInflater layoutInflater) {
+        mView = layoutInflater.inflate(R.layout.bubble_clock, null);
+        mDigitalClock = (TextClock) mView.findViewById(R.id.digital_clock);
+        mAnalogClock = (ImageClock) mView.findViewById(R.id.analog_clock);
+
+        mLockClockContainer = layoutInflater.inflate(R.layout.digital_clock, null);
+        mLockClock = (TextClock) mLockClockContainer.findViewById(R.id.lock_screen_clock);
+        mLockClock.setVisibility(View.GONE);
+
+        mDarkController = new CrossFadeDarkController(mDigitalClock, mLockClock);
+    }
+
+    @Override
+    public View getView() {
+        return mLockClockContainer;
+    }
+
+    @Override
+    public View getBigClockView() {
+        return mView;
+    }
+
+    @Override
+    public void setStyle(Style style) {}
+
+    @Override
+    public void setTextColor(int color) {
+        mLockClock.setTextColor(color);
+        mDigitalClock.setTextColor(color);
+    }
+
+    @Override
+    public void dozeTimeTick() {
+        mAnalogClock.onTimeChanged();
+    }
+
+    @Override
+    public void setDarkAmount(float darkAmount) {
+        mDarkController.setDarkAmount(darkAmount);
+    }
+
+    @Override
+    public void onTimeZoneChanged(TimeZone timeZone) {
+        mAnalogClock.onTimeZoneChanged(timeZone);
+    }
+
+    @Override
+    public boolean shouldShowStatusArea() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
new file mode 100644
index 0000000..5aa5668
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java
@@ -0,0 +1,93 @@
+/*
+ * 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.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.keyguard.R;
+
+/**
+ * Positions clock faces (analog, digital, typographic) and handles pixel shifting
+ * to prevent screen burn-in.
+ */
+public class ClockLayout extends FrameLayout {
+
+    /**
+     * Clock face views.
+     */
+    private View mDigitalClock;
+    private View mAnalogClock;
+
+    /**
+     * Pixel shifting amplitidues used to prevent screen burn-in.
+     */
+    private int mBurnInPreventionOffsetX;
+    private int mBurnInPreventionOffsetY;
+
+    public ClockLayout(Context context) {
+        this(context, null);
+    }
+
+    public ClockLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ClockLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mDigitalClock = findViewById(R.id.digital_clock);
+        mAnalogClock = findViewById(R.id.analog_clock);
+
+        // Get pixel shifting X, Y amplitudes from resources.
+        Resources resources = getResources();
+        mBurnInPreventionOffsetX = resources.getDimensionPixelSize(
+            R.dimen.burn_in_prevention_offset_x);
+        mBurnInPreventionOffsetY = resources.getDimensionPixelSize(
+            R.dimen.burn_in_prevention_offset_y);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        final float offsetX = getBurnInOffset(mBurnInPreventionOffsetX, true);
+        final float offsetY = getBurnInOffset(mBurnInPreventionOffsetY, false);
+
+        // Put digital clock in two left corner of the screen.
+        if (mDigitalClock != null) {
+            mDigitalClock.setX(0.1f * getWidth() + offsetX);
+            mDigitalClock.setY(0.1f * getHeight() + offsetY);
+        }
+
+        // Put the analog clock in the middle of the screen.
+        if (mAnalogClock != null) {
+            mAnalogClock.setX(Math.max(0f, 0.5f * (getWidth() - mAnalogClock.getWidth()))
+                    + offsetX);
+            mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight()))
+                    + offsetY);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java b/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java
new file mode 100644
index 0000000..3c3f475
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.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 com.android.keyguard.clock;
+
+import android.view.View;
+
+/**
+ * Controls transition to dark state by cross fading between views.
+ */
+final class CrossFadeDarkController {
+
+    private final View mFadeInView;
+    private final View mFadeOutView;
+
+    /**
+     * Creates a new controller that fades between views.
+     *
+     * @param fadeInView View to fade in when transitioning to AOD.
+     * @param fadeOutView View to fade out when transitioning to AOD.
+     */
+    CrossFadeDarkController(View fadeInView, View fadeOutView) {
+        mFadeInView = fadeInView;
+        mFadeOutView = fadeOutView;
+    }
+
+    /**
+     * Sets the amount the system has transitioned to the dark state.
+     *
+     * @param darkAmount Amount of transition to dark state: 1f for AOD and 0f for lock screen.
+     */
+    void setDarkAmount(float darkAmount) {
+        mFadeInView.setAlpha(Math.max(0f, 2f * darkAmount - 1f));
+        if (darkAmount == 0f) {
+            mFadeInView.setVisibility(View.GONE);
+        } else {
+            if (mFadeInView.getVisibility() == View.GONE) {
+                mFadeInView.setVisibility(View.VISIBLE);
+            }
+        }
+        mFadeOutView.setAlpha(Math.max(0f, 1f - 2f * darkAmount));
+        if (darkAmount == 1f) {
+            mFadeOutView.setVisibility(View.GONE);
+        } else {
+            if (mFadeOutView.getVisibility() == View.GONE) {
+                mFadeOutView.setVisibility(View.VISIBLE);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java
new file mode 100644
index 0000000..2c709e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java
@@ -0,0 +1,94 @@
+/*
+ * 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.Context;
+import android.text.format.DateFormat;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.keyguard.R;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * Clock composed of two images that rotate with the time.
+ *
+ * The images are the clock hands. ImageClock expects two child ImageViews
+ * with ids hour_hand and minute_hand.
+ */
+public class ImageClock extends FrameLayout {
+
+    private ImageView mHourHand;
+    private ImageView mMinuteHand;
+    private Calendar mTime;
+    private String mDescFormat;
+    private TimeZone mTimeZone;
+
+    public ImageClock(Context context) {
+        this(context, null);
+    }
+
+    public ImageClock(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ImageClock(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mTime = Calendar.getInstance();
+        mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern();
+    }
+
+    /**
+     * Call when the time changes to update the rotation of the clock hands.
+     */
+    public void onTimeChanged() {
+        mTime.setTimeInMillis(System.currentTimeMillis());
+        final float hourAngle = mTime.get(Calendar.HOUR) * 30f;
+        mHourHand.setRotation(hourAngle);
+        final float minuteAngle = mTime.get(Calendar.MINUTE) * 6f;
+        mMinuteHand.setRotation(minuteAngle);
+        setContentDescription(DateFormat.format(mDescFormat, mTime));
+        invalidate();
+    }
+
+    /**
+     * Call when the time zone has changed to update clock hands.
+     *
+     * @param timeZone The updated time zone that will be used.
+     */
+    public void onTimeZoneChanged(TimeZone timeZone) {
+        mTimeZone = timeZone;
+        mTime.setTimeZone(timeZone);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mHourHand = findViewById(R.id.hour_hand);
+        mMinuteHand = findViewById(R.id.minute_hand);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mTime = Calendar.getInstance(mTimeZone != null ? mTimeZone : TimeZone.getDefault());
+        onTimeChanged();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index d7bf77d..957d772 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -284,8 +284,9 @@
     @Nullable
     private PendingIntent getAppOverlayIntent(NotificationEntry notif) {
         Notification notification = notif.notification.getNotification();
-        if (canLaunchInActivityView(notification.getAppOverlayIntent())) {
-            return notification.getAppOverlayIntent();
+        if (canLaunchInActivityView(notification.getBubbleMetadata() != null
+                ? notification.getBubbleMetadata().getIntent() : null)) {
+            return notification.getBubbleMetadata().getIntent();
         } else if (shouldUseContentIntent(mContext)
                 && canLaunchInActivityView(notification.contentIntent)) {
             Log.d(TAG, "[addBubble " + notif.key
@@ -446,15 +447,16 @@
         StatusBarNotification n = entry.notification;
         boolean canAppOverlay = false;
         try {
-            canAppOverlay = mNotificationManagerService.areAppOverlaysAllowedForPackage(
+            canAppOverlay = mNotificationManagerService.areBubblesAllowedForPackage(
                     n.getPackageName(), n.getUid());
         } catch (RemoteException e) {
             Log.w(TAG, "Error calling NoMan to determine if app can overlay", e);
         }
 
         boolean canChannelOverlay = mNotificationEntryManager.getNotificationData().getChannel(
-                entry.key).canOverlayApps();
-        boolean hasOverlayIntent = n.getNotification().getAppOverlayIntent() != null;
+                entry.key).canBubble();
+        boolean hasOverlayIntent = n.getNotification().getBubbleMetadata() != null
+                && n.getNotification().getBubbleMetadata().getIntent() != null;
         return hasOverlayIntent && canChannelOverlay && canAppOverlay;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 9422101..f79ad71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -541,7 +541,8 @@
 
     @VisibleForTesting
     void doUpdateMobileControllers() {
-        List<SubscriptionInfo> subscriptions = mSubscriptionManager.getActiveSubscriptionInfoList();
+        List<SubscriptionInfo> subscriptions = mSubscriptionManager
+                .getActiveSubscriptionInfoList(true);
         if (subscriptions == null) {
             subscriptions = Collections.emptyList();
         }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java
new file mode 100644
index 0000000..9e946fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package 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.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class BubbleClockControllerTest extends SysuiTestCase {
+
+    private BubbleClockController mClockController;
+
+    @Before
+    public void setUp() {
+        LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+        mClockController = BubbleClockController.build(layoutInflater);
+    }
+
+    @Test
+    public void setDarkAmount_fadeIn() {
+        ViewGroup smallClockFrame = (ViewGroup) mClockController.getView();
+        View smallClock = smallClockFrame.getChildAt(0);
+        // WHEN dark amount is set to AOD
+        mClockController.setDarkAmount(1f);
+        // THEN small clock should not be shown.
+        assertThat(smallClock.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setTextColor_setDigitalClock() {
+        ViewGroup smallClock = (ViewGroup) mClockController.getView();
+        // WHEN text color is set
+        mClockController.setTextColor(42);
+        // THEN child of small clock should have text color set.
+        TextView digitalClock = (TextView) smallClock.getChildAt(0);
+        assertThat(digitalClock.getCurrentTextColor()).isEqualTo(42);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java
new file mode 100644
index 0000000..fd7657f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class CrossFadeDarkControllerTest extends SysuiTestCase {
+
+    private View mViewFadeIn;
+    private View mViewFadeOut;
+    private CrossFadeDarkController mDarkController;
+
+    @Before
+    public void setUp() {
+        mViewFadeIn = new TextView(getContext());
+        mViewFadeIn.setVisibility(View.VISIBLE);
+        mViewFadeIn.setAlpha(1f);
+        mViewFadeOut = new TextView(getContext());
+        mViewFadeOut.setVisibility(View.VISIBLE);
+        mViewFadeOut.setAlpha(1f);
+
+        mDarkController = new CrossFadeDarkController(mViewFadeIn, mViewFadeOut);
+    }
+
+    @Test
+    public void setDarkAmount_fadeIn() {
+        // WHEN dark amount corresponds to AOD
+        mDarkController.setDarkAmount(1f);
+        // THEN fade in view should be faded in and fade out view faded out.
+        assertThat(mViewFadeIn.getAlpha()).isEqualTo(1f);
+        assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewFadeOut.getAlpha()).isEqualTo(0f);
+        assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void setDarkAmount_fadeOut() {
+        // WHEN dark amount corresponds to lock screen
+        mDarkController.setDarkAmount(0f);
+        // THEN fade out view should bed faded out and fade in view faded in.
+        assertThat(mViewFadeIn.getAlpha()).isEqualTo(0f);
+        assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewFadeOut.getAlpha()).isEqualTo(1f);
+        assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void setDarkAmount_partialFadeIn() {
+        // WHEN dark amount corresponds to a partial transition
+        mDarkController.setDarkAmount(0.9f);
+        // THEN views should have intermediate alpha value.
+        assertThat(mViewFadeIn.getAlpha()).isGreaterThan(0f);
+        assertThat(mViewFadeIn.getAlpha()).isLessThan(1f);
+        assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void setDarkAmount_partialFadeOut() {
+        // WHEN dark amount corresponds to a partial transition
+        mDarkController.setDarkAmount(0.1f);
+        // THEN views should have intermediate alpha value.
+        assertThat(mViewFadeOut.getAlpha()).isGreaterThan(0f);
+        assertThat(mViewFadeOut.getAlpha()).isLessThan(1f);
+        assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
index 4583770..3415c72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
@@ -91,7 +91,7 @@
         mMockPm = mock(PackageManager.class);
         mMockListener = mock(PluginListener.class);
         mMockManager = mock(PluginManagerImpl.class);
-        when(mMockManager.getClassLoader(any(), any())).thenReturn(getClass().getClassLoader());
+        when(mMockManager.getClassLoader(any())).thenReturn(getClass().getClassLoader());
         mMockEnabler = mock(PluginEnabler.class);
         when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler);
         mMockVersionInfo = mock(VersionInfo.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
index 76e68f1..536c043 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
@@ -25,6 +25,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -135,9 +136,13 @@
         });
         resetExceptionHandler();
 
+        String sourceDir = "myPlugin";
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.sourceDir = sourceDir;
+        applicationInfo.packageName = WHITELISTED_PACKAGE;
         mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
-        assertNull(mPluginManager.getOneShotPlugin("myPlugin", TestPlugin.class));
-        assertNull(mPluginManager.getClassLoader("myPlugin", WHITELISTED_PACKAGE));
+        assertNull(mPluginManager.getOneShotPlugin(sourceDir, TestPlugin.class));
+        assertNull(mPluginManager.getClassLoader(applicationInfo));
     }
 
     @Test
@@ -152,9 +157,16 @@
         });
         resetExceptionHandler();
 
+        String sourceDir = "myPlugin";
+        ApplicationInfo whiteListedApplicationInfo = new ApplicationInfo();
+        whiteListedApplicationInfo.sourceDir = sourceDir;
+        whiteListedApplicationInfo.packageName = WHITELISTED_PACKAGE;
+        ApplicationInfo invalidApplicationInfo = new ApplicationInfo();
+        invalidApplicationInfo.sourceDir = sourceDir;
+        invalidApplicationInfo.packageName = "com.android.invalidpackage";
         mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
-        assertNotNull(mPluginManager.getClassLoader("myPlugin", WHITELISTED_PACKAGE));
-        assertNull(mPluginManager.getClassLoader("myPlugin", "com.android.invalidpackage"));
+        assertNotNull(mPluginManager.getClassLoader(whiteListedApplicationInfo));
+        assertNull(mPluginManager.getClassLoader(invalidApplicationInfo));
     }
 
     @Test
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 2b13f86..8952594 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -27,6 +27,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.drawable.Icon;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
@@ -220,8 +221,7 @@
             notificationBuilder.setGroup(groupKey);
         }
         if (isBubble) {
-            PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
-            notificationBuilder.setAppOverlayIntent(bubbleIntent);
+            notificationBuilder.setBubbleMetadata(makeBubbleMetadata());
         }
         return notificationBuilder.build();
     }
@@ -282,4 +282,14 @@
         mGroupManager.onEntryAdded(entry);
         return row;
     }
+
+    private Notification.BubbleMetadata makeBubbleMetadata() {
+        PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        return new Notification.BubbleMetadata.Builder()
+                .setIntent(bubbleIntent)
+                .setTitle("bubble title")
+                .setIcon(Icon.createWithResource(mContext, 1))
+                .setDesiredHeight(314)
+                .build();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index fdbf090..c1f8885 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -182,6 +182,7 @@
             subs.add(subscription);
         }
         when(mMockSm.getActiveSubscriptionInfoList()).thenReturn(subs);
+        when(mMockSm.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(subs);
         mNetworkController.doUpdateMobileControllers();
     }
 
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 44edb56..bb5d288 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6803,6 +6803,15 @@
     ACTION_NFC_PAYMENT_ALWAYS_SETTING = 1623;
     // ---- End Q Constants, all Q constants go above this line ----
 
+    // OPEN: Settings > System > Input & Gesture > Skip song gesture
+    // OS: Q
+    SETTINGS_GESTURE_SKIP_SONG = 1624;
+
+    // OPEN: Settings > System > Input & Gesture > Silence gesture
+    // OS: Q
+    SETTINGS_GESTURE_SILENCE = 1625;
+
+    // ---- End Q Constants, all Q constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 24fd7b9..ec6d20d 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -701,6 +701,11 @@
         public void onBackKeyPressed() {
             if (sDebug) Slog.d(TAG, "onBackKeyPressed()");
             mUi.hideAll(null);
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service =
+                        getServiceForUserLocked(UserHandle.getCallingUserId());
+                service.onBackKeyPressed();
+            }
         }
 
         @Override
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index a6bb049..d037b08 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -187,6 +187,15 @@
     }
 
     @GuardedBy("mLock")
+    void onBackKeyPressed() {
+        final RemoteAugmentedAutofillService remoteService =
+                getRemoteAugmentedAutofillServiceLocked();
+        if (remoteService != null) {
+            remoteService.onDestroyAutofillWindowsRequest();
+        }
+    }
+
+    @GuardedBy("mLock")
     @Override // from PerUserSystemService
     protected boolean updateLocked(boolean disabled) {
         destroySessionsLocked();
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index a8ff9b0..5d8d8fa 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -109,8 +109,8 @@
     /**
      * Called by {@link Session} when it's time to destroy all augmented autofill requests.
      */
-    public void onDestroyAutofillWindowsRequest(int sessionId) {
-        scheduleAsyncRequest((s) -> s.onDestroyFillWindowRequest(sessionId));
+    public void onDestroyAutofillWindowsRequest() {
+        scheduleAsyncRequest((s) -> s.onDestroyAllFillWindowsRequest());
     }
 
     // TODO(b/111330312): inline into PendingAutofillRequest if it doesn't have any other subclass
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index cf4963c..a5ef21a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2619,7 +2619,7 @@
                 currentValue);
 
         if (mAugmentedAutofillDestroyer == null) {
-            mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest(id);
+            mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
         }
         return mAugmentedAutofillDestroyer;
     }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 09aa421..01778cd 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -17,6 +17,9 @@
 package com.android.server.contentcapture;
 
 import static android.service.contentcapture.ContentCaptureService.setClientState;
+import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
+import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID;
+import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE;
 
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
 import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
@@ -39,7 +42,6 @@
 import android.service.contentcapture.SnapshotData;
 import android.util.ArrayMap;
 import android.util.Slog;
-import android.view.contentcapture.ContentCaptureSession;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
@@ -165,7 +167,7 @@
         if (!isEnabledLocked()) {
             // TODO: it would be better to split in differet reasons, like
             // STATE_DISABLED_NO_SERVICE and STATE_DISABLED_BY_DEVICE_POLICY
-            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE,
+            setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
                     /* binder= */ null);
             return;
         }
@@ -184,7 +186,7 @@
         if (existingSession != null) {
             Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
                     + ": ignoring because it already exists for " + existingSession.mActivityToken);
-            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_DUPLICATED_ID,
+            setClientState(clientReceiver, STATE_DISABLED | STATE_DUPLICATED_ID,
                     /* binder=*/ null);
             return;
         }
@@ -197,8 +199,7 @@
             // TODO(b/119613670): log metrics
             Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
                     + ": ignoring because service is not set");
-            // TODO(b/111276913): use a new disabled state?
-            setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE,
+            setClientState(clientReceiver, STATE_DISABLED | STATE_NO_SERVICE,
                     /* binder= */ null);
             return;
         }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 869d564..312b83c 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1041,12 +1041,13 @@
 
         @Override
         public void onSetProperties(ProviderProperties properties) {
-            // move calls coming from below LMS onto a different thread to avoid deadlock
-            runInternal(() -> {
-                synchronized (mLock) {
-                    mProperties = properties;
-                }
-            });
+            // because this does not invoke any other methods which might result in calling back
+            // into the location provider, it is safe to run this on the calling thread. it is also
+            // currently necessary to run this on the calling thread to ensure that property changes
+            // are publicly visibly immediately, ie for mock providers which are created.
+            synchronized (mLock) {
+                mProperties = properties;
+            }
         }
 
         @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bd6fa49..d292783 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -32,6 +32,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
 import static android.content.pm.PackageManager.GET_PROVIDERS;
+import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -318,7 +319,6 @@
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.TriFunction;
 import com.android.server.AlarmManagerInternal;
-import com.android.server.appop.AppOpsService;
 import com.android.server.AttributeCache;
 import com.android.server.DeviceIdleController;
 import com.android.server.DisplayThread;
@@ -337,6 +337,7 @@
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
+import com.android.server.appop.AppOpsService;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.pm.Installer;
@@ -658,9 +659,47 @@
 
     /**
      * When an app has restrictions on the other apps that can have associations with it,
-     * it appears here with a set of the allowed apps.
+     * it appears here with a set of the allowed apps and also track debuggability of the app.
      */
-    ArrayMap<String, ArraySet<String>> mAllowedAssociations;
+    ArrayMap<String, PackageAssociationInfo> mAllowedAssociations;
+
+    /**
+     * Tracks association information for a particular package along with debuggability.
+     * <p> Associations for a package A are allowed to package B if B is part of the
+     *     allowed associations for A or if A is debuggable.
+     */
+    private final class PackageAssociationInfo {
+        private final String mSourcePackage;
+        private final ArraySet<String> mAllowedPackageAssociations;
+        private boolean mIsDebuggable;
+
+        PackageAssociationInfo(String sourcePackage, ArraySet<String> allowedPackages,
+                boolean isDebuggable) {
+            mSourcePackage = sourcePackage;
+            mAllowedPackageAssociations = allowedPackages;
+            mIsDebuggable = isDebuggable;
+        }
+
+        /**
+         * Returns true if {@code mSourcePackage} is allowed association with
+         * {@code targetPackage}.
+         */
+        boolean isPackageAssociationAllowed(String targetPackage) {
+            return mIsDebuggable || mAllowedPackageAssociations.contains(targetPackage);
+        }
+
+        boolean isDebuggable() {
+            return mIsDebuggable;
+        }
+
+        void setDebuggable(boolean isDebuggable) {
+            mIsDebuggable = isDebuggable;
+        }
+
+        ArraySet<String> getAllowedPackageAssociations() {
+            return mAllowedPackageAssociations;
+        }
+    }
 
     /**
      * All of the processes we currently have running organized by pid.
@@ -2392,12 +2431,10 @@
      * If it does not, give it an empty set.
      */
     void requireAllowedAssociationsLocked(String packageName) {
-        if (mAllowedAssociations == null) {
-            mAllowedAssociations = new ArrayMap<>(
-                    SystemConfig.getInstance().getAllowedAssociations());
-        }
+        ensureAllowedAssociations();
         if (mAllowedAssociations.get(packageName) == null) {
-            mAllowedAssociations.put(packageName, new ArraySet<>());
+            mAllowedAssociations.put(packageName, new PackageAssociationInfo(packageName,
+                    new ArraySet<>(), /* isDebuggable = */ false));
         }
     }
 
@@ -2408,10 +2445,7 @@
      * association is implicitly allowed.
      */
     boolean validateAssociationAllowedLocked(String pkg1, int uid1, String pkg2, int uid2) {
-        if (mAllowedAssociations == null) {
-            mAllowedAssociations = new ArrayMap<>(
-                    SystemConfig.getInstance().getAllowedAssociations());
-        }
+        ensureAllowedAssociations();
         // Interactions with the system uid are always allowed, since that is the core system
         // that everyone needs to be able to interact with. Also allow reflexive associations
         // within the same uid.
@@ -2419,24 +2453,57 @@
                 || UserHandle.getAppId(uid2) == SYSTEM_UID) {
             return true;
         }
-        // We won't allow this association if either pkg1 or pkg2 has a limit on the
-        // associations that are allowed with it, and the other package is not explicitly
-        // specified as one of those associations.
-        ArraySet<String> pkgs = mAllowedAssociations.get(pkg1);
-        if (pkgs != null) {
-            if (!pkgs.contains(pkg2)) {
-                return false;
-            }
+
+        // Check for association on both source and target packages.
+        PackageAssociationInfo pai = mAllowedAssociations.get(pkg1);
+        if (pai != null && !pai.isPackageAssociationAllowed(pkg2)) {
+            return false;
         }
-        pkgs = mAllowedAssociations.get(pkg2);
-        if (pkgs != null) {
-            return pkgs.contains(pkg1);
+        pai = mAllowedAssociations.get(pkg2);
+        if (pai != null && !pai.isPackageAssociationAllowed(pkg1)) {
+            return false;
         }
         // If no explicit associations are provided in the manifest, then assume the app is
         // allowed associations with any package.
         return true;
     }
 
+    /** Sets up allowed associations for system prebuilt packages from system config (if needed). */
+    private void ensureAllowedAssociations() {
+        if (mAllowedAssociations == null) {
+            ArrayMap<String, ArraySet<String>> allowedAssociations =
+                    SystemConfig.getInstance().getAllowedAssociations();
+            mAllowedAssociations = new ArrayMap<>(allowedAssociations.size());
+            PackageManagerInternal pm = getPackageManagerInternalLocked();
+            for (int i = 0; i < allowedAssociations.size(); i++) {
+                final String pkg = allowedAssociations.keyAt(i);
+                final ArraySet<String> asc = allowedAssociations.valueAt(i);
+
+                // Query latest debuggable flag from package-manager.
+                boolean isDebuggable = false;
+                try {
+                    ApplicationInfo ai = AppGlobals.getPackageManager()
+                            .getApplicationInfo(pkg, MATCH_ALL, 0);
+                    if (ai != null) {
+                        isDebuggable = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+                    }
+                } catch (RemoteException e) {
+                    /* ignore */
+                }
+                mAllowedAssociations.put(pkg, new PackageAssociationInfo(pkg, asc, isDebuggable));
+            }
+        }
+    }
+
+    /** Updates allowed associations for app info (specifically, based on debuggability).  */
+    private void updateAssociationForApp(ApplicationInfo appInfo) {
+        ensureAllowedAssociations();
+        PackageAssociationInfo pai = mAllowedAssociations.get(appInfo.packageName);
+        if (pai != null) {
+            pai.setDebuggable((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
+        }
+    }
+
     @Override
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
             throws RemoteException {
@@ -10910,14 +10977,14 @@
     void dumpAllowedAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage) {
         boolean needSep = false;
-        boolean printedAnything = false;
 
         pw.println("ACTIVITY MANAGER ALLOWED ASSOCIATION STATE (dumpsys activity allowed-associations)");
         boolean printed = false;
         if (mAllowedAssociations != null) {
             for (int i = 0; i < mAllowedAssociations.size(); i++) {
                 final String pkg = mAllowedAssociations.keyAt(i);
-                final ArraySet<String> asc = mAllowedAssociations.valueAt(i);
+                final ArraySet<String> asc =
+                        mAllowedAssociations.valueAt(i).getAllowedPackageAssociations();
                 boolean printedHeader = false;
                 for (int j = 0; j < asc.size(); j++) {
                     if (dumpPackage == null || pkg.equals(dumpPackage)
@@ -10926,7 +10993,6 @@
                             pw.println("  Allowed associations (by restricted package):");
                             printed = true;
                             needSep = true;
-                            printedAnything = true;
                         }
                         if (!printedHeader) {
                             pw.print("  * ");
@@ -10938,6 +11004,9 @@
                         pw.println(asc.valueAt(j));
                     }
                 }
+                if (mAllowedAssociations.valueAt(i).isDebuggable()) {
+                    pw.println("      (debuggable)");
+                }
             }
         }
         if (!printed) {
@@ -14519,6 +14588,7 @@
                                     + " ssp=" + ssp + " data=" + data);
                             return ActivityManager.BROADCAST_SUCCESS;
                         }
+                        updateAssociationForApp(aInfo);
                         mAtmInternal.onPackageReplaced(aInfo);
                         mServices.updateServiceApplicationInfoLocked(aInfo);
                         sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d704a3e..9a4ca1f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7824,8 +7824,10 @@
 
     /** see AudioPolicy.setUidDeviceAffinity() */
     public int setUidDeviceAffinity(IAudioPolicyCallback pcb, int uid,
-            @NonNull int[] deviceTypes,
-            @NonNull String[] deviceAddresses) {
+            @NonNull int[] deviceTypes, @NonNull String[] deviceAddresses) {
+        if (DEBUG_AP) {
+            Log.d(TAG, "setUidDeviceAffinity for " + pcb.asBinder() + " uid:" + uid);
+        }
         synchronized (mAudioPolicies) {
             final AudioPolicyProxy app =
                     checkUpdateForPolicy(pcb, "Cannot change device affinity in audio policy");
@@ -7835,21 +7837,23 @@
             if (!app.hasMixRoutedToDevices(deviceTypes, deviceAddresses)) {
                 return AudioManager.ERROR;
             }
+            return app.setUidDeviceAffinities(uid, deviceTypes, deviceAddresses);
         }
-        return AudioManager.SUCCESS;
     }
 
     /** see AudioPolicy.removeUidDeviceAffinity() */
     public int removeUidDeviceAffinity(IAudioPolicyCallback pcb, int uid) {
+        if (DEBUG_AP) {
+            Log.d(TAG, "removeUidDeviceAffinity for " + pcb.asBinder() + " uid:" + uid);
+        }
         synchronized (mAudioPolicies) {
             final AudioPolicyProxy app =
                     checkUpdateForPolicy(pcb, "Cannot remove device affinity in audio policy");
             if (app == null) {
                 return AudioManager.ERROR;
             }
-
+            return app.removeUidDeviceAffinities(uid);
         }
-        return AudioManager.SUCCESS;
     }
 
     public int setFocusPropertiesForPolicy(int duckingBehavior, IAudioPolicyCallback pcb) {
@@ -8160,27 +8164,41 @@
             Binder.restoreCallingIdentity(identity);
         }
 
-        void setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+        int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
             final Integer Uid = new Integer(uid);
+            int res;
             if (mUidDeviceAffinities.remove(Uid) != null) {
                 final long identity = Binder.clearCallingIdentity();
-                AudioSystem.removeUidDeviceAffinities(uid);
+                res = AudioSystem.removeUidDeviceAffinities(uid);
                 Binder.restoreCallingIdentity(identity);
+                if (res != AudioSystem.SUCCESS) {
+                    Log.e(TAG, "AudioSystem. removeUidDeviceAffinities(" + uid + ") failed, "
+                            + " cannot call AudioSystem.setUidDeviceAffinities");
+                    return AudioManager.ERROR;
+                }
             }
             final long identity = Binder.clearCallingIdentity();
-            final int res = AudioSystem.setUidDeviceAffinities(uid, types, addresses);
+            res = AudioSystem.setUidDeviceAffinities(uid, types, addresses);
             Binder.restoreCallingIdentity(identity);
             if (res == AudioSystem.SUCCESS) {
                 mUidDeviceAffinities.put(Uid, new AudioDeviceArray(types, addresses));
+                return AudioManager.SUCCESS;
             }
+            Log.e(TAG, "AudioSystem. setUidDeviceAffinities(" + uid + ") failed");
+            return AudioManager.ERROR;
         }
 
-        void removeUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
+        int removeUidDeviceAffinities(int uid) {
             if (mUidDeviceAffinities.remove(new Integer(uid)) != null) {
                 final long identity = Binder.clearCallingIdentity();
-                AudioSystem.removeUidDeviceAffinities(uid);
+                final int res = AudioSystem.removeUidDeviceAffinities(uid);
                 Binder.restoreCallingIdentity(identity);
+                if (res == AudioSystem.SUCCESS) {
+                    return AudioManager.SUCCESS;
+                }
             }
+            Log.e(TAG, "AudioSystem. removeUidDeviceAffinities failed");
+            return AudioManager.ERROR;
         }
     };
 
diff --git a/services/core/java/com/android/server/content/SyncLogger.java b/services/core/java/com/android/server/content/SyncLogger.java
index 5cbe5b9..fc20ef20 100644
--- a/services/core/java/com/android/server/content/SyncLogger.java
+++ b/services/core/java/com/android/server/content/SyncLogger.java
@@ -16,6 +16,7 @@
 
 package com.android.server.content;
 
+import android.accounts.Account;
 import android.app.job.JobParameters;
 import android.os.Build;
 import android.os.Environment;
@@ -31,6 +32,8 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IntPair;
 import com.android.server.IoThread;
+import com.android.server.content.SyncManager.ActiveSyncContext;
+import com.android.server.content.SyncStorageEngine.EndPoint;
 
 import libcore.io.IoUtils;
 
@@ -309,4 +312,20 @@
             }
         }
     }
+
+    static String logSafe(Account account) {
+        return account == null ? "[null]" : account.toSafeString();
+    }
+
+    static String logSafe(EndPoint endPoint) {
+        return endPoint == null ? "[null]" : endPoint.toSafeString();
+    }
+
+    static String logSafe(SyncOperation operation) {
+        return operation == null ? "[null]" : operation.toSafeString();
+    }
+
+    static String logSafe(ActiveSyncContext asc) {
+        return asc == null ? "[null]" : asc.toSafeString();
+    }
 }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 8b93e04..7096477 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -16,6 +16,8 @@
 
 package com.android.server.content;
 
+import static com.android.server.content.SyncLogger.logSafe;
+
 import android.accounts.Account;
 import android.accounts.AccountAndUser;
 import android.accounts.AccountManager;
@@ -1140,7 +1142,7 @@
             /* ignore - local call */
         }
         if (checkAccountAccess && !canAccessAccount(account, owningPackage, owningUid)) {
-            Log.w(TAG, "Access to " + account + " denied for package "
+            Log.w(TAG, "Access to " + logSafe(account) + " denied for package "
                     + owningPackage + " in UID " + syncAdapterInfo.uid);
             return AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS;
         }
@@ -1474,7 +1476,8 @@
         if (!syncOperation.ignoreBackoff()) {
             Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(syncOperation.target);
             if (backoff == null) {
-                Slog.e(TAG, "Couldn't find backoff values for " + syncOperation.target);
+                Slog.e(TAG, "Couldn't find backoff values for "
+                        + logSafe(syncOperation.target));
                 backoff = new Pair<Long, Long>(SyncStorageEngine.NOT_IN_BACKOFF_MODE,
                         SyncStorageEngine.NOT_IN_BACKOFF_MODE);
             }
@@ -1745,8 +1748,8 @@
             scheduleSyncOperationH(operation);
         } else {
             // Otherwise do not reschedule.
-            Log.d(TAG, "not retrying sync operation because the error is a hard error: "
-                    + operation);
+            Log.e(TAG, "not retrying sync operation because the error is a hard error: "
+                    + logSafe(operation));
         }
     }
 
@@ -1881,11 +1884,12 @@
             sendSyncFinishedOrCanceledMessage(this, result);
         }
 
-        public void toString(StringBuilder sb) {
+        public void toString(StringBuilder sb, boolean logSafe) {
             sb.append("startTime ").append(mStartTime)
                     .append(", mTimeoutStartTime ").append(mTimeoutStartTime)
                     .append(", mHistoryRowId ").append(mHistoryRowId)
-                    .append(", syncOperation ").append(mSyncOperation);
+                    .append(", syncOperation ").append(
+                        logSafe ? logSafe(mSyncOperation) : mSyncOperation);
         }
 
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -1947,7 +1951,13 @@
 
         public String toString() {
             StringBuilder sb = new StringBuilder();
-            toString(sb);
+            toString(sb, false);
+            return sb.toString();
+        }
+
+        public String toSafeString() {
+            StringBuilder sb = new StringBuilder();
+            toString(sb, true);
             return sb.toString();
         }
 
@@ -2036,7 +2046,7 @@
         int count = 0;
         for (SyncOperation op: pendingSyncs) {
             if (!op.isPeriodic) {
-                pw.println(op.dump(null, false, buckets));
+                pw.println(op.dump(null, false, buckets, /*logSafe=*/ false));
                 count++;
             }
         }
@@ -2053,7 +2063,7 @@
         int count = 0;
         for (SyncOperation op: pendingSyncs) {
             if (op.isPeriodic) {
-                pw.println(op.dump(null, false, buckets));
+                pw.println(op.dump(null, false, buckets, /*logSafe=*/ false));
                 count++;
             }
         }
@@ -2186,7 +2196,7 @@
             sb.setLength(0);
             pw.print(formatDurationHMS(sb, durationInSeconds));
             pw.print(" - ");
-            pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets));
+            pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets, /*logSafe=*/ false));
             pw.println();
         }
         pw.println();
@@ -2974,7 +2984,7 @@
                     case SyncHandler.MESSAGE_CANCEL:
                         SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj;
                         Bundle extras = msg.peekData();
-                        if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        if (isLoggable) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: "
                                     + endpoint + " bundle: " + extras);
                         }
@@ -2985,9 +2995,11 @@
                         SyncFinishedOrCancelledMessagePayload payload =
                                 (SyncFinishedOrCancelledMessagePayload) msg.obj;
                         if (!isSyncStillActiveH(payload.activeSyncContext)) {
-                            Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
-                                    + "sync is no longer active: "
-                                    + payload.activeSyncContext);
+                            if (isLoggable) {
+                                Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
+                                        + "sync is no longer active: "
+                                        + payload.activeSyncContext);
+                            }
                             break;
                         }
                         if (isLoggable) {
@@ -3002,7 +3014,7 @@
 
                     case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
                         ServiceConnectionData msgData = (ServiceConnectionData) msg.obj;
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        if (isLoggable) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
                                     + msgData.activeSyncContext);
                         }
@@ -3018,7 +3030,7 @@
                     case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
                         final ActiveSyncContext currentSyncContext =
                                 ((ServiceConnectionData) msg.obj).activeSyncContext;
-                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        if (isLoggable) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
                                     + currentSyncContext);
                         }
@@ -3053,7 +3065,7 @@
 
                     case SyncHandler.MESSAGE_MONITOR_SYNC:
                         ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj;
-                        if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        if (isLoggable) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " +
                                     monitoredSyncContext.mSyncOperation.target);
                         }
@@ -3061,7 +3073,7 @@
                         if (isSyncNotUsingNetworkH(monitoredSyncContext)) {
                             Log.w(TAG, String.format(
                                     "Detected sync making no progress for %s. cancelling.",
-                                    monitoredSyncContext));
+                                    logSafe(monitoredSyncContext)));
                             SyncJobService.callJobFinished(
                                     monitoredSyncContext.mSyncOperation.jobId, false,
                                     "no network activity");
@@ -3558,7 +3570,8 @@
             } catch (RuntimeException exc) {
                 mLogger.log("Sync failed with RuntimeException: ", exc.toString());
                 closeActiveSyncContext(activeSyncContext);
-                Slog.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc);
+                Slog.e(TAG, "Caught RuntimeException while starting the sync "
+                        + logSafe(syncOperation), exc);
             }
         }
 
@@ -3658,7 +3671,8 @@
                         reschedulePeriodicSyncH(syncOperation);
                     }
                 } else {
-                    Log.w(TAG, "failed sync operation " + syncOperation + ", " + syncResult);
+                    Log.w(TAG, "failed sync operation "
+                            + logSafe(syncOperation) + ", " + syncResult);
 
                     syncOperation.retries++;
                     if (syncOperation.retries > mConstants.getMaxRetriesWithAppStandbyExemption()) {
@@ -4042,11 +4056,6 @@
         getJobScheduler().cancel(op.jobId);
     }
 
-    private void wtfWithLog(String message) {
-        Slog.wtf(TAG, message);
-        mLogger.log("WTF: ", message);
-    }
-
     public void resetTodayStats() {
         mSyncStorageEngine.resetTodayStats(/*force=*/ true);
     }
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 25edf40..2abc2e6 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -363,14 +363,19 @@
 
     @Override
     public String toString() {
-        return dump(null, true, null);
+        return dump(null, true, null, false);
     }
 
-    String dump(PackageManager pm, boolean shorter, SyncAdapterStateFetcher appStates) {
+    public String toSafeString() {
+        return dump(null, true, null, true);
+    }
+
+    String dump(PackageManager pm, boolean shorter, SyncAdapterStateFetcher appStates,
+            boolean logSafe) {
         StringBuilder sb = new StringBuilder();
         sb.append("JobId=").append(jobId)
                 .append(" ")
-                .append(target.account.name)
+                .append(logSafe ? "***" : target.account.name)
                 .append("/")
                 .append(target.account.type)
                 .append(" u")
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index bfd1791..6b441a0 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -16,6 +16,8 @@
 
 package com.android.server.content;
 
+import static com.android.server.content.SyncLogger.logSafe;
+
 import android.accounts.Account;
 import android.accounts.AccountAndUser;
 import android.accounts.AccountManager;
@@ -225,6 +227,15 @@
             sb.append(":u" + userId);
             return sb.toString();
         }
+
+        public String toSafeString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(account == null ? "ALL ACCS" : logSafe(account))
+                    .append("/")
+                    .append(provider == null ? "ALL PDRS" : provider);
+            sb.append(":u" + userId);
+            return sb.toString();
+        }
     }
 
     public static class AuthorityInfo {
@@ -1861,8 +1872,8 @@
 
                 }
             } else {
-                Slog.w(TAG, "Failure adding authority: account="
-                        + accountName + " auth=" + authorityName
+                Slog.w(TAG, "Failure adding authority:"
+                        + " auth=" + authorityName
                         + " enabled=" + enabled
                         + " syncable=" + syncable);
             }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2340b77..cb3f91b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -792,6 +792,16 @@
         }
     }
 
+    private void setVirtualDisplayStateInternal(IBinder appToken, boolean isOn) {
+        synchronized (mSyncRoot) {
+            if (mVirtualDisplayAdapter == null) {
+                return;
+            }
+
+            mVirtualDisplayAdapter.setVirtualDisplayStateLocked(appToken, isOn);
+        }
+    }
+
     private void registerDefaultDisplayAdapters() {
         // Register default display adapters.
         synchronized (mSyncRoot) {
@@ -1930,6 +1940,16 @@
         }
 
         @Override // Binder call
+        public void setVirtualDisplayState(IVirtualDisplayCallback callback, boolean isOn) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                setVirtualDisplayStateInternal(callback.asBinder(), isOn);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 5aa585f..1ca8dd3 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -147,6 +147,13 @@
         return device;
     }
 
+    void setVirtualDisplayStateLocked(IBinder appToken, boolean isOn) {
+        VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
+        if (device != null) {
+            device.setDisplayState(isOn);
+        }
+    }
+
     /**
      * Returns the next unique index for the uniqueIdPrefix
      */
@@ -206,6 +213,7 @@
         private int mPendingChanges;
         private int mUniqueIndex;
         private Display.Mode mMode;
+        private boolean mIsDisplayOn;
 
         public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
                 int ownerUid, String ownerPackageName,
@@ -226,6 +234,7 @@
             mDisplayState = Display.STATE_UNKNOWN;
             mPendingChanges |= PENDING_SURFACE_CHANGE;
             mUniqueIndex = uniqueIndex;
+            mIsDisplayOn = surface != null;
         }
 
         @Override
@@ -304,6 +313,14 @@
             }
         }
 
+        void setDisplayState(boolean isOn) {
+            if (mIsDisplayOn != isOn) {
+                mIsDisplayOn = isOn;
+                mInfo = null;
+                sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
+            }
+        }
+
         public void stopLocked() {
             setSurfaceLocked(null);
             mStopped = true;
@@ -375,7 +392,9 @@
                 mInfo.type = Display.TYPE_VIRTUAL;
                 mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
                         DisplayDeviceInfo.TOUCH_NONE : DisplayDeviceInfo.TOUCH_VIRTUAL;
-                mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF;
+
+                mInfo.state = mIsDisplayOn ? Display.STATE_ON : Display.STATE_OFF;
+
                 mInfo.ownerUid = mOwnerUid;
                 mInfo.ownerPackageName = mOwnerPackageName;
             }
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index ba21b78..af71624 100755
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -51,6 +51,8 @@
     private static final int STATE_WAITING_FOR_OSD_NAME = 3;
     // State in which the action is waiting for gathering vendor id of non-local devices.
     private static final int STATE_WAITING_FOR_VENDOR_ID = 4;
+    // State in which the action is waiting for devices to be ready
+    private static final int STATE_WAITING_FOR_DEVICES = 5;
 
     /**
      * Interface used to report result of device discovery.
@@ -89,7 +91,20 @@
     private final DeviceDiscoveryCallback mCallback;
     private int mProcessedDeviceCount = 0;
     private int mTimeoutRetry = 0;
-    private boolean mIsTvDevice = source().mService.isTvDevice();
+    private boolean mIsTvDevice = localDevice().mService.isTvDevice();
+    private final int mDelayPeriod;
+
+    /**
+     * Constructor.
+     *
+     * @param source an instance of {@link HdmiCecLocalDevice}.
+     * @param delay delay action for this period between query Physical Address and polling
+     */
+    DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback, int delay) {
+        super(source);
+        mCallback = Preconditions.checkNotNull(callback);
+        mDelayPeriod = delay;
+    }
 
     /**
      * Constructor.
@@ -97,8 +112,7 @@
      * @param source an instance of {@link HdmiCecLocalDevice}.
      */
     DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) {
-        super(source);
-        mCallback = Preconditions.checkNotNull(callback);
+        this(source, callback, 0);
     }
 
     @Override
@@ -117,7 +131,11 @@
 
                 Slog.v(TAG, "Device detected: " + ackedAddress);
                 allocateDevices(ackedAddress);
-                startPhysicalAddressStage();
+                if (mDelayPeriod > 0) {
+                    startToDelayAction();
+                } else {
+                    startPhysicalAddressStage();
+                }
             }
         }, Constants.POLL_ITERATION_REVERSE_ORDER
             | Constants.POLL_STRATEGY_REMOTES_DEVICES, HdmiConfig.DEVICE_POLLING_RETRY);
@@ -131,6 +149,13 @@
         }
     }
 
+    private void startToDelayAction() {
+        Slog.v(TAG, "Waiting for connected devices to be ready");
+        mState = STATE_WAITING_FOR_DEVICES;
+
+        checkAndProceedStage();
+    }
+
     private void startPhysicalAddressStage() {
         Slog.v(TAG, "Start [Physical Address Stage]:" + mDevices.size());
         mProcessedDeviceCount = 0;
@@ -159,6 +184,11 @@
         addTimer(mState, HdmiConfig.TIMEOUT_MS);
     }
 
+    private void delayActionWithTimePeriod(int timeDelay) {
+        mActionTimer.clearTimerMessage();
+        addTimer(mState, timeDelay);
+    }
+
     private void startOsdNameStage() {
         Slog.v(TAG, "Start [Osd Name Stage]:" + mDevices.size());
         mProcessedDeviceCount = 0;
@@ -385,6 +415,9 @@
     private void sendQueryCommand() {
         int address = mDevices.get(mProcessedDeviceCount).mLogicalAddress;
         switch (mState) {
+            case STATE_WAITING_FOR_DEVICES:
+                delayActionWithTimePeriod(mDelayPeriod);
+                return;
             case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
                 queryPhysicalAddress(address);
                 return;
@@ -405,6 +438,10 @@
             return;
         }
 
+        if (mState == STATE_WAITING_FOR_DEVICES) {
+            startPhysicalAddressStage();
+            return;
+        }
         if (++mTimeoutRetry < HdmiConfig.TIMEOUT_RETRY) {
             sendQueryCommand();
             return;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index e777ce8..86be585 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -77,7 +77,7 @@
 
     private static final int NUM_LOGICAL_ADDRESS = 16;
 
-    private static final int MAX_CEC_MESSAGE_HISTORY = 20;
+    private static final int MAX_CEC_MESSAGE_HISTORY = 200;
 
     // Predicate for whether the given logical address is remote device's one or not.
     private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
@@ -682,7 +682,7 @@
 
     void dump(final IndentingPrintWriter pw) {
         for (int i = 0; i < mLocalDevices.size(); ++i) {
-            pw.println("HdmiCecLocalDevice #" + i + ":");
+            pw.println("HdmiCecLocalDevice #" + mLocalDevices.keyAt(i) + ":");
             pw.increaseIndent();
             mLocalDevices.valueAt(i).dump(pw);
             pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 0e4e334..5c1b3de 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -26,22 +26,29 @@
 import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.media.AudioDeviceInfo;
+import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.media.tv.TvContract;
 import android.os.SystemProperties;
+import android.provider.Settings.Global;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.hdmi.Constants.AudioCodec;
 import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 
 import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.stream.Collectors;
+
 
 /**
  * Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android
@@ -83,10 +90,10 @@
 
     protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
         super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
-        mSystemAudioControlFeatureEnabled = true;
-        // TODO(amyjojo) make System Audio Control controllable by users
-        /*mSystemAudioControlFeatureEnabled =
-        mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/
+        mRoutingControlFeatureEnabled =
+            mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, true);
+        mSystemAudioControlFeatureEnabled =
+            mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
         // TODO(amyjojo): make the map ro property.
         mTvInputs.put(Constants.CEC_SWITCH_HDMI1,
                 "com.droidlogic.tvinput/.services.Hdmi1InputService/HW5");
@@ -267,7 +274,7 @@
             int systemAudioOnPowerOnProp, boolean lastSystemAudioControlStatus) {
         if ((systemAudioOnPowerOnProp == ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
                 || ((systemAudioOnPowerOnProp == USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
-                && lastSystemAudioControlStatus)) {
+                && lastSystemAudioControlStatus && isSystemAudioControlFeatureEnabled())) {
             addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
         }
     }
@@ -400,8 +407,11 @@
     @ServiceThreadOnly
     protected boolean handleGiveAudioStatus(HdmiCecMessage message) {
         assertRunOnServiceThread();
-
-        reportAudioStatus(message);
+        if (isSystemAudioControlFeatureEnabled()) {
+            reportAudioStatus(message.getSource());
+        } else {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+        }
         return true;
     }
 
@@ -478,14 +488,91 @@
 
     private byte[] getSupportedShortAudioDescriptors(
             AudioDeviceInfo deviceInfo, @AudioCodec int[] audioFormatCodes) {
-        // TODO(b/80297701) implement
-        return new byte[] {};
+        ArrayList<byte[]> sads = new ArrayList<>(audioFormatCodes.length);
+        for (@AudioCodec int audioFormatCode : audioFormatCodes) {
+            byte[] sad = getSupportedShortAudioDescriptor(deviceInfo, audioFormatCode);
+            if (sad != null) {
+                if (sad.length == 3) {
+
+                    sads.add(sad);
+                } else {
+                    HdmiLogger.warning(
+                            "Dropping Short Audio Descriptor with length %d for requested codec %x",
+                            sad.length, audioFormatCode);
+                }
+            }
+        }
+        // Short Audio Descriptors are always 3 bytes long.
+        byte[] bytes = new byte[sads.size() * 3];
+        int index = 0;
+        for (byte[] sad : sads) {
+            System.arraycopy(sad, 0, bytes, index, 3);
+            index += 3;
+        }
+        return bytes;
+    }
+
+    /**
+     * Returns a 3 byte short audio descriptor as described in CEC 1.4 table 29 or null if the
+     * audioFormatCode is not supported.
+     */
+    @Nullable
+    private byte[] getSupportedShortAudioDescriptor(
+            AudioDeviceInfo deviceInfo, @AudioCodec int audioFormatCode) {
+        switch (audioFormatCode) {
+            case Constants.AUDIO_CODEC_NONE: {
+                return null;
+            }
+            case Constants.AUDIO_CODEC_LPCM: {
+                return getLpcmShortAudioDescriptor(deviceInfo);
+            }
+            // TODO(b/80297701): implement the rest of the codecs
+            case Constants.AUDIO_CODEC_DD:
+            case Constants.AUDIO_CODEC_MPEG1:
+            case Constants.AUDIO_CODEC_MP3:
+            case Constants.AUDIO_CODEC_MPEG2:
+            case Constants.AUDIO_CODEC_AAC:
+            case Constants.AUDIO_CODEC_DTS:
+            case Constants.AUDIO_CODEC_ATRAC:
+            case Constants.AUDIO_CODEC_ONEBITAUDIO:
+            case Constants.AUDIO_CODEC_DDP:
+            case Constants.AUDIO_CODEC_DTSHD:
+            case Constants.AUDIO_CODEC_TRUEHD:
+            case Constants.AUDIO_CODEC_DST:
+            case Constants.AUDIO_CODEC_WMAPRO:
+            default: {
+                return null;
+            }
+        }
+    }
+
+    @Nullable
+    private byte[] getLpcmShortAudioDescriptor(AudioDeviceInfo deviceInfo) {
+        // TODO(b/80297701): implement
+        return null;
     }
 
     @Nullable
     private AudioDeviceInfo getSystemAudioDeviceInfo() {
-        // TODO(b/80297701) implement
-        // Get the audio device used for system audio mode.
+        AudioManager audioManager = mService.getContext().getSystemService(AudioManager.class);
+        if (audioManager == null) {
+            HdmiLogger.error(
+                    "Error getting system audio device because AudioManager not available.");
+            return null;
+        }
+        AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
+        HdmiLogger.debug("Found %d audio input devices", devices.length);
+        for (AudioDeviceInfo device : devices) {
+            HdmiLogger.debug("%s at port %s", device.getProductName(), device.getPort());
+            HdmiLogger.debug("Supported encodings are %s",
+                    Arrays.stream(device.getEncodings()).mapToObj(
+                            AudioFormat::toLogFriendlyEncoding
+                    ).collect(Collectors.joining(", ")));
+            // TODO(b/80297701) use the actual device type that system audio mode is connected to.
+            if (device.getType() == AudioDeviceInfo.TYPE_HDMI_ARC) {
+                return device;
+            }
+        }
         return null;
     }
 
@@ -583,17 +670,20 @@
             .setWiredDeviceConnectionState(AudioSystem.DEVICE_IN_HDMI, enabled ? 1 : 0, "", "");
     }
 
-    private void reportAudioStatus(HdmiCecMessage message) {
+    void reportAudioStatus(int source) {
         assertRunOnServiceThread();
 
         int volume = mService.getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC);
         boolean mute = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
         int maxVolume = mService.getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        int minVolume = mService.getAudioManager().getStreamMinVolume(AudioManager.STREAM_MUSIC);
         int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
+        HdmiLogger.debug("Reporting volume %i (%i-%i) as CEC volume %i", volume,
+                minVolume, maxVolume, scaledVolume);
 
         mService.sendCecCommand(
                 HdmiCecMessageBuilder.buildReportAudioStatus(
-                        mAddress, message.getSource(), scaledVolume, mute));
+                        mAddress, source, scaledVolume, mute));
     }
 
     /**
@@ -688,6 +778,13 @@
         HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
     }
 
+    void onSystemAduioControlFeatureSupportChanged(boolean enabled) {
+        setSystemAudioControlFeatureEnabled(enabled);
+        if (enabled) {
+            addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
+        }
+    }
+
     @ServiceThreadOnly
     void setSystemAudioControlFeatureEnabled(boolean enabled) {
         assertRunOnServiceThread();
@@ -697,6 +794,14 @@
     }
 
     @ServiceThreadOnly
+    void setRoutingControlFeatureEnables(boolean enabled) {
+        assertRunOnServiceThread();
+        synchronized (mLock) {
+            mRoutingControlFeatureEnabled = enabled;
+        }
+    }
+
+    @ServiceThreadOnly
     void doManualPortSwitching(int portId, IHdmiControlCallback callback) {
         assertRunOnServiceThread();
         // TODO: validate port ID
@@ -831,6 +936,10 @@
     }
 
     protected void routeToInputFromPortId(int portId) {
+        if (!isRoutingControlFeatureEnabled()) {
+            HdmiLogger.debug("Routing Control Feature is not enabled.");
+            return;
+        }
         if (mArcIntentUsed) {
             routeToTvInputFromPortId(portId);
         } else {
@@ -978,4 +1087,20 @@
         }
         mDeviceInfos.clear();
     }
+
+    @Override
+    protected void dump(IndentingPrintWriter pw) {
+        pw.println("HdmiCecLocalDeviceAudioSystem:");
+        pw.increaseIndent();
+        pw.println("mSystemAudioActivated: " + mSystemAudioActivated);
+        pw.println("mSystemAudioControlFeatureEnabled: " + mSystemAudioControlFeatureEnabled);
+        pw.println("mTvSystemAudioModeSupport: " + mTvSystemAudioModeSupport);
+        pw.println("mArcEstablished: " + mArcEstablished);
+        pw.println("mArcIntentUsed: " + mArcIntentUsed);
+        HdmiUtils.dumpMap(pw, "mTvInputs:", mTvInputs);
+        HdmiUtils.dumpSparseArray(pw, "mDeviceInfos:", mDeviceInfos);
+        pw.decreaseIndent();
+        super.dump(pw);
+    }
+
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index a95f7f1..cbddaf5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -66,6 +66,10 @@
     @LocalActivePort
     protected int mLocalActivePort = Constants.CEC_SWITCH_HOME;
 
+    // Whether the Routing Coutrol feature is enabled or not. True by default.
+    @GuardedBy("mLock")
+    protected boolean mRoutingControlFeatureEnabled;
+
     protected HdmiCecLocalDeviceSource(HdmiControlService service, int deviceType) {
         super(service, deviceType);
     }
@@ -123,7 +127,9 @@
         }
         setIsActiveSource(physicalAddress == mService.getPhysicalAddress());
         updateDevicePowerStatus(logicalAddress, HdmiControlManager.POWER_STATUS_ON);
-        switchInputOnReceivingNewActivePath(physicalAddress);
+        if (isRoutingControlFeatureEnabled()) {
+            switchInputOnReceivingNewActivePath(physicalAddress);
+        }
         return true;
     }
 
@@ -153,6 +159,10 @@
     @ServiceThreadOnly
     protected boolean handleRoutingChange(HdmiCecMessage message) {
         assertRunOnServiceThread();
+        if (!isRoutingControlFeatureEnabled()) {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+            return true;
+        }
         int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2);
         // if the current device is a pure playback device
         if (!mIsSwitchDevice
@@ -168,6 +178,10 @@
     @ServiceThreadOnly
     protected boolean handleRoutingInformation(HdmiCecMessage message) {
         assertRunOnServiceThread();
+        if (!isRoutingControlFeatureEnabled()) {
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+            return true;
+        }
         int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
         // if the current device is a pure playback device
         if (!mIsSwitchDevice
@@ -279,6 +293,12 @@
         }
     }
 
+    boolean isRoutingControlFeatureEnabled() {
+        synchronized (mLock) {
+            return mRoutingControlFeatureEnabled;
+        }
+    }
+
     // Check if the device is trying to switch to the same input that is active right now.
     // This can help avoid redundant port switching.
     protected boolean isSwitchingToTheSameInput(@LocalActivePort int activePort) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index c005615..f8b3962 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -17,6 +17,7 @@
 package com.android.server.hdmi;
 
 import android.annotation.Nullable;
+
 import libcore.util.EmptyArray;
 
 import java.util.Arrays;
@@ -111,12 +112,11 @@
     @Override
     public String toString() {
         StringBuffer s = new StringBuffer();
-        s.append(String.format("<%s> src: %d, dst: %d",
-                opcodeToString(mOpcode), mSource, mDestination));
+        s.append(String.format("<%s> %X%X:%02X",
+                opcodeToString(mOpcode), mSource, mDestination, mOpcode));
         if (mParams.length > 0) {
-            s.append(", params:");
             for (byte data : mParams) {
-                s.append(String.format(" %02X", data));
+                s.append(String.format(":%02X", data));
             }
         }
         return s.toString();
@@ -133,7 +133,7 @@
             case Constants.MESSAGE_TUNER_STEP_DECREMENT:
                 return "Tuner Step Decrement";
             case Constants.MESSAGE_TUNER_DEVICE_STATUS:
-                return "Tuner Device Staus";
+                return "Tuner Device Status";
             case Constants.MESSAGE_GIVE_TUNER_DEVICE_STATUS:
                 return "Give Tuner Device Status";
             case Constants.MESSAGE_RECORD_ON:
@@ -207,7 +207,7 @@
             case Constants.MESSAGE_DEVICE_VENDOR_ID:
                 return "Device Vendor Id";
             case Constants.MESSAGE_VENDOR_COMMAND:
-                return "Vendor Commandn";
+                return "Vendor Command";
             case Constants.MESSAGE_VENDOR_REMOTE_BUTTON_DOWN:
                 return "Vendor Remote Button Down";
             case Constants.MESSAGE_VENDOR_REMOTE_BUTTON_UP:
@@ -215,7 +215,7 @@
             case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID:
                 return "Give Device Vendor Id";
             case Constants.MESSAGE_MENU_REQUEST:
-                return "Menu REquest";
+                return "Menu Request";
             case Constants.MESSAGE_MENU_STATUS:
                 return "Menu Status";
             case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
@@ -247,7 +247,7 @@
             case Constants.MESSAGE_SET_EXTERNAL_TIMER:
                 return "Set External Timer";
             case Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR:
-                return "Repot Short Audio Descriptor";
+                return "Report Short Audio Descriptor";
             case Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR:
                 return "Request Short Audio Descriptor";
             case Constants.MESSAGE_INITIATE_ARC:
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 2d6e762..aabe1ad 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -26,6 +26,7 @@
 import static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING;
 import static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE;
 import static com.android.server.hdmi.Constants.OPTION_MHL_SERVICE_CONTROL;
+import static com.android.server.power.ShutdownThread.SHUTDOWN_ACTION_PROPERTY;
 
 import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
@@ -184,9 +185,10 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             assertRunOnServiceThread();
+            boolean isReboot = SystemProperties.get(SHUTDOWN_ACTION_PROPERTY).contains("1");
             switch (intent.getAction()) {
                 case Intent.ACTION_SCREEN_OFF:
-                    if (isPowerOnOrTransient()) {
+                    if (isPowerOnOrTransient() && !isReboot) {
                         onStandby(STANDBY_SCREEN_OFF);
                     }
                     break;
@@ -202,7 +204,7 @@
                     }
                     break;
                 case Intent.ACTION_SHUTDOWN:
-                    if (isPowerOnOrTransient()) {
+                    if (isPowerOnOrTransient() && !isReboot) {
                         onStandby(STANDBY_SHUTDOWN);
                     }
                     break;
@@ -345,6 +347,10 @@
     @Nullable
     private Looper mIoLooper;
 
+    // Thread safe physical address
+    @GuardedBy("mLock")
+    private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
+
     // Last input port before switching to the MHL port. Should switch back to this port
     // when the mobile device sends the request one touch play with off.
     // Gets invalidated if we go to other port/input.
@@ -564,7 +570,8 @@
                 Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
                 Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
                 Global.MHL_INPUT_SWITCHING_ENABLED,
-                Global.MHL_POWER_CHARGE_ENABLED
+                Global.MHL_POWER_CHARGE_ENABLED,
+                Global.HDMI_CEC_SWITCH_ENABLED
         };
         for (String s : settings) {
             resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
@@ -605,6 +612,14 @@
                     if (isTvDeviceEnabled()) {
                         tv().setSystemAudioControlFeatureEnabled(enabled);
                     }
+                    if (isAudioSystemDevice()) {
+                        audioSystem().onSystemAduioControlFeatureSupportChanged(enabled);
+                    }
+                    break;
+                case Global.HDMI_CEC_SWITCH_ENABLED:
+                    if (isAudioSystemDevice()) {
+                        audioSystem().setRoutingControlFeatureEnables(enabled);
+                    }
                     break;
                 case Global.MHL_INPUT_SWITCHING_ENABLED:
                     setMhlInputChangeEnabled(enabled);
@@ -734,6 +749,10 @@
         assertRunOnServiceThread();
         HdmiPortInfo[] cecPortInfo = null;
 
+        synchronized (mLock) {
+            mPhysicalAddress = getPhysicalAddress();
+        }
+
         // CEC HAL provides majority of the info while MHL does only MHL support flag for
         // each port. Return empty array if CEC HAL didn't provide the info.
         if (mCecController != null) {
@@ -1532,6 +1551,14 @@
         }
 
         @Override
+        public int getPhysicalAddress() {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                return mPhysicalAddress;
+            }
+        }
+
+        @Override
         public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
             enforceAccessPermission();
             runOnServiceThread(new Runnable() {
@@ -1834,14 +1861,28 @@
                         Slog.w(TAG, "audio system is not in system audio mode");
                         return;
                     }
-                    int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
+                    audioSystem().reportAudioStatus(Constants.ADDR_TV);
+                }
+            });
+        }
 
-                    sendCecCommand(HdmiCecMessageBuilder
-                            .buildReportAudioStatus(
-                                    device.getDeviceInfo().getLogicalAddress(),
-                                    Constants.ADDR_TV,
-                                    scaledVolume,
-                                    isMute));
+        @Override
+        public void setSystemAudioModeOnForAudioOnlySource() {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (!isAudioSystemDevice()) {
+                        Slog.e(TAG, "Not an audio system device. Won't set system audio mode on");
+                        return;
+                    }
+                    if (!audioSystem().checkSupportAndSetSystemAudioMode(true)) {
+                        Slog.e(TAG, "System Audio Mode is not supported.");
+                        return;
+                    }
+                    sendCecCommand(
+                            HdmiCecMessageBuilder.buildSetSystemAudioMode(
+                                    audioSystem().mAddress, Constants.ADDR_BROADCAST, true));
                 }
             });
         }
@@ -1851,27 +1892,28 @@
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) return;
             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
 
-            pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
             pw.println("mProhibitMode: " + mProhibitMode);
-            if (mCecController != null) {
-                pw.println("mCecController: ");
-                pw.increaseIndent();
-                mCecController.dump(pw);
-                pw.decreaseIndent();
-            }
+            pw.println("mPowerStatus: " + mPowerStatus);
+
+            // System settings
+            pw.println("System_settings:");
+            pw.increaseIndent();
+            pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
+            pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled);
+            pw.decreaseIndent();
 
             pw.println("mMhlController: ");
             pw.increaseIndent();
             mMhlController.dump(pw);
             pw.decreaseIndent();
 
-            pw.println("mPortInfo: ");
-            pw.increaseIndent();
-            for (HdmiPortInfo hdmiPortInfo : mPortInfo) {
-                pw.println("- " + hdmiPortInfo);
+            HdmiUtils.dumpIterable(pw, "mPortInfo:", mPortInfo);
+            if (mCecController != null) {
+                pw.println("mCecController: ");
+                pw.increaseIndent();
+                mCecController.dump(pw);
+                pw.decreaseIndent();
             }
-            pw.decreaseIndent();
-            pw.println("mPowerStatus: " + mPowerStatus);
         }
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 2a8117f..2110682 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -20,9 +20,13 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.util.IndentingPrintWriter;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+
 
 /**
  * Various utilities to handle HDMI CEC messages.
@@ -317,4 +321,74 @@
                 info.getPhysicalAddress(), info.getPortId(), info.getDeviceType(),
                 info.getVendorId(), info.getDisplayName(), newPowerStatus);
     }
+
+    /**
+     * Dump a {@link SparseArray} to the print writer.
+     *
+     * <p>The dump is formatted:
+     * <pre>
+     *     name:
+     *        key = value
+     *        key = value
+     *        ...
+     * </pre>
+     */
+    static <T> void dumpSparseArray(IndentingPrintWriter pw, String name,
+            SparseArray<T> sparseArray) {
+        printWithTrailingColon(pw, name);
+        pw.increaseIndent();
+        int size = sparseArray.size();
+        for (int i = 0; i < size; i++) {
+            int key = sparseArray.keyAt(i);
+            T value = sparseArray.get(key);
+            pw.printPair(Integer.toString(key), value);
+            pw.println();
+        }
+        pw.decreaseIndent();
+    }
+
+    private static void printWithTrailingColon(IndentingPrintWriter pw, String name) {
+        pw.println(name.endsWith(":") ? name : name.concat(":"));
+    }
+
+    /**
+     * Dump a {@link Map} to the print writer.
+     *
+     * <p>The dump is formatted:
+     * <pre>
+     *     name:
+     *        key = value
+     *        key = value
+     *        ...
+     * </pre>
+     */
+    static <K, V> void dumpMap(IndentingPrintWriter pw, String name, Map<K, V> map) {
+        printWithTrailingColon(pw, name);
+        pw.increaseIndent();
+        for (Map.Entry<K, V> entry: map.entrySet()) {
+            pw.printPair(entry.getKey().toString(), entry.getValue());
+            pw.println();
+        }
+        pw.decreaseIndent();
+    }
+
+    /**
+     * Dump a {@link Map} to the print writer.
+     *
+     * <p>The dump is formatted:
+     * <pre>
+     *     name:
+     *        value
+     *        value
+     *        ...
+     * </pre>
+     */
+    static <T> void dumpIterable(IndentingPrintWriter pw, String name, Iterable<T> values) {
+        printWithTrailingColon(pw, name);
+        pw.increaseIndent();
+        for (T value : values) {
+            pw.println(value);
+        }
+        pw.decreaseIndent();
+    }
 }
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 269767a..dfb98c3 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -64,6 +64,7 @@
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.StatsLog;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.location.GpsNetInitiatedHandler;
@@ -1704,6 +1705,24 @@
                         ", response: " + userResponse);
             }
             native_send_ni_response(notificationId, userResponse);
+
+            StatsLog.write(StatsLog.GNSS_NI_EVENT_REPORTED,
+                    StatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_RESPONSE,
+                    notificationId,
+                    /* niType= */ 0,
+                    /* needNotify= */ false,
+                    /* needVerify= */ false,
+                    /* privacyOverride= */ false,
+                    /* timeout= */ 0,
+                    /* defaultResponse= */ 0,
+                    /* requestorId= */ null,
+                    /* text= */ null,
+                    /* requestorIdEncoding= */ 0,
+                    /* textEncoding= */ 0,
+                    mSuplEsEnabled,
+                    mEnabled,
+                    userResponse);
+
             return true;
         }
     };
@@ -1753,6 +1772,22 @@
         notification.textEncoding = textEncoding;
 
         mNIHandler.handleNiNotification(notification);
+        StatsLog.write(StatsLog.GNSS_NI_EVENT_REPORTED,
+                StatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_REQUEST,
+                notification.notificationId,
+                notification.niType,
+                notification.needNotify,
+                notification.needVerify,
+                notification.privacyOverride,
+                notification.timeout,
+                notification.defaultResponse,
+                notification.requestorId,
+                notification.text,
+                notification.requestorIdEncoding,
+                notification.textEncoding,
+                mSuplEsEnabled,
+                mEnabled,
+                /* userResponse= */ 0);
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 86bc9f3..fe91c63 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -52,7 +52,6 @@
         mExtras = null;
 
         setProperties(properties);
-        setEnabled(true);
     }
 
     /** Sets the enabled state of this mock provider. */
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 20eebe7..7323e93 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2311,22 +2311,22 @@
         }
 
         @Override
-        public boolean areAppOverlaysAllowed(String pkg) {
-            return areAppOverlaysAllowedForPackage(pkg, Binder.getCallingUid());
+        public boolean areBubblesAllowed(String pkg) {
+            return areBubblesAllowedForPackage(pkg, Binder.getCallingUid());
         }
 
         @Override
-        public boolean areAppOverlaysAllowedForPackage(String pkg, int uid) {
-            enforceSystemOrSystemUIOrSamePackage("Caller not system or systemui or same package",
-                    pkg);
-            return mPreferencesHelper.areAppOverlaysAllowed(pkg, uid);
+        public boolean areBubblesAllowedForPackage(String pkg, int uid) {
+            enforceSystemOrSystemUIOrSamePackage(pkg,
+                    "Caller not system or systemui or same package");
+            return mPreferencesHelper.areBubblessAllowed(pkg, uid);
         }
 
         @Override
-        public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) {
+        public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
             checkCallerIsSystem();
 
-            mPreferencesHelper.setAppOverlaysAllowed(pkg, uid, allowed);
+            mPreferencesHelper.setBubblesAllowed(pkg, uid, allowed);
             handleSavePolicyFile();
         }
 
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 28f6972..7a21aa2 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -81,7 +81,7 @@
     private static final String ATT_NAME = "name";
     private static final String ATT_UID = "uid";
     private static final String ATT_ID = "id";
-    private static final String ATT_APP_OVERLAY = "overlay";
+    private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
     private static final String ATT_PRIORITY = "priority";
     private static final String ATT_VISIBILITY = "visibility";
     private static final String ATT_IMPORTANCE = "importance";
@@ -94,8 +94,9 @@
     private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
     private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
     private static final boolean DEFAULT_SHOW_BADGE = true;
-    private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
+    private static final boolean DEFAULT_ALLOW_BUBBLE = true;
     private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE  = false;
+
     /**
      * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
      * fields.
@@ -108,7 +109,7 @@
     @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
     public @interface LockableAppFields {
         int USER_LOCKED_IMPORTANCE = 0x00000001;
-        int USER_LOCKED_APP_OVERLAY = 0x00000002;
+        int USER_LOCKED_BUBBLE = 0x00000002;
     }
 
     // pkg|uid => PackagePreferences
@@ -176,7 +177,7 @@
                                     XmlUtils.readBooleanAttribute(
                                             parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
                                     XmlUtils.readBooleanAttribute(
-                                            parser, ATT_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
+                                            parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
                             r.importance = XmlUtils.readIntAttribute(
                                     parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
                             r.priority = XmlUtils.readIntAttribute(
@@ -272,11 +273,11 @@
     private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) {
         return getOrCreatePackagePreferences(pkg, uid,
                 DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
-                DEFAULT_ALLOW_APP_OVERLAY);
+                DEFAULT_ALLOW_BUBBLE);
     }
 
     private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance,
-            int priority, int visibility, boolean showBadge, boolean allowAppOverlay) {
+            int priority, int visibility, boolean showBadge, boolean allowBubble) {
         final String key = packagePreferencesKey(pkg, uid);
         synchronized (mPackagePreferences) {
             PackagePreferences
@@ -290,7 +291,7 @@
                 r.priority = priority;
                 r.visibility = visibility;
                 r.showBadge = showBadge;
-                r.appOverlay = allowAppOverlay;
+                r.allowBubble = allowBubble;
 
                 try {
                     createDefaultChannelIfNeeded(r);
@@ -392,7 +393,7 @@
                                 || r.channels.size() > 0
                                 || r.groups.size() > 0
                                 || r.delegate != null
-                                || r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY;
+                                || r.allowBubble != DEFAULT_ALLOW_BUBBLE;
                 if (hasNonDefaultSettings) {
                     out.startTag(null, TAG_PACKAGE);
                     out.attribute(null, ATT_NAME, r.pkg);
@@ -405,8 +406,8 @@
                     if (r.visibility != DEFAULT_VISIBILITY) {
                         out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
                     }
-                    if (r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY) {
-                        out.attribute(null, ATT_APP_OVERLAY, Boolean.toString(r.appOverlay));
+                    if (r.allowBubble != DEFAULT_ALLOW_BUBBLE) {
+                        out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(r.allowBubble));
                     }
                     out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
                     out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
@@ -452,14 +453,28 @@
         out.endTag(null, TAG_RANKING);
     }
 
-    public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) {
+    /**
+     * Sets whether bubbles are allowed.
+     *
+     * @param pkg the package to allow or not allow bubbles for.
+     * @param uid the uid to allow or not allow bubbles for.
+     * @param allowed whether bubbles are allowed.
+     */
+    public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
         PackagePreferences p = getOrCreatePackagePreferences(pkg, uid);
-        p.appOverlay = allowed;
-        p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_APP_OVERLAY;
+        p.allowBubble = allowed;
+        p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
     }
 
-    public boolean areAppOverlaysAllowed(String pkg, int uid) {
-        return getOrCreatePackagePreferences(pkg, uid).appOverlay;
+    /**
+     * Whether bubbles are allowed.
+     *
+     * @param pkg the package to check if bubbles are allowed for
+     * @param uid the uid to check if bubbles are allowed for.
+     * @return whether bubbles are allowed.
+     */
+    public boolean areBubblessAllowed(String pkg, int uid) {
+        return getOrCreatePackagePreferences(pkg, uid).allowBubble;
     }
 
     public int getAppLockedFields(String pkg, int uid) {
@@ -1232,8 +1247,8 @@
         if (original.canShowBadge() != update.canShowBadge()) {
             update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
         }
-        if (original.isAppOverlayAllowed() != update.isAppOverlayAllowed()) {
-            update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY);
+        if (original.isBubbleAllowed() != update.isBubbleAllowed()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE);
         }
     }
 
@@ -1654,7 +1669,7 @@
         int priority = DEFAULT_PRIORITY;
         int visibility = DEFAULT_VISIBILITY;
         boolean showBadge = DEFAULT_SHOW_BADGE;
-        boolean appOverlay = DEFAULT_ALLOW_APP_OVERLAY;
+        boolean allowBubble = DEFAULT_ALLOW_BUBBLE;
         int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
         boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE;
         List<String> futureOemLockedChannels = new ArrayList<>();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 945d7ad..6932390 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -239,6 +239,7 @@
 import android.os.storage.StorageManagerInternal;
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
+import android.permission.PermissionControllerManager;
 import android.provider.MediaStore;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
@@ -317,7 +318,6 @@
 import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionManagerService;
 import com.android.server.pm.permission.PermissionsState;
-import com.android.server.pm.permission.PermissionsState.PermissionState;
 import com.android.server.security.VerityUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -370,6 +370,7 @@
 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;
@@ -443,6 +444,8 @@
     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 int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
     private static final int NFC_UID = Process.NFC_UID;
@@ -19573,28 +19576,32 @@
             throw new SecurityException("Only the system may call getPermissionGrantBackup()");
         }
 
-        ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
-        try {
-            final XmlSerializer serializer = new FastXmlSerializer();
-            serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
-            serializer.startDocument(null, true);
-            serializer.startTag(null, TAG_PERMISSION_BACKUP);
+        AtomicReference<byte[]> backup = new AtomicReference<>();
+        mContext.getSystemService(PermissionControllerManager.class).getRuntimePermissionBackup(
+                UserHandle.of(userId), mContext.getMainExecutor(), (b) -> {
+                    synchronized (backup) {
+                        backup.set(b);
+                        backup.notifyAll();
+                    }
+                });
 
-            synchronized (mPackages) {
-                serializeRuntimePermissionGrantsLPr(serializer, userId);
-            }
+        long start = System.currentTimeMillis();
+        synchronized (backup) {
+            while (backup.get() == null) {
+                long timeLeft = start + BACKUP_TIMEOUT_MILLIS - System.currentTimeMillis();
+                if (timeLeft <= 0) {
+                    return null;
+                }
 
-            serializer.endTag(null, TAG_PERMISSION_BACKUP);
-            serializer.endDocument();
-            serializer.flush();
-        } catch (Exception e) {
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Unable to write default apps for backup", e);
+                try {
+                    backup.wait(timeLeft);
+                } catch (InterruptedException ignored) {
+                    return null;
+                }
             }
-            return null;
         }
 
-        return dataStream.toByteArray();
+        return backup.get();
     }
 
     @Override
@@ -19620,66 +19627,6 @@
     }
 
     @GuardedBy("mPackages")
-    private void serializeRuntimePermissionGrantsLPr(XmlSerializer serializer, final int userId)
-            throws IOException {
-        serializer.startTag(null, TAG_ALL_GRANTS);
-
-        final int N = mSettings.mPackages.size();
-        for (int i = 0; i < N; i++) {
-            final PackageSetting ps = mSettings.mPackages.valueAt(i);
-            boolean pkgGrantsKnown = false;
-
-            PermissionsState packagePerms = ps.getPermissionsState();
-
-            for (PermissionState state : packagePerms.getRuntimePermissionStates(userId)) {
-                final int grantFlags = state.getFlags();
-                // only look at grants that are not system/policy fixed
-                if ((grantFlags & SYSTEM_RUNTIME_GRANT_MASK) == 0) {
-                    final boolean isGranted = state.isGranted();
-                    // And only back up the user-twiddled state bits
-                    if (isGranted || (grantFlags & USER_RUNTIME_GRANT_MASK) != 0) {
-                        final String packageName = mSettings.mPackages.keyAt(i);
-                        if (!pkgGrantsKnown) {
-                            serializer.startTag(null, TAG_GRANT);
-                            serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
-                            pkgGrantsKnown = true;
-                        }
-
-                        final boolean userSet =
-                                (grantFlags & FLAG_PERMISSION_USER_SET) != 0;
-                        final boolean userFixed =
-                                (grantFlags & FLAG_PERMISSION_USER_FIXED) != 0;
-                        final boolean revoke =
-                                (grantFlags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
-
-                        serializer.startTag(null, TAG_PERMISSION);
-                        serializer.attribute(null, ATTR_PERMISSION_NAME, state.getName());
-                        if (isGranted) {
-                            serializer.attribute(null, ATTR_IS_GRANTED, "true");
-                        }
-                        if (userSet) {
-                            serializer.attribute(null, ATTR_USER_SET, "true");
-                        }
-                        if (userFixed) {
-                            serializer.attribute(null, ATTR_USER_FIXED, "true");
-                        }
-                        if (revoke) {
-                            serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
-                        }
-                        serializer.endTag(null, TAG_PERMISSION);
-                    }
-                }
-            }
-
-            if (pkgGrantsKnown) {
-                serializer.endTag(null, TAG_GRANT);
-            }
-        }
-
-        serializer.endTag(null, TAG_ALL_GRANTS);
-    }
-
-    @GuardedBy("mPackages")
     private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId)
             throws XmlPullParserException, IOException {
         String pkgName = null;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 4884e91..4e71a05 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -2065,6 +2065,17 @@
         mContext.unregisterReceiver(mShutdownEventReceiver);
         cancelAnomalyAlarm();
         cancelPullingAlarm();
+
+        BinderCallsStatsService.Internal binderStats =
+                LocalServices.getService(BinderCallsStatsService.Internal.class);
+        if (binderStats != null) {
+            binderStats.reset();
+        }
+
+        LooperStats looperStats = LocalServices.getService(LooperStats.class);
+        if (looperStats != null) {
+            looperStats.reset();
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 5f56fe5..177f244 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -462,22 +462,19 @@
             mOccluded = false;
             mDismissingKeyguardActivity = null;
 
-            // Only the top activity of the focused stack on each display may control it's
-            // occluded state.
-            final ActivityStack focusedStack = display.getFocusedStack();
-            if (focusedStack != null) {
-                final ActivityRecord topDismissing =
-                        focusedStack.getTopDismissingKeyguardActivity();
-                mOccluded = focusedStack.topActivityOccludesKeyguard() || (topDismissing != null
-                                && focusedStack.topRunningActivityLocked() == topDismissing
-                                && controller.canShowWhileOccluded(
+            final ActivityStack stack = getStackForControllingOccluding(display);
+            if (stack != null) {
+                final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
+                mOccluded = stack.topActivityOccludesKeyguard() || (topDismissing != null
+                        && stack.topRunningActivityLocked() == topDismissing
+                        && controller.canShowWhileOccluded(
                                 true /* dismissKeyguard */,
                                 false /* showWhenLocked */));
-                if (focusedStack.getTopDismissingKeyguardActivity() != null) {
-                    mDismissingKeyguardActivity = focusedStack.getTopDismissingKeyguardActivity();
+                if (stack.getTopDismissingKeyguardActivity() != null) {
+                    mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
                 }
-                mOccluded |= controller.mWindowManager.isShowingDream();
             }
+            mOccluded |= controller.mWindowManager.isShowingDream();
 
             // TODO(b/113840485): Handle app transition for individual display, and apply occluded
             // state change to secondary displays.
@@ -492,6 +489,23 @@
             }
         }
 
+        /**
+         * Gets the stack used to check the occluded state.
+         * <p>
+         * Only the top non-pinned activity of the focusable stack on each display can control its
+         * occlusion state.
+         */
+        private ActivityStack getStackForControllingOccluding(ActivityDisplay display) {
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (stack != null && stack.isFocusableAndVisible()
+                        && !stack.inPinnedWindowingMode()) {
+                    return stack;
+                }
+            }
+            return null;
+        }
+
         void dumpStatus(PrintWriter pw, String prefix) {
             final StringBuilder sb = new StringBuilder();
             sb.append(prefix);
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 4a553cf..e944858 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1278,28 +1278,28 @@
     }
 
     /**
-     * Checks if the root activity requires a particular orientation (either by override or
+     * Checks if the top activity requires a particular orientation (either by override or
      * activityInfo) and returns that. Otherwise, this returns ORIENTATION_UNDEFINED.
      */
-    private int getRootActivityRequestedOrientation() {
-        ActivityRecord root = getRootActivity();
+    private int getTopActivityRequestedOrientation() {
+        ActivityRecord top = getTopActivity();
         if (getRequestedOverrideConfiguration().orientation != ORIENTATION_UNDEFINED
-                || root == null) {
+                || top == null) {
             return getRequestedOverrideConfiguration().orientation;
         }
-        int rootScreenOrientation = root.getOrientation();
-        if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+        int screenOrientation = top.getOrientation();
+        if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
             // NOSENSOR means the display's "natural" orientation, so return that.
             ActivityDisplay display = mStack != null ? mStack.getDisplay() : null;
             if (display != null && display.mDisplayContent != null) {
                 return mStack.getDisplay().mDisplayContent.getNaturalOrientation();
             }
-        } else if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+        } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
             // LOCKED means the activity's orientation remains unchanged, so return existing value.
-            return root.getConfiguration().orientation;
-        } else if (ActivityInfo.isFixedOrientationLandscape(rootScreenOrientation)) {
+            return top.getConfiguration().orientation;
+        } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) {
             return ORIENTATION_LANDSCAPE;
-        } else if (ActivityInfo.isFixedOrientationPortrait(rootScreenOrientation)) {
+        } else if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) {
             return ORIENTATION_PORTRAIT;
         }
         return ORIENTATION_UNDEFINED;
@@ -2196,9 +2196,9 @@
             // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent"
             outOverrideBounds.setEmpty();
 
-            // If the task or its root activity require a different orientation, make it fit the
+            // If the task or its top activity requires a different orientation, make it fit the
             // available bounds by scaling down its bounds.
-            int forcedOrientation = getRootActivityRequestedOrientation();
+            int forcedOrientation = getTopActivityRequestedOrientation();
             if (forcedOrientation != ORIENTATION_UNDEFINED
                     && forcedOrientation != newParentConfig.orientation) {
                 final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index b290bc5..bce944d 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -856,7 +856,7 @@
 
 Return<void> GnssMeasurementCallback::gnssMeasurementCb_2_0(
         const IGnssMeasurementCallback_V2_0::GnssData& data) {
-    // TODO(b/119571122): implement gnssMeasurementCb_2_0
+    translateAndSetGnssData(data);
     return Void();
 }
 
@@ -894,9 +894,8 @@
     return data.measurementCount;
 }
 
-template<>
-size_t GnssMeasurementCallback::getMeasurementCount<IGnssMeasurementCallback_V1_1::GnssData>
-        (const IGnssMeasurementCallback_V1_1::GnssData& data) {
+template<class T>
+size_t GnssMeasurementCallback::getMeasurementCount(const T& data) {
     return data.measurements.size();
 }
 
@@ -958,6 +957,17 @@
             ADR_STATE_HALF_CYCLE_REPORTED));
 }
 
+// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement");
+template<>
+void GnssMeasurementCallback::translateSingleGnssMeasurement
+        <IGnssMeasurementCallback_V2_0::GnssMeasurement>(
+        const IGnssMeasurementCallback_V2_0::GnssMeasurement* measurement_V2_0,
+        JavaObject& object) {
+    translateSingleGnssMeasurement(&(measurement_V2_0->v1_1), object);
+
+    SET(CodeType, (static_cast<int32_t>(measurement_V2_0->codeType)));
+}
+
 jobject GnssMeasurementCallback::translateGnssClock(
        JNIEnv* env, const IGnssMeasurementCallback_V1_0::GnssClock* clock) {
     JavaObject object(env, "android/location/GnssClock");
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index c8e6782..4a48468 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -85,6 +85,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -793,6 +794,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 114098433)
     public void testAllListeners() throws Exception {
         final AppStateTrackerTestable instance = newInstance();
         callStart(instance);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 41d5691..8171469 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -61,6 +61,7 @@
 import android.view.Display;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -416,6 +417,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 119774928)
     public void testEnabledState() throws Exception {
         TestParoleListener paroleListener = new TestParoleListener();
         mController.addListener(paroleListener);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 83c1c76..94b21af 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3511,9 +3511,9 @@
     }
 
     @Test
-    public void testAppOverlay() throws Exception {
-        mBinderService.setAppOverlaysAllowed(PKG, mUid, false);
-        assertFalse(mBinderService.areAppOverlaysAllowedForPackage(PKG, mUid));
+    public void testBubble() throws Exception {
+        mBinderService.setBubblesAllowed(PKG, mUid, false);
+        assertFalse(mBinderService.areBubblesAllowedForPackage(PKG, mUid));
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 0fcfea7..24a1f8c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -809,7 +809,7 @@
         channel.setBypassDnd(true);
         channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
         channel.setShowBadge(true);
-        channel.setAllowAppOverlay(false);
+        channel.setAllowBubbles(false);
         int lockMask = 0;
         for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
             lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
@@ -826,7 +826,7 @@
         assertFalse(savedChannel.canBypassDnd());
         assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
         assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
-        assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps());
+        assertEquals(channel.canBubble(), savedChannel.canBubble());
 
         verify(mHandler, never()).requestSort();
     }
@@ -840,7 +840,7 @@
         channel.setBypassDnd(true);
         channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
         channel.setShowBadge(true);
-        channel.setAllowAppOverlay(false);
+        channel.setAllowBubbles(false);
         int lockMask = 0;
         for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
             lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
@@ -857,7 +857,7 @@
         assertFalse(savedChannel.canBypassDnd());
         assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
         assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
-        assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps());
+        assertEquals(channel.canBubble(), savedChannel.canBubble());
     }
 
     @Test
@@ -969,16 +969,16 @@
     }
 
     @Test
-    public void testLockFields_appOverlay() {
+    public void testLockFields_allowBubble() {
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
         assertEquals(0,
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel().getId(), false)
                         .getUserLockedFields());
 
         final NotificationChannel update = getChannel();
-        update.setAllowAppOverlay(false);
+        update.setAllowBubbles(false);
         mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update, true);
-        assertEquals(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY,
+        assertEquals(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE,
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update.getId(), false)
                         .getUserLockedFields());
     }
@@ -2161,30 +2161,30 @@
     }
 
     @Test
-    public void testAllowAppOverlay_defaults() throws Exception {
-        assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+    public void testAllowBubbles_defaults() throws Exception {
+        assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
         loadStreamXml(baos, false);
 
-        assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
+        assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O));
         assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
     }
 
     @Test
-    public void testAllowAppOverlay_xml() throws Exception {
-        mHelper.setAppOverlaysAllowed(PKG_O, UID_O, false);
-        assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
-        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY,
+    public void testAllowBubbles_xml() throws Exception {
+        mHelper.setBubblesAllowed(PKG_O, UID_O, false);
+        assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O));
+        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE,
                 mHelper.getAppLockedFields(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
         loadStreamXml(baos, false);
 
-        assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O));
-        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY,
+        assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O));
+        assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE,
                 mHelper.getAppLockedFields(PKG_O, UID_O));
     }
 
@@ -2290,14 +2290,14 @@
         mHelper.lockChannelsForOEM(new String[] {PKG_O});
 
         NotificationChannel update = new NotificationChannel("a", "a", IMPORTANCE_NONE);
-        update.setAllowAppOverlay(false);
+        update.setAllowBubbles(false);
 
         mHelper.updateNotificationChannel(PKG_O, UID_O, update, true);
 
         assertEquals(IMPORTANCE_HIGH,
                 mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance());
         assertEquals(false,
-                mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canOverlayApps());
+                mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble());
 
         mHelper.updateNotificationChannel(PKG_O, UID_O, update, true);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index fa42289..51bebbb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -39,7 +39,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 /**
@@ -52,15 +54,34 @@
 @Presubmit
 public class AppTransitionTests extends WindowTestsBase {
 
+    private static RootWindowContainer sOriginalRootWindowContainer;
+
     private DisplayContent mDc;
 
+    @BeforeClass
+    public static void setUpRootWindowContainerMock() {
+        final WindowManagerService wm = WmServiceUtils.getWindowManagerService();
+        // For unit test, we don't need to test performSurfacePlacement to prevent some abnormal
+        // interaction with surfaceflinger native side.
+        sOriginalRootWindowContainer = wm.mRoot;
+        // Creating spied mock of RootWindowContainer shouldn't be done in @Before, since it will
+        // create unnecessary nested spied objects chain, because WindowManagerService object under
+        // test is a single instance shared among all tests that extend WindowTestsBase class.
+        // Instead it should be done once before running all tests in this test class.
+        wm.mRoot = spy(wm.mRoot);
+        doNothing().when(wm.mRoot).performSurfacePlacement(anyBoolean());
+    }
+
+    @AfterClass
+    public static void tearDownRootWindowContainerMock() {
+        final WindowManagerService wm = WmServiceUtils.getWindowManagerService();
+        wm.mRoot = sOriginalRootWindowContainer;
+        sOriginalRootWindowContainer = null;
+    }
+
     @Before
     public void setUp() throws Exception {
         mDc = mWm.getDefaultDisplayContentLocked();
-        // For unit test,  we don't need to test performSurfacePlacement to prevent some
-        // abnormal interaction with surfaceflinger native side.
-        mWm.mRoot = spy(mWm.mRoot);
-        doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 8a98cbe..cdb578d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -217,12 +217,17 @@
         info.logicalHeight = fullScreenBounds.height();
         ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
         assertTrue(mRootActivityContainer.getActivityDisplay(display.mDisplayId) != null);
+        // Override display orientation. Normally this is available via DisplayContent, but DC
+        // is mocked-out.
+        display.getRequestedOverrideConfiguration().orientation =
+                Configuration.ORIENTATION_LANDSCAPE;
+        display.onRequestedOverrideConfigurationChanged(
+                display.getRequestedOverrideConfiguration());
         ActivityStack stack = new StackBuilder(mRootActivityContainer)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
         TaskRecord task = stack.getChildAt(0);
-        ActivityRecord root = task.getRootActivity();
-        ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build();
-        assertEquals(root, task.getRootActivity());
+        ActivityRecord root = task.getTopActivity();
+        assertEquals(root, task.getTopActivity());
 
         assertEquals(fullScreenBounds, task.getBounds());
 
@@ -233,16 +238,22 @@
         assertTrue(task.getBounds().width() < task.getBounds().height());
         assertEquals(fullScreenBounds.height(), task.getBounds().height());
 
-        // Setting non-root app has no effect
-        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE);
-        assertTrue(task.getBounds().width() < task.getBounds().height());
+        // Top activity gets used
+        ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build();
+        assertEquals(top, task.getTopActivity());
+        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
+        assertTrue(task.getBounds().width() > task.getBounds().height());
+        assertEquals(task.getBounds().width(), fullScreenBounds.width());
 
         // Setting app to unspecified restores
-        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_UNSPECIFIED);
+        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_UNSPECIFIED);
         assertEquals(fullScreenBounds, task.getBounds());
 
         // Setting app to fixed landscape and changing display
-        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE);
+        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
+        // simulate display orientation changing (normally done via DisplayContent)
+        display.getRequestedOverrideConfiguration().orientation =
+                Configuration.ORIENTATION_PORTRAIT;
         display.setBounds(fullScreenBoundsPort);
         assertTrue(task.getBounds().width() > task.getBounds().height());
         assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc
index e95041b..09cdbd5 100644
--- a/startop/view_compiler/apk_layout_compiler.cc
+++ b/startop/view_compiler/apk_layout_compiler.cc
@@ -79,9 +79,9 @@
   return visitor.can_compile();
 }
 
-void CompileApkLayouts(const std::string& filename, CompilationTarget target,
-                       std::ostream& target_out) {
-  auto assets = android::ApkAssets::Load(filename);
+namespace {
+void CompileApkAssetsLayouts(const std::unique_ptr<const android::ApkAssets>& assets,
+                             CompilationTarget target, std::ostream& target_out) {
   android::AssetManager2 resources;
   resources.SetApkAssets({assets.get()});
 
@@ -155,5 +155,20 @@
     target_out.write(image.ptr<const char>(), image.size());
   }
 }
+}  // namespace
+
+void CompileApkLayouts(const std::string& filename, CompilationTarget target,
+                       std::ostream& target_out) {
+  auto assets = android::ApkAssets::Load(filename);
+  CompileApkAssetsLayouts(assets, target, target_out);
+}
+
+void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target,
+                         std::ostream& target_out) {
+  constexpr const char* friendly_name{"viewcompiler assets"};
+  auto assets = android::ApkAssets::LoadFromFd(
+      std::move(fd), friendly_name, /*system=*/false, /*force_shared_lib=*/false);
+  CompileApkAssetsLayouts(assets, target, target_out);
+}
 
 }  // namespace startop
diff --git a/startop/view_compiler/apk_layout_compiler.h b/startop/view_compiler/apk_layout_compiler.h
index c85ddd6..03bd545 100644
--- a/startop/view_compiler/apk_layout_compiler.h
+++ b/startop/view_compiler/apk_layout_compiler.h
@@ -19,12 +19,16 @@
 
 #include <string>
 
+#include "android-base/unique_fd.h"
+
 namespace startop {
 
 enum class CompilationTarget { kJavaLanguage, kDex };
 
 void CompileApkLayouts(const std::string& filename, CompilationTarget target,
                        std::ostream& target_out);
+void CompileApkLayoutsFd(android::base::unique_fd fd, CompilationTarget target,
+                         std::ostream& target_out);
 
 }  // namespace startop
 
diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc
index 871a421..11ecde2 100644
--- a/startop/view_compiler/main.cc
+++ b/startop/view_compiler/main.cc
@@ -49,6 +49,7 @@
 
 DEFINE_bool(apk, false, "Compile layouts in an APK");
 DEFINE_bool(dex, false, "Generate a DEX file instead of Java");
+DEFINE_int32(infd, -1, "Read input from the given file descriptor");
 DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
 DEFINE_string(package, "", "The package name for the generated class (required)");
 
@@ -95,7 +96,7 @@
 int main(int argc, char** argv) {
   constexpr size_t kProgramName = 0;
   constexpr size_t kFileNameParam = 1;
-  constexpr size_t kNumRequiredArgs = 2;
+  constexpr size_t kNumRequiredArgs = 1;
 
   gflags::SetUsageMessage(
       "Compile XML layout files into equivalent Java language code\n"
@@ -104,12 +105,11 @@
   gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true);
 
   gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package");
-  if (argc != kNumRequiredArgs || cmd.is_default) {
+  if (argc < kNumRequiredArgs || cmd.is_default) {
     gflags::ShowUsageWithFlags(argv[kProgramName]);
     return 1;
   }
 
-  const char* const filename = argv[kFileNameParam];
   const bool is_stdout = FLAGS_out == kStdoutFilename;
 
   std::ofstream outfile;
@@ -118,13 +118,23 @@
   }
 
   if (FLAGS_apk) {
-    startop::CompileApkLayouts(
-        filename,
-        FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage,
-        is_stdout ? std::cout : outfile);
+    const startop::CompilationTarget target =
+        FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage;
+    if (FLAGS_infd >= 0) {
+      startop::CompileApkLayoutsFd(
+          android::base::unique_fd{FLAGS_infd}, target, is_stdout ? std::cout : outfile);
+    } else {
+      if (argc < 2) {
+        gflags::ShowUsageWithFlags(argv[kProgramName]);
+        return 1;
+      }
+      const char* const filename = argv[kFileNameParam];
+      startop::CompileApkLayouts(filename, target, is_stdout ? std::cout : outfile);
+    }
     return 0;
   }
 
+  const char* const filename = argv[kFileNameParam];
   const string layout_name = startop::util::FindLayoutNameFromFilename(filename);
 
   XMLDocument xml;
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 2a648bd..8523554 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -501,4 +501,18 @@
      */
     public static final String ACTION_LINE1_NUMBER_ERROR_DETECTED =
             "com.android.internal.telephony.ACTION_LINE1_NUMBER_ERROR_DETECTED";
+
+    /**
+     * Broadcast action to notify radio bug.
+     *
+     * Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * @hide
+     */
+    public static final String ACTION_REPORT_RADIO_BUG =
+            "com.android.internal.telephony.ACTION_REPORT_RADIO_BUG";
+
+    // ACTION_REPORT_RADIO_BUG extra keys
+    public static final String EXTRA_SLOT_ID = "slotId";
+    public static final String EXTRA_RADIO_BUG_TYPE = "radioBugType";
 }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 28dd9b4..85871fe 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1886,6 +1886,7 @@
         if (creatorName != null) sbuf.append(" cname=" + creatorName);
         if (lastUpdateUid != 0) sbuf.append(" luid=" + lastUpdateUid);
         if (lastUpdateName != null) sbuf.append(" lname=" + lastUpdateName);
+        if (updateIdentifier != null) sbuf.append(" updateIdentifier=" + updateIdentifier);
         sbuf.append(" lcuid=" + lastConnectUid);
         sbuf.append(" userApproved=" + userApprovedAsString(userApproved));
         sbuf.append(" noInternetAccessExpected=" + noInternetAccessExpected);
@@ -2281,6 +2282,7 @@
             mRandomizedMacAddress = source.mRandomizedMacAddress;
             macRandomizationSetting = source.macRandomizationSetting;
             requirePMF = source.requirePMF;
+            updateIdentifier = source.updateIdentifier;
         }
     }
 
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index c744f18..7bff68a 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -59,6 +59,7 @@
         WifiConfiguration config = new WifiConfiguration();
         config.setPasspointManagementObjectTree(cookie);
         config.trusted = false;
+        config.updateIdentifier = "1234";
         MacAddress macBeforeParcel = config.getOrCreateRandomizedMacAddress();
         Parcel parcelW = Parcel.obtain();
         config.writeToParcel(parcelW, 0);
@@ -73,6 +74,7 @@
         // lacking a useful config.equals, check two fields near the end.
         assertEquals(cookie, reconfig.getMoTree());
         assertEquals(macBeforeParcel, reconfig.getOrCreateRandomizedMacAddress());
+        assertEquals(config.updateIdentifier, reconfig.updateIdentifier);
         assertFalse(reconfig.trusted);
 
         Parcel parcelWW = Parcel.obtain();
@@ -251,6 +253,18 @@
     }
 
     /**
+     * Verifies that updateIdentifier should be copied for copy constructor.
+     */
+    @Test
+    public void testUpdateIdentifierForCopyConstructor() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.updateIdentifier = "1234";
+        WifiConfiguration copyConfig = new WifiConfiguration(config);
+
+        assertEquals(config.updateIdentifier, copyConfig.updateIdentifier);
+    }
+
+    /**
      * Verifies that the serialization/de-serialization for softap config works.
      */
     @Test