Merge "fs-verity: fix broken test testInstallEverything"
diff --git a/core/api/current.txt b/core/api/current.txt
index 2b94694..3af4394 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -20899,8 +20899,11 @@
     method public void onRoutingChanged(android.media.AudioRouting);
   }
 
-  public final class AudioTimestamp {
+  public final class AudioTimestamp implements android.os.Parcelable {
     ctor public AudioTimestamp();
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioTimestamp> CREATOR;
     field public static final int TIMEBASE_BOOTTIME = 1; // 0x1
     field public static final int TIMEBASE_MONOTONIC = 0; // 0x0
     field public long framePosition;
@@ -49747,7 +49750,7 @@
     method public static void request(@NonNull android.view.Surface, @Nullable android.graphics.Rect, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
     method public static void request(@NonNull android.view.Window, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
     method public static void request(@NonNull android.view.Window, @Nullable android.graphics.Rect, @NonNull android.graphics.Bitmap, @NonNull android.view.PixelCopy.OnPixelCopyFinishedListener, @NonNull android.os.Handler);
-    method public static void request(@NonNull android.view.PixelCopy.Request);
+    method public static void request(@NonNull android.view.PixelCopy.Request, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.Result>);
     field public static final int ERROR_DESTINATION_INVALID = 5; // 0x5
     field public static final int ERROR_SOURCE_INVALID = 4; // 0x4
     field public static final int ERROR_SOURCE_NO_DATA = 3; // 0x3
@@ -49763,14 +49766,14 @@
   public static final class PixelCopy.Request {
     method @Nullable public android.graphics.Bitmap getDestinationBitmap();
     method @Nullable public android.graphics.Rect getSourceRect();
-    method @NonNull public static android.view.PixelCopy.Request.Builder ofSurface(@NonNull android.view.Surface, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.Result>);
-    method @NonNull public static android.view.PixelCopy.Request.Builder ofSurface(@NonNull android.view.SurfaceView, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.Result>);
-    method @NonNull public static android.view.PixelCopy.Request.Builder ofWindow(@NonNull android.view.Window, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.Result>);
-    method @NonNull public static android.view.PixelCopy.Request.Builder ofWindow(@NonNull android.view.View, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.PixelCopy.Result>);
   }
 
   public static final class PixelCopy.Request.Builder {
     method @NonNull public android.view.PixelCopy.Request build();
+    method @NonNull public static android.view.PixelCopy.Request.Builder ofSurface(@NonNull android.view.Surface);
+    method @NonNull public static android.view.PixelCopy.Request.Builder ofSurface(@NonNull android.view.SurfaceView);
+    method @NonNull public static android.view.PixelCopy.Request.Builder ofWindow(@NonNull android.view.Window);
+    method @NonNull public static android.view.PixelCopy.Request.Builder ofWindow(@NonNull android.view.View);
     method @NonNull public android.view.PixelCopy.Request.Builder setDestinationBitmap(@Nullable android.graphics.Bitmap);
     method @NonNull public android.view.PixelCopy.Request.Builder setSourceRect(@Nullable android.graphics.Rect);
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 8ff9f87..99be1a2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -79,6 +79,7 @@
     field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
     field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
     field public static final String BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE = "android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE";
+    field public static final String BIND_WEARABLE_SENSING_SERVICE = "android.permission.BIND_WEARABLE_SENSING_SERVICE";
     field public static final String BLUETOOTH_MAP = "android.permission.BLUETOOTH_MAP";
     field public static final String BRICK = "android.permission.BRICK";
     field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
@@ -199,6 +200,7 @@
     field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
     field public static final String MANAGE_WALLPAPER_EFFECTS_GENERATION = "android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION";
     field public static final String MANAGE_WEAK_ESCROW_TOKEN = "android.permission.MANAGE_WEAK_ESCROW_TOKEN";
+    field public static final String MANAGE_WEARABLE_SENSING_SERVICE = "android.permission.MANAGE_WEARABLE_SENSING_SERVICE";
     field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE";
     field public static final String MARK_DEVICE_ORGANIZATION_OWNED = "android.permission.MARK_DEVICE_ORGANIZATION_OWNED";
     field public static final String MEDIA_RESOURCE_OVERRIDE_PID = "android.permission.MEDIA_RESOURCE_OVERRIDE_PID";
@@ -2876,6 +2878,21 @@
 
 }
 
+package android.app.wearable {
+
+  public class WearableSensingManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideData(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideDataStream(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    field public static final int STATUS_ACCESS_DENIED = 5; // 0x5
+    field public static final int STATUS_SERVICE_UNAVAILABLE = 3; // 0x3
+    field public static final int STATUS_SUCCESS = 1; // 0x1
+    field public static final int STATUS_UNKNOWN = 0; // 0x0
+    field public static final int STATUS_UNSUPPORTED = 2; // 0x2
+    field public static final int STATUS_WEARABLE_UNAVAILABLE = 4; // 0x4
+  }
+
+}
+
 package android.apphibernation {
 
   public class AppHibernationManager {
@@ -3095,6 +3112,7 @@
     field public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
     field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
     field public static final String APP_PREDICTION_SERVICE = "app_prediction";
+    field public static final String AUDIO_DEVICE_VOLUME_SERVICE = "audio_device_volume";
     field public static final String BACKUP_SERVICE = "backup";
     field public static final String BATTERY_STATS_SERVICE = "batterystats";
     field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
@@ -3130,6 +3148,7 @@
     field public static final String UWB_SERVICE = "uwb";
     field public static final String VR_SERVICE = "vrmanager";
     field public static final String WALLPAPER_EFFECTS_GENERATION_SERVICE = "wallpaper_effects_generation";
+    field public static final String WEARABLE_SENSING_SERVICE = "wearable_sensing";
     field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
     field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
     field public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
@@ -6293,7 +6312,7 @@
   }
 
   public class AudioDeviceVolumeManager {
-    ctor public AudioDeviceVolumeManager(@NonNull android.content.Context);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.VolumeInfo getDeviceVolume(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolume(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
   }
 
@@ -6631,8 +6650,9 @@
     method public int getMaxVolumeIndex();
     method public int getMinVolumeIndex();
     method public int getStreamType();
-    method @Nullable public android.media.audiopolicy.AudioVolumeGroup getVolumeGroup();
+    method @NonNull public android.media.audiopolicy.AudioVolumeGroup getVolumeGroup();
     method public int getVolumeIndex();
+    method public boolean hasMuteCommand();
     method public boolean hasStreamType();
     method public boolean hasVolumeGroup();
     method public boolean isMuted();
@@ -10574,6 +10594,7 @@
     field public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
     field public static final String NAMESPACE_TRANSPARENCY_METADATA = "transparency_metadata";
     field public static final String NAMESPACE_UWB = "uwb";
+    field public static final String NAMESPACE_WEARABLE_SENSING = "wearable_sensing";
     field public static final String NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT = "window_manager_native_boot";
   }
 
@@ -12356,6 +12377,18 @@
 
 }
 
+package android.service.wearable {
+
+  public abstract class WearableSensingService extends android.app.Service {
+    ctor public WearableSensingService();
+    method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method @BinderThread public abstract void onDataProvided(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @BinderThread public abstract void onDataStreamProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    field public static final String SERVICE_INTERFACE = "android.service.wearable.WearableSensingService";
+  }
+
+}
+
 package android.telecom {
 
   @Deprecated public class AudioState implements android.os.Parcelable {
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 8ec313ec..f2eced3 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -11,7 +11,7 @@
 per-file ApplicationThreadConstants.java = file:/services/core/java/com/android/server/am/OWNERS
 per-file BroadcastOptions.java = file:/services/core/java/com/android/server/am/OWNERS
 per-file ContentProviderHolder* = file:/services/core/java/com/android/server/am/OWNERS
-per-file ForegroundService* = file:/services/core/java/com/android/server/am/OWNERS
+per-file *ForegroundService* = file:/services/core/java/com/android/server/am/OWNERS
 per-file IActivityController.aidl = file:/services/core/java/com/android/server/am/OWNERS
 per-file IActivityManager.aidl = file:/services/core/java/com/android/server/am/OWNERS
 per-file IApplicationThread.aidl = file:/services/core/java/com/android/server/am/OWNERS
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e5cb0ea..6857984 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -118,6 +118,7 @@
 import android.location.ICountryDetector;
 import android.location.ILocationManager;
 import android.location.LocationManager;
+import android.media.AudioDeviceVolumeManager;
 import android.media.AudioManager;
 import android.media.MediaFrameworkInitializer;
 import android.media.MediaFrameworkPlatformInitializer;
@@ -343,6 +344,13 @@
                 return new AudioManager(ctx);
             }});
 
+        registerService(Context.AUDIO_DEVICE_VOLUME_SERVICE, AudioDeviceVolumeManager.class,
+                new CachedServiceFetcher<AudioDeviceVolumeManager>() {
+            @Override
+            public AudioDeviceVolumeManager createService(ContextImpl ctx) {
+                return new AudioDeviceVolumeManager(ctx);
+            }});
+
         registerService(Context.MEDIA_ROUTER_SERVICE, MediaRouter.class,
                 new CachedServiceFetcher<MediaRouter>() {
             @Override
diff --git a/core/java/android/app/wearable/IWearableSensingManager.aidl b/core/java/android/app/wearable/IWearableSensingManager.aidl
new file mode 100644
index 0000000..ff37bd8
--- /dev/null
+++ b/core/java/android/app/wearable/IWearableSensingManager.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.wearable;
+
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.SharedMemory;
+
+/**
+ * Interface for a WearableSensingManager for managing wearable sensing service.
+ *
+ * @hide
+ */
+interface IWearableSensingManager {
+     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
+     void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
+     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
+     void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback);
+}
\ No newline at end of file
diff --git a/core/java/android/app/wearable/OWNERS b/core/java/android/app/wearable/OWNERS
new file mode 100644
index 0000000..073e2d7
--- /dev/null
+++ b/core/java/android/app/wearable/OWNERS
@@ -0,0 +1,3 @@
+charliewang@google.com
+oni@google.com
+volnov@google.com
\ No newline at end of file
diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java
new file mode 100644
index 0000000..a71590b
--- /dev/null
+++ b/core/java/android/app/wearable/WearableSensingManager.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.wearable;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.content.Context;
+import android.os.Binder;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.SharedMemory;
+import android.service.wearable.WearableSensingService;
+import android.system.OsConstants;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Allows granted apps to manage the WearableSensingService.
+ * Applications are responsible for managing the connection to Wearables. Applications can choose
+ * to provide a data stream to the WearableSensingService to use for
+ * computing {@link AmbientContextEvent}s. Applications can also optionally provide their own
+ * defined data to power the detection of {@link AmbientContextEvent}s.
+ * Methods on this class requires the caller to hold and be granted the
+ * {@link Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE}.
+ *
+ * @hide
+ */
+
+@SystemApi
+@SystemService(Context.WEARABLE_SENSING_SERVICE)
+public class WearableSensingManager {
+    /**
+     * The bundle key for the service status query result, used in
+     * {@code RemoteCallback#sendResult}.
+     *
+     * @hide
+     */
+    public static final String STATUS_RESPONSE_BUNDLE_KEY =
+            "android.app.wearable.WearableSensingStatusBundleKey";
+
+
+    /**
+     * An unknown status.
+     */
+    public static final int STATUS_UNKNOWN = 0;
+
+    /**
+     * The value of the status code that indicates success.
+     */
+    public static final int STATUS_SUCCESS = 1;
+
+    /**
+     * The value of the status code that indicates one or more of the
+     * requested events are not supported.
+     */
+    public static final int STATUS_UNSUPPORTED = 2;
+
+    /**
+     * The value of the status code that indicates service not available.
+     */
+    public static final int STATUS_SERVICE_UNAVAILABLE = 3;
+
+    /**
+     * The value of the status code that there's no connection to the wearable.
+     */
+    public static final int STATUS_WEARABLE_UNAVAILABLE = 4;
+
+    /**
+     * The value of the status code that the app is not granted access.
+     */
+    public static final int STATUS_ACCESS_DENIED = 5;
+
+    /** @hide */
+    @IntDef(prefix = { "STATUS_" }, value = {
+            STATUS_UNKNOWN,
+            STATUS_SUCCESS,
+            STATUS_UNSUPPORTED,
+            STATUS_SERVICE_UNAVAILABLE,
+            STATUS_WEARABLE_UNAVAILABLE,
+            STATUS_ACCESS_DENIED
+    }) public @interface StatusCode {}
+
+    private final Context mContext;
+    private final IWearableSensingManager mService;
+
+    /**
+     * {@hide}
+     */
+    public WearableSensingManager(Context context, IWearableSensingManager service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Provides a data stream to the WearableSensingService that's backed by the
+     * parcelFileDescriptor, and sends the result to the {@link Consumer} right after the call.
+     * This is used by applications that will also provide an implementation of
+     * an isolated WearableSensingService. If the data stream was provided successfully
+     * {@link WearableSensingManager#STATUS_SUCCESS} will be provided.
+     *
+     * @param parcelFileDescriptor The data stream to provide
+     * @param executor Executor on which to run the consumer callback
+     * @param statusConsumer A consumer that handles the status codes, which is returned
+     *                 right after the call.
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
+    public void provideDataStream(
+            @NonNull ParcelFileDescriptor parcelFileDescriptor,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull @StatusCode Consumer<Integer> statusConsumer) {
+        try {
+            RemoteCallback callback = new RemoteCallback(result -> {
+                int status = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> statusConsumer.accept(status));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            });
+            mService.provideDataStream(parcelFileDescriptor, callback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets configuration and provides read-only data in a {@link PersistableBundle} that may be
+     * used by the WearableSensingService, and sends the result to the {@link Consumer}
+     * right after the call. It is dependent on the application to
+     * define the type of data to provide. This is used by applications that will also
+     * provide an implementation of an isolated WearableSensingService. If the data was
+     * provided successfully {@link WearableSensingManager#STATUS_SUCCESS} will be povided.
+     *
+     * @param data Application configuration data to provide to the {@link WearableSensingService}.
+     *             PersistableBundle does not allow any remotable objects or other contents
+     *             that can be used to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob to
+     *                     provide to the {@link WearableSensingService}. Use this to provide the
+     *                     sensing models data or other such data to the trusted process.
+     *                     The sharedMemory must be read only and protected with
+     *                     {@link OsConstants.PROT_READ}.
+     *                     Other operations will be removed by the system.
+     * @param executor Executor on which to run the consumer callback
+     * @param statusConsumer A consumer that handles the status codes, which is returned
+     *                     right after the call
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
+    public void provideData(
+            @NonNull PersistableBundle data, @Nullable SharedMemory sharedMemory,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull @StatusCode Consumer<Integer> statusConsumer) {
+        try {
+            RemoteCallback callback = new RemoteCallback(result -> {
+                int status = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> statusConsumer.accept(status));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            });
+            mService.provideData(data, sharedMemory, callback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9dc3836..0b20078 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3850,6 +3850,7 @@
             WIFI_RTT_RANGING_SERVICE,
             NSD_SERVICE,
             AUDIO_SERVICE,
+            AUDIO_DEVICE_VOLUME_SERVICE,
             AUTH_SERVICE,
             FINGERPRINT_SERVICE,
             //@hide: FACE_SERVICE,
@@ -4693,6 +4694,18 @@
     public static final String AUDIO_SERVICE = "audio";
 
     /**
+     * @hide
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.media.AudioDeviceVolumeManager} for handling management of audio device
+     * (e.g. speaker, USB headset) volume.
+     *
+     * @see #getSystemService(String)
+     * @see android.media.AudioDeviceVolumeManager
+     */
+    @SystemApi
+    public static final String AUDIO_DEVICE_VOLUME_SERVICE = "audio_device_volume";
+
+    /**
      * Use with {@link #getSystemService(String)} to retrieve a {@link
      * android.media.MediaTranscodingManager} for transcoding media.
      *
@@ -6065,6 +6078,17 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.wearable.WearableSensingManager}.
+     *
+     * @see #getSystemService(String)
+     * @see WearableSensingManager
+     * @hide
+     */
+    @SystemApi
+    public static final String WEARABLE_SENSING_SERVICE = "wearable_sensing";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.healthconnect.HealthConnectManager}.
      *
      * @see #getSystemService(String)
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 8a09cd7..19e7bd4 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -769,6 +769,15 @@
             "ambient_context_manager_service";
 
     /**
+     * Namespace for WearableSensingManagerService related features.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_WEARABLE_SENSING =
+            "wearable_sensing";
+
+    /**
      * Namespace for Vendor System Native related features.
      *
      * @hide
diff --git a/core/java/android/service/wearable/IWearableSensingService.aidl b/core/java/android/service/wearable/IWearableSensingService.aidl
new file mode 100644
index 0000000..ba71174
--- /dev/null
+++ b/core/java/android/service/wearable/IWearableSensingService.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.wearable;
+
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.SharedMemory;
+
+/**
+ * Interface for a concrete implementation to provide AmbientContextEvents to the framework for
+ * wearables.
+ *
+ * @hide
+ */
+oneway interface IWearableSensingService {
+    void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
+    void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback);
+}
\ No newline at end of file
diff --git a/core/java/android/service/wearable/OWNERS b/core/java/android/service/wearable/OWNERS
new file mode 100644
index 0000000..073e2d7
--- /dev/null
+++ b/core/java/android/service/wearable/OWNERS
@@ -0,0 +1,3 @@
+charliewang@google.com
+oni@google.com
+volnov@google.com
\ No newline at end of file
diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java
new file mode 100644
index 0000000..a1c7658
--- /dev/null
+++ b/core/java/android/service/wearable/WearableSensingService.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.wearable;
+
+import android.annotation.BinderThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.wearable.WearableSensingManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.SharedMemory;
+import android.util.Slog;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Abstract base class for sensing with wearable devices. An example of this is {@link
+ *AmbientContextEvent} detection.
+ *
+ * <p> A service that provides requested sensing events to the system, such as a {@link
+ *AmbientContextEvent}. The system's default WearableSensingService implementation is configured in
+ * {@code config_defaultWearableSensingService}. If this config has no value, a stub is
+ * returned.
+ *
+ * <p> An implementation of a WearableSensingService should be an isolated service. Using the
+ * "isolatedProcess=true" attribute in the service's configurations. </p>
+ **
+ * <pre>
+ * {@literal
+ * <service android:name=".YourWearableSensingService"
+ *          android:permission="android.permission.BIND_WEARABLE_SENSING_SERVICE"
+ *          android:isolatedProcess="true">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class WearableSensingService extends Service {
+    private static final String TAG = WearableSensingService.class.getSimpleName();
+
+    /**
+     * The bundle key for this class of object, used in {@code RemoteCallback#sendResult}.
+     *
+     * @hide
+     */
+    public static final String STATUS_RESPONSE_BUNDLE_KEY =
+            "android.app.wearable.WearableSensingStatusBundleKey";
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service. To be supported, the
+     * service must also require the
+     * {@link android.Manifest.permission#BIND_WEARABLE_SENSING_SERVICE}
+     * permission so that other applications can not abuse it.
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.service.wearable.WearableSensingService";
+
+    @Nullable
+    @Override
+    public final IBinder onBind(@NonNull Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return new IWearableSensingService.Stub() {
+                /** {@inheritDoc} */
+                @Override
+                public void provideDataStream(
+                        ParcelFileDescriptor parcelFileDescriptor,
+                        RemoteCallback callback) {
+                    Objects.requireNonNull(parcelFileDescriptor);
+                    Consumer<Integer> consumer = response -> {
+                        Bundle bundle = new Bundle();
+                        bundle.putInt(
+                                STATUS_RESPONSE_BUNDLE_KEY,
+                                response);
+                        callback.sendResult(bundle);
+                    };
+                    WearableSensingService.this.onDataStreamProvided(
+                            parcelFileDescriptor, consumer);
+                }
+
+                /** {@inheritDoc} */
+                @Override
+                public void provideData(
+                        PersistableBundle data,
+                        SharedMemory sharedMemory,
+                        RemoteCallback callback) {
+                    Objects.requireNonNull(data);
+                    Consumer<Integer> consumer = response -> {
+                        Bundle bundle = new Bundle();
+                        bundle.putInt(
+                                STATUS_RESPONSE_BUNDLE_KEY,
+                                response);
+                        callback.sendResult(bundle);
+                    };
+                    WearableSensingService.this.onDataProvided(data, sharedMemory, consumer);
+                }
+            };
+        }
+        Slog.w(TAG, "Incorrect service interface, returning null.");
+        return null;
+    }
+
+    /**
+     * Called when a data stream to the wearable is provided. This data stream can be used to obtain
+     * data from a wearable device. It is up to the implementation to maintain the data stream and
+     * close the data stream when it is finished.
+     *
+     * @param parcelFileDescriptor The data stream to the wearable
+     * @param statusConsumer the consumer for the service status.
+     */
+    @BinderThread
+    public abstract void onDataStreamProvided(@NonNull ParcelFileDescriptor parcelFileDescriptor,
+            @NonNull Consumer<Integer> statusConsumer);
+
+    /**
+     * Called when configurations and read-only data in a {@link PersistableBundle}
+     * can be used by the WearableSensingService and sends the result to the {@link Consumer}
+     * right after the call. It is dependent on the application to define the type of data to
+     * provide. This is used by applications that will also provide an implementation of an isolated
+     * WearableSensingService. If the data was provided successfully
+     * {@link WearableSensingManager#STATUS_SUCCESS} will be provided.
+     *
+     * @param data Application configuration data to provide to the {@link WearableSensingService}.
+     *             PersistableBundle does not allow any remotable objects or other contents
+     *             that can be used to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob to
+     *                     provide to the {@link WearableSensingService}. Use this to provide the
+     *                     sensing models data or other such data to the trusted process.
+     * @param statusConsumer the consumer for the service status.
+     */
+    @BinderThread
+    public abstract void onDataProvided(
+            @NonNull PersistableBundle data,
+            @Nullable SharedMemory sharedMemory,
+            @NonNull Consumer<Integer> statusConsumer);
+}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 720813a..ef18458 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -947,112 +947,108 @@
                     + " left=" + (mWindowSpaceLeft != mLocation[0])
                     + " top=" + (mWindowSpaceTop != mLocation[1]));
 
+            mVisible = mRequestedVisible;
+            mWindowSpaceLeft = mLocation[0];
+            mWindowSpaceTop = mLocation[1];
+            mSurfaceWidth = myWidth;
+            mSurfaceHeight = myHeight;
+            mFormat = mRequestedFormat;
+            mAlpha = alpha;
+            mLastWindowVisibility = mWindowVisibility;
+            mTransformHint = viewRoot.getBufferTransformHint();
+            mSubLayer = mRequestedSubLayer;
+
+            mScreenRect.left = mWindowSpaceLeft;
+            mScreenRect.top = mWindowSpaceTop;
+            mScreenRect.right = mWindowSpaceLeft + getWidth();
+            mScreenRect.bottom = mWindowSpaceTop + getHeight();
+            if (translator != null) {
+                translator.translateRectInAppWindowToScreen(mScreenRect);
+            }
+
+            final Rect surfaceInsets = viewRoot.mWindowAttributes.surfaceInsets;
+            mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
+            // Collect all geometry changes and apply these changes on the RenderThread worker
+            // via the RenderNode.PositionUpdateListener.
+            final Transaction surfaceUpdateTransaction = new Transaction();
+            if (creating) {
+                updateOpaqueFlag();
+                final String name = "SurfaceView[" + viewRoot.getTitle().toString() + "]";
+                createBlastSurfaceControls(viewRoot, name, surfaceUpdateTransaction);
+            } else if (mSurfaceControl == null) {
+                return;
+            }
+
+            final boolean redrawNeeded = sizeChanged || creating || hintChanged
+                    || (mVisible && !mDrawFinished) || alphaChanged || relativeZChanged;
+            boolean shouldSyncBuffer =
+                    redrawNeeded && viewRoot.wasRelayoutRequested() && viewRoot.isInLocalSync();
+            SyncBufferTransactionCallback syncBufferTransactionCallback = null;
+            if (shouldSyncBuffer) {
+                syncBufferTransactionCallback = new SyncBufferTransactionCallback();
+                mBlastBufferQueue.syncNextTransaction(
+                        false /* acquireSingleBuffer */,
+                        syncBufferTransactionCallback::onTransactionReady);
+            }
+
+            final boolean realSizeChanged = performSurfaceTransaction(viewRoot, translator,
+                    creating, sizeChanged, hintChanged, relativeZChanged,
+                    surfaceUpdateTransaction);
+
             try {
-                mVisible = mRequestedVisible;
-                mWindowSpaceLeft = mLocation[0];
-                mWindowSpaceTop = mLocation[1];
-                mSurfaceWidth = myWidth;
-                mSurfaceHeight = myHeight;
-                mFormat = mRequestedFormat;
-                mAlpha = alpha;
-                mLastWindowVisibility = mWindowVisibility;
-                mTransformHint = viewRoot.getBufferTransformHint();
-                mSubLayer = mRequestedSubLayer;
+                SurfaceHolder.Callback[] callbacks = null;
 
-                mScreenRect.left = mWindowSpaceLeft;
-                mScreenRect.top = mWindowSpaceTop;
-                mScreenRect.right = mWindowSpaceLeft + getWidth();
-                mScreenRect.bottom = mWindowSpaceTop + getHeight();
-                if (translator != null) {
-                    translator.translateRectInAppWindowToScreen(mScreenRect);
+                final boolean surfaceChanged = creating;
+                if (mSurfaceCreated && (surfaceChanged || (!mVisible && visibleChanged))) {
+                    mSurfaceCreated = false;
+                    notifySurfaceDestroyed();
                 }
 
-                final Rect surfaceInsets = viewRoot.mWindowAttributes.surfaceInsets;
-                mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
-                // Collect all geometry changes and apply these changes on the RenderThread worker
-                // via the RenderNode.PositionUpdateListener.
-                final Transaction surfaceUpdateTransaction = new Transaction();
-                if (creating) {
-                    updateOpaqueFlag();
-                    final String name = "SurfaceView[" + viewRoot.getTitle().toString() + "]";
-                    createBlastSurfaceControls(viewRoot, name, surfaceUpdateTransaction);
-                } else if (mSurfaceControl == null) {
-                    return;
-                }
+                copySurface(creating /* surfaceControlCreated */, sizeChanged);
 
-                final boolean redrawNeeded = sizeChanged || creating || hintChanged
-                        || (mVisible && !mDrawFinished) || alphaChanged || relativeZChanged;
-                boolean shouldSyncBuffer =
-                        redrawNeeded && viewRoot.wasRelayoutRequested() && viewRoot.isInLocalSync();
-                SyncBufferTransactionCallback syncBufferTransactionCallback = null;
-                if (shouldSyncBuffer) {
-                    syncBufferTransactionCallback = new SyncBufferTransactionCallback();
-                    mBlastBufferQueue.syncNextTransaction(
-                            false /* acquireSingleBuffer */,
-                            syncBufferTransactionCallback::onTransactionReady);
-                }
-
-                final boolean realSizeChanged = performSurfaceTransaction(viewRoot, translator,
-                        creating, sizeChanged, hintChanged, relativeZChanged,
-                        surfaceUpdateTransaction);
-
-                try {
-                    SurfaceHolder.Callback[] callbacks = null;
-
-                    final boolean surfaceChanged = creating;
-                    if (mSurfaceCreated && (surfaceChanged || (!mVisible && visibleChanged))) {
-                        mSurfaceCreated = false;
-                        notifySurfaceDestroyed();
+                if (mVisible && mSurface.isValid()) {
+                    if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
+                        mSurfaceCreated = true;
+                        mIsCreating = true;
+                        if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                                + "visibleChanged -- surfaceCreated");
+                        callbacks = getSurfaceCallbacks();
+                        for (SurfaceHolder.Callback c : callbacks) {
+                            c.surfaceCreated(mSurfaceHolder);
+                        }
                     }
-
-                    copySurface(creating /* surfaceControlCreated */, sizeChanged);
-
-                    if (mVisible && mSurface.isValid()) {
-                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
-                            mSurfaceCreated = true;
-                            mIsCreating = true;
-                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
-                                    + "visibleChanged -- surfaceCreated");
+                    if (creating || formatChanged || sizeChanged || hintChanged
+                            || visibleChanged || realSizeChanged) {
+                        if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                                + "surfaceChanged -- format=" + mFormat
+                                + " w=" + myWidth + " h=" + myHeight);
+                        if (callbacks == null) {
                             callbacks = getSurfaceCallbacks();
-                            for (SurfaceHolder.Callback c : callbacks) {
-                                c.surfaceCreated(mSurfaceHolder);
-                            }
                         }
-                        if (creating || formatChanged || sizeChanged || hintChanged
-                                || visibleChanged || realSizeChanged) {
-                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
-                                    + "surfaceChanged -- format=" + mFormat
-                                    + " w=" + myWidth + " h=" + myHeight);
-                            if (callbacks == null) {
-                                callbacks = getSurfaceCallbacks();
-                            }
-                            for (SurfaceHolder.Callback c : callbacks) {
-                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
-                            }
-                        }
-                        if (redrawNeeded) {
-                            if (DEBUG) {
-                                Log.i(TAG, System.identityHashCode(this) + " surfaceRedrawNeeded");
-                            }
-                            if (callbacks == null) {
-                                callbacks = getSurfaceCallbacks();
-                            }
-
-                            if (shouldSyncBuffer) {
-                                handleSyncBufferCallback(callbacks, syncBufferTransactionCallback);
-                            } else {
-                                handleSyncNoBuffer(callbacks);
-                            }
+                        for (SurfaceHolder.Callback c : callbacks) {
+                            c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
                         }
                     }
-                } finally {
-                    mIsCreating = false;
-                    if (mSurfaceControl != null && !mSurfaceCreated) {
-                        releaseSurfaces(false /* releaseSurfacePackage*/);
+                    if (redrawNeeded) {
+                        if (DEBUG) {
+                            Log.i(TAG, System.identityHashCode(this) + " surfaceRedrawNeeded");
+                        }
+                        if (callbacks == null) {
+                            callbacks = getSurfaceCallbacks();
+                        }
+
+                        if (shouldSyncBuffer) {
+                            handleSyncBufferCallback(callbacks, syncBufferTransactionCallback);
+                        } else {
+                            handleSyncNoBuffer(callbacks);
+                        }
                     }
                 }
-            } catch (Exception ex) {
-                Log.e(TAG, "Exception configuring surface", ex);
+            } finally {
+                mIsCreating = false;
+                if (mSurfaceControl != null && !mSurfaceCreated) {
+                    releaseSurfaces(false /* releaseSurfacePackage*/);
+                }
             }
             if (DEBUG) Log.v(
                 TAG, "Layout: x=" + mScreenRect.left + " y=" + mScreenRect.top
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index fc64eb9..1063532 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -815,6 +815,18 @@
     }
 
     /**
+     * Clears container adjacent.
+     * @param root the root container to clear the adjacent roots for.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction clearAdjacentRoots(
+            @NonNull WindowContainerToken root) {
+        mHierarchyOps.add(HierarchyOp.createForClearAdjacentRoots(root.asBinder()));
+        return this;
+    }
+
+    /**
      * Merges another WCT into this one.
      * @param transfer When true, this will transfer everything from other potentially leaving
      *                 other in an unusable state. When false, other is left alone, but
@@ -1229,6 +1241,7 @@
         public static final int HIERARCHY_OP_TYPE_REMOVE_TASK = 20;
         public static final int HIERARCHY_OP_TYPE_FINISH_ACTIVITY = 21;
         public static final int HIERARCHY_OP_TYPE_SET_COMPANION_TASK_FRAGMENT = 22;
+        public static final int HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS = 23;
 
         // The following key(s) are for use with mLaunchOptions:
         // When launching a task (eg. from recents), this is the taskId to be launched.
@@ -1365,6 +1378,13 @@
                     .build();
         }
 
+        /** Create a hierarchy op for clearing adjacent root tasks. */
+        public static HierarchyOp createForClearAdjacentRoots(@NonNull IBinder root) {
+            return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS)
+                    .setContainer(root)
+                    .build();
+        }
+
         /** Only creates through {@link Builder}. */
         private HierarchyOp(int type) {
             mType = type;
@@ -1560,6 +1580,8 @@
                 case HIERARCHY_OP_TYPE_SET_COMPANION_TASK_FRAGMENT:
                     return "{setCompanionTaskFragment: container = " + mContainer + " companion = "
                             + mReparent + "}";
+                case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS:
+                    return "{ClearAdjacentRoot: container=" + mContainer + "}";
                 default:
                     return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
                             + " mToTop=" + mToTop
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f14ffc8..5a7abcc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6763,6 +6763,21 @@
     <permission android:name="android.permission.MANAGE_DEVICE_LOCK_STATE"
                 android:protectionLevel="internal|role" />
 
+    <!-- @SystemApi Required by a WearableSensingService to
+          ensure that only the caller with this permission can bind to it.
+          <p> Protection level: signature
+     @hide
+    -->
+    <permission android:name="android.permission.BIND_WEARABLE_SENSING_SERVICE"
+                android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an app to manage the wearable sensing service.
+             <p>Protection level: signature|privileged
+             @hide
+        -->
+    <permission android:name="android.permission.MANAGE_WEARABLE_SENSING_SERVICE"
+                android:protectionLevel="signature|privileged" />
+
     <!-- Allows applications to use the long running jobs APIs.
          <p>This is a special access permission that can be revoked by the system or the user.
          <p>Apps need to target API {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or above
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6c18259..b92d96f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4149,6 +4149,12 @@
    -->
     <string name="config_defaultAmbientContextDetectionService" translatable="false"></string>
 
+    <!-- The component name for the default system wearable sensing service.
+        This service must be trusted, as it can be activated without explicit consent of the user.
+        See android.service.wearable.WearableSensingService.
+   -->
+    <string name="config_defaultWearableSensingService" translatable="false"></string>
+
     <!-- Component name that accepts ACTION_SEND intents for requesting ambient context consent. -->
     <string translatable="false" name="config_defaultAmbientContextConsentComponent"></string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 691d48e..9d7e6f8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3768,6 +3768,7 @@
   <java-symbol type="string" name="config_defaultAmbientContextConsentComponent" />
   <java-symbol type="string" name="config_ambientContextPackageNameExtraKey" />
   <java-symbol type="string" name="config_ambientContextEventArrayExtraKey" />
+  <java-symbol type="string" name="config_defaultWearableSensingService" />
   <java-symbol type="string" name="config_retailDemoPackage" />
   <java-symbol type="string" name="config_retailDemoPackageSignature" />
 
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java
new file mode 100644
index 0000000..a2d8467
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/RadioServiceUserControllerTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.broadcastradio;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.os.Binder;
+import android.os.UserHandle;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+/**
+ * Tests for {@link com.android.server.broadcastradio.RadioServiceUserController}
+ */
+public final class RadioServiceUserControllerTest extends ExtendedRadioMockitoTestCase {
+
+    private static final int USER_ID_1 = 11;
+    private static final int USER_ID_2 = 12;
+
+    @Mock
+    private UserHandle mUserHandleMock;
+
+    @Override
+    protected void initializeSession(StaticMockitoSessionBuilder builder) {
+        builder.spyStatic(ActivityManager.class)
+                .spyStatic(Binder.class);
+    }
+
+    @Before
+    public void setUp() {
+        doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
+        doReturn(USER_ID_1).when(() -> ActivityManager.getCurrentUser());
+    }
+
+    @Test
+    public void isCurrentUser_forCurrentUser_returnsFalse() {
+        when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
+
+        assertWithMessage("Current user")
+                .that(RadioServiceUserController.isCurrentOrSystemUser()).isTrue();
+    }
+
+    @Test
+    public void isCurrentUser_forNonCurrentUser_returnsFalse() {
+        when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_2);
+
+        assertWithMessage("Non-current user")
+                .that(RadioServiceUserController.isCurrentOrSystemUser()).isFalse();
+    }
+
+    @Test
+    public void isCurrentUser_forSystemUser_returnsTrue() {
+        when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM);
+
+        assertWithMessage("System user")
+                .that(RadioServiceUserController.isCurrentOrSystemUser()).isTrue();
+    }
+}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
index 93214e5..36aa915 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java
@@ -20,6 +20,7 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -28,6 +29,9 @@
 import static org.mockito.Mockito.when;
 
 import android.hardware.broadcastradio.IBroadcastRadio;
+import android.hardware.radio.Announcement;
+import android.hardware.radio.IAnnouncementListener;
+import android.hardware.radio.ICloseHandle;
 import android.hardware.radio.ITuner;
 import android.hardware.radio.ITunerCallback;
 import android.hardware.radio.RadioManager;
@@ -54,6 +58,7 @@
     private static final int DAB_RADIO_MODULE_ID = 1;
     private static final ArrayList<String> SERVICE_LIST =
             new ArrayList<>(Arrays.asList("FmService", "DabService"));
+    private static final int[] TEST_ENABLED_TYPES = new int[]{Announcement.TYPE_TRAFFIC};
 
     private BroadcastRadioServiceImpl mBroadcastRadioService;
     private IBinder.DeathRecipient mFmDeathRecipient;
@@ -78,6 +83,14 @@
     private TunerSession mFmTunerSessionMock;
     @Mock
     private ITunerCallback mTunerCallbackMock;
+    @Mock
+    private ICloseHandle mFmCloseHandleMock;
+    @Mock
+    private ICloseHandle mDabCloseHandleMock;
+    @Mock
+    private IAnnouncementListener mAnnouncementListenerMock;
+    @Mock
+    private IBinder mListenerBinderMock;
 
     @Override
     protected void initializeSession(StaticMockitoSessionBuilder builder) {
@@ -141,6 +154,19 @@
     }
 
     @Test
+    public void openSession_forNonCurrentUser_throwsException() throws Exception {
+        createBroadcastRadioService();
+        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+
+        IllegalStateException thrown = assertThrows(IllegalStateException.class,
+                () -> mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID,
+                        /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock));
+
+        assertWithMessage("Exception for opening session by non-current user")
+                .that(thrown).hasMessageThat().contains("Cannot open session for non-current user");
+    }
+
+    @Test
     public void binderDied_forDeathRecipient() throws Exception {
         createBroadcastRadioService();
 
@@ -151,6 +177,22 @@
                 .that(mBroadcastRadioService.hasModule(FM_RADIO_MODULE_ID)).isFalse();
     }
 
+    @Test
+    public void addAnnouncementListener_addsOnAllRadioModules() throws Exception {
+        createBroadcastRadioService();
+        when(mAnnouncementListenerMock.asBinder()).thenReturn(mListenerBinderMock);
+        when(mFmRadioModuleMock.addAnnouncementListener(any(), any()))
+                .thenReturn(mFmCloseHandleMock);
+        when(mDabRadioModuleMock.addAnnouncementListener(any(), any()))
+                .thenReturn(mDabCloseHandleMock);
+
+        mBroadcastRadioService.addAnnouncementListener(TEST_ENABLED_TYPES,
+                mAnnouncementListenerMock);
+
+        verify(mFmRadioModuleMock).addAnnouncementListener(any(), any());
+        verify(mDabRadioModuleMock).addAnnouncementListener(any(), any());
+    }
+
     private void createBroadcastRadioService() throws RemoteException {
         doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
         mockServiceManager();
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index a29e9c5..87d0ea4 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -330,6 +330,20 @@
     }
 
     @Test
+    public void tune_forCurrentUser_doesNotTune() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+        RadioManager.ProgramInfo tuneInfo =
+                AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
+
+        mTunerSessions[0].tune(initialSel);
+
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+                .onCurrentProgramInfoChanged(tuneInfo);
+    }
+
+    @Test
     public void step_withDirectionUp() throws Exception {
         long initFreq = AM_FM_FREQUENCY_LIST[1];
         ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
index 99e7043..0b7bbea 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/BroadcastRadioServiceHidlTest.java
@@ -156,6 +156,19 @@
     }
 
     @Test
+    public void openSession_forNonCurrentUser_throwsException() throws Exception {
+        createBroadcastRadioService();
+        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+
+        IllegalStateException thrown = assertThrows(IllegalStateException.class,
+                () -> mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID,
+                        /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock));
+
+        assertWithMessage("Exception for opening session by non-current user")
+                .that(thrown).hasMessageThat().contains("Cannot open session for non-current user");
+    }
+
+    @Test
     public void addAnnouncementListener_addsOnAllRadioModules() throws Exception {
         createBroadcastRadioService();
         when(mAnnouncementListenerMock.asBinder()).thenReturn(mBinderMock);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index 8884053..b7da5d0 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -337,6 +338,20 @@
     }
 
     @Test
+    public void tune_forCurrentUser_doesNotTune() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+        ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+        RadioManager.ProgramInfo tuneInfo =
+                TestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
+
+        mTunerSessions[0].tune(initialSel);
+
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+                .onCurrentProgramInfoChanged(tuneInfo);
+    }
+
+    @Test
     public void step_withDirectionUp() throws Exception {
         long initFreq = AM_FM_FREQUENCY_LIST[1];
         ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq);
@@ -427,6 +442,18 @@
     }
 
     @Test
+    public void cancel_forNonCurrentUser() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+        mTunerSessions[0].tune(initialSel);
+        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+
+        mTunerSessions[0].cancel();
+
+        verify(mHalTunerSessionMock, never()).cancel();
+    }
+
+    @Test
     public void getImage_withInvalidId_throwsIllegalArgumentException() throws Exception {
         openAidlClients(/* numClients= */ 1);
         int imageId = Constants.INVALID_IMAGE;
diff --git a/graphics/java/android/view/PixelCopy.java b/graphics/java/android/view/PixelCopy.java
index 889edb3..82ced43 100644
--- a/graphics/java/android/view/PixelCopy.java
+++ b/graphics/java/android/view/PixelCopy.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.graphics.Bitmap;
 import android.graphics.HardwareRenderer;
 import android.graphics.Rect;
@@ -355,27 +356,23 @@
      * PixelCopy.Request.ofSurface factories to create a {@link Request.Builder} for the
      * given source content. After setting any optional parameters, such as
      * {@link Builder#setSourceRect(Rect)}, build the request with {@link Builder#build()} and
-     * then execute it with {@link PixelCopy#request(Request)}
+     * then execute it with {@link PixelCopy#request(Request, Executor, Consumer)}
      */
     public static final class Request {
         private final Surface mSource;
-        private final Consumer<Result> mListener;
-        private final Executor mListenerThread;
         private final Rect mSourceInsets;
         private Rect mSrcRect;
         private Bitmap mDest;
 
-        private Request(Surface source, Rect sourceInsets, Executor listenerThread,
-                        Consumer<Result> listener) {
+        private Request(Surface source, Rect sourceInsets) {
             this.mSource = source;
             this.mSourceInsets = sourceInsets;
-            this.mListenerThread = listenerThread;
-            this.mListener = listener;
         }
 
         /**
          * A builder to create the complete PixelCopy request, which is then executed by calling
-         * {@link #request(Request)} with the built request returned from {@link #build()}
+         * {@link #request(Request, Executor, Consumer)} with the built request returned from
+         * {@link #build()}
          */
         public static final class Builder {
             private Request mRequest;
@@ -384,6 +381,78 @@
                 mRequest = request;
             }
 
+            /**
+             * Creates a PixelCopy request for the given {@link Window}
+             * @param source The Window to copy from
+             * @return A {@link Builder} builder to set the optional params & execute the request
+             */
+            @SuppressLint("BuilderSetStyle")
+            public static @NonNull Builder ofWindow(@NonNull Window source) {
+                final Rect insets = new Rect();
+                final Surface surface = sourceForWindow(source, insets);
+                return new Builder(new Request(surface, insets));
+            }
+
+            /**
+             * Creates a PixelCopy request for the {@link Window} that the given {@link View} is
+             * attached to.
+             *
+             * Note that this copy request is not cropped to the area the View occupies by default.
+             * If that behavior is desired, use {@link View#getLocationInWindow(int[])} combined
+             * with {@link Builder#setSourceRect(Rect)} to set a crop area to restrict the copy
+             * operation.
+             *
+             * @param source A View that {@link View#isAttachedToWindow() is attached} to a window
+             *               that will be used to retrieve the window to copy from.
+             * @return A {@link Builder} builder to set the optional params & execute the request
+             */
+            @SuppressLint("BuilderSetStyle")
+            public static @NonNull Builder ofWindow(@NonNull View source) {
+                if (source == null || !source.isAttachedToWindow()) {
+                    throw new IllegalArgumentException(
+                            "View must not be null & must be attached to window");
+                }
+                final Rect insets = new Rect();
+                Surface surface = null;
+                final ViewRootImpl root = source.getViewRootImpl();
+                if (root != null) {
+                    surface = root.mSurface;
+                    insets.set(root.mWindowAttributes.surfaceInsets);
+                }
+                if (surface == null || !surface.isValid()) {
+                    throw new IllegalArgumentException(
+                            "Window doesn't have a backing surface!");
+                }
+                return new Builder(new Request(surface, insets));
+            }
+
+            /**
+             * Creates a PixelCopy request for the given {@link Surface}
+             *
+             * @param source The Surface to copy from. Must be {@link Surface#isValid() valid}.
+             * @return A {@link Builder} builder to set the optional params & execute the request
+             */
+            @SuppressLint("BuilderSetStyle")
+            public static @NonNull Builder ofSurface(@NonNull Surface source) {
+                if (source == null || !source.isValid()) {
+                    throw new IllegalArgumentException("Source must not be null & must be valid");
+                }
+                return new Builder(new Request(source, null));
+            }
+
+            /**
+             * Creates a PixelCopy request for the {@link Surface} belonging to the
+             * given {@link SurfaceView}
+             *
+             * @param source The SurfaceView to copy from. The backing surface must be
+             *               {@link Surface#isValid() valid}
+             * @return A {@link Builder} builder to set the optional params & execute the request
+             */
+            @SuppressLint("BuilderSetStyle")
+            public static @NonNull Builder ofSurface(@NonNull SurfaceView source) {
+                return ofSurface(source.getHolder().getSurface());
+            }
+
             private void requireNotBuilt() {
                 if (mRequest == null) {
                     throw new IllegalStateException("build() already called on this builder");
@@ -439,89 +508,6 @@
         }
 
         /**
-         * Creates a PixelCopy request for the given {@link Window}
-         * @param source The Window to copy from
-         * @param callbackExecutor The executor to run the callback on
-         * @param listener The callback for when the copy request is completed
-         * @return A {@link Builder} builder to set the optional params & execute the request
-         */
-        public static @NonNull Builder ofWindow(@NonNull Window source,
-                                                @NonNull Executor callbackExecutor,
-                                                @NonNull Consumer<Result> listener) {
-            final Rect insets = new Rect();
-            final Surface surface = sourceForWindow(source, insets);
-            return new Builder(new Request(surface, insets, callbackExecutor, listener));
-        }
-
-        /**
-         * Creates a PixelCopy request for the {@link Window} that the given {@link View} is
-         * attached to.
-         *
-         * Note that this copy request is not cropped to the area the View occupies by default. If
-         * that behavior is desired, use {@link View#getLocationInWindow(int[])} combined with
-         * {@link Builder#setSourceRect(Rect)} to set a crop area to restrict the copy operation.
-         *
-         * @param source A View that {@link View#isAttachedToWindow() is attached} to a window that
-         *               will be used to retrieve the window to copy from.
-         * @param callbackExecutor The executor to run the callback on
-         * @param listener The callback for when the copy request is completed
-         * @return A {@link Builder} builder to set the optional params & execute the request
-         */
-        public static @NonNull Builder ofWindow(@NonNull View source,
-                                                @NonNull Executor callbackExecutor,
-                                                @NonNull Consumer<Result> listener) {
-            if (source == null || !source.isAttachedToWindow()) {
-                throw new IllegalArgumentException(
-                        "View must not be null & must be attached to window");
-            }
-            final Rect insets = new Rect();
-            Surface surface = null;
-            final ViewRootImpl root = source.getViewRootImpl();
-            if (root != null) {
-                surface = root.mSurface;
-                insets.set(root.mWindowAttributes.surfaceInsets);
-            }
-            if (surface == null || !surface.isValid()) {
-                throw new IllegalArgumentException(
-                        "Window doesn't have a backing surface!");
-            }
-            return new Builder(new Request(surface, insets, callbackExecutor, listener));
-        }
-
-        /**
-         * Creates a PixelCopy request for the given {@link Surface}
-         *
-         * @param source The Surface to copy from. Must be {@link Surface#isValid() valid}.
-         * @param callbackExecutor The executor to run the callback on
-         * @param listener The callback for when the copy request is completed
-         * @return A {@link Builder} builder to set the optional params & execute the request
-         */
-        public static @NonNull Builder ofSurface(@NonNull Surface source,
-                                                 @NonNull Executor callbackExecutor,
-                                                 @NonNull Consumer<Result> listener) {
-            if (source == null || !source.isValid()) {
-                throw new IllegalArgumentException("Source must not be null & must be valid");
-            }
-            return new Builder(new Request(source, null, callbackExecutor, listener));
-        }
-
-        /**
-         * Creates a PixelCopy request for the {@link Surface} belonging to the
-         * given {@link SurfaceView}
-         *
-         * @param source The SurfaceView to copy from. The backing surface must be
-         *               {@link Surface#isValid() valid}
-         * @param callbackExecutor The executor to run the callback on
-         * @param listener The callback for when the copy request is completed
-         * @return A {@link Builder} builder to set the optional params & execute the request
-         */
-        public static @NonNull Builder ofSurface(@NonNull SurfaceView source,
-                                                 @NonNull Executor callbackExecutor,
-                                                 @NonNull Consumer<Result> listener) {
-            return ofSurface(source.getHolder().getSurface(), callbackExecutor, listener);
-        }
-
-        /**
          * @return The destination bitmap as set by {@link Builder#setDestinationBitmap(Bitmap)}
          */
         public @Nullable Bitmap getDestinationBitmap() {
@@ -538,9 +524,10 @@
         /**
          * @hide
          */
-        public void request() {
+        public void request(@NonNull Executor callbackExecutor,
+                            @NonNull Consumer<Result> listener) {
             if (!mSource.isValid()) {
-                mListenerThread.execute(() -> mListener.accept(
+                callbackExecutor.execute(() -> listener.accept(
                         new Result(ERROR_SOURCE_INVALID, null)));
                 return;
             }
@@ -548,7 +535,7 @@
                     adjustSourceRectForInsets(mSourceInsets, mSrcRect), mDest) {
                 @Override
                 public void onCopyFinished(int result) {
-                    mListenerThread.execute(() -> mListener.accept(
+                    callbackExecutor.execute(() -> listener.accept(
                             new Result(result, mDestinationBitmap)));
                 }
             });
@@ -558,9 +545,12 @@
     /**
      * Executes the pixel copy request
      * @param request The request to execute
+     * @param callbackExecutor The executor to run the callback on
+     * @param listener The callback for when the copy request is completed
      */
-    public static void request(@NonNull Request request) {
-        request.request();
+    public static void request(@NonNull Request request, @NonNull Executor callbackExecutor,
+                               @NonNull Consumer<Result> listener) {
+        request.request(callbackExecutor, listener);
     }
 
     private PixelCopy() {}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
index a7d47ef..13afa49 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -86,13 +86,23 @@
     /** Animation for target that is opening in a change transition. */
     @NonNull
     Animation createChangeBoundsOpenAnimation(@NonNull RemoteAnimationTarget target) {
-        final Rect bounds = target.localBounds;
-        // The target will be animated in from left or right depends on its position.
-        final int startLeft = bounds.left == 0 ? -bounds.width() : bounds.width();
+        final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
+        final Rect bounds = target.screenSpaceBounds;
+        final int startLeft;
+        final int startTop;
+        if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
+            // The window will be animated in from left or right depending on its position.
+            startTop = 0;
+            startLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
+        } else {
+            // The window will be animated in from top or bottom depending on its position.
+            startTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
+            startLeft = 0;
+        }
 
         // The position should be 0-based as we will post translate in
         // TaskFragmentAnimationAdapter#onAnimationUpdate
-        final Animation animation = new TranslateAnimation(startLeft, 0, 0, 0);
+        final Animation animation = new TranslateAnimation(startLeft, 0, startTop, 0);
         animation.setInterpolator(mFastOutExtraSlowInInterpolator);
         animation.setDuration(CHANGE_ANIMATION_DURATION);
         animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
@@ -103,13 +113,24 @@
     /** Animation for target that is closing in a change transition. */
     @NonNull
     Animation createChangeBoundsCloseAnimation(@NonNull RemoteAnimationTarget target) {
-        final Rect bounds = target.localBounds;
-        // The target will be animated out to left or right depends on its position.
-        final int endLeft = bounds.left == 0 ? -bounds.width() : bounds.width();
+        final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
+        // TODO(b/258126915): we want to keep track of the closing start bounds
+        final Rect bounds = target.screenSpaceBounds;
+        final int endTop;
+        final int endLeft;
+        if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
+            // The window will be animated out to left or right depending on its position.
+            endTop = 0;
+            endLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
+        } else {
+            // The window will be animated out to top or bottom depending on its position.
+            endTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
+            endLeft = 0;
+        }
 
         // The position should be 0-based as we will post translate in
         // TaskFragmentAnimationAdapter#onAnimationUpdate
-        final Animation animation = new TranslateAnimation(0, endLeft, 0, 0);
+        final Animation animation = new TranslateAnimation(0, endLeft, 0, endTop);
         animation.setInterpolator(mFastOutExtraSlowInInterpolator);
         animation.setDuration(CHANGE_ANIMATION_DURATION);
         animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index 65a7d09..d10a674 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -86,11 +86,11 @@
         final int startLeft;
         final int startTop;
         if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
-            // The window will be animated in from left or right depends on its position.
+            // The window will be animated in from left or right depending on its position.
             startTop = 0;
             startLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
         } else {
-            // The window will be animated in from top or bottom depends on its position.
+            // The window will be animated in from top or bottom depending on its position.
             startTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
             startLeft = 0;
         }
@@ -114,11 +114,11 @@
         final int endTop;
         final int endLeft;
         if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
-            // The window will be animated out to left or right depends on its position.
+            // The window will be animated out to left or right depending on its position.
             endTop = 0;
             endLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
         } else {
-            // The window will be animated out to top or bottom depends on its position.
+            // The window will be animated out to top or bottom depending on its position.
             endTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
             endLeft = 0;
         }
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 06ffb72..3fa369d 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -571,7 +571,7 @@
 
   // Retrieve the package group from the package id of the resource id.
   if (UNLIKELY(!is_valid_resid(resid))) {
-    LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
+    LOG(ERROR) << base::StringPrintf("Invalid resource ID 0x%08x.", resid);
     return base::unexpected(std::nullopt);
   }
 
@@ -580,7 +580,7 @@
   const uint16_t entry_idx = get_entry_id(resid);
   uint8_t package_idx = package_ids_[package_id];
   if (UNLIKELY(package_idx == 0xff)) {
-    ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
+    ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for resource ID 0x%08x.",
                                              package_id, resid);
     return base::unexpected(std::nullopt);
   }
diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java
index 40f6dc5..4e2ce91 100644
--- a/media/java/android/media/AudioDeviceVolumeManager.java
+++ b/media/java/android/media/AudioDeviceVolumeManager.java
@@ -21,7 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.IBinder;
@@ -44,8 +43,7 @@
 @SystemApi
 public class AudioDeviceVolumeManager {
 
-    // define when using Log.*
-    //private static final String TAG = "AudioDeviceVolumeManager";
+    private static final String TAG = "AudioDeviceVolumeManager";
 
     /** @hide
      * Indicates no special treatment in the handling of the volume adjustment */
@@ -70,20 +68,15 @@
     private static IAudioService sService;
 
     private final @NonNull String mPackageName;
-    private final @Nullable String mAttributionTag;
 
     /**
+     * @hide
      * Constructor
      * @param context the Context for the device volume operations
      */
-    @SuppressLint("ManagerConstructor")
-    // reason for suppression: even though the functionality handled by this class is implemented in
-    // AudioService, we want to avoid bloating android.media.AudioManager
-    // with @SystemApi functionality
     public AudioDeviceVolumeManager(@NonNull Context context) {
         Objects.requireNonNull(context);
         mPackageName = context.getApplicationContext().getOpPackageName();
-        mAttributionTag = context.getApplicationContext().getAttributionTag();
     }
 
     /**
@@ -325,10 +318,11 @@
      * @param ada the device for which volume is to be modified
      */
     @SystemApi
+    // TODO alternatively require MODIFY_AUDIO_SYSTEM_SETTINGS when defined
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public void setDeviceVolume(@NonNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada) {
         try {
-            getService().setDeviceVolume(vi, ada, mPackageName, mAttributionTag);
+            getService().setDeviceVolume(vi, ada, mPackageName);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
@@ -336,6 +330,30 @@
 
     /**
      * @hide
+     * Returns the volume on the given audio device for the given volume information.
+     * For instance if using a {@link VolumeInfo} configured for {@link AudioManager#STREAM_ALARM},
+     * it will return the alarm volume. When no volume index has ever been set for the given
+     * device, the default volume will be returned (the volume setting that would have been
+     * applied if playback for that use case had started).
+     * @param vi the volume information, only stream-based volumes are supported. Information
+     *           other than the stream type is ignored.
+     * @param ada the device for which volume is to be retrieved
+     */
+    @SystemApi
+    // TODO alternatively require MODIFY_AUDIO_SYSTEM_SETTINGS when defined
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public @NonNull VolumeInfo getDeviceVolume(@NonNull VolumeInfo vi,
+            @NonNull AudioDeviceAttributes ada) {
+        try {
+            return getService().getDeviceVolume(vi, ada, mPackageName);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return VolumeInfo.getDefaultVolumeInfo();
+    }
+
+    /**
+     * @hide
      * Return human-readable name for volume behavior
      * @param behavior one of the volume behaviors defined in AudioManager
      * @return a string for the given behavior
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 17d7045..f3931df 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1212,7 +1212,13 @@
         }
     }
 
-    private static boolean isPublicStreamType(int streamType) {
+    /**
+     * @hide
+     * Checks whether a stream type is part of the public SDK
+     * @param streamType
+     * @return true if the stream type is available in SDK
+     */
+    public static boolean isPublicStreamType(int streamType) {
         switch (streamType) {
             case STREAM_VOICE_CALL:
             case STREAM_SYSTEM:
diff --git a/media/java/android/media/AudioTimestamp.aidl b/media/java/android/media/AudioTimestamp.aidl
new file mode 100644
index 0000000..2c161b3
--- /dev/null
+++ b/media/java/android/media/AudioTimestamp.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+parcelable AudioTimestamp;
diff --git a/media/java/android/media/AudioTimestamp.java b/media/java/android/media/AudioTimestamp.java
index be8ca15..a3277e5 100644
--- a/media/java/android/media/AudioTimestamp.java
+++ b/media/java/android/media/AudioTimestamp.java
@@ -16,11 +16,14 @@
 
 package android.media;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-import android.annotation.IntDef;
-
 /**
  * Structure that groups a position in frame units relative to an assumed audio stream,
  * together with the estimated time when that frame enters or leaves the audio
@@ -33,8 +36,7 @@
  * @see AudioTrack#getTimestamp AudioTrack.getTimestamp(AudioTimestamp)
  * @see AudioRecord#getTimestamp AudioRecord.getTimestamp(AudioTimestamp, int)
  */
-public final class AudioTimestamp
-{
+public final class AudioTimestamp implements Parcelable {
     /**
      * Clock monotonic or its equivalent on the system,
      * in the same units and timebase as {@link java.lang.System#nanoTime}.
@@ -86,4 +88,47 @@
      * with a timebase of {@link #TIMEBASE_MONOTONIC}.
      */
     public long nanoTime;
+
+    public AudioTimestamp() {
+    }
+
+    private AudioTimestamp(@NonNull Parcel in) {
+        framePosition = in.readLong();
+        nanoTime = in.readLong();
+    }
+
+    @Override
+    public String toString() {
+        return "AudioTimeStamp:"
+                + " framePos=" + framePosition
+                + " nanoTime=" + nanoTime;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeLong(framePosition);
+        dest.writeLong(nanoTime);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Creates an instance from a {@link Parcel}.
+     */
+    @NonNull
+    public static final Creator<AudioTimestamp> CREATOR = new Creator<>() {
+        @Override
+        public AudioTimestamp createFromParcel(@NonNull Parcel in) {
+            return new AudioTimestamp(in);
+        }
+
+        @Override
+        public AudioTimestamp[] newArray(int size) {
+            return new AudioTimestamp[size];
+        }
+    };
 }
+
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7c9e494..2e766d5 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -102,7 +102,10 @@
             in String callingPackage, in String attributionTag);
 
     void setDeviceVolume(in VolumeInfo vi, in AudioDeviceAttributes ada,
-            in String callingPackage, in String attributionTag);
+            in String callingPackage);
+
+    VolumeInfo getDeviceVolume(in VolumeInfo vi, in AudioDeviceAttributes ada,
+            in String callingPackage);
 
     oneway void handleVolumeKey(in KeyEvent event, boolean isOnTv,
             String callingPackage, String caller);
diff --git a/media/java/android/media/VolumeInfo.java b/media/java/android/media/VolumeInfo.java
index 6b4f604..afb44bb 100644
--- a/media/java/android/media/VolumeInfo.java
+++ b/media/java/android/media/VolumeInfo.java
@@ -28,7 +28,6 @@
 import android.os.ServiceManager;
 import android.util.Log;
 
-import java.util.List;
 import java.util.Objects;
 
 /**
@@ -36,33 +35,36 @@
  * A class to represent volume information.
  * Can be used to represent volume associated with a stream type or {@link AudioVolumeGroup}.
  * Volume index is optional when used to represent a category of volume.
- * Index ranges are supported too, making the representation of volume changes agnostic to the
- * range (e.g. can be used to map BT A2DP absolute volume range to internal range).
+ * Volume ranges are supported too, making the representation of volume changes agnostic regarding
+ * the range of values that are supported (e.g. can be used to map BT A2DP absolute volume range to
+ * internal range).
  */
 @SystemApi
 public final class VolumeInfo implements Parcelable {
     private static final String TAG = "VolumeInfo";
 
     private final boolean mUsesStreamType; // false implies AudioVolumeGroup is used
+    private final boolean mHasMuteCommand;
     private final boolean mIsMuted;
     private final int mVolIndex;
     private final int mMinVolIndex;
     private final int mMaxVolIndex;
-    private final int mVolGroupId;
-    private final int mStreamType;
+    private final @Nullable AudioVolumeGroup mVolGroup;
+    private final @AudioManager.PublicStreamTypes int mStreamType;
 
     private static IAudioService sService;
     private static VolumeInfo sDefaultVolumeInfo;
 
-    private VolumeInfo(boolean usesStreamType, boolean isMuted, int volIndex,
-            int minVolIndex, int maxVolIndex,
-            int volGroupId, int streamType) {
+    private VolumeInfo(boolean usesStreamType, boolean hasMuteCommand, boolean isMuted,
+            int volIndex, int minVolIndex, int maxVolIndex,
+            AudioVolumeGroup volGroup, int streamType) {
         mUsesStreamType = usesStreamType;
+        mHasMuteCommand = hasMuteCommand;
         mIsMuted = isMuted;
         mVolIndex = volIndex;
         mMinVolIndex = minVolIndex;
         mMaxVolIndex = maxVolIndex;
-        mVolGroupId = volGroupId;
+        mVolGroup = volGroup;
         mStreamType = streamType;
     }
 
@@ -79,8 +81,10 @@
     /**
      * Returns the associated stream type, or will throw if {@link #hasStreamType()} returned false.
      * @return a stream type value, see AudioManager.STREAM_*
+     * @throws IllegalStateException when called on a VolumeInfo not configured for
+     *      stream types.
      */
-    public int getStreamType() {
+    public @AudioManager.PublicStreamTypes int getStreamType() {
         if (!mUsesStreamType) {
             throw new IllegalStateException("VolumeInfo doesn't use stream types");
         }
@@ -99,24 +103,28 @@
     /**
      * Returns the associated volume group, or will throw if {@link #hasVolumeGroup()} returned
      * false.
-     * @return the volume group corresponding to this VolumeInfo, or null if an error occurred
-     * in the volume group management
+     * @return the volume group corresponding to this VolumeInfo
+     * @throws IllegalStateException when called on a VolumeInfo not configured for
+     * volume groups.
      */
-    public @Nullable AudioVolumeGroup getVolumeGroup() {
+    public @NonNull AudioVolumeGroup getVolumeGroup() {
         if (mUsesStreamType) {
             throw new IllegalStateException("VolumeInfo doesn't use AudioVolumeGroup");
         }
-        List<AudioVolumeGroup> volGroups = AudioVolumeGroup.getAudioVolumeGroups();
-        for (AudioVolumeGroup group : volGroups) {
-            if (group.getId() == mVolGroupId) {
-                return group;
-            }
-        }
-        return null;
+        return mVolGroup;
     }
 
     /**
-     * Returns whether this instance is conveying a mute state.
+     * Return whether this instance is conveying a mute state
+     * @return true if the muted state was explicitly set for this instance
+     */
+    public boolean hasMuteCommand() {
+        return mHasMuteCommand;
+    }
+
+    /**
+     * Returns whether this instance is conveying a mute state that was explicitly set
+     * by {@link Builder#setMuted(boolean)}, false otherwise
      * @return true if the volume state is muted
      */
     public boolean isMuted() {
@@ -183,18 +191,21 @@
      */
     public static final class Builder {
         private boolean mUsesStreamType = true; // false implies AudioVolumeGroup is used
-        private int mStreamType = AudioManager.STREAM_MUSIC;
+        private @AudioManager.PublicStreamTypes int mStreamType = AudioManager.STREAM_MUSIC;
+        private boolean mHasMuteCommand = false;
         private boolean mIsMuted = false;
         private int mVolIndex = INDEX_NOT_SET;
         private int mMinVolIndex = INDEX_NOT_SET;
         private int mMaxVolIndex = INDEX_NOT_SET;
-        private int mVolGroupId = -Integer.MIN_VALUE;
+        private @Nullable AudioVolumeGroup mVolGroup;
 
         /**
          * Builder constructor for stream type-based VolumeInfo
          */
-        public Builder(int streamType) {
-            // TODO validate stream type
+        public Builder(@AudioManager.PublicStreamTypes int streamType) {
+            if (!AudioManager.isPublicStreamType(streamType)) {
+                throw new IllegalArgumentException("Not a valid public stream type " + streamType);
+            }
             mUsesStreamType = true;
             mStreamType = streamType;
         }
@@ -206,7 +217,7 @@
             Objects.requireNonNull(volGroup);
             mUsesStreamType = false;
             mStreamType = -Integer.MIN_VALUE;
-            mVolGroupId = volGroup.getId();
+            mVolGroup = volGroup;
         }
 
         /**
@@ -217,11 +228,12 @@
             Objects.requireNonNull(info);
             mUsesStreamType = info.mUsesStreamType;
             mStreamType = info.mStreamType;
+            mHasMuteCommand = info.mHasMuteCommand;
             mIsMuted = info.mIsMuted;
             mVolIndex = info.mVolIndex;
             mMinVolIndex = info.mMinVolIndex;
             mMaxVolIndex = info.mMaxVolIndex;
-            mVolGroupId = info.mVolGroupId;
+            mVolGroup = info.mVolGroup;
         }
 
         /**
@@ -230,6 +242,7 @@
          * @return the same builder instance
          */
         public @NonNull Builder setMuted(boolean isMuted) {
+            mHasMuteCommand = true;
             mIsMuted = isMuted;
             return this;
         }
@@ -239,7 +252,6 @@
          * @param volIndex a 0 or greater value, or {@link #INDEX_NOT_SET} if unknown
          * @return the same builder instance
          */
-        // TODO should we allow muted true + volume index set? (useful when toggling mute on/off?)
         public @NonNull Builder setVolumeIndex(int volIndex) {
             if (volIndex != INDEX_NOT_SET && volIndex < 0) {
                 throw new IllegalArgumentException("Volume index cannot be negative");
@@ -294,9 +306,9 @@
                 throw new IllegalArgumentException("Min volume index:" + mMinVolIndex
                         + " greater than max index:" + mMaxVolIndex);
             }
-            return new VolumeInfo(mUsesStreamType, mIsMuted,
+            return new VolumeInfo(mUsesStreamType, mHasMuteCommand, mIsMuted,
                     mVolIndex, mMinVolIndex, mMaxVolIndex,
-                    mVolGroupId, mStreamType);
+                    mVolGroup, mStreamType);
         }
     }
 
@@ -304,8 +316,8 @@
     // Parcelable
     @Override
     public int hashCode() {
-        return Objects.hash(mUsesStreamType, mStreamType, mIsMuted,
-                mVolIndex, mMinVolIndex, mMaxVolIndex, mVolGroupId);
+        return Objects.hash(mUsesStreamType, mHasMuteCommand, mStreamType, mIsMuted,
+                mVolIndex, mMinVolIndex, mMaxVolIndex, mVolGroup);
     }
 
     @Override
@@ -316,19 +328,20 @@
         VolumeInfo that = (VolumeInfo) o;
         return ((mUsesStreamType == that.mUsesStreamType)
                 && (mStreamType == that.mStreamType)
-            && (mIsMuted == that.mIsMuted)
-            && (mVolIndex == that.mVolIndex)
-            && (mMinVolIndex == that.mMinVolIndex)
-            && (mMaxVolIndex == that.mMaxVolIndex)
-            && (mVolGroupId == that.mVolGroupId));
+                && (mHasMuteCommand == that.mHasMuteCommand)
+                && (mIsMuted == that.mIsMuted)
+                && (mVolIndex == that.mVolIndex)
+                && (mMinVolIndex == that.mMinVolIndex)
+                && (mMaxVolIndex == that.mMaxVolIndex)
+                && Objects.equals(mVolGroup, that.mVolGroup));
     }
 
     @Override
     public String toString() {
         return new String("VolumeInfo:"
                 + (mUsesStreamType ? (" streamType:" + mStreamType)
-                    : (" volGroupId" + mVolGroupId))
-                + " muted:" + mIsMuted
+                    : (" volGroup:" + mVolGroup))
+                + (mHasMuteCommand ? (" muted:" + mIsMuted) : ("[no mute cmd]"))
                 + ((mVolIndex != INDEX_NOT_SET) ? (" volIndex:" + mVolIndex) : "")
                 + ((mMinVolIndex != INDEX_NOT_SET) ? (" min:" + mMinVolIndex) : "")
                 + ((mMaxVolIndex != INDEX_NOT_SET) ? (" max:" + mMaxVolIndex) : ""));
@@ -343,21 +356,29 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeBoolean(mUsesStreamType);
         dest.writeInt(mStreamType);
+        dest.writeBoolean(mHasMuteCommand);
         dest.writeBoolean(mIsMuted);
         dest.writeInt(mVolIndex);
         dest.writeInt(mMinVolIndex);
         dest.writeInt(mMaxVolIndex);
-        dest.writeInt(mVolGroupId);
+        if (!mUsesStreamType) {
+            mVolGroup.writeToParcel(dest, 0 /*ignored*/);
+        }
     }
 
     private VolumeInfo(@NonNull Parcel in) {
         mUsesStreamType = in.readBoolean();
         mStreamType = in.readInt();
+        mHasMuteCommand = in.readBoolean();
         mIsMuted = in.readBoolean();
         mVolIndex = in.readInt();
         mMinVolIndex = in.readInt();
         mMaxVolIndex = in.readInt();
-        mVolGroupId = in.readInt();
+        if (!mUsesStreamType) {
+            mVolGroup = AudioVolumeGroup.CREATOR.createFromParcel(in);
+        } else {
+            mVolGroup = null;
+        }
     }
 
     public static final @NonNull Parcelable.Creator<VolumeInfo> CREATOR =
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index dd3c967..1ee2a26 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -7,6 +7,7 @@
   <string name="string_create_in_another_place">Create in another place</string>
   <string name="string_save_to_another_place">Save to another place</string>
   <string name="string_use_another_device">Use another device</string>
+  <string name="string_save_to_another_device">Save to another device</string>
   <string name="string_no_thanks">No thanks</string>
   <string name="passkey_creation_intro_title">A simple way to sign in safely</string>
   <string name="passkey_creation_intro_body">Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more</string>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 8bff8bc..2bede9a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -172,6 +172,7 @@
         {CreateScreenState.PASSKEY_INTRO} else {CreateScreenState.PROVIDER_SELECTION}
       } else {CreateScreenState.CREATION_OPTION_SELECTION},
       requestDisplayInfo,
+      false,
       if (hasDefault) {
         ActiveEntry(defaultProvider, defaultProvider.createOptions.first())
       } else null
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 87d8a73b..9f73aef 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -80,14 +80,16 @@
           enabledProviderList = uiState.enabledProviders,
           disabledProviderList = uiState.disabledProviders,
           onCancel = viewModel::onCancel,
-          onOptionSelected = viewModel::onMoreOptionsRowSelectedForFirstUse,
+          onOptionSelected = viewModel::onEntrySelectedFromFirstUseScreen,
           onDisabledPasswordManagerSelected = viewModel::onDisabledPasswordManagerSelected,
+          onRemoteEntrySelected = selectEntryCallback,
         )
         CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard(
           requestDisplayInfo = uiState.requestDisplayInfo,
           enabledProviderList = uiState.enabledProviders,
           providerInfo = uiState.activeEntry?.activeProvider!!,
           createOptionInfo = uiState.activeEntry.activeEntryInfo as CreateOptionInfo,
+          showActiveEntryOnly = uiState.showActiveEntryOnly,
           onOptionSelected = selectEntryCallback,
           onConfirm = confirmEntryCallback,
           onCancel = viewModel::onCancel,
@@ -98,7 +100,7 @@
           enabledProviderList = uiState.enabledProviders,
           disabledProviderList = uiState.disabledProviders,
           onBackButtonSelected = viewModel::onBackButtonSelected,
-          onOptionSelected = viewModel::onMoreOptionsRowSelected,
+          onOptionSelected = viewModel::onEntrySelectedFromMoreOptionScreen,
           onDisabledPasswordManagerSelected = viewModel::onDisabledPasswordManagerSelected,
           onRemoteEntrySelected = selectEntryCallback,
         )
@@ -184,7 +186,8 @@
   disabledProviderList: List<DisabledProviderInfo>?,
   onOptionSelected: (ActiveEntry) -> Unit,
   onDisabledPasswordManagerSelected: () -> Unit,
-  onCancel: () -> Unit
+  onCancel: () -> Unit,
+  onRemoteEntrySelected: (EntryInfo) -> Unit,
 ) {
   Card() {
     Column() {
@@ -254,10 +257,23 @@
           }
         }
       }
-      Divider(
-        thickness = 24.dp,
-        color = Color.Transparent
-      )
+      // TODO: handle the error situation that if multiple remoteInfos exists
+      enabledProviderList.forEach { enabledProvider ->
+        if (enabledProvider.remoteEntry != null) {
+          TextButton(
+            onClick = {
+              onRemoteEntrySelected(enabledProvider.remoteEntry!!) },
+            modifier = Modifier
+              .padding(horizontal = 24.dp)
+              .align(alignment = Alignment.CenterHorizontally)
+          ) {
+            Text(
+              text = stringResource(R.string.string_save_to_another_device),
+              textAlign = TextAlign.Center,
+            )
+          }
+        }
+      }
       Row(
         horizontalArrangement = Arrangement.Start,
         modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
@@ -416,6 +432,7 @@
   enabledProviderList: List<EnabledProviderInfo>,
   providerInfo: EnabledProviderInfo,
   createOptionInfo: CreateOptionInfo,
+  showActiveEntryOnly: Boolean,
   onOptionSelected: (EntryInfo) -> Unit,
   onConfirm: () -> Unit,
   onCancel: () -> Unit,
@@ -473,41 +490,43 @@
           onOptionSelected = onOptionSelected
         )
       }
-      var createOptionsSize = 0
-      enabledProviderList.forEach{
-        enabledProvider -> createOptionsSize += enabledProvider.createOptions.size}
-      if (createOptionsSize > 1) {
-        TextButton(
-          onClick = onMoreOptionsSelected,
-          modifier = Modifier
-          .padding(horizontal = 24.dp)
-          .align(alignment = Alignment.CenterHorizontally)){
-          Text(
-              text =
-                when (requestDisplayInfo.type) {
-                  TYPE_PUBLIC_KEY_CREDENTIAL ->
-                    stringResource(R.string.string_create_in_another_place)
-                  else -> stringResource(R.string.string_save_to_another_place)},
-            textAlign = TextAlign.Center,
-          )
-        }
-      } else if (
-        requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL
-      ) {
-        // TODO: handle the error situation that if multiple remoteInfos exists
-        enabledProviderList.forEach { enabledProvider ->
-          if (enabledProvider.remoteEntry != null) {
-            TextButton(
-              onClick = {
-                onOptionSelected(enabledProvider.remoteEntry!!) },
-              modifier = Modifier
-                .padding(horizontal = 24.dp)
-                .align(alignment = Alignment.CenterHorizontally)
-            ) {
-              Text(
-                text = stringResource(R.string.string_use_another_device),
-                textAlign = TextAlign.Center,
-              )
+      if (!showActiveEntryOnly) {
+        var createOptionsSize = 0
+        enabledProviderList.forEach{
+          enabledProvider -> createOptionsSize += enabledProvider.createOptions.size}
+        if (createOptionsSize > 1) {
+          TextButton(
+            onClick = onMoreOptionsSelected,
+            modifier = Modifier
+            .padding(horizontal = 24.dp)
+            .align(alignment = Alignment.CenterHorizontally)){
+            Text(
+                text =
+                  when (requestDisplayInfo.type) {
+                    TYPE_PUBLIC_KEY_CREDENTIAL ->
+                      stringResource(R.string.string_create_in_another_place)
+                    else -> stringResource(R.string.string_save_to_another_place)},
+              textAlign = TextAlign.Center,
+            )
+          }
+        } else if (
+          requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL
+        ) {
+          // TODO: handle the error situation that if multiple remoteInfos exists
+          enabledProviderList.forEach { enabledProvider ->
+            if (enabledProvider.remoteEntry != null) {
+              TextButton(
+                onClick = {
+                  onOptionSelected(enabledProvider.remoteEntry!!) },
+                modifier = Modifier
+                  .padding(horizontal = 24.dp)
+                  .align(alignment = Alignment.CenterHorizontally)
+              ) {
+                Text(
+                  text = stringResource(R.string.string_use_another_device),
+                  textAlign = TextAlign.Center,
+                )
+              }
             }
           }
         }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
index f7e9fd0..0f685a1 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
@@ -36,6 +36,7 @@
   val disabledProviders: List<DisabledProviderInfo>? = null,
   val currentScreenState: CreateScreenState,
   val requestDisplayInfo: RequestDisplayInfo,
+  val showActiveEntryOnly: Boolean,
   val activeEntry: ActiveEntry? = null,
   val selectedEntry: EntryInfo? = null,
 )
@@ -56,13 +57,18 @@
   }
 
   fun onConfirmIntro() {
-    if (uiState.enabledProviders.size > 1) {
-      uiState = uiState.copy(
-        currentScreenState = CreateScreenState.PROVIDER_SELECTION
+    var createOptionSize = 0
+    uiState.enabledProviders.forEach {
+      enabledProvider -> createOptionSize += enabledProvider.createOptions.size}
+    uiState = if (createOptionSize > 1) {
+      uiState.copy(
+        currentScreenState = CreateScreenState.PROVIDER_SELECTION,
+        showActiveEntryOnly = true
       )
-    } else if (uiState.enabledProviders.size == 1){
-      uiState = uiState.copy(
+    } else if (createOptionSize == 1){
+      uiState.copy(
         currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
+        showActiveEntryOnly = false,
         activeEntry = ActiveEntry(uiState.enabledProviders.first(),
           uiState.enabledProviders.first().createOptions.first()
         )
@@ -90,16 +96,18 @@
     )
   }
 
-  fun onMoreOptionsRowSelected(activeEntry: ActiveEntry) {
+  fun onEntrySelectedFromMoreOptionScreen(activeEntry: ActiveEntry) {
     uiState = uiState.copy(
       currentScreenState = CreateScreenState.MORE_OPTIONS_ROW_INTRO,
+      showActiveEntryOnly = false,
       activeEntry = activeEntry
     )
   }
 
-  fun onMoreOptionsRowSelectedForFirstUse(activeEntry: ActiveEntry) {
+  fun onEntrySelectedFromFirstUseScreen(activeEntry: ActiveEntry) {
     uiState = uiState.copy(
       currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
+      showActiveEntryOnly = true,
       activeEntry = activeEntry
     )
   }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 941e770..6d20c91 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -23,6 +23,7 @@
 import com.android.settingslib.spa.framework.common.SpaEnvironment
 import com.android.settingslib.spa.framework.common.createSettingsPage
 import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
+import com.android.settingslib.spa.gallery.dialog.AlterDialogPageProvider
 import com.android.settingslib.spa.gallery.home.HomePageProvider
 import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
 import com.android.settingslib.spa.gallery.page.ChartPageProvider
@@ -72,6 +73,7 @@
                 ActionButtonPageProvider,
                 ProgressBarPageProvider,
                 ChartPageProvider,
+                AlterDialogPageProvider,
             ),
             rootPages = listOf(
                 HomePageProvider.createSettingsPage(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlterDialogPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlterDialogPage.kt
new file mode 100644
index 0000000..a57df0f
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlterDialogPage.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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.settingslib.spa.gallery.dialog
+
+import android.os.Bundle
+import androidx.compose.material3.Text
+import com.android.settingslib.spa.framework.common.SettingsEntry
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.widget.dialog.AlertDialogButton
+import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+
+private const val TITLE = "AlterDialogPage"
+
+object AlterDialogPageProvider : SettingsPageProvider {
+    override val name = "AlterDialogPage"
+    private val owner = createSettingsPage()
+
+    override fun buildEntry(arguments: Bundle?): List<SettingsEntry> = listOf(
+        SettingsEntryBuilder.create("AlterDialog", owner).setUiLayoutFn {
+            val alertDialogPresenter = rememberAlertDialogPresenter(
+                confirmButton = AlertDialogButton("Ok"),
+                dismissButton = AlertDialogButton("Cancel"),
+                title = "Title",
+                text = { Text("Text") },
+            )
+            Preference(object : PreferenceModel {
+                override val title = "Show AlterDialog"
+                override val onClick = alertDialogPresenter::open
+            })
+        }.build(),
+    )
+
+    fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner)
+        .setIsAllowSearch(true)
+        .setUiLayoutFn {
+            Preference(object : PreferenceModel {
+                override val title = TITLE
+                override val onClick = navigator(name)
+            })
+        }
+
+    override fun getTitle(arguments: Bundle?) = TITLE
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
index 83c72c7..6d53dae 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
@@ -27,6 +27,7 @@
 import com.android.settingslib.spa.gallery.R
 import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
 import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
+import com.android.settingslib.spa.gallery.dialog.AlterDialogPageProvider
 import com.android.settingslib.spa.gallery.page.ArgumentPageModel
 import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
 import com.android.settingslib.spa.gallery.page.ChartPageProvider
@@ -58,6 +59,7 @@
             ActionButtonPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             ProgressBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             ChartPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+            AlterDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
         )
     }
 
diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle
index 8543596..b1d8d0d 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle
+++ b/packages/SettingsLib/Spa/spa/build.gradle
@@ -55,6 +55,8 @@
 }
 
 dependencies {
+    String jetpack_lifecycle_version = "2.6.0-alpha03"
+
     api "androidx.appcompat:appcompat:1.7.0-alpha01"
     api "androidx.slice:slice-builders:1.1.0-alpha02"
     api "androidx.slice:slice-core:1.1.0-alpha02"
@@ -63,8 +65,9 @@
     api "androidx.compose.material:material-icons-extended:$jetpack_compose_version"
     api "androidx.compose.runtime:runtime-livedata:$jetpack_compose_version"
     api "androidx.compose.ui:ui-tooling-preview:$jetpack_compose_version"
-    api "androidx.lifecycle:lifecycle-livedata-ktx:2.6.0-alpha03"
-    api "androidx.navigation:navigation-compose:2.6.0-alpha03"
+    api "androidx.lifecycle:lifecycle-livedata-ktx:$jetpack_lifecycle_version"
+    api "androidx.lifecycle:lifecycle-runtime-compose:$jetpack_lifecycle_version"
+    api "androidx.navigation:navigation-compose:2.6.0-alpha04"
     api "com.github.PhilJay:MPAndroidChart:v3.1.0-alpha"
     api "com.google.android.material:material:1.7.0-alpha03"
     debugApi "androidx.compose.ui:ui-tooling:$jetpack_compose_version"
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt
new file mode 100644
index 0000000..d6f5328
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2022 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.settingslib.spa.widget.dialog
+
+import android.content.res.Configuration
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.DialogProperties
+
+data class AlertDialogButton(
+    val text: String,
+    val onClick: () -> Unit = {},
+)
+
+interface AlertDialogPresenter {
+    /** Opens the dialog. */
+    fun open()
+
+    /** Closes the dialog. */
+    fun close()
+}
+
+@Composable
+fun rememberAlertDialogPresenter(
+    confirmButton: AlertDialogButton? = null,
+    dismissButton: AlertDialogButton? = null,
+    title: String? = null,
+    text: @Composable (() -> Unit)? = null,
+): AlertDialogPresenter {
+    var openDialog by rememberSaveable { mutableStateOf(false) }
+    val alertDialogPresenter = remember {
+        object : AlertDialogPresenter {
+            override fun open() {
+                openDialog = true
+            }
+
+            override fun close() {
+                openDialog = false
+            }
+        }
+    }
+    if (openDialog) {
+        alertDialogPresenter.SettingsAlertDialog(confirmButton, dismissButton, title, text)
+    }
+    return alertDialogPresenter
+}
+
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+private fun AlertDialogPresenter.SettingsAlertDialog(
+    confirmButton: AlertDialogButton?,
+    dismissButton: AlertDialogButton?,
+    title: String?,
+    text: @Composable (() -> Unit)?,
+) {
+    val configuration = LocalConfiguration.current
+    AlertDialog(
+        onDismissRequest = ::close,
+        modifier = when (configuration.orientation) {
+            Configuration.ORIENTATION_LANDSCAPE -> {
+                Modifier.width(configuration.screenWidthDp.dp * 0.6f)
+            }
+            else -> Modifier
+        },
+        confirmButton = { confirmButton?.let { Button(it) } },
+        dismissButton = dismissButton?.let { { Button(it) } },
+        title = title?.let { { Text(it) } },
+        text = text?.let {
+            {
+                Column(Modifier.verticalScroll(rememberScrollState())) {
+                    text()
+                }
+            }
+        },
+        properties = DialogProperties(usePlatformDefaultWidth = false),
+    )
+}
+
+@Composable
+private fun AlertDialogPresenter.Button(button: AlertDialogButton) {
+    TextButton(
+        onClick = {
+            close()
+            button.onClick()
+        },
+    ) {
+        Text(button.text)
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/MoreOptions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/MoreOptions.kt
index 5e201df..84fea15 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/MoreOptions.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/MoreOptions.kt
@@ -17,7 +17,6 @@
 package com.android.settingslib.spa.widget.scaffold
 
 import androidx.appcompat.R
-import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.MoreVert
 import androidx.compose.material3.DropdownMenu
@@ -36,7 +35,7 @@
 /**
  * Scope for the children of [MoreOptionsAction].
  */
-interface MoreOptionsScope : ColumnScope {
+interface MoreOptionsScope {
     fun dismiss()
 
     @Composable
@@ -61,7 +60,7 @@
     val onDismiss = { expanded = false }
     DropdownMenu(expanded = expanded, onDismissRequest = onDismiss) {
         val moreOptionsScope = remember(this) {
-            object : MoreOptionsScope, ColumnScope by this {
+            object : MoreOptionsScope {
                 override fun dismiss() {
                     onDismiss()
                 }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogTest.kt
new file mode 100644
index 0000000..9468f95
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2022 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.settingslib.spa.widget.dialog
+
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.onDialogText
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsAlertDialogTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Test
+    fun title_whenDialogNotOpen_notDisplayed() {
+        composeTestRule.setContent {
+            rememberAlertDialogPresenter(title = TITLE)
+        }
+
+        composeTestRule.onDialogText(TITLE).assertDoesNotExist()
+    }
+
+    @Test
+    fun title_displayed() {
+        setAndOpenDialog {
+            rememberAlertDialogPresenter(title = TITLE)
+        }
+
+        composeTestRule.onDialogText(TITLE).assertIsDisplayed()
+    }
+
+    @Test
+    fun text_displayed() {
+        setAndOpenDialog {
+            rememberAlertDialogPresenter(text = { Text(TEXT) })
+        }
+
+        composeTestRule.onDialogText(TEXT).assertIsDisplayed()
+    }
+
+    @Test
+    fun confirmButton_displayed() {
+        setAndOpenDialog {
+            rememberAlertDialogPresenter(confirmButton = AlertDialogButton(CONFIRM_TEXT))
+        }
+
+        composeTestRule.onDialogText(CONFIRM_TEXT).assertIsDisplayed()
+    }
+
+    @Test
+    fun confirmButton_clickable() {
+        var confirmButtonClicked = false
+        setAndOpenDialog {
+            rememberAlertDialogPresenter(confirmButton = AlertDialogButton(CONFIRM_TEXT) {
+                confirmButtonClicked = true
+            })
+        }
+
+        composeTestRule.onDialogText(CONFIRM_TEXT).performClick()
+
+        assertThat(confirmButtonClicked).isTrue()
+    }
+
+    @Test
+    fun dismissButton_displayed() {
+        setAndOpenDialog {
+            rememberAlertDialogPresenter(dismissButton = AlertDialogButton(DISMISS_TEXT))
+        }
+
+        composeTestRule.onDialogText(DISMISS_TEXT).assertIsDisplayed()
+    }
+
+    @Test
+    fun dismissButton_clickable() {
+        var dismissButtonClicked = false
+        setAndOpenDialog {
+            rememberAlertDialogPresenter(dismissButton = AlertDialogButton(DISMISS_TEXT) {
+                dismissButtonClicked = true
+            })
+        }
+
+        composeTestRule.onDialogText(DISMISS_TEXT).performClick()
+
+        assertThat(dismissButtonClicked).isTrue()
+    }
+
+    private fun setAndOpenDialog(dialog: @Composable () -> AlertDialogPresenter) {
+        composeTestRule.setContent {
+            val dialogPresenter = dialog()
+            LaunchedEffect(Unit) {
+                dialogPresenter.open()
+            }
+        }
+    }
+
+    private companion object {
+        const val CONFIRM_TEXT = "Confirm"
+        const val DISMISS_TEXT = "Dismiss"
+        const val TITLE = "Title"
+        const val TEXT = "Text"
+    }
+}
diff --git a/packages/SystemUI/res/values-h700dp/dimens.xml b/packages/SystemUI/customization/res/values-h700dp/dimens.xml
similarity index 92%
rename from packages/SystemUI/res/values-h700dp/dimens.xml
rename to packages/SystemUI/customization/res/values-h700dp/dimens.xml
index fbd985e..2a15981a 100644
--- a/packages/SystemUI/res/values-h700dp/dimens.xml
+++ b/packages/SystemUI/customization/res/values-h700dp/dimens.xml
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ Copyright (C) 2022 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.
@@ -17,4 +17,4 @@
 <resources>
     <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) -->
     <dimen name="large_clock_text_size">170dp</dimen>
-</resources>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-h700dp/dimens.xml b/packages/SystemUI/customization/res/values-h800dp/dimens.xml
similarity index 86%
copy from packages/SystemUI/res/values-h700dp/dimens.xml
copy to packages/SystemUI/customization/res/values-h800dp/dimens.xml
index fbd985e..60afc8a 100644
--- a/packages/SystemUI/res/values-h700dp/dimens.xml
+++ b/packages/SystemUI/customization/res/values-h800dp/dimens.xml
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ Copyright (C) 2022 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -16,5 +16,5 @@
 
 <resources>
     <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) -->
-    <dimen name="large_clock_text_size">170dp</dimen>
+    <dimen name="large_clock_text_size">200dp</dimen>
 </resources>
diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml
index 8f90f0f..ba8f284 100644
--- a/packages/SystemUI/customization/res/values/dimens.xml
+++ b/packages/SystemUI/customization/res/values/dimens.xml
@@ -17,8 +17,8 @@
 -->
 <resources>
     <!-- Clock maximum font size (dp is intentional, to prevent any further scaling) -->
-    <dimen name="large_clock_text_size">150sp</dimen>
-    <dimen name="small_clock_text_size">86sp</dimen>
+    <dimen name="large_clock_text_size">150dp</dimen>
+    <dimen name="small_clock_text_size">86dp</dimen>
 
     <!-- Default line spacing multiplier between hours and minutes of the keyguard clock -->
     <item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item>
diff --git a/packages/SystemUI/res/drawable/ic_ring_volume.xml b/packages/SystemUI/res/drawable/ic_ring_volume.xml
new file mode 100644
index 0000000..343fe5d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ring_volume.xml
@@ -0,0 +1,26 @@
+<!--
+    Copyright (C) 2022 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?android:attr/colorControlNormal">
+  <path
+      android:pathData="M11,7V2H13V7ZM17.6,9.85 L16.2,8.4 19.75,4.85 21.15,6.3ZM6.4,9.85 L2.85,6.3 4.25,4.85 7.8,8.4ZM12,12Q14.95,12 17.812,13.188Q20.675,14.375 22.9,16.75Q23.2,17.05 23.2,17.45Q23.2,17.85 22.9,18.15L20.6,20.4Q20.325,20.675 19.963,20.7Q19.6,20.725 19.3,20.5L16.4,18.3Q16.2,18.15 16.1,17.95Q16,17.75 16,17.5V14.65Q15.05,14.35 14.05,14.175Q13.05,14 12,14Q10.95,14 9.95,14.175Q8.95,14.35 8,14.65V17.5Q8,17.75 7.9,17.95Q7.8,18.15 7.6,18.3L4.7,20.5Q4.4,20.725 4.038,20.7Q3.675,20.675 3.4,20.4L1.1,18.15Q0.8,17.85 0.8,17.45Q0.8,17.05 1.1,16.75Q3.3,14.375 6.175,13.188Q9.05,12 12,12ZM6,15.35Q5.275,15.725 4.6,16.212Q3.925,16.7 3.2,17.3L4.2,18.3L6,16.9ZM18,15.4V16.9L19.8,18.3L20.8,17.35Q20.075,16.7 19.4,16.225Q18.725,15.75 18,15.4ZM6,15.35Q6,15.35 6,15.35Q6,15.35 6,15.35ZM18,15.4Q18,15.4 18,15.4Q18,15.4 18,15.4Z"
+      android:fillColor="?android:attr/colorPrimary"/>
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_ring_volume_off.xml b/packages/SystemUI/res/drawable/ic_ring_volume_off.xml
new file mode 100644
index 0000000..74f30d1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ring_volume_off.xml
@@ -0,0 +1,34 @@
+<!--
+    Copyright (C) 2022 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?android:attr/colorControlNormal">
+<path
+      android:pathData="M0.8,4.2l8.1,8.1c-2.2,0.5 -5.2,1.6 -7.8,4.4c-0.4,0.4 -0.4,1 0,1.4l2.3,2.3c0.3,0.3 0.9,0.4 1.3,0.1l2.9,-2.2C7.8,18.1 8,17.8 8,17.5v-2.9c0.9,-0.3 1.7,-0.5 2.7,-0.6l8.5,8.5l1.4,-1.4L2.2,2.8L0.8,4.2z"
+    android:fillColor="?android:attr/colorPrimary"/>
+  <path
+      android:pathData="M11,2h2v5h-2z"
+      android:fillColor="?android:attr/colorPrimary"/>
+  <path
+      android:pathData="M21.2,6.3l-1.4,-1.4l-3.6,3.6l1.4,1.4C17.6,9.8 21,6.3 21.2,6.3z"
+      android:fillColor="?android:attr/colorPrimary"/>
+  <path
+      android:pathData="M22.9,16.7c-2.8,-3 -6.2,-4.1 -8.4,-4.5l7.2,7.2l1.3,-1.3C23.3,17.7 23.3,17.1 22.9,16.7z"
+      android:fillColor="?android:attr/colorPrimary"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_speaker_mute.xml b/packages/SystemUI/res/drawable/ic_speaker_mute.xml
new file mode 100644
index 0000000..4e402cf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_speaker_mute.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?android:attr/textColorPrimary"
+    android:autoMirrored="true">
+    <path android:fillColor="#FFFFFFFF"
+        android:pathData="M19.8,22.6 L16.775,19.575Q16.15,19.975 15.45,20.263Q14.75,20.55 14,20.725V18.675Q14.35,18.55 14.688,18.425Q15.025,18.3 15.325,18.125L12,14.8V20L7,15H3V9H6.2L1.4,4.2L2.8,2.8L21.2,21.2ZM19.6,16.8 L18.15,15.35Q18.575,14.575 18.788,13.725Q19,12.875 19,11.975Q19,9.625 17.625,7.775Q16.25,5.925 14,5.275V3.225Q17.1,3.925 19.05,6.362Q21,8.8 21,11.975Q21,13.3 20.638,14.525Q20.275,15.75 19.6,16.8ZM16.25,13.45 L14,11.2V7.95Q15.175,8.5 15.838,9.6Q16.5,10.7 16.5,12Q16.5,12.375 16.438,12.738Q16.375,13.1 16.25,13.45ZM12,9.2 L9.4,6.6 12,4ZM10,15.15V12.8L8.2,11H5V13H7.85ZM9.1,11.9Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_speaker_on.xml b/packages/SystemUI/res/drawable/ic_speaker_on.xml
new file mode 100644
index 0000000..2a90e05
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_speaker_on.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?android:attr/textColorPrimary"
+    android:autoMirrored="true">
+    <path android:fillColor="#FFFFFFFF"
+        android:pathData="M14,20.725V18.675Q16.25,18.025 17.625,16.175Q19,14.325 19,11.975Q19,9.625 17.625,7.775Q16.25,5.925 14,5.275V3.225Q17.1,3.925 19.05,6.362Q21,8.8 21,11.975Q21,15.15 19.05,17.587Q17.1,20.025 14,20.725ZM3,15V9H7L12,4V20L7,15ZM14,16V7.95Q15.175,8.5 15.838,9.6Q16.5,10.7 16.5,12Q16.5,13.275 15.838,14.362Q15.175,15.45 14,16ZM10,8.85 L7.85,11H5V13H7.85L10,15.15ZM7.5,12Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml
index 94fe209..8efd6f0 100644
--- a/packages/SystemUI/res/values-h800dp/dimens.xml
+++ b/packages/SystemUI/res/values-h800dp/dimens.xml
@@ -15,9 +15,6 @@
   -->
 
 <resources>
-    <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) -->
-    <dimen name="large_clock_text_size">200dp</dimen>
-
     <!-- With the large clock, move up slightly from the center -->
     <dimen name="keyguard_large_clock_top_margin">-112dp</dimen>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
index cbd0875..9999f08 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
@@ -117,7 +117,7 @@
             @Override
             public void onSampleCollected(float medianLuma) {
                 if (mSamplingEnabled) {
-                    updateMediaLuma(medianLuma);
+                    updateMedianLuma(medianLuma);
                 }
             }
         };
@@ -261,7 +261,7 @@
         }
     }
 
-    private void updateMediaLuma(float medianLuma) {
+    private void updateMedianLuma(float medianLuma) {
         mCurrentMedianLuma = medianLuma;
 
         // If the difference between the new luma and the current luma is larger than threshold
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 268b55e..e2f86bd 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -205,10 +205,6 @@
     @JvmField val QS_SECONDARY_DATA_SUB_INFO = releasedFlag(508, "qs_secondary_data_sub_info")
 
     // 600- status bar
-    // TODO(b/254513246): Tracking Bug
-    val STATUS_BAR_USER_SWITCHER =
-        resourceBooleanFlag(602, R.bool.flag_user_switcher_chip, "status_bar_user_switcher")
-
     // TODO(b/254512623): Tracking Bug
     @Deprecated("Replaced by mobile and wifi specific flags.")
     val NEW_STATUS_BAR_PIPELINE_BACKEND =
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 81b839e..833a6a4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -72,6 +72,7 @@
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.VibrationEffect;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.text.InputFilter;
@@ -108,6 +109,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.view.RotationPolicy;
@@ -125,11 +128,15 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.util.AlphaTintDrawableWrapper;
+import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.RoundedCornerProgressDrawable;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
@@ -186,6 +193,9 @@
     private ViewGroup mDialogRowsView;
     private ViewGroup mRinger;
 
+    private DeviceConfigProxy mDeviceConfigProxy;
+    private Executor mExecutor;
+
     /**
      * Container for the top part of the dialog, which contains the ringer, the ringer drawer, the
      * volume rows, and the ellipsis button. This does not include the live caption button.
@@ -274,6 +284,13 @@
     private BackgroundBlurDrawable mDialogRowsViewBackground;
     private final InteractionJankMonitor mInteractionJankMonitor;
 
+    private boolean mSeparateNotification;
+
+    @VisibleForTesting
+    int mVolumeRingerIconDrawableId;
+    @VisibleForTesting
+    int mVolumeRingerMuteIconDrawableId;
+
     public VolumeDialogImpl(
             Context context,
             VolumeDialogController volumeDialogController,
@@ -283,7 +300,9 @@
             MediaOutputDialogFactory mediaOutputDialogFactory,
             VolumePanelFactory volumePanelFactory,
             ActivityStarter activityStarter,
-            InteractionJankMonitor interactionJankMonitor) {
+            InteractionJankMonitor interactionJankMonitor,
+            DeviceConfigProxy deviceConfigProxy,
+            Executor executor) {
         mContext =
                 new ContextThemeWrapper(context, R.style.volume_dialog_theme);
         mController = volumeDialogController;
@@ -323,6 +342,50 @@
         }
 
         initDimens();
+
+        mDeviceConfigProxy = deviceConfigProxy;
+        mExecutor = executor;
+        mSeparateNotification = mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
+        updateRingerModeIconSet();
+    }
+
+    /**
+     * If ringer and notification are the same stream (T and earlier), use notification-like bell
+     * icon set.
+     * If ringer and notification are separated, then use generic speaker icons.
+     */
+    private void updateRingerModeIconSet() {
+        if (mSeparateNotification) {
+            mVolumeRingerIconDrawableId = R.drawable.ic_speaker_on;
+            mVolumeRingerMuteIconDrawableId = R.drawable.ic_speaker_mute;
+        } else {
+            mVolumeRingerIconDrawableId = R.drawable.ic_volume_ringer;
+            mVolumeRingerMuteIconDrawableId = R.drawable.ic_volume_ringer_mute;
+        }
+
+        if (mRingerDrawerMuteIcon != null) {
+            mRingerDrawerMuteIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
+        }
+        if (mRingerDrawerNormalIcon != null) {
+            mRingerDrawerNormalIcon.setImageResource(mVolumeRingerIconDrawableId);
+        }
+    }
+
+    /**
+     * Change icon for ring stream (not ringer mode icon)
+     */
+    private void updateRingRowIcon() {
+        Optional<VolumeRow> volumeRow = mRows.stream().filter(row -> row.stream == STREAM_RING)
+                .findFirst();
+        if (volumeRow.isPresent()) {
+            VolumeRow volRow = volumeRow.get();
+            volRow.iconRes = mSeparateNotification ? R.drawable.ic_ring_volume
+                    : R.drawable.ic_volume_ringer;
+            volRow.iconMuteRes = mSeparateNotification ? R.drawable.ic_ring_volume_off
+                    : R.drawable.ic_volume_ringer_mute;
+            volRow.setIcon(volRow.iconRes, mContext.getTheme());
+        }
     }
 
     @Override
@@ -339,6 +402,9 @@
         mController.getState();
 
         mConfigurationController.addCallback(this);
+
+        mDeviceConfigProxy.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+                mExecutor, this::onDeviceConfigChange);
     }
 
     @Override
@@ -346,6 +412,24 @@
         mController.removeCallback(mControllerCallbackH);
         mHandler.removeCallbacksAndMessages(null);
         mConfigurationController.removeCallback(this);
+        mDeviceConfigProxy.removeOnPropertiesChangedListener(this::onDeviceConfigChange);
+    }
+
+    /**
+     * Update ringer mode icon based on the config
+     */
+    private void onDeviceConfigChange(DeviceConfig.Properties properties) {
+        Set<String> changeSet = properties.getKeyset();
+        if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) {
+            boolean newVal = properties.getBoolean(
+                    SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
+            if (newVal != mSeparateNotification) {
+                mSeparateNotification = newVal;
+                updateRingerModeIconSet();
+                updateRingRowIcon();
+
+            }
+        }
     }
 
     @Override
@@ -554,6 +638,8 @@
         mRingerDrawerNormalIcon = mDialog.findViewById(R.id.volume_drawer_normal_icon);
         mRingerDrawerNewSelectionBg = mDialog.findViewById(R.id.volume_drawer_selection_background);
 
+        updateRingerModeIconSet();
+
         setupRingerDrawer();
 
         mODICaptionsView = mDialog.findViewById(R.id.odi_captions);
@@ -577,8 +663,14 @@
             addRow(AudioManager.STREAM_MUSIC,
                     R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
             if (!AudioSystem.isSingleVolume(mContext)) {
-                addRow(AudioManager.STREAM_RING,
-                        R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, false);
+                if (mSeparateNotification) {
+                    addRow(AudioManager.STREAM_RING, R.drawable.ic_ring_volume,
+                            R.drawable.ic_ring_volume_off, true, false);
+                } else {
+                    addRow(AudioManager.STREAM_RING, R.drawable.ic_volume_ringer,
+                            R.drawable.ic_volume_ringer, true, false);
+                }
+
                 addRow(STREAM_ALARM,
                         R.drawable.ic_alarm, R.drawable.ic_volume_alarm_mute, true, false);
                 addRow(AudioManager.STREAM_VOICE_CALL,
@@ -1534,8 +1626,8 @@
                     mRingerIcon.setTag(Events.ICON_STATE_VIBRATE);
                     break;
                 case AudioManager.RINGER_MODE_SILENT:
-                    mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
-                    mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
+                    mRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
+                    mSelectedRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
                     mRingerIcon.setTag(Events.ICON_STATE_MUTE);
                     addAccessibilityDescription(mRingerIcon, RINGER_MODE_SILENT,
                             mContext.getString(R.string.volume_ringer_hint_unmute));
@@ -1544,14 +1636,14 @@
                 default:
                     boolean muted = (mAutomute && ss.level == 0) || ss.muted;
                     if (!isZenMuted && muted) {
-                        mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
-                        mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
+                        mRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
+                        mSelectedRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
                         addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
                                 mContext.getString(R.string.volume_ringer_hint_unmute));
                         mRingerIcon.setTag(Events.ICON_STATE_MUTE);
                     } else {
-                        mRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
-                        mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
+                        mRingerIcon.setImageResource(mVolumeRingerIconDrawableId);
+                        mSelectedRingerIcon.setImageResource(mVolumeRingerIconDrawableId);
                         if (mController.hasVibrator()) {
                             addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
                                     mContext.getString(R.string.volume_ringer_hint_vibrate));
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index c5792b9..8f10fa6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -20,6 +20,7 @@
 import android.media.AudioManager;
 
 import com.android.internal.jank.InteractionJankMonitor;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.VolumeDialog;
@@ -27,11 +28,14 @@
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.volume.VolumeComponent;
 import com.android.systemui.volume.VolumeDialogComponent;
 import com.android.systemui.volume.VolumeDialogImpl;
 import com.android.systemui.volume.VolumePanelFactory;
 
+import java.util.concurrent.Executor;
+
 import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
@@ -55,7 +59,9 @@
             MediaOutputDialogFactory mediaOutputDialogFactory,
             VolumePanelFactory volumePanelFactory,
             ActivityStarter activityStarter,
-            InteractionJankMonitor interactionJankMonitor) {
+            InteractionJankMonitor interactionJankMonitor,
+            DeviceConfigProxy deviceConfigProxy,
+            @Main Executor executor) {
         VolumeDialogImpl impl = new VolumeDialogImpl(
                 context,
                 volumeDialogController,
@@ -65,7 +71,9 @@
                 mediaOutputDialogFactory,
                 volumePanelFactory,
                 activityStarter,
-                interactionJankMonitor);
+                interactionJankMonitor,
+                deviceConfigProxy,
+                executor);
         impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
         impl.setAutomute(true);
         impl.setSilentMode(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 2e74bf5..a0b4eab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
 
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -28,6 +29,7 @@
 import android.app.KeyguardManager;
 import android.media.AudioManager;
 import android.os.SystemClock;
+import android.provider.DeviceConfig;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.InputDevice;
@@ -38,6 +40,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
@@ -49,6 +52,9 @@
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.DeviceConfigProxyFake;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -71,6 +77,8 @@
     View mDrawerVibrate;
     View mDrawerMute;
     View mDrawerNormal;
+    private DeviceConfigProxyFake mDeviceConfigProxy;
+    private FakeExecutor mExecutor;
 
     @Mock
     VolumeDialogController mVolumeDialogController;
@@ -97,6 +105,9 @@
 
         getContext().addMockSystemService(KeyguardManager.class, mKeyguard);
 
+        mDeviceConfigProxy = new DeviceConfigProxyFake();
+        mExecutor = new FakeExecutor(new FakeSystemClock());
+
         mDialog = new VolumeDialogImpl(
                 getContext(),
                 mVolumeDialogController,
@@ -106,7 +117,9 @@
                 mMediaOutputDialogFactory,
                 mVolumePanelFactory,
                 mActivityStarter,
-                mInteractionJankMonitor);
+                mInteractionJankMonitor,
+                mDeviceConfigProxy,
+                mExecutor);
         mDialog.init(0, null);
         State state = createShellState();
         mDialog.onStateChangedH(state);
@@ -123,6 +136,9 @@
                 VolumePrefs.SHOW_RINGER_TOAST_COUNT + 1);
 
         Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false);
+
+        mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
     }
 
     private State createShellState() {
@@ -292,6 +308,35 @@
                 AudioManager.RINGER_MODE_NORMAL, false);
     }
 
+    /**
+     * Ideally we would look at the ringer ImageView and check its assigned drawable id, but that
+     * API does not exist. So we do the next best thing; we check the cached icon id.
+     */
+    @Test
+    public void notificationVolumeSeparated_theRingerIconChanges() {
+        mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false);
+
+        mExecutor.runAllReady(); // for the config change to take effect
+
+        // assert icon is new based on res id
+        assertEquals(mDialog.mVolumeRingerIconDrawableId,
+                R.drawable.ic_speaker_on);
+        assertEquals(mDialog.mVolumeRingerMuteIconDrawableId,
+                R.drawable.ic_speaker_mute);
+    }
+
+    @Test
+    public void notificationVolumeNotSeparated_theRingerIconRemainsTheSame() {
+        mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
+
+        mExecutor.runAllReady();
+
+        assertEquals(mDialog.mVolumeRingerIconDrawableId, R.drawable.ic_volume_ringer);
+        assertEquals(mDialog.mVolumeRingerMuteIconDrawableId, R.drawable.ic_volume_ringer_mute);
+    }
+
 /*
     @Test
     public void testContentDescriptions() {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 088eddb..a61a61b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -162,6 +162,7 @@
         "android.hardware.biometrics.face-V1.0-java",
         "android.hardware.biometrics.fingerprint-V2.3-java",
         "android.hardware.oemlock-V1.0-java",
+        "android.hardware.oemlock-V1-java",
         "android.hardware.configstore-V1.1-java",
         "android.hardware.ir-V1-java",
         "android.hardware.rebootescrow-V1-java",
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3cf53ef..3231240 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3622,6 +3622,18 @@
         }
     }
 
+    // TODO enforce MODIFY_AUDIO_SYSTEM_SETTINGS when defined
+    private void enforceModifyAudioRoutingOrSystemSettingsPermission() {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+                != PackageManager.PERMISSION_GRANTED
+                /*&& mContext.checkCallingOrSelfPermission(
+                        android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+                            != PackageManager.PERMISSION_DENIED*/) {
+            throw new SecurityException(
+                    "Missing MODIFY_AUDIO_ROUTING or MODIFY_AUDIO_SYSTEM_SETTINGS permission");
+        }
+    }
+
     private void enforceAccessUltrasoundPermission() {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -3734,20 +3746,35 @@
     }
 
     /** @see AudioDeviceVolumeManager#setDeviceVolume(VolumeInfo, AudioDeviceAttributes)
-     * Part of service interface, check permissions and parameters here */
+     * Part of service interface, check permissions and parameters here
+     * Note calling package is for logging purposes only, not to be trusted
+     */
     public void setDeviceVolume(@NonNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada,
-            @NonNull String callingPackage, @Nullable String attributionTag) {
-        enforceModifyAudioRoutingPermission();
+            @NonNull String callingPackage) {
+        enforceModifyAudioRoutingOrSystemSettingsPermission();
         Objects.requireNonNull(vi);
         Objects.requireNonNull(ada);
         Objects.requireNonNull(callingPackage);
+
         if (!vi.hasStreamType()) {
             Log.e(TAG, "Unsupported non-stream type based VolumeInfo", new Exception());
             return;
         }
         int index = vi.getVolumeIndex();
-        if (index == VolumeInfo.INDEX_NOT_SET) {
-            throw new IllegalArgumentException("changing device volume requires a volume index");
+        if (index == VolumeInfo.INDEX_NOT_SET && !vi.hasMuteCommand()) {
+            throw new IllegalArgumentException(
+                    "changing device volume requires a volume index or mute command");
+        }
+
+        // TODO handle unmuting if current audio device
+        // if a stream is not muted but the VolumeInfo is for muting, set the volume index
+        // for the device to min volume
+        if (vi.hasMuteCommand() && vi.isMuted() && !isStreamMute(vi.getStreamType())) {
+            setStreamVolumeWithAttributionInt(vi.getStreamType(),
+                    mStreamStates[vi.getStreamType()].getMinIndex(),
+                    /*flags*/ 0,
+                    ada, callingPackage, null);
+            return;
         }
 
         AudioService.sVolumeLogger.enqueueAndLog("setDeviceVolume" + " from:" + callingPackage
@@ -3772,7 +3799,7 @@
             }
         }
         setStreamVolumeWithAttributionInt(vi.getStreamType(), index, /*flags*/ 0,
-                ada, callingPackage, attributionTag);
+                ada, callingPackage, null);
     }
 
     /** Retain API for unsupported app usage */
@@ -4682,6 +4709,36 @@
         }
     }
 
+    /**
+     * @see AudioDeviceVolumeManager#getDeviceVolume(VolumeInfo, AudioDeviceAttributes)
+     */
+    public @NonNull VolumeInfo getDeviceVolume(@NonNull VolumeInfo vi,
+            @NonNull AudioDeviceAttributes ada, @NonNull String callingPackage) {
+        enforceModifyAudioRoutingOrSystemSettingsPermission();
+        Objects.requireNonNull(vi);
+        Objects.requireNonNull(ada);
+        Objects.requireNonNull(callingPackage);
+        if (!vi.hasStreamType()) {
+            Log.e(TAG, "Unsupported non-stream type based VolumeInfo", new Exception());
+            return getDefaultVolumeInfo();
+        }
+
+        int streamType = vi.getStreamType();
+        final VolumeInfo.Builder vib = new VolumeInfo.Builder(vi);
+        vib.setMinVolumeIndex(mStreamStates[streamType].mIndexMin);
+        vib.setMaxVolumeIndex(mStreamStates[streamType].mIndexMax);
+        synchronized (VolumeStreamState.class) {
+            final int index;
+            if (isFixedVolumeDevice(ada.getInternalType())) {
+                index = (mStreamStates[streamType].mIndexMax + 5) / 10;
+            } else {
+                index = (mStreamStates[streamType].getIndex(ada.getInternalType()) + 5) / 10;
+            }
+            vib.setVolumeIndex(index);
+            return vib.setMuted(mStreamStates[streamType].mIsMuted).build();
+        }
+    }
+
     /** @see AudioManager#getStreamMaxVolume(int) */
     public int getStreamMaxVolume(int streamType) {
         ensureValidStreamType(streamType);
@@ -4722,7 +4779,6 @@
             sDefaultVolumeInfo = new VolumeInfo.Builder(AudioSystem.STREAM_MUSIC)
                     .setMinVolumeIndex(getStreamMinVolume(AudioSystem.STREAM_MUSIC))
                     .setMaxVolumeIndex(getStreamMaxVolume(AudioSystem.STREAM_MUSIC))
-                    .setMuted(false)
                     .build();
         }
         return sDefaultVolumeInfo;
@@ -7965,6 +8021,23 @@
             }
         }
 
+        public @NonNull VolumeInfo getVolumeInfo(int device) {
+            synchronized (VolumeStreamState.class) {
+                int index = mIndexMap.get(device, -1);
+                if (index == -1) {
+                    // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
+                    index = mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT);
+                }
+                final VolumeInfo vi = new VolumeInfo.Builder(mStreamType)
+                        .setMinVolumeIndex(mIndexMin)
+                        .setMaxVolumeIndex(mIndexMax)
+                        .setVolumeIndex(index)
+                        .setMuted(isFullyMuted())
+                        .build();
+                return vi;
+            }
+        }
+
         public boolean hasIndexForDevice(int device) {
             synchronized (VolumeStreamState.class) {
                 return (mIndexMap.get(device, -1) != -1);
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index bac8916..4c6110b 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -25,7 +25,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.Context;
-import android.hardware.oemlock.V1_0.IOemLock;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -58,15 +57,18 @@
     private OemLock mOemLock;
 
     public static boolean isHalPresent() {
-        return VendorLock.getOemLockHalService() != null;
+        return (VendorLockHidl.getOemLockHalService() != null)
+                || (VendorLockAidl.getOemLockHalService() != null);
     }
 
     /** Select the OEM lock implementation */
     private static OemLock getOemLock(Context context) {
-        final IOemLock oemLockHal = VendorLock.getOemLockHalService();
-        if (oemLockHal != null) {
-            Slog.i(TAG, "Using vendor lock via the HAL");
-            return new VendorLock(context, oemLockHal);
+        if (VendorLockAidl.getOemLockHalService() != null) {
+            Slog.i(TAG, "Using vendor lock via the HAL(aidl)");
+            return new VendorLockAidl(context);
+        } else if (VendorLockHidl.getOemLockHalService() != null) {
+            Slog.i(TAG, "Using vendor lock via the HAL(hidl)");
+            return new VendorLockHidl(context);
         } else {
             Slog.i(TAG, "Using persistent data block based lock");
             return new PersistentDataBlockLock(context);
diff --git a/services/core/java/com/android/server/oemlock/VendorLockAidl.java b/services/core/java/com/android/server/oemlock/VendorLockAidl.java
new file mode 100644
index 0000000..82d45ab
--- /dev/null
+++ b/services/core/java/com/android/server/oemlock/VendorLockAidl.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.oemlock;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.oemlock.IOemLock;
+import android.hardware.oemlock.OemLockSecureStatus;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+/** Uses the OEM lock HAL. */
+class VendorLockAidl extends OemLock {
+    private static final String TAG = "OemLock";
+    private IOemLock mOemLock;
+
+    static IOemLock getOemLockHalService() {
+        return IOemLock.Stub.asInterface(
+                ServiceManager.waitForDeclaredService(IOemLock.DESCRIPTOR + "/default"));
+    }
+
+    VendorLockAidl(Context context) {
+        mOemLock = getOemLockHalService();
+    }
+
+    @Override
+    @Nullable
+    String getLockName() {
+        try {
+            return mOemLock.getName();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to get name from HAL", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) {
+        try {
+            final int status;
+            if (signature == null) {
+                status = mOemLock.setOemUnlockAllowedByCarrier(allowed, new byte[0]);
+            } else {
+                status = mOemLock.setOemUnlockAllowedByCarrier(allowed, signature);
+            }
+            switch (status) {
+                case OemLockSecureStatus.OK:
+                    Slog.i(TAG, "Updated carrier allows OEM lock state to: " + allowed);
+                    return;
+
+                case OemLockSecureStatus.INVALID_SIGNATURE:
+                    if (signature == null) {
+                        throw new IllegalArgumentException("Signature required for carrier unlock");
+                    }
+                    throw new SecurityException(
+                            "Invalid signature used in attempt to carrier unlock");
+
+                default:
+                    Slog.e(TAG, "Unknown return value indicates code is out of sync with HAL");
+                    // Fallthrough
+                case OemLockSecureStatus.FAILED:
+                    throw new RuntimeException("Failed to set carrier OEM unlock state");
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to set carrier state with HAL", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    boolean isOemUnlockAllowedByCarrier() {
+        try {
+            return mOemLock.isOemUnlockAllowedByCarrier();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to get carrier state from HAL");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    void setOemUnlockAllowedByDevice(boolean allowedByDevice) {
+        try {
+            mOemLock.setOemUnlockAllowedByDevice(allowedByDevice);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to set device state with HAL", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    boolean isOemUnlockAllowedByDevice() {
+
+        try {
+            return mOemLock.isOemUnlockAllowedByDevice();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to get devie state from HAL");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/oemlock/VendorLock.java b/services/core/java/com/android/server/oemlock/VendorLockHidl.java
similarity index 87%
rename from services/core/java/com/android/server/oemlock/VendorLock.java
rename to services/core/java/com/android/server/oemlock/VendorLockHidl.java
index 9c876da..fe76787 100644
--- a/services/core/java/com/android/server/oemlock/VendorLock.java
+++ b/services/core/java/com/android/server/oemlock/VendorLockHidl.java
@@ -27,10 +27,8 @@
 import java.util.ArrayList;
 import java.util.NoSuchElementException;
 
-/**
- * Uses the OEM lock HAL.
- */
-class VendorLock extends OemLock {
+/** Uses the OEM lock HAL. */
+class VendorLockHidl extends OemLock {
     private static final String TAG = "OemLock";
 
     private Context mContext;
@@ -40,29 +38,30 @@
         try {
             return IOemLock.getService(/* retry */ true);
         } catch (NoSuchElementException e) {
-            Slog.i(TAG, "OemLock HAL not present on device");
+            Slog.i(TAG, "OemLock Hidl HAL not present on device");
             return null;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
-    VendorLock(Context context, IOemLock oemLock) {
+    VendorLockHidl(Context context) {
         mContext = context;
-        mOemLock = oemLock;
+        mOemLock = getOemLockHalService();
     }
 
     @Override
     @Nullable
     String getLockName() {
-        final Integer[] requestStatus = new Integer[1];
         final String[] lockName = new String[1];
+        final Integer[] requestStatus = new Integer[1];
 
         try {
-            mOemLock.getName((status, name) -> {
-                requestStatus[0] = status;
-                lockName[0] = name;
-            });
+            mOemLock.getName(
+                    (status, name) -> {
+                        requestStatus[0] = status;
+                        lockName[0] = name;
+                    });
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to get name from HAL", e);
             throw e.rethrowFromSystemServer();
@@ -113,14 +112,14 @@
 
     @Override
     boolean isOemUnlockAllowedByCarrier() {
-        final Integer[] requestStatus = new Integer[1];
         final Boolean[] allowedByCarrier = new Boolean[1];
-
+        final Integer[] requestStatus = new Integer[1];
         try {
-            mOemLock.isOemUnlockAllowedByCarrier((status, allowed) -> {
-                requestStatus[0] = status;
-                allowedByCarrier[0] = allowed;
-            });
+            mOemLock.isOemUnlockAllowedByCarrier(
+                    (status, allowed) -> {
+                        requestStatus[0] = status;
+                        allowedByCarrier[0] = allowed;
+                    });
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to get carrier state from HAL");
             throw e.rethrowFromSystemServer();
@@ -161,14 +160,15 @@
 
     @Override
     boolean isOemUnlockAllowedByDevice() {
-        final Integer[] requestStatus = new Integer[1];
         final Boolean[] allowedByDevice = new Boolean[1];
 
+        final Integer[] requestStatus = new Integer[1];
         try {
-            mOemLock.isOemUnlockAllowedByDevice((status, allowed) -> {
-                requestStatus[0] = status;
-                allowedByDevice[0] = allowed;
-            });
+            mOemLock.isOemUnlockAllowedByDevice(
+                    (status, allowed) -> {
+                        requestStatus[0] = status;
+                        allowedByDevice[0] = allowed;
+                    });
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to get devie state from HAL");
             throw e.rethrowFromSystemServer();
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 5dd5d81..b02d1a8 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1816,8 +1816,6 @@
 
         // Collect files we care for fs-verity setup.
         ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
-        // NB: These files will become only accessible if the signing key is loaded in kernel's
-        // .fs-verity keyring.
         fsverityCandidates.put(pkg.getBaseApkPath(),
                 VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath()));
 
@@ -1855,20 +1853,6 @@
                         throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
                                 "fs-verity signature does not verify against a known key");
                     }
-                } else {
-                    // Without signature, we don't need to access the digest right away and can
-                    // enable fs-verity in background (since this is a blocking call).
-                    new Thread("fsverity-setup") {
-                        @Override public void run() {
-                            try {
-                                VerityUtils.setUpFsverity(filePath, (byte[]) null);
-                            } catch (IOException e) {
-                                // There's nothing we can do if the setup failed. Since fs-verity is
-                                // optional, just ignore the error for now.
-                                Slog.e(TAG, "Failed to enable fs-verity to " + filePath);
-                            }
-                        }
-                    }.start();
                 }
             } catch (IOException e) {
                 throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
@@ -2243,6 +2227,22 @@
                 }
                 incrementalStorages.add(storage);
             }
+
+            try {
+                if (!VerityUtils.hasFsverity(pkg.getBaseApkPath())) {
+                    VerityUtils.setUpFsverity(pkg.getBaseApkPath(), (byte[]) null);
+                }
+                for (String path : pkg.getSplitCodePaths()) {
+                    if (!VerityUtils.hasFsverity(path)) {
+                        VerityUtils.setUpFsverity(path, (byte[]) null);
+                    }
+                }
+            } catch (IOException e) {
+                // There's nothing we can do if the setup failed. Since fs-verity is
+                // optional, just ignore the error for now.
+                Slog.e(TAG, "Failed to fully enable fs-verity to " + packageName);
+            }
+
             // Hardcode previousAppId to 0 to disable any data migration (http://b/221088088)
             mAppDataHelper.prepareAppDataPostCommitLIF(pkg, 0);
             if (installRequest.isClearCodeCache()) {
diff --git a/services/core/java/com/android/server/pm/KnownPackages.java b/services/core/java/com/android/server/pm/KnownPackages.java
index dcf7152..154709a 100644
--- a/services/core/java/com/android/server/pm/KnownPackages.java
+++ b/services/core/java/com/android/server/pm/KnownPackages.java
@@ -47,6 +47,7 @@
             PACKAGE_RETAIL_DEMO,
             PACKAGE_RECENTS,
             PACKAGE_AMBIENT_CONTEXT_DETECTION,
+            PACKAGE_WEARABLE_SENSING,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KnownPackage {
@@ -71,9 +72,10 @@
     public static final int PACKAGE_RETAIL_DEMO = 16;
     public static final int PACKAGE_RECENTS = 17;
     public static final int PACKAGE_AMBIENT_CONTEXT_DETECTION = 18;
+    public static final int PACKAGE_WEARABLE_SENSING = 19;
     // Integer value of the last known package ID. Increases as new ID is added to KnownPackage.
     // Please note the numbers should be continuous.
-    public static final int LAST_KNOWN_PACKAGE = PACKAGE_AMBIENT_CONTEXT_DETECTION;
+    public static final int LAST_KNOWN_PACKAGE = PACKAGE_WEARABLE_SENSING;
 
     private final DefaultAppProvider mDefaultAppProvider;
     private final String mRequiredInstallerPackage;
@@ -86,6 +88,7 @@
     private final String mConfiguratorPackage;
     private final String mIncidentReportApproverPackage;
     private final String mAmbientContextDetectionPackage;
+    private final String mWearableSensingPackage;
     private final String mAppPredictionServicePackage;
     private final String mCompanionPackage;
     private final String mRetailDemoPackage;
@@ -97,9 +100,9 @@
             String[] requiredVerifierPackages, String defaultTextClassifierPackage,
             String systemTextClassifierPackageName, String requiredPermissionControllerPackage,
             String configuratorPackage, String incidentReportApproverPackage,
-            String ambientContextDetectionPackage, String appPredictionServicePackage,
-            String companionPackageName, String retailDemoPackage,
-            String overlayConfigSignaturePackage, String recentsPackage) {
+            String ambientContextDetectionPackage, String wearableSensingPackage,
+            String appPredictionServicePackage, String companionPackageName,
+            String retailDemoPackage, String overlayConfigSignaturePackage, String recentsPackage) {
         mDefaultAppProvider = defaultAppProvider;
         mRequiredInstallerPackage = requiredInstallerPackage;
         mRequiredUninstallerPackage = requiredUninstallerPackage;
@@ -111,6 +114,7 @@
         mConfiguratorPackage = configuratorPackage;
         mIncidentReportApproverPackage = incidentReportApproverPackage;
         mAmbientContextDetectionPackage = ambientContextDetectionPackage;
+        mWearableSensingPackage = wearableSensingPackage;
         mAppPredictionServicePackage = appPredictionServicePackage;
         mCompanionPackage = companionPackageName;
         mRetailDemoPackage = retailDemoPackage;
@@ -165,6 +169,8 @@
                 return "Recents";
             case PACKAGE_AMBIENT_CONTEXT_DETECTION:
                 return "Ambient Context Detection";
+            case PACKAGE_WEARABLE_SENSING:
+                return "Wearable sensing";
         }
         return "Unknown";
     }
@@ -194,6 +200,8 @@
                 return snapshot.filterOnlySystemPackages(mIncidentReportApproverPackage);
             case PACKAGE_AMBIENT_CONTEXT_DETECTION:
                 return snapshot.filterOnlySystemPackages(mAmbientContextDetectionPackage);
+            case PACKAGE_WEARABLE_SENSING:
+                return snapshot.filterOnlySystemPackages(mWearableSensingPackage);
             case PACKAGE_APP_PREDICTOR:
                 return snapshot.filterOnlySystemPackages(mAppPredictionServicePackage);
             case PACKAGE_COMPANION:
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 50eb926..8f8cc8a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -940,6 +940,7 @@
     final @Nullable String mOverlayConfigSignaturePackage;
     final @Nullable String mRecentsPackage;
     final @Nullable String mAmbientContextDetectionPackage;
+    final @Nullable String mWearableSensingPackage;
     private final @NonNull String mRequiredSdkSandboxPackage;
 
     @GuardedBy("mLock")
@@ -1713,6 +1714,7 @@
         mRetailDemoPackage = testParams.retailDemoPackage;
         mRecentsPackage = testParams.recentsPackage;
         mAmbientContextDetectionPackage = testParams.ambientContextDetectionPackage;
+        mWearableSensingPackage = testParams.wearableSensingPackage;
         mConfiguratorPackage = testParams.configuratorPackage;
         mAppPredictionServicePackage = testParams.appPredictionServicePackage;
         mIncidentReportApproverPackage = testParams.incidentReportApproverPackage;
@@ -2072,6 +2074,9 @@
             mAmbientContextDetectionPackage = ensureSystemPackageName(computer,
                     getPackageFromComponentString(
                             R.string.config_defaultAmbientContextDetectionService));
+            mWearableSensingPackage = ensureSystemPackageName(computer,
+                    getPackageFromComponentString(
+                            R.string.config_defaultWearableSensingService));
 
             // Now that we know all of the shared libraries, update all clients to have
             // the correct library paths.
@@ -6067,6 +6072,7 @@
                     mConfiguratorPackage,
                     mIncidentReportApproverPackage,
                     mAmbientContextDetectionPackage,
+                    mWearableSensingPackage,
                     mAppPredictionServicePackage,
                     COMPANION_PACKAGE_NAME,
                     mRetailDemoPackage,
@@ -7105,6 +7111,7 @@
                 mConfiguratorPackage,
                 mIncidentReportApproverPackage,
                 mAmbientContextDetectionPackage,
+                mWearableSensingPackage,
                 mAppPredictionServicePackage,
                 COMPANION_PACKAGE_NAME,
                 mRetailDemoPackage,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 4391fdd..bffbb84 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -94,6 +94,7 @@
     public @Nullable String retailDemoPackage;
     public @Nullable String recentsPackage;
     public @Nullable String ambientContextDetectionPackage;
+    public @Nullable String wearableSensingPackage;
     public ComponentName resolveComponentName;
     public ArrayMap<String, AndroidPackage> packages;
     public boolean enableFreeCacheV2;
diff --git a/services/core/java/com/android/server/wearable/OWNERS b/services/core/java/com/android/server/wearable/OWNERS
new file mode 100644
index 0000000..073e2d7
--- /dev/null
+++ b/services/core/java/com/android/server/wearable/OWNERS
@@ -0,0 +1,3 @@
+charliewang@google.com
+oni@google.com
+volnov@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
new file mode 100644
index 0000000..b2bbcda
--- /dev/null
+++ b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wearable;
+
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.SharedMemory;
+import android.service.wearable.IWearableSensingService;
+import android.service.wearable.WearableSensingService;
+import android.util.Slog;
+
+import com.android.internal.infra.ServiceConnector;
+
+/** Manages the connection to the remote wearable sensing service. */
+final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearableSensingService> {
+    private static final String TAG =
+            com.android.server.wearable.RemoteWearableSensingService.class.getSimpleName();
+    private final static boolean DEBUG = false;
+
+    RemoteWearableSensingService(Context context, ComponentName serviceName,
+            int userId) {
+        super(context, new Intent(
+                        WearableSensingService.SERVICE_INTERFACE).setComponent(serviceName),
+                BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+                IWearableSensingService.Stub::asInterface);
+
+        // Bind right away
+        connect();
+    }
+
+    @Override
+    protected long getAutoDisconnectTimeoutMs() {
+        // Disable automatic unbinding.
+        return -1;
+    }
+
+    /**
+     * Provides the implementation a data stream to the wearable.
+     *
+     * @param parcelFileDescriptor The data stream to the wearable
+     * @param callback The callback for service status
+     */
+    public void provideDataStream(ParcelFileDescriptor parcelFileDescriptor,
+            RemoteCallback callback) {
+        if (DEBUG) {
+            Slog.i(TAG, "Providing data stream.");
+        }
+        post(service -> service.provideDataStream(parcelFileDescriptor, callback));
+    }
+
+    /**
+     * Provides the implementation data.
+     *
+     * @param data Application configuration data to provide to the implementation.
+     * @param sharedMemory The unrestricted data blob to provide to the implementation.
+     * @param callback The callback for service status
+     */
+    public void provideData(PersistableBundle data,
+            SharedMemory sharedMemory,
+            RemoteCallback callback) {
+        if (DEBUG) {
+            Slog.i(TAG, "Providing data.");
+        }
+        post(service -> service.provideData(data, sharedMemory, callback));
+    }
+}
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
new file mode 100644
index 0000000..e73fd0f
--- /dev/null
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wearable;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.wearable.WearableSensingManager;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.Bundle;
+import android.system.OsConstants;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.SharedMemory;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+import java.io.PrintWriter;
+
+/**
+ * Per-user manager service for managing sensing {@link AmbientContextEvent}s on Wearables.
+ */
+final class WearableSensingManagerPerUserService extends
+        AbstractPerUserSystemService<WearableSensingManagerPerUserService,
+                WearableSensingManagerService> {
+    private static final String TAG = WearableSensingManagerPerUserService.class.getSimpleName();
+
+    @Nullable
+    @VisibleForTesting
+    RemoteWearableSensingService mRemoteService;
+
+    private ComponentName mComponentName;
+
+    WearableSensingManagerPerUserService(
+            @NonNull WearableSensingManagerService master, Object lock, @UserIdInt int userId) {
+        super(master, lock, userId);
+    }
+
+    static void notifyStatusCallback(RemoteCallback statusCallback, int statusCode) {
+        Bundle bundle = new Bundle();
+        bundle.putInt(
+                WearableSensingManager.STATUS_RESPONSE_BUNDLE_KEY, statusCode);
+        statusCallback.sendResult(bundle);
+    }
+
+    void destroyLocked() {
+        Slog.d(TAG, "Trying to cancel the remote request. Reason: Service destroyed.");
+        if (mRemoteService != null) {
+            synchronized (mLock) {
+                mRemoteService.unbind();
+                mRemoteService = null;
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void ensureRemoteServiceInitiated() {
+        if (mRemoteService == null) {
+            mRemoteService = new RemoteWearableSensingService(
+                    getContext(), mComponentName, getUserId());
+        }
+    }
+
+    /**
+     * get the currently bound component name.
+     */
+    @VisibleForTesting
+    ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+
+    /**
+     * Resolves and sets up the service if it had not been done yet. Returns true if the service
+     * is available.
+     */
+    @GuardedBy("mLock")
+    @VisibleForTesting
+    boolean setUpServiceIfNeeded() {
+        if (mComponentName == null) {
+            mComponentName = updateServiceInfoLocked();
+        }
+        if (mComponentName == null) {
+            return false;
+        }
+
+        ServiceInfo serviceInfo;
+        try {
+            serviceInfo = AppGlobals.getPackageManager().getServiceInfo(
+                    mComponentName, 0, mUserId);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "RemoteException while setting up service");
+            return false;
+        }
+        return serviceInfo != null;
+    }
+
+    @Override
+    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+            throws PackageManager.NameNotFoundException {
+        ServiceInfo serviceInfo;
+        try {
+            serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                    0, mUserId);
+            if (serviceInfo != null) {
+                final String permission = serviceInfo.permission;
+                if (!Manifest.permission.BIND_WEARABLE_SENSING_SERVICE.equals(
+                        permission)) {
+                    throw new SecurityException(String.format(
+                            "Service %s requires %s permission. Found %s permission",
+                            serviceInfo.getComponentName(),
+                            Manifest.permission.BIND_WEARABLE_SENSING_SERVICE,
+                            serviceInfo.permission));
+                }
+            }
+        } catch (RemoteException e) {
+            throw new PackageManager.NameNotFoundException(
+                    "Could not get service for " + serviceComponent);
+        }
+        return serviceInfo;
+    }
+
+    @Override
+    protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
+        synchronized (super.mLock) {
+            super.dumpLocked(prefix, pw);
+        }
+        if (mRemoteService != null) {
+            mRemoteService.dump("", new IndentingPrintWriter(pw, "  "));
+        }
+    }
+
+    /**
+     * Handles sending the provided data stream for the wearable to the wearable sensing service.
+     */
+    public void onProvideDataStream(
+            ParcelFileDescriptor parcelFileDescriptor,
+            RemoteCallback callback) {
+        Slog.i(TAG, "onProvideDataStream in per user service.");
+        synchronized (mLock) {
+            if (!setUpServiceIfNeeded()) {
+                Slog.w(TAG, "Detection service is not available at this moment.");
+                notifyStatusCallback(callback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                return;
+            }
+            Slog.i(TAG, "calling over to remote servvice.");
+            ensureRemoteServiceInitiated();
+            mRemoteService.provideDataStream(parcelFileDescriptor, callback);
+        }
+    }
+
+    /**
+     * Handles sending the provided data to the wearable sensing service.
+     */
+    public void onProvidedData(PersistableBundle data,
+            SharedMemory sharedMemory,
+            RemoteCallback callback) {
+        synchronized (mLock) {
+            if (!setUpServiceIfNeeded()) {
+                Slog.w(TAG, "Detection service is not available at this moment.");
+                notifyStatusCallback(callback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                return;
+            }
+            ensureRemoteServiceInitiated();
+            if (sharedMemory != null) {
+                sharedMemory.setProtect(OsConstants.PROT_READ);
+            }
+            mRemoteService.provideData(data, sharedMemory, callback);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
new file mode 100644
index 0000000..e155a06
--- /dev/null
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wearable;
+
+import static android.provider.DeviceConfig.NAMESPACE_WEARABLE_SENSING;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.wearable.IWearableSensingManager;
+import android.app.wearable.WearableSensingManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.ResultReceiver;
+import android.os.SharedMemory;
+import android.os.ShellCallback;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+import com.android.server.pm.KnownPackages;
+
+import java.io.FileDescriptor;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * System service for managing sensing {@link AmbientContextEvent}s on Wearables.
+ */
+public class WearableSensingManagerService extends
+        AbstractMasterSystemService<WearableSensingManagerService,
+                WearableSensingManagerPerUserService> {
+    private static final String TAG = WearableSensingManagerService.class.getSimpleName();
+    private static final String KEY_SERVICE_ENABLED = "service_enabled";
+
+    /** Default value in absence of {@link DeviceConfig} override. */
+    private static final boolean DEFAULT_SERVICE_ENABLED = true;
+    public static final int MAX_TEMPORARY_SERVICE_DURATION_MS = 30000;
+
+    private final Context mContext;
+    volatile boolean mIsServiceEnabled;
+
+    public WearableSensingManagerService(Context context) {
+        super(context,
+                new FrameworkResourcesServiceNameResolver(
+                        context,
+                        R.string.config_defaultWearableSensingService),
+                /*disallowProperty=*/null,
+                PACKAGE_UPDATE_POLICY_REFRESH_EAGER
+                        | /*To avoid high latency*/ PACKAGE_RESTART_POLICY_REFRESH_EAGER);
+        mContext = context;
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(
+                Context.WEARABLE_SENSING_SERVICE, new WearableSensingManagerInternal());
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            DeviceConfig.addOnPropertiesChangedListener(
+                    NAMESPACE_WEARABLE_SENSING,
+                    getContext().getMainExecutor(),
+                    (properties) -> onDeviceConfigChange(properties.getKeyset()));
+
+            mIsServiceEnabled = DeviceConfig.getBoolean(
+                    NAMESPACE_WEARABLE_SENSING,
+                    KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+        }
+    }
+
+
+    private void onDeviceConfigChange(@NonNull Set<String> keys) {
+        if (keys.contains(KEY_SERVICE_ENABLED)) {
+            mIsServiceEnabled = DeviceConfig.getBoolean(
+                    NAMESPACE_WEARABLE_SENSING,
+                    KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+        }
+    }
+
+    @Override
+    protected WearableSensingManagerPerUserService newServiceLocked(int resolvedUserId,
+            boolean disabled) {
+        return new WearableSensingManagerPerUserService(this, mLock, resolvedUserId);
+    }
+
+    @Override
+    protected void onServiceRemoved(
+            WearableSensingManagerPerUserService service, @UserIdInt int userId) {
+        Slog.d(TAG, "onServiceRemoved");
+        service.destroyLocked();
+    }
+
+    @Override
+    protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
+        Slog.d(TAG, "onServicePackageRestartedLocked.");
+    }
+
+    @Override
+    protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
+        Slog.d(TAG, "onServicePackageUpdatedLocked.");
+    }
+
+    @Override
+    protected void enforceCallingPermissionForManagement() {
+        getContext().enforceCallingPermission(
+                Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+    }
+
+    @Override
+    protected int getMaximumTemporaryServiceDurationMs() {
+        return MAX_TEMPORARY_SERVICE_DURATION_MS;
+    }
+
+    /** Returns {@code true} if the detection service is configured on this device. */
+    public static boolean isDetectionServiceConfigured() {
+        final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+        final String[] packageNames = pmi.getKnownPackageNames(
+                KnownPackages.PACKAGE_WEARABLE_SENSING, UserHandle.USER_SYSTEM);
+        boolean isServiceConfigured = (packageNames.length != 0);
+        Slog.i(TAG, "Wearable sensing service configured: " + isServiceConfigured);
+        return isServiceConfigured;
+    }
+
+    /**
+     * Returns the AmbientContextManagerPerUserService component for this user.
+     */
+    public ComponentName getComponentName(@UserIdInt int userId) {
+        synchronized (mLock) {
+            final WearableSensingManagerPerUserService service = getServiceForUserLocked(userId);
+            if (service != null) {
+                return service.getComponentName();
+            }
+        }
+        return null;
+    }
+
+    private final class WearableSensingManagerInternal extends IWearableSensingManager.Stub {
+        final WearableSensingManagerPerUserService mService = getServiceForUserLocked(
+                UserHandle.getCallingUserId());
+
+        @Override
+        public void provideDataStream(
+                ParcelFileDescriptor parcelFileDescriptor,
+                RemoteCallback callback) {
+            Slog.i(TAG, "WearableSensingManagerInternal provideDataStream.");
+            Objects.requireNonNull(parcelFileDescriptor);
+            Objects.requireNonNull(callback);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available.");
+                WearableSensingManagerPerUserService.notifyStatusCallback(callback,
+                        WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                return;
+            }
+            mService.onProvideDataStream(parcelFileDescriptor, callback);
+        }
+
+        @Override
+        public void provideData(
+                PersistableBundle data,
+                SharedMemory sharedMemory,
+                RemoteCallback callback) {
+            Slog.i(TAG, "WearableSensingManagerInternal provideData.");
+            Objects.requireNonNull(data);
+            Objects.requireNonNull(callback);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available.");
+                WearableSensingManagerPerUserService.notifyStatusCallback(callback,
+                        WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                return;
+            }
+            mService.onProvidedData(data, sharedMemory, callback);
+        }
+
+        @Override
+        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+                String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 8dc1f0b..4c35178 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -23,6 +23,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_FINISH_ACTIVITY;
@@ -850,6 +851,10 @@
                 effects |= setAdjacentRootsHierarchyOp(hop);
                 break;
             }
+            case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS: {
+                effects |= clearAdjacentRootsHierarchyOp(hop);
+                break;
+            }
             case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: {
                 final TaskFragmentCreationParams taskFragmentCreationOptions =
                         hop.getTaskFragmentCreationOptions();
@@ -1486,6 +1491,17 @@
         return TRANSACT_EFFECTS_LIFECYCLE;
     }
 
+    private int clearAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) {
+        final TaskFragment root = WindowContainer.fromBinder(hop.getContainer()).asTaskFragment();
+        if (!root.mCreatedByOrganizer) {
+            throw new IllegalArgumentException("clearAdjacentRootsHierarchyOp: Not created by"
+                    + " organizer root=" + root);
+        }
+
+        root.resetAdjacentTaskFragment();
+        return TRANSACT_EFFECTS_LIFECYCLE;
+    }
+
     private void sanitizeWindowContainer(WindowContainer wc) {
         if (!(wc instanceof TaskFragment) && !(wc instanceof DisplayArea)) {
             throw new RuntimeException("Invalid token in task fragment or displayArea transaction");
@@ -1650,6 +1666,10 @@
                             WindowContainer.fromBinder(hop.getAdjacentRoot()),
                             organizer);
                     break;
+                case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS:
+                    enforceTaskFragmentOrganized(func,
+                            WindowContainer.fromBinder(hop.getContainer()), organizer);
+                    break;
                 case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
                     // We are allowing organizer to create TaskFragment. We will check the
                     // ownerToken in #createTaskFragment, and trigger error callback if that is not
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/CrossProfileAppsServiceImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/CrossProfileAppsServiceImplTest.java
index d6d1574..129efc6 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/CrossProfileAppsServiceImplTest.java
@@ -38,6 +38,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ResolveInfo;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.UserHandle;
@@ -47,7 +48,6 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.SparseArray;
 
-import com.android.activitycontext.ActivityContext;
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
 import com.android.server.LocalServices;
@@ -611,34 +611,23 @@
         mTestInjector.setCallingUserId(PROFILE_OF_PRIMARY_USER);
 
         Bundle options = ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle();
-        IBinder result = ActivityContext.getWithContext(activity -> {
-            try {
-                IBinder targetTask = activity.getActivityToken();
-                mCrossProfileAppsServiceImpl.startActivityAsUser(
-                        mIApplicationThread,
-                        PACKAGE_ONE,
-                        FEATURE_ID,
-                        ACTIVITY_COMPONENT,
-                        UserHandle.of(PRIMARY_USER).getIdentifier(),
-                        true,
-                        targetTask,
-                        options);
-                return targetTask;
-            } catch (Exception re) {
-                return null;
-            }
-        });
-        if (result == null) {
-            throw new Exception();
-        }
-
+        Binder targetTask = new Binder();
+        mCrossProfileAppsServiceImpl.startActivityAsUser(
+                mIApplicationThread,
+                PACKAGE_ONE,
+                FEATURE_ID,
+                ACTIVITY_COMPONENT,
+                UserHandle.of(PRIMARY_USER).getIdentifier(),
+                true,
+                targetTask,
+                options);
         verify(mActivityTaskManagerInternal)
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         eq(PACKAGE_ONE),
                         eq(FEATURE_ID),
                         any(Intent.class),
-                        eq(result),
+                        eq(targetTask),
                         anyInt(),
                         eq(options),
                         eq(PRIMARY_USER));
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
index 7acb6d6..7f54b63 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
@@ -84,12 +84,12 @@
         final AudioDeviceAttributes usbDevice = new AudioDeviceAttributes(
                 /*native type*/ AudioSystem.DEVICE_OUT_USB_DEVICE, /*address*/ "bla");
 
-        mAudioService.setDeviceVolume(volMin, usbDevice, mPackageName, TAG);
+        mAudioService.setDeviceVolume(volMin, usbDevice, mPackageName);
         mTestLooper.dispatchAll();
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
                         AudioManager.STREAM_MUSIC, minIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
 
-        mAudioService.setDeviceVolume(volMid, usbDevice, mPackageName, TAG);
+        mAudioService.setDeviceVolume(volMid, usbDevice, mPackageName);
         mTestLooper.dispatchAll();
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
                 AudioManager.STREAM_MUSIC, midIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 690c2aa..c73e237 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -736,11 +736,12 @@
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, task1);
 
-        task1.setAdjacentTaskFragment(null);
-        task2.setAdjacentTaskFragment(null);
         wct = new WindowContainerTransaction();
+        wct.clearAdjacentRoots(info1.token);
         wct.clearLaunchAdjacentFlagRoot(info1.token);
         mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+        assertEquals(task1.getAdjacentTaskFragment(), null);
+        assertEquals(task2.getAdjacentTaskFragment(), null);
         assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, null);
     }