Merge "Move up the check if the editor belongs to the current user" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index e6e835b..0fdd880 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -24,6 +24,7 @@
     ":android.chre.flags-aconfig-java{.generated_srcjars}",
     ":android.companion.flags-aconfig-java{.generated_srcjars}",
     ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
+    ":android.companion.virtualdevice.flags-aconfig-java{.generated_srcjars}",
     ":android.content.flags-aconfig-java{.generated_srcjars}",
     ":android.content.pm.flags-aconfig-java{.generated_srcjars}",
     ":android.content.res.flags-aconfig-java{.generated_srcjars}",
diff --git a/MEMORY_OWNERS b/MEMORY_OWNERS
new file mode 100644
index 0000000..89ce5140
--- /dev/null
+++ b/MEMORY_OWNERS
@@ -0,0 +1,6 @@
+surenb@google.com
+tjmercier@google.com
+kaleshsingh@google.com
+jyescas@google.com
+carlosgalo@google.com
+jji@google.com
diff --git a/api/Android.bp b/api/Android.bp
index ef64a89..27c372a 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -223,7 +223,7 @@
     name: "sdk-annotations.zip",
     defaults: ["sdk-annotations-defaults"],
     srcs: [
-        ":android-non-updatable-doc-stubs{.annotations.zip}",
+        ":android-non-updatable-doc-stubs{.exportable.annotations.zip}",
         ":all-modules-public-annotations",
     ],
 }
@@ -232,7 +232,7 @@
     name: "sdk-annotations-system.zip",
     defaults: ["sdk-annotations-defaults"],
     srcs: [
-        ":android-non-updatable-doc-stubs-system{.annotations.zip}",
+        ":android-non-updatable-doc-stubs-system{.exportable.annotations.zip}",
         ":all-modules-system-annotations",
     ],
 }
@@ -241,7 +241,7 @@
     name: "sdk-annotations-module-lib.zip",
     defaults: ["sdk-annotations-defaults"],
     srcs: [
-        ":android-non-updatable-doc-stubs-module-lib{.annotations.zip}",
+        ":android-non-updatable-doc-stubs-module-lib{.exportable.annotations.zip}",
         ":all-modules-module-lib-annotations",
     ],
 }
@@ -250,7 +250,7 @@
     name: "sdk-annotations-system-server.zip",
     defaults: ["sdk-annotations-defaults"],
     srcs: [
-        ":android-non-updatable-doc-stubs-system-server{.annotations.zip}",
+        ":android-non-updatable-doc-stubs-system-server{.exportable.annotations.zip}",
         ":all-modules-system-server-annotations",
     ],
 }
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index 7ee4319..e8fcf4b 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -221,7 +221,7 @@
     name: "offline-sdk-docs",
     defaults: ["framework-docs-default"],
     srcs: [
-        ":framework-doc-stubs",
+        ":framework-doc-stubs{.exportable}",
     ],
     hdf: [
         "android.whichdoc offline",
@@ -242,7 +242,7 @@
     name: "offline-sdk-referenceonly-docs",
     defaults: ["framework-docs-default"],
     srcs: [
-        ":framework-doc-stubs",
+        ":framework-doc-stubs{.exportable}",
     ],
     hdf: [
         "android.whichdoc offline",
@@ -286,7 +286,7 @@
     name: "ds-docs-java",
     defaults: ["framework-docs-default"],
     srcs: [
-        ":framework-doc-stubs",
+        ":framework-doc-stubs{.exportable}",
     ],
     hdf: [
         "android.whichdoc online",
@@ -320,7 +320,7 @@
 droiddoc {
     name: "ds-docs-kt",
     srcs: [
-        ":framework-doc-stubs",
+        ":framework-doc-stubs{.exportable}",
     ],
     flags: [
         "-noJdkLink",
@@ -374,7 +374,7 @@
     name: "ds-static-docs",
     defaults: ["framework-docs-default"],
     srcs: [
-        ":framework-doc-stubs",
+        ":framework-doc-stubs{.exportable}",
     ],
     hdf: [
         "android.whichdoc online",
@@ -391,7 +391,7 @@
     name: "ds-ref-navtree-docs",
     defaults: ["framework-docs-default"],
     srcs: [
-        ":framework-doc-stubs",
+        ":framework-doc-stubs{.exportable}",
     ],
     hdf: [
         "android.whichdoc online",
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 59c0128..852abdf 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -961,7 +961,7 @@
 java_library {
     name: "android_stubs_current_with_test_libs",
     static_libs: [
-        "android_stubs_current",
+        "android_stubs_current_exportable",
         "android.test.base.stubs",
         "android.test.mock.stubs",
         "android.test.runner.stubs",
@@ -976,7 +976,7 @@
 java_library {
     name: "android_system_stubs_current_with_test_libs",
     static_libs: [
-        "android_system_stubs_current",
+        "android_system_stubs_current_exportable",
         "android.test.base.stubs.system",
         "android.test.mock.stubs.system",
         "android.test.runner.stubs.system",
@@ -991,7 +991,7 @@
 java_library {
     name: "android_module_stubs_current_with_test_libs",
     static_libs: [
-        "android_module_lib_stubs_current",
+        "android_module_lib_stubs_current_exportable",
         "android.test.base.stubs",
         "android.test.mock.stubs",
         "android.test.runner.stubs",
@@ -1006,7 +1006,7 @@
 java_library {
     name: "android_system_server_stubs_current_with_test_libs",
     static_libs: [
-        "android_system_server_stubs_current",
+        "android_system_server_stubs_current_exportable",
         "android.test.base.stubs.system",
         "android.test.mock.stubs.system",
         "android.test.runner.stubs.system",
diff --git a/core/api/current.txt b/core/api/current.txt
index b17e3343..a3775b0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6880,6 +6880,7 @@
     method public CharSequence getName();
     method @Nullable public String getParentChannelId();
     method public android.net.Uri getSound();
+    method @FlaggedApi("android.app.notification_channel_vibration_effect_api") @Nullable public android.os.VibrationEffect getVibrationEffect();
     method public long[] getVibrationPattern();
     method public boolean hasUserSetImportance();
     method public boolean hasUserSetSound();
@@ -6899,6 +6900,7 @@
     method public void setName(CharSequence);
     method public void setShowBadge(boolean);
     method public void setSound(android.net.Uri, android.media.AudioAttributes);
+    method @FlaggedApi("android.app.notification_channel_vibration_effect_api") public void setVibrationEffect(@Nullable android.os.VibrationEffect);
     method public void setVibrationPattern(long[]);
     method public boolean shouldShowLights();
     method public boolean shouldVibrate();
@@ -9512,8 +9514,8 @@
     method public static android.appwidget.AppWidgetManager getInstance(android.content.Context);
     method @FlaggedApi("android.appwidget.flags.generated_previews") @Nullable public android.widget.RemoteViews getWidgetPreview(@NonNull android.content.ComponentName, @Nullable android.os.UserHandle, int);
     method public boolean isRequestPinAppWidgetSupported();
-    method @Deprecated public void notifyAppWidgetViewDataChanged(int[], int);
-    method @Deprecated public void notifyAppWidgetViewDataChanged(int, int);
+    method public void notifyAppWidgetViewDataChanged(int[], int);
+    method public void notifyAppWidgetViewDataChanged(int, int);
     method public void partiallyUpdateAppWidget(int[], android.widget.RemoteViews);
     method public void partiallyUpdateAppWidget(int, android.widget.RemoteViews);
     method @FlaggedApi("android.appwidget.flags.generated_previews") public void removeWidgetPreview(@NonNull android.content.ComponentName, int);
@@ -13839,6 +13841,7 @@
     method public android.content.res.AssetFileDescriptor openRawResourceFd(@RawRes int) throws android.content.res.Resources.NotFoundException;
     method public void parseBundleExtra(String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException;
     method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method @FlaggedApi("android.content.res.register_resource_paths") public static void registerResourcePaths(@NonNull String, @NonNull android.content.pm.ApplicationInfo);
     method public void removeLoaders(@NonNull android.content.res.loader.ResourcesLoader...);
     method @Deprecated public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
     field @AnyRes public static final int ID_NULL = 0; // 0x0
@@ -19151,11 +19154,13 @@
   }
 
   public final class CameraExtensionCharacteristics {
+    method @FlaggedApi("com.android.internal.camera.flags.camera_extensions_characteristics_get") public <T> T get(int, @NonNull android.hardware.camera2.CameraCharacteristics.Key<T>);
     method @NonNull public java.util.Set<android.hardware.camera2.CaptureRequest.Key> getAvailableCaptureRequestKeys(int);
     method @NonNull public java.util.Set<android.hardware.camera2.CaptureResult.Key> getAvailableCaptureResultKeys(int);
     method @Nullable public android.util.Range<java.lang.Long> getEstimatedCaptureLatencyRangeMillis(int, @NonNull android.util.Size, int);
     method @NonNull public <T> java.util.List<android.util.Size> getExtensionSupportedSizes(int, @NonNull Class<T>);
     method @NonNull public java.util.List<android.util.Size> getExtensionSupportedSizes(int, int);
+    method @FlaggedApi("com.android.internal.camera.flags.camera_extensions_characteristics_get") @NonNull public java.util.Set<android.hardware.camera2.CameraCharacteristics.Key> getKeys(int);
     method @NonNull public java.util.List<android.util.Size> getPostviewSupportedSizes(int, @NonNull android.util.Size, int);
     method @NonNull public java.util.List<java.lang.Integer> getSupportedExtensions();
     method public boolean isCaptureProcessProgressAvailable(int);
@@ -20266,6 +20271,7 @@
     method @Nullable public android.hardware.input.HostUsiVersion getHostUsiVersion(@NonNull android.view.Display);
     method @Nullable public android.view.InputDevice getInputDevice(int);
     method public int[] getInputDeviceIds();
+    method @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") @Nullable public android.view.InputDevice.ViewBehavior getInputDeviceViewBehavior(int);
     method @FloatRange(from=0, to=1) public float getMaximumObscuringOpacityForTouch();
     method public boolean isStylusPointerIconEnabled();
     method public void registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler);
@@ -27318,6 +27324,7 @@
     field public static final int VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN = 18; // 0x12
     field public static final int VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 6; // 0x6
     field public static final int VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 5; // 0x5
+    field @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public static final int VIDEO_UNAVAILABLE_REASON_STOPPED = 19; // 0x13
     field public static final int VIDEO_UNAVAILABLE_REASON_TUNING = 1; // 0x1
     field public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0; // 0x0
     field public static final int VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2; // 0x2
@@ -27409,6 +27416,7 @@
     method public void onRemoveBroadcastInfo(int);
     method public void onRequestAd(@NonNull android.media.tv.AdRequest);
     method public void onRequestBroadcastInfo(@NonNull android.media.tv.BroadcastInfoRequest);
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void onResumePlayback();
     method public boolean onSelectAudioPresentation(int, int);
     method public boolean onSelectTrack(int, @Nullable String);
     method public abstract void onSetCaptionEnabled(boolean);
@@ -27416,6 +27424,7 @@
     method public abstract void onSetStreamVolume(@FloatRange(from=0.0, to=1.0) float);
     method public abstract boolean onSetSurface(@Nullable android.view.Surface);
     method public void onSetTvMessageEnabled(int, boolean);
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void onStopPlayback(int);
     method public void onSurfaceChanged(int, int, int);
     method public long onTimeShiftGetCurrentPosition();
     method public long onTimeShiftGetStartPosition();
@@ -27550,6 +27559,7 @@
     method public boolean onUnhandledInputEvent(android.view.InputEvent);
     method public void overrideTvAppAttributionSource(@NonNull android.content.AttributionSource);
     method public void reset();
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void resumePlayback();
     method public void selectAudioPresentation(int, int);
     method public void selectTrack(int, String);
     method public void sendAppPrivateCommand(@NonNull String, android.os.Bundle);
@@ -27562,6 +27572,7 @@
     method public void setTvMessageEnabled(int, boolean);
     method public void setZOrderMediaOverlay(boolean);
     method public void setZOrderOnTop(boolean);
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void stopPlayback(int);
     method public void timeShiftPause();
     method public void timeShiftPlay(String, android.net.Uri);
     method public void timeShiftResume();
@@ -27611,6 +27622,59 @@
 package android.media.tv.ad {
 
   @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public class TvAdManager {
+    method @NonNull public java.util.List<android.media.tv.ad.TvAdServiceInfo> getTvAdServiceList();
+  }
+
+  public abstract static class TvAdManager.TvAdServiceCallback {
+    ctor public TvAdManager.TvAdServiceCallback();
+    method public void onAdServiceAdded(@NonNull String);
+    method public void onAdServiceRemoved(@NonNull String);
+    method public void onAdServiceUpdated(@NonNull String);
+  }
+
+  @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public abstract class TvAdService extends android.app.Service {
+    ctor public TvAdService();
+    method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
+    method @Nullable public abstract android.media.tv.ad.TvAdService.Session onCreateSession(@NonNull String, @NonNull String);
+    field public static final String SERVICE_INTERFACE = "android.media.tv.ad.TvAdService";
+    field public static final String SERVICE_META_DATA = "android.media.tv.ad.service";
+  }
+
+  public abstract static class TvAdService.Session implements android.view.KeyEvent.Callback {
+    ctor public TvAdService.Session(@NonNull android.content.Context);
+    method @CallSuper public void layoutSurface(int, int, int, int);
+    method public boolean onGenericMotionEvent(@NonNull android.view.MotionEvent);
+    method public boolean onKeyDown(int, @Nullable android.view.KeyEvent);
+    method public boolean onKeyLongPress(int, @Nullable android.view.KeyEvent);
+    method public boolean onKeyMultiple(int, int, @Nullable android.view.KeyEvent);
+    method public boolean onKeyUp(int, @Nullable android.view.KeyEvent);
+    method public abstract void onRelease();
+    method public abstract boolean onSetSurface(@Nullable android.view.Surface);
+    method public void onSurfaceChanged(int, int, int);
+    method public boolean onTouchEvent(@NonNull android.view.MotionEvent);
+    method public boolean onTrackballEvent(@NonNull android.view.MotionEvent);
+  }
+
+  @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public final class TvAdServiceInfo implements android.os.Parcelable {
+    ctor public TvAdServiceInfo(@NonNull android.content.Context, @NonNull android.content.ComponentName);
+    method public int describeContents();
+    method @NonNull public String getId();
+    method @Nullable public android.content.pm.ServiceInfo getServiceInfo();
+    method @NonNull public java.util.List<java.lang.String> getSupportedTypes();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.ad.TvAdServiceInfo> CREATOR;
+  }
+
+  @FlaggedApi("android.media.tv.flags.enable_ad_service_fw") public class TvAdView extends android.view.ViewGroup {
+    ctor public TvAdView(@NonNull android.content.Context);
+    ctor public TvAdView(@NonNull android.content.Context, @Nullable android.util.AttributeSet);
+    ctor public TvAdView(@NonNull android.content.Context, @Nullable android.util.AttributeSet, int);
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void onLayout(boolean, int, int, int, int);
+    method public void onMeasure(int, int);
+    method public void onVisibilityChanged(@NonNull android.view.View, int);
+    method public void prepareAdService(@NonNull String, @NonNull String);
   }
 
 }
@@ -27748,6 +27812,7 @@
     method public void onRecordingTuned(@NonNull String, @NonNull android.net.Uri);
     method public abstract void onRelease();
     method public void onResetInteractiveApp();
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void onSelectedTrackInfo(@NonNull java.util.List<android.media.tv.TvTrackInfo>);
     method public abstract boolean onSetSurface(@Nullable android.view.Surface);
     method public void onSetTeletextAppEnabled(boolean);
     method public void onSignalStrength(int);
@@ -27782,6 +27847,7 @@
     method @CallSuper public void requestCurrentVideoBounds();
     method @CallSuper public void requestScheduleRecording(@NonNull String, @NonNull String, @NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.os.Bundle);
     method @CallSuper public void requestScheduleRecording(@NonNull String, @NonNull String, @NonNull android.net.Uri, long, long, int, @NonNull android.os.Bundle);
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") @CallSuper public void requestSelectedTrackInfo();
     method @CallSuper public void requestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
     method @CallSuper public void requestStartRecording(@NonNull String, @Nullable android.net.Uri);
     method @CallSuper public void requestStopRecording(@NonNull String);
@@ -27846,6 +27912,7 @@
     method public void sendCurrentChannelUri(@Nullable android.net.Uri);
     method public void sendCurrentTvInputId(@Nullable String);
     method public void sendCurrentVideoBounds(@NonNull android.graphics.Rect);
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void sendSelectedTrackInfo(@Nullable java.util.List<android.media.tv.TvTrackInfo>);
     method public void sendSigningResult(@NonNull String, @NonNull byte[]);
     method public void sendStreamVolume(float);
     method public void sendTimeShiftMode(int);
@@ -27881,6 +27948,7 @@
     method public void onRequestCurrentVideoBounds(@NonNull String);
     method public void onRequestScheduleRecording(@NonNull String, @NonNull String, @NonNull String, @NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.os.Bundle);
     method public void onRequestScheduleRecording(@NonNull String, @NonNull String, @NonNull String, @NonNull android.net.Uri, long, long, int, @NonNull android.os.Bundle);
+    method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void onRequestSelectedTrackInfo(@NonNull String);
     method public void onRequestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
     method public void onRequestStartRecording(@NonNull String, @NonNull String, @Nullable android.net.Uri);
     method public void onRequestStopRecording(@NonNull String, @NonNull String);
@@ -45157,13 +45225,13 @@
     method @FlaggedApi("com.android.internal.telephony.flags.enforce_subscription_user_filter") @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES) public android.telephony.SubscriptionManager createForAllUserProfiles();
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>);
     method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context);
-    method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList();
+    method @Nullable public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList();
     method public static int getActiveDataSubscriptionId();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getActiveSubscriptionInfoCount();
     method public int getActiveSubscriptionInfoCountMax();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList();
+    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getActiveSubscriptionInfoList();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, "carrier privileges"}) public java.util.List<android.telephony.SubscriptionInfo> getAllSubscriptionInfoList();
     method @NonNull public java.util.List<android.telephony.SubscriptionInfo> getCompleteActiveSubscriptionInfoList();
     method public static int getDefaultDataSubscriptionId();
@@ -50437,6 +50505,10 @@
     method public boolean isFromSource(int);
   }
 
+  @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") public static final class InputDevice.ViewBehavior {
+    method @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") public boolean shouldSmoothScroll(int, int);
+  }
+
   public abstract class InputEvent implements android.os.Parcelable {
     method public int describeContents();
     method public final android.view.InputDevice getDevice();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index d331455..783bebd 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -132,6 +132,11 @@
     field public static final int MATCH_STATIC_SHARED_AND_SDK_LIBRARIES = 67108864; // 0x4000000
   }
 
+  @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public class SignedPackage {
+    method @NonNull public byte[] getCertificateDigest();
+    method @NonNull public String getPkgName();
+  }
+
 }
 
 package android.hardware.usb {
@@ -379,6 +384,10 @@
     field public static final int DEVICE_INITIAL_SDK_INT;
   }
 
+  public class Environment {
+    method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") @NonNull public static java.io.File getDataSystemDeDirectory();
+  }
+
   public class IpcDataCache<Query, Result> {
     ctor public IpcDataCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.os.IpcDataCache.QueryHandler<Query,Result>);
     method public void disableForCurrentProcess();
@@ -428,6 +437,8 @@
 
   public class SystemConfigManager {
     method @NonNull public java.util.List<android.content.ComponentName> getEnabledComponentOverrides(@NonNull String);
+    method @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES) public java.util.Set<android.content.pm.SignedPackage> getEnhancedConfirmationTrustedInstallers();
+    method @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES) public java.util.Set<android.content.pm.SignedPackage> getEnhancedConfirmationTrustedPackages();
   }
 
   public final class Trace {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 01f54ad..50764b2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3991,6 +3991,7 @@
     method @NonNull public boolean canUserUninstall(@NonNull String, @NonNull android.os.UserHandle);
     method @NonNull public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_METADATA) public android.os.PersistableBundle getAppMetadata(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @FlaggedApi("android.content.pm.asl_in_apk_app_metadata_source") @RequiresPermission(android.Manifest.permission.GET_APP_METADATA) public int getAppMetadataSource(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, @NonNull android.content.pm.PackageManager.ApplicationInfoFlags, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public android.content.pm.dex.ArtManager getArtManager();
@@ -4043,6 +4044,10 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List<java.lang.String>);
     field public static final String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS";
     field public static final String ACTION_REQUEST_PERMISSIONS_FOR_OTHER = "android.content.pm.action.REQUEST_PERMISSIONS_FOR_OTHER";
+    field @FlaggedApi("android.content.pm.asl_in_apk_app_metadata_source") public static final int APP_METADATA_SOURCE_APK = 1; // 0x1
+    field @FlaggedApi("android.content.pm.asl_in_apk_app_metadata_source") public static final int APP_METADATA_SOURCE_INSTALLER = 2; // 0x2
+    field @FlaggedApi("android.content.pm.asl_in_apk_app_metadata_source") public static final int APP_METADATA_SOURCE_SYSTEM_IMAGE = 3; // 0x3
+    field @FlaggedApi("android.content.pm.asl_in_apk_app_metadata_source") public static final int APP_METADATA_SOURCE_UNKNOWN = 0; // 0x0
     field public static final int DELETE_ALL_USERS = 2; // 0x2
     field public static final int DELETE_FAILED_ABORTED = -5; // 0xfffffffb
     field public static final int DELETE_FAILED_DEVICE_POLICY_MANAGER = -2; // 0xfffffffe
@@ -4581,6 +4586,7 @@
     ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") protected AdvancedExtender(@NonNull android.hardware.camera2.CameraManager);
     method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract java.util.List<android.hardware.camera2.CaptureRequest.Key> getAvailableCaptureRequestKeys(@NonNull String);
     method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract java.util.List<android.hardware.camera2.CaptureResult.Key> getAvailableCaptureResultKeys(@NonNull String);
+    method @FlaggedApi("com.android.internal.camera.flags.camera_extensions_characteristics_get") @NonNull public abstract java.util.List<android.util.Pair<android.hardware.camera2.CameraCharacteristics.Key,java.lang.Object>> getAvailableCharacteristicsKeyValues();
     method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public long getMetadataVendorId(@NonNull String);
     method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract android.hardware.camera2.extension.SessionProcessor getSessionProcessor();
     method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public abstract java.util.Map<java.lang.Integer,java.util.List<android.util.Size>> getSupportedCaptureOutputResolutions(@NonNull String);
@@ -13464,7 +13470,9 @@
     method public void setTelecomCallId(@NonNull String);
     field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000
     field public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 262144; // 0x40000
+    field @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final String EVENT_CALL_QUALITY_REPORT = "android.telecom.event.CALL_QUALITY_REPORT";
     field public static final String EVENT_DEVICE_TO_DEVICE_MESSAGE = "android.telecom.event.DEVICE_TO_DEVICE_MESSAGE";
+    field @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final String EXTRA_CALL_QUALITY_REPORT = "android.telecom.extra.CALL_QUALITY_REPORT";
     field public static final String EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE = "android.telecom.extra.DEVICE_TO_DEVICE_MESSAGE_TYPE";
     field public static final String EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE = "android.telecom.extra.DEVICE_TO_DEVICE_MESSAGE_VALUE";
     field public static final String EXTRA_DISABLE_ADD_CALL = "android.telecom.extra.DISABLE_ADD_CALL";
@@ -13732,6 +13740,7 @@
     method @Deprecated public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
+    method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isInSelfManagedCall(@NonNull String, @NonNull android.os.UserHandle, boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
     field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
@@ -14581,7 +14590,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getActiveSubscriptionIdList();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public byte[] getAllSimSpecificSettingsForBackup();
-    method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
+    method @Nullable public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
     method @NonNull public static android.content.res.Resources getResourcesForSubId(@NonNull android.content.Context, int);
@@ -14835,7 +14844,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean matchesCurrentSimOperator(@NonNull String, int, @Nullable String);
     method public boolean needsOtaServiceProvisioning();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
-    method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(android.Manifest.permission.DUMP) public void persistEmergencyCallDiagnosticData(@NonNull String, @NonNull android.telephony.TelephonyManager.EmergencyCallDiagnosticParams);
+    method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(android.Manifest.permission.DUMP) public void persistEmergencyCallDiagnosticData(@NonNull String, @NonNull android.telephony.TelephonyManager.EmergencyCallDiagnosticData);
     method @RequiresPermission(android.Manifest.permission.REBOOT) public int prepareForUnattendedReboot();
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerCarrierPrivilegesCallback(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CarrierPrivilegesCallback);
@@ -15014,19 +15023,19 @@
     method public default void onCarrierServiceChanged(@Nullable String, int);
   }
 
-  @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final class TelephonyManager.EmergencyCallDiagnosticParams {
+  @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final class TelephonyManager.EmergencyCallDiagnosticData {
     method public long getLogcatCollectionStartTimeMillis();
     method public boolean isLogcatCollectionEnabled();
-    method public boolean isTelecomDumpSysCollectionEnabled();
-    method public boolean isTelephonyDumpSysCollectionEnabled();
+    method public boolean isTelecomDumpsysCollectionEnabled();
+    method public boolean isTelephonyDumpsysCollectionEnabled();
   }
 
-  public static final class TelephonyManager.EmergencyCallDiagnosticParams.Builder {
-    ctor public TelephonyManager.EmergencyCallDiagnosticParams.Builder();
-    method @NonNull public android.telephony.TelephonyManager.EmergencyCallDiagnosticParams build();
-    method @NonNull public android.telephony.TelephonyManager.EmergencyCallDiagnosticParams.Builder setLogcatCollectionStartTimeMillis(long);
-    method @NonNull public android.telephony.TelephonyManager.EmergencyCallDiagnosticParams.Builder setTelecomDumpSysCollectionEnabled(boolean);
-    method @NonNull public android.telephony.TelephonyManager.EmergencyCallDiagnosticParams.Builder setTelephonyDumpSysCollectionEnabled(boolean);
+  public static final class TelephonyManager.EmergencyCallDiagnosticData.Builder {
+    ctor public TelephonyManager.EmergencyCallDiagnosticData.Builder();
+    method @NonNull public android.telephony.TelephonyManager.EmergencyCallDiagnosticData build();
+    method @NonNull public android.telephony.TelephonyManager.EmergencyCallDiagnosticData.Builder setLogcatCollectionStartTimeMillis(long);
+    method @NonNull public android.telephony.TelephonyManager.EmergencyCallDiagnosticData.Builder setTelecomDumpsysCollectionEnabled(boolean);
+    method @NonNull public android.telephony.TelephonyManager.EmergencyCallDiagnosticData.Builder setTelephonyDumpsysCollectionEnabled(boolean);
   }
 
   public static class TelephonyManager.ModemActivityInfoException extends java.lang.Exception {
@@ -15040,7 +15049,7 @@
 
   @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public class TelephonyRegistryManager {
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String);
-    method public void notifyOutgoingEmergencyCall(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOutgoingEmergencyCall(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
   }
 
   public final class ThermalMitigationRequest implements android.os.Parcelable {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 77add41..850f149 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1745,6 +1745,7 @@
     method @NonNull public java.util.List<java.lang.String> getKeyboardLayoutDescriptorsForInputDevice(@NonNull android.view.InputDevice);
     method @NonNull public String getKeyboardLayoutTypeForLayoutDescriptor(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.REMAP_MODIFIER_KEYS) public java.util.Map<java.lang.Integer,java.lang.Integer> getModifierKeyRemapping();
+    method public int getMousePointerSpeed();
     method @RequiresPermission(android.Manifest.permission.REMAP_MODIFIER_KEYS) public void remapModifierKey(int, int);
     method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void removeKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
     method public void removeUniqueIdAssociation(@NonNull String);
@@ -1754,6 +1755,7 @@
 
   public class InputSettings {
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, @FloatRange(from=0, to=1) float);
+    field public static final int DEFAULT_POINTER_SPEED = 0; // 0x0
   }
 
 }
@@ -2882,6 +2884,10 @@
     field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
   }
 
+  public static final class Settings.System extends android.provider.Settings.NameValueTable {
+    field public static final String POINTER_SPEED = "pointer_speed";
+  }
+
   public static final class Telephony.Sms.Intents {
     field public static final String SMS_CARRIER_PROVISION_ACTION = "android.provider.Telephony.SMS_CARRIER_PROVISION";
   }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 4f1db7d..d8aded40 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1269,6 +1269,22 @@
         return appMetadata != null ? appMetadata : new PersistableBundle();
     }
 
+    @Override
+    public @AppMetadataSource int getAppMetadataSource(@NonNull String packageName)
+            throws NameNotFoundException {
+        Objects.requireNonNull(packageName, "packageName cannot be null");
+        int source = PackageManager.APP_METADATA_SOURCE_UNKNOWN;
+        try {
+            source = mPM.getAppMetadataSource(packageName, getUserId());
+        } catch (ParcelableException e) {
+            e.maybeRethrow(NameNotFoundException.class);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return source;
+    }
+
     @SuppressWarnings("unchecked")
     @Override
     public List<PackageInfo> getPackagesHoldingPermissions(String[] permissions, int flags) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 8883907..0a34d36 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4649,13 +4649,24 @@
          * to turn it off and use a normal notification, as this can be extremely
          * disruptive.
          *
-         * <p>
-         * The system UI may choose to display a heads-up notification, instead of
-         * launching this intent, while the user is using the device.
-         * </p>
          * <p>Apps targeting {@link Build.VERSION_CODES#Q} and above will have to request
          * a permission ({@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}) in order to
-         * use full screen intents.</p>
+         * use full screen intents. </p>
+         * <p>
+         * Prior to {@link Build.VERSION_CODES#TIRAMISU}, the system may display a
+         * heads up notification (which may display on screen longer than other heads up
+         * notifications), instead of launching the intent, while the user is using the device.
+         * From {@link Build.VERSION_CODES#TIRAMISU},
+         * the system UI will display a heads up notification, instead of launching this intent,
+         * while the user is using the device. This notification will display with emphasized
+         * action buttons. If the posting app holds
+         * {@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}, then the heads
+         * up notification will appear persistently until the user dismisses or snoozes it, or
+         * the app cancels it. If the posting app does not hold
+         * {@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}, then the notification will
+         * appear as heads up notification even when the screen is locked or turned off, and this
+         * notification will only be persistent for 60 seconds.
+         * </p>
          * <p>
          * To be launched as a full screen intent, the notification must also be posted to a
          * channel with importance level set to IMPORTANCE_HIGH or higher.
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 15d692a..ab5395e 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,6 +15,7 @@
  */
 package android.app;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -30,6 +31,9 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.VibrationEffect;
+import android.os.vibrator.persistence.VibrationXmlParser;
+import android.os.vibrator.persistence.VibrationXmlSerializer;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
 import android.text.TextUtils;
@@ -49,6 +53,8 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
 import java.util.Arrays;
 import java.util.Objects;
 
@@ -146,6 +152,7 @@
     private static final String ATT_LIGHTS = "lights";
     private static final String ATT_LIGHT_COLOR = "light_color";
     private static final String ATT_VIBRATION = "vibration";
+    private static final String ATT_VIBRATION_EFFECT = "vibration_effect";
     private static final String ATT_VIBRATION_ENABLED = "vibration_enabled";
     private static final String ATT_SOUND = "sound";
     private static final String ATT_USAGE = "usage";
@@ -253,7 +260,8 @@
     private boolean mSoundRestored = false;
     private boolean mLights;
     private int mLightColor = DEFAULT_LIGHT_COLOR;
-    private long[] mVibration;
+    private long[] mVibrationPattern;
+    private VibrationEffect mVibrationEffect;
     // Bitwise representation of fields that have been changed by the user, preventing the app from
     // making changes to these fields.
     private int mUserLockedFields;
@@ -324,9 +332,13 @@
             mSound = null;
         }
         mLights = in.readByte() != 0;
-        mVibration = in.createLongArray();
-        if (mVibration != null && mVibration.length > MAX_VIBRATION_LENGTH) {
-            mVibration = Arrays.copyOf(mVibration, MAX_VIBRATION_LENGTH);
+        mVibrationPattern = in.createLongArray();
+        if (mVibrationPattern != null && mVibrationPattern.length > MAX_VIBRATION_LENGTH) {
+            mVibrationPattern = Arrays.copyOf(mVibrationPattern, MAX_VIBRATION_LENGTH);
+        }
+        if (Flags.notificationChannelVibrationEffectApi()) {
+            mVibrationEffect =
+                    in.readInt() != 0 ? VibrationEffect.CREATOR.createFromParcel(in) : null;
         }
         mUserLockedFields = in.readInt();
         mUserVisibleTaskShown = in.readByte() != 0;
@@ -381,7 +393,15 @@
             dest.writeByte((byte) 0);
         }
         dest.writeByte(mLights ? (byte) 1 : (byte) 0);
-        dest.writeLongArray(mVibration);
+        dest.writeLongArray(mVibrationPattern);
+        if (Flags.notificationChannelVibrationEffectApi()) {
+            if (mVibrationEffect != null) {
+                dest.writeInt(1);
+                mVibrationEffect.writeToParcel(dest, /* flags= */ 0);
+            } else {
+                dest.writeInt(0);
+            }
+        }
         dest.writeInt(mUserLockedFields);
         dest.writeByte(mUserVisibleTaskShown ? (byte) 1 : (byte) 0);
         dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0);
@@ -585,8 +605,8 @@
 
     /**
      * Sets the vibration pattern for notifications posted to this channel. If the provided
-     * pattern is valid (non-null, non-empty), will enable vibration on this channel
-     * (equivalent to calling {@link #enableVibration(boolean)} with {@code true}).
+     * pattern is valid (non-null, non-empty with at least 1 non-zero value), will enable vibration
+     * on this channel (equivalent to calling {@link #enableVibration(boolean)} with {@code true}).
      * Otherwise, vibration will be disabled unless {@link #enableVibration(boolean)} is
      * used with {@code true}, in which case the default vibration will be used.
      *
@@ -595,7 +615,56 @@
      */
     public void setVibrationPattern(long[] vibrationPattern) {
         this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0;
-        this.mVibration = vibrationPattern;
+        this.mVibrationPattern = vibrationPattern;
+        if (Flags.notificationChannelVibrationEffectApi()) {
+            try {
+                this.mVibrationEffect =
+                        VibrationEffect.createWaveform(vibrationPattern, /* repeat= */ -1);
+            } catch (IllegalArgumentException | NullPointerException e) {
+                this.mVibrationEffect = null;
+            }
+        }
+    }
+
+    /**
+     * Sets a {@link VibrationEffect} for notifications posted to this channel. If the
+     * provided effect is non-null, will enable vibration on this channel (equivalent
+     * to calling {@link #enableVibration(boolean)} with {@code true}). Otherwise
+     * vibration will be disabled unless {@link #enableVibration(boolean)} is used with
+     * {@code true}, in which case the default vibration will be used.
+     *
+     * <p>The effect passed here will be returned from {@link #getVibrationEffect()}.
+     * If the provided {@link VibrationEffect} is an equivalent to a wave-form
+     * vibration pattern, the equivalent wave-form pattern will be returned from
+     * {@link #getVibrationPattern()}.
+     *
+     * <p>Note that some {@link VibrationEffect}s may not be playable on some devices.
+     * In cases where such an effect is passed here, vibration will still be enabled
+     * for the channel, but the default vibration will be used. Nonetheless, the
+     * provided effect will be stored and be returned from {@link #getVibrationEffect}
+     * calls, and could be used by the same channel on a different device, for example,
+     * in cases the user backs up and restores to a device that does have the ability
+     * to play the effect, where that effect will be used instead of the default. To
+     * avoid such issues that could make the vibration behavior of your notification
+     * channel differ among different devices, it's recommended that you avoid
+     * vibration effect primitives, as the support for them differs widely among
+     * devices (read {@link VibrationEffect.Composition} for more on vibration
+     * primitives).
+     *
+     * <p>Only modifiable before the channel is submitted to
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
+     *
+     * @see #getVibrationEffect()
+     * @see Vibrator#areEffectsSupported(int...)
+     * @see Vibrator#arePrimitivesSupported(int...)
+     */
+    @FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API)
+    public void setVibrationEffect(@Nullable VibrationEffect effect) {
+        this.mVibrationEnabled = effect != null;
+        this.mVibrationEffect = effect;
+        this.mVibrationPattern =
+                effect == null
+                ? null : effect.computeCreateWaveformOffOnTimingsOrNull();
     }
 
     /**
@@ -768,7 +837,35 @@
      * vibration is not enabled ({@link #shouldVibrate()}).
      */
     public long[] getVibrationPattern() {
-        return mVibration;
+        return mVibrationPattern;
+    }
+
+    /**
+     * Returns the {@link VibrationEffect} for notifications posted to this channel.
+     * The returned effect is derived from either the effect provided in the
+     * {@link #setVibrationEffect(VibrationEffect)} method, or the equivalent vibration effect
+     * of the pattern set via the {@link #setVibrationPattern(long[])} method, based on setter
+     * method that was called last.
+     *
+     * The returned effect will be ignored in one of the following cases:
+     * <ul>
+     *   <li> vibration is not enabled for the channel (i.e. {@link #shouldVibrate()}
+     *        returns {@code false}).
+     *   <li> the effect is not supported/playable by the device. In this case, if
+     *        vibration is enabled for the channel, the default channel vibration will
+     *        be used instead.
+     * </ul>
+     *
+     * @return the {@link VibrationEffect} set via {@link
+     *         #setVibrationEffect(VibrationEffect)}, or the equivalent of the
+     *         vibration set via {@link #setVibrationPattern(long[])}.
+     *
+     *  @see VibrationEffect#createWaveform(long[], int)
+     */
+    @FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API)
+    @Nullable
+    public VibrationEffect getVibrationEffect() {
+        return mVibrationEffect;
     }
 
     /**
@@ -991,7 +1088,19 @@
 
         enableLights(safeBool(parser, ATT_LIGHTS, false));
         setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR));
+        // Set the pattern before the effect, so that we can properly handle cases where the pattern
+        // is null, but the effect is not null (i.e. for non-waveform VibrationEffects - the ones
+        // which cannot be represented as a vibration pattern).
         setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null));
+        if (Flags.notificationChannelVibrationEffectApi()) {
+            VibrationEffect vibrationEffect = safeVibrationEffect(parser, ATT_VIBRATION_EFFECT);
+            if (vibrationEffect != null) {
+                // Restore the effect only if it is not null. This allows to avoid undoing a
+                // `setVibrationPattern` call above, if that was done with a non-null pattern
+                // (e.g. back up from a version that did not support `setVibrationEffect`).
+                setVibrationEffect(vibrationEffect);
+            }
+        }
         enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false));
         setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false));
         setDeleted(safeBool(parser, ATT_DELETED, false));
@@ -1180,6 +1289,9 @@
         if (getVibrationPattern() != null) {
             out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern()));
         }
+        if (getVibrationEffect() != null) {
+            out.attribute(null, ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
+        }
         if (getUserLockedFields() != 0) {
             out.attributeInt(null, ATT_USER_LOCKED, getUserLockedFields());
         }
@@ -1260,6 +1372,9 @@
         record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
         record.put(ATT_FG_SERVICE_SHOWN, Boolean.toString(isUserVisibleTaskShown()));
         record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern()));
+        if (getVibrationEffect() != null) {
+            record.put(ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
+        }
         record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
         record.put(ATT_DELETED, Boolean.toString(isDeleted()));
         record.put(ATT_DELETED_TIME_MS, Long.toString(getDeletedTimeMs()));
@@ -1287,6 +1402,30 @@
         return val == null ? null : Uri.parse(val);
     }
 
+    private static String vibrationToString(VibrationEffect effect) {
+        StringWriter writer = new StringWriter();
+        try {
+            VibrationXmlSerializer.serialize(
+                    effect, writer, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS);
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to serialize vibration: " + effect, e);
+        }
+        return writer.toString();
+    }
+
+    private static VibrationEffect safeVibrationEffect(TypedXmlPullParser parser, String att) {
+        final String val = parser.getAttributeValue(null, att);
+        if (val != null) {
+            try {
+                return VibrationXmlParser.parseVibrationEffect(
+                        new StringReader(val), VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS);
+            } catch (IOException e) {
+                Log.e(TAG, "Unable to read serialized vibration effect", e);
+            }
+        }
+        return null;
+    }
+
     private static int safeInt(TypedXmlPullParser parser, String att, int defValue) {
         return parser.getAttributeInt(null, att, defValue);
     }
@@ -1361,7 +1500,8 @@
                 && Objects.equals(getName(), that.getName())
                 && Objects.equals(mDesc, that.mDesc)
                 && Objects.equals(getSound(), that.getSound())
-                && Arrays.equals(mVibration, that.mVibration)
+                && Arrays.equals(mVibrationPattern, that.mVibrationPattern)
+                && Objects.equals(getVibrationEffect(), that.getVibrationEffect())
                 && Objects.equals(getGroup(), that.getGroup())
                 && Objects.equals(getAudioAttributes(), that.getAudioAttributes())
                 && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp
@@ -1379,9 +1519,9 @@
                 getUserLockedFields(), isUserVisibleTaskShown(),
                 mVibrationEnabled, mShowBadge, isDeleted(), getDeletedTimeMs(),
                 getGroup(), getAudioAttributes(), isBlockable(), mAllowBubbles,
-                mImportanceLockedDefaultApp, mOriginalImportance,
+                mImportanceLockedDefaultApp, mOriginalImportance, getVibrationEffect(),
                 mParentId, mConversationId, mDemoted, mImportantConvo);
-        result = 31 * result + Arrays.hashCode(mVibration);
+        result = 31 * result + Arrays.hashCode(mVibrationPattern);
         return result;
     }
 
@@ -1413,7 +1553,9 @@
                 + ", mSound=" + mSound
                 + ", mLights=" + mLights
                 + ", mLightColor=" + mLightColor
-                + ", mVibration=" + Arrays.toString(mVibration)
+                + ", mVibrationPattern=" + Arrays.toString(mVibrationPattern)
+                + ", mVibrationEffect="
+                        + (mVibrationEffect == null ? "null" : mVibrationEffect.toString())
                 + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields)
                 + ", mUserVisibleTaskShown=" + mUserVisibleTaskShown
                 + ", mVibrationEnabled=" + mVibrationEnabled
@@ -1448,8 +1590,8 @@
         }
         proto.write(NotificationChannelProto.USE_LIGHTS, mLights);
         proto.write(NotificationChannelProto.LIGHT_COLOR, mLightColor);
-        if (mVibration != null) {
-            for (long v : mVibration) {
+        if (mVibrationPattern != null) {
+            for (long v : mVibrationPattern) {
                 proto.write(NotificationChannelProto.VIBRATION, v);
             }
         }
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index a5d4a14..c40b23e 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -50,3 +50,10 @@
   description: "Adds a new voicemail category for notifications"
   bug: "322806700"
 }
+
+flag {
+  name: "notification_channel_vibration_effect_api"
+  namespace: "systemui"
+  description: "This flag enables the API to allow setting VibrationEffect for NotificationChannels"
+  bug: "241732519"
+}
\ No newline at end of file
diff --git a/core/java/android/app/wearable/flags.aconfig b/core/java/android/app/wearable/flags.aconfig
index 074ce9b..5e8bdb5 100644
--- a/core/java/android/app/wearable/flags.aconfig
+++ b/core/java/android/app/wearable/flags.aconfig
@@ -5,4 +5,25 @@
     namespace: "machine_learning"
     description: "This flag enables the WearableSensingManager#STATUS_UNSUPPORTED_OPERATION status code API."
     bug: "301427767"
+}
+
+flag {
+    name: "enable_data_request_observer_api"
+    namespace: "machine_learning"
+    description: "This flag enables the API to register a data request observer on WearableSensingManager."
+    bug: "301427767"
+}
+
+flag {
+    name: "enable_provide_wearable_connection_api"
+    namespace: "machine_learning"
+    description: "This flag enables the WearableSensingManager#provideWearableConnection API."
+    bug: "301427767"
+}
+
+flag {
+    name: "enable_hotword_wearable_sensing_api"
+    namespace: "machine_learning"
+    description: "This flag enables the APIs related to hotword in WearableSensingManager and WearableSensingService."
+    bug: "310055381"
 }
\ No newline at end of file
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 6204edc..eb82e1f 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -822,18 +822,7 @@
      *
      * @param appWidgetIds  The AppWidget instances to notify of view data changes.
      * @param viewId        The collection view id.
-     * @deprecated The corresponding API
-     * {@link RemoteViews#setRemoteAdapter(int, Intent)} associated with this method has been
-     * deprecated. Moving forward please use
-     * {@link RemoteViews#setRemoteAdapter(int, android.widget.RemoteViews.RemoteCollectionItems)}
-     * instead to set {@link android.widget.RemoteViews.RemoteCollectionItems} for the remote
-     * adapter and update the widget views by calling {@link #updateAppWidget(int[], RemoteViews)},
-     * {@link #updateAppWidget(int, RemoteViews)},
-     * {@link #updateAppWidget(ComponentName, RemoteViews)},
-     * {@link #partiallyUpdateAppWidget(int[], RemoteViews)},
-     * or {@link #partiallyUpdateAppWidget(int, RemoteViews)}, whichever applicable.
      */
-    @Deprecated
     public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
         if (mService == null) {
             return;
@@ -884,18 +873,7 @@
      *
      * @param appWidgetId  The AppWidget instance to notify of view data changes.
      * @param viewId       The collection view id.
-     * @deprecated The corresponding API
-     * {@link RemoteViews#setRemoteAdapter(int, Intent)} associated with this method has been
-     * deprecated. Moving forward please use
-     * {@link RemoteViews#setRemoteAdapter(int, android.widget.RemoteViews.RemoteCollectionItems)}
-     * instead to set {@link android.widget.RemoteViews.RemoteCollectionItems} for the remote
-     * adapter and update the widget views by calling {@link #updateAppWidget(int[], RemoteViews)},
-     * {@link #updateAppWidget(int, RemoteViews)},
-     * {@link #updateAppWidget(ComponentName, RemoteViews)},
-     * {@link #partiallyUpdateAppWidget(int[], RemoteViews)},
-     * or {@link #partiallyUpdateAppWidget(int, RemoteViews)}, whichever applicable.
      */
-    @Deprecated
     public void notifyAppWidgetViewDataChanged(int appWidgetId, int viewId) {
         if (mService == null) {
             return;
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index c1e443d..39f6de7 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -26,6 +26,7 @@
 import android.companion.virtual.camera.VirtualCamera;
 import android.companion.virtual.camera.VirtualCameraConfig;
 import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtualdevice.flags.Flags;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -351,7 +352,11 @@
             @Nullable Executor executor,
             @Nullable VirtualAudioDevice.AudioConfigurationChangeCallback callback) {
         if (mVirtualAudioDevice == null) {
-            mVirtualAudioDevice = new VirtualAudioDevice(mContext, mVirtualDevice, display,
+            Context context = mContext;
+            if (Flags.deviceAwareRecordAudioPermission()) {
+                context = mContext.createDeviceContext(getDeviceId());
+            }
+            mVirtualAudioDevice = new VirtualAudioDevice(context, mVirtualDevice, display,
                     executor, callback, () -> mVirtualAudioDevice = null);
         }
         return mVirtualAudioDevice;
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index d26890f..ab8db6e 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -21,3 +21,11 @@
      description: "Enable discovery of the Virtual Camera HAL without a VINTF entry"
      bug: "305170199"
 }
+
+flag {
+     namespace: "virtual_devices"
+     name: "device_aware_record_audio_permission"
+     description: "Enable device-aware RECORD_AUDIO permission"
+     bug: "291737188"
+     is_fixed_read_only: true
+}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index e9b94c9..87fb843 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -185,7 +185,7 @@
      *
      * @param context A Context object which should be some mock instance (like the
      * instance of {@link android.test.mock.MockContext}).
-     * @param readPermission The read permision you want this instance should have in the
+     * @param readPermission The read permission you want this instance should have in the
      * test, which is available via {@link #getReadPermission()}.
      * @param writePermission The write permission you want this instance should have
      * in the test, which is available via {@link #getWritePermission()}.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9253998..a126363 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -387,7 +387,7 @@
      * {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
      * <li>When querying a provider, where no QUERY_ARG_SQL* otherwise exists in
      * the arguments {@link Bundle}, the Content framework will attempt to
-     * synthesize an QUERY_ARG_SQL* argument using the corresponding
+     * synthesize a QUERY_ARG_SQL* argument using the corresponding
      * QUERY_ARG_SORT* values.
      */
     public static final String QUERY_ARG_SORT_COLUMNS = "android:query-arg-sort-columns";
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index 1f25fd0..451c0e5 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -80,7 +80,7 @@
             long timeout);
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES,android.Manifest.permission.REQUEST_DELETE_PACKAGES})")
-    void requestArchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle);
+    void requestArchive(String packageName, String callerPackageName, int flags, in IntentSender statusReceiver, in UserHandle userHandle);
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
     void requestUnarchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 380de96..08f1853 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -843,4 +843,7 @@
     Bitmap getArchivedAppIcon(String packageName, in UserHandle user, String callingPackageName);
 
     boolean isAppArchivable(String packageName, in UserHandle user);
+
+    @EnforcePermission("GET_APP_METADATA")
+    int getAppMetadataSource(String packageName, int userId);
 }
diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS
index 53dd3bf..fb95608895 100644
--- a/core/java/android/content/pm/OWNERS
+++ b/core/java/android/content/pm/OWNERS
@@ -10,3 +10,4 @@
 per-file UserInfo* = file:/MULTIUSER_OWNERS
 per-file *UserProperties* = file:/MULTIUSER_OWNERS
 per-file *multiuser* = file:/MULTIUSER_OWNERS
+per-file IBackgroundInstallControlService.aidl = file:/services/core/java/com/android/server/pm/BACKGROUND_INSTALL_OWNERS
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index c4bf18d..5df23c0 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2362,8 +2362,8 @@
     public void requestArchive(@NonNull String packageName, @NonNull IntentSender statusReceiver)
             throws PackageManager.NameNotFoundException {
         try {
-            mInstaller.requestArchive(packageName, mInstallerPackageName, statusReceiver,
-                    new UserHandle(mUserId));
+            mInstaller.requestArchive(packageName, mInstallerPackageName, /*flags=*/ 0,
+                    statusReceiver, new UserHandle(mUserId));
         } catch (ParcelableException e) {
             e.maybeRethrow(PackageManager.NameNotFoundException.class);
         } catch (RemoteException e) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bc29f8b..407ffbb 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2992,6 +2992,46 @@
     @SystemApi
     public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4;
 
+
+    /**
+     * Indicates that the app metadata does not exist or its source is unknown.
+     * @hide
+     */
+    @FlaggedApi(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+    @SystemApi
+    public static final int APP_METADATA_SOURCE_UNKNOWN = 0;
+    /**
+     * Indicates that the app metadata is provided by the APK itself.
+     * @hide
+     */
+    @FlaggedApi(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+    @SystemApi
+    public static final int APP_METADATA_SOURCE_APK = 1;
+    /**
+     * Indicates that the app metadata is provided by the installer that installed the app.
+     * @hide
+     */
+    @FlaggedApi(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+    @SystemApi
+    public static final int APP_METADATA_SOURCE_INSTALLER = 2;
+    /**
+     * Indicates that the app metadata is provided as part of the system image.
+     * @hide
+     */
+    @FlaggedApi(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+    @SystemApi
+    public static final int APP_METADATA_SOURCE_SYSTEM_IMAGE = 3;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "APP_METADATA_SOURCE_" }, value = {
+            APP_METADATA_SOURCE_UNKNOWN,
+            APP_METADATA_SOURCE_APK,
+            APP_METADATA_SOURCE_INSTALLER,
+            APP_METADATA_SOURCE_SYSTEM_IMAGE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AppMetadataSource {}
+
     /**
      * Can be used as the {@code millisecondsToDelay} argument for
      * {@link PackageManager#extendVerificationTimeout}. This is the
@@ -6309,6 +6349,29 @@
         throw new UnsupportedOperationException("getAppMetadata not implemented in subclass");
     }
 
+
+    /**
+     * Returns the source of the app metadata that is currently associated with the given package.
+     * The value can be {@link #APP_METADATA_SOURCE_UNKNOWN}, {@link #APP_METADATA_SOURCE_APK},
+     * {@link #APP_METADATA_SOURCE_INSTALLER} or {@link #APP_METADATA_SOURCE_SYSTEM_IMAGE}.
+     *
+     * Note: an app can have the app metadata included in the APK, but if the installer also
+     * provides an app metadata during the installation, the one provided by the installer will
+     * take precedence.
+     *
+     * @param packageName The package name for which to get the app metadata source.
+     * @throws NameNotFoundException if no such package is available to the caller.
+     * @throws SecurityException if the caller doesn't have the required permission.
+     * @hide
+     */
+    @FlaggedApi(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+    @SystemApi
+    @RequiresPermission(Manifest.permission.GET_APP_METADATA)
+    public @AppMetadataSource int getAppMetadataSource(@NonNull String packageName)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException("getAppMetadataSource not implemented in subclass");
+    }
+
     /**
      * Return a List of all installed packages that are currently holding any of
      * the given permissions.
diff --git a/core/java/android/content/pm/SignedPackage.java b/core/java/android/content/pm/SignedPackage.java
new file mode 100644
index 0000000..4d1b136
--- /dev/null
+++ b/core/java/android/content/pm/SignedPackage.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 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.content.pm;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * A data class representing a package and (SHA-256 hash of) a signing certificate.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@FlaggedApi(android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+public class SignedPackage {
+    @NonNull
+    private final SignedPackageParcel mData;
+
+    /** @hide */
+    public SignedPackage(@NonNull String pkgName, @NonNull byte[] certificateDigest) {
+        SignedPackageParcel data = new SignedPackageParcel();
+        data.pkgName = pkgName;
+        data.certificateDigest = certificateDigest;
+        mData = data;
+    }
+
+    /** @hide */
+    public SignedPackage(@NonNull SignedPackageParcel data) {
+        mData = data;
+    }
+
+    /** @hide */
+    public final @NonNull SignedPackageParcel getData() {
+        return mData;
+    }
+
+    public @NonNull String getPkgName() {
+        return mData.pkgName;
+    }
+
+    public @NonNull byte[] getCertificateDigest() {
+        return mData.certificateDigest;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SignedPackage that)) return false;
+        return mData.pkgName.equals(that.mData.pkgName) && Arrays.equals(mData.certificateDigest,
+                that.mData.certificateDigest);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mData.pkgName, Arrays.hashCode(mData.certificateDigest));
+    }
+}
diff --git a/core/java/android/content/pm/SignedPackageParcel.aidl b/core/java/android/content/pm/SignedPackageParcel.aidl
new file mode 100644
index 0000000..7957f7f
--- /dev/null
+++ b/core/java/android/content/pm/SignedPackageParcel.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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.content.pm;
+
+import android.content.ComponentName;
+
+/** @hide */
+parcelable SignedPackageParcel {
+    String pkgName;
+    byte[] certificateDigest;
+}
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 19bce0b..f31521d 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -199,3 +199,10 @@
     bug: "282783453"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "set_pre_verified_domains"
+    namespace: "package_manager_service"
+    description: "Feature flag to enable pre-verified domains"
+    bug: "307327678"
+}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 1b37092..3671980 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -29,6 +29,7 @@
 import android.annotation.DimenRes;
 import android.annotation.Discouraged;
 import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
 import android.annotation.FontRes;
 import android.annotation.FractionRes;
 import android.annotation.IntegerRes;
@@ -46,6 +47,7 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.Config;
+import android.content.pm.ApplicationInfo;
 import android.content.res.loader.ResourcesLoader;
 import android.graphics.Movie;
 import android.graphics.Typeface;
@@ -2825,4 +2827,22 @@
             }
         }
     }
+
+    /**
+     * Register the resources paths of a package (e.g. a shared library). This will collect the
+     * package resources' paths from its ApplicationInfo and add them to all existing and future
+     * contexts while the application is running.
+     * A second call with the same uniqueId is a no-op.
+     * The paths are not persisted during application restarts. The application is responsible for
+     * calling the API again if this happens.
+     *
+     * @param uniqueId The unique id for the ApplicationInfo object, to detect and ignore repeated
+     *                 API calls.
+     * @param appInfo The ApplicationInfo that contains resources paths of the package.
+     */
+    @FlaggedApi(android.content.res.Flags.FLAG_REGISTER_RESOURCE_PATHS)
+    public static void registerResourcePaths(@NonNull String uniqueId,
+            @NonNull ApplicationInfo appInfo) {
+        throw new UnsupportedOperationException("The implementation has not been done yet.");
+    }
 }
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index db81e84..f660770 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -40,3 +40,12 @@
     description: "Feature flag for creating an frro from a 9-patch"
     bug: "309232726"
 }
+
+flag {
+    name: "register_resource_paths"
+    namespace: "resource_manager"
+    description: "Feature flag for register resource paths for shared library"
+    bug: "306202569"
+    # This flag is read in ResourcesImpl at boot time.
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java
index c3a09ae..e8d5d37 100644
--- a/core/java/android/credentials/selection/IntentFactory.java
+++ b/core/java/android/credentials/selection/IntentFactory.java
@@ -20,6 +20,7 @@
 
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.content.ComponentName;
@@ -49,7 +50,7 @@
     public static Intent createCredentialSelectorIntent(
             @NonNull RequestInfo requestInfo,
             @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
-            @NonNull
+            @Nullable
             ArrayList<ProviderData> enabledProviderDataList,
             @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
             @NonNull
@@ -57,23 +58,30 @@
             @NonNull ResultReceiver resultReceiver,
             boolean isRequestForAllOptions) {
 
-        Intent intent = createCredentialSelectorIntent(requestInfo, enabledProviderDataList,
-                disabledProviderDataList, resultReceiver);
+        Intent intent;
+        if (enabledProviderDataList != null) {
+            intent = createCredentialSelectorIntent(requestInfo, enabledProviderDataList,
+                    disabledProviderDataList, resultReceiver);
+        } else {
+            intent = createCredentialSelectorIntent(requestInfo,
+                    disabledProviderDataList, resultReceiver);
+        }
         intent.putExtra(Constants.EXTRA_REQ_FOR_ALL_OPTIONS, isRequestForAllOptions);
 
         return intent;
     }
 
-    /** Generate a new launch intent to the Credential Selector UI. */
+    /**
+     * Generate a new launch intent to the Credential Selector UI.
+     *
+     * @hide
+     */
     @NonNull
     public static Intent createCredentialSelectorIntent(
             @NonNull RequestInfo requestInfo,
             @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
-                    @NonNull
-                    ArrayList<ProviderData> enabledProviderDataList,
-            @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
-                    @NonNull
-                    ArrayList<DisabledProviderData> disabledProviderDataList,
+            @NonNull
+            ArrayList<DisabledProviderData> disabledProviderDataList,
             @NonNull ResultReceiver resultReceiver) {
         Intent intent = new Intent();
         ComponentName componentName =
@@ -83,9 +91,6 @@
                                         com.android.internal.R.string
                                                 .config_credentialManagerDialogComponent));
         intent.setComponent(componentName);
-
-        intent.putParcelableArrayListExtra(
-                ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, enabledProviderDataList);
         intent.putParcelableArrayListExtra(
                 ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST, disabledProviderDataList);
         intent.putExtra(RequestInfo.EXTRA_REQUEST_INFO, requestInfo);
@@ -95,6 +100,24 @@
         return intent;
     }
 
+    /** Generate a new launch intent to the Credential Selector UI. */
+    @NonNull
+    public static Intent createCredentialSelectorIntent(
+            @NonNull RequestInfo requestInfo,
+            @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
+            @NonNull
+            ArrayList<ProviderData> enabledProviderDataList,
+            @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
+            @NonNull
+            ArrayList<DisabledProviderData> disabledProviderDataList,
+            @NonNull ResultReceiver resultReceiver) {
+        Intent intent = createCredentialSelectorIntent(requestInfo,
+                disabledProviderDataList, resultReceiver);
+        intent.putParcelableArrayListExtra(
+                ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, enabledProviderDataList);
+        return intent;
+    }
+
     /**
      * Creates an Intent that cancels any UI matching the given request token id.
      *
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index cb1d3f5..3b7ade2 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -511,7 +511,7 @@
     Bundle getExtras();
 
     /**
-     * This is an out-of-band way for the the user of a cursor to communicate with the cursor. The
+     * This is an out-of-band way for the user of a cursor to communicate with the cursor. The
      * structure of each bundle is entirely defined by the cursor.
      *
      * <p>One use of this is to tell a cursor that it should retry its network request after it
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 47f5b4c..451d6fb 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -5313,7 +5313,7 @@
      * </code></pre>
      * <ul>
      * <li>VIDEO_STABILIZATION_MODES: {OFF, PREVIEW}</li>
-     * <li>AE_TARGET_FPS_RANGE: &lbrace;{<em>, 30}, {</em>, 60}&rbrace;</li>
+     * <li>AE_TARGET_FPS_RANGE: { {<em>, 30}, {</em>, 60} }</li>
      * <li>DYNAMIC_RANGE_PROFILE: {STANDARD, HLG10}</li>
      * </ul>
      * <p>This key is available on all devices.</p>
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 1867a17..7abe821 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -624,6 +624,120 @@
     }
 
     /**
+     * Gets an extension specific camera characteristics field value.
+     *
+     * <p>An extension can have a reduced set of camera capabilities (such as limited zoom ratio
+     * range, available video stabilization modes, etc). This API enables applications to query for
+     * an extension’s specific camera characteristics. Applications are recommended to prioritize
+     * obtaining camera characteristics using this API when using an extension. A {@code null}
+     * result indicates that the extension specific characteristic is not defined or available.
+     *
+     * @param extension The extension type.
+     * @param key The characteristics field to read.
+     * @return The value of that key, or {@code null} if the field is not set.
+     *
+     * @throws IllegalArgumentException if the key is not valid or extension type is not a supported
+     * device-specific extension.
+     */
+    @FlaggedApi(Flags.FLAG_CAMERA_EXTENSIONS_CHARACTERISTICS_GET)
+    public <T> @Nullable T get(@Extension int extension,
+            @NonNull CameraCharacteristics.Key<T> key) {
+        final IBinder token = new Binder(TAG + "#get:" + mCameraId);
+        boolean success = registerClient(mContext, token);
+        if (!success) {
+            throw new IllegalArgumentException("Unsupported extensions");
+        }
+
+        try {
+            if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) {
+                throw new IllegalArgumentException("Unsupported extension");
+            }
+
+            if (areAdvancedExtensionsSupported() && getKeys(extension).contains(key)) {
+                IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+                extender.init(mCameraId, mCharacteristicsMapNative);
+                CameraMetadataNative metadata =
+                        extender.getAvailableCharacteristicsKeyValues(mCameraId);
+                CameraCharacteristics fallbackCharacteristics = mCharacteristicsMap.get(mCameraId);
+                if (metadata == null) {
+                    return fallbackCharacteristics.get(key);
+                }
+                CameraCharacteristics characteristics = new CameraCharacteristics(metadata);
+                T value = characteristics.get(key);
+                return value == null ? fallbackCharacteristics.get(key) : value;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to query the extension for the specified key! Extension "
+                    + "service does not respond!");
+        } finally {
+            unregisterClient(mContext, token);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the {@link CameraCharacteristics} keys that have extension-specific values.
+     *
+     * <p>An application can query the value from the key using
+     * {@link #get(int, CameraCharacteristics.Key)} API.
+     *
+     * @param extension The extension type.
+     * @return An unmodifiable set of keys that are extension specific.
+     *
+     * @throws IllegalArgumentException in case the extension type is not a
+     * supported device-specific extension
+     */
+    @FlaggedApi(Flags.FLAG_CAMERA_EXTENSIONS_CHARACTERISTICS_GET)
+    public @NonNull Set<CameraCharacteristics.Key> getKeys(@Extension int extension) {
+        final IBinder token =
+                new Binder(TAG + "#getKeys:" + mCameraId);
+        boolean success = registerClient(mContext, token);
+        if (!success) {
+            throw new IllegalArgumentException("Unsupported extensions");
+        }
+
+        HashSet<CameraCharacteristics.Key> ret = new HashSet<>();
+
+        try {
+            if (!isExtensionSupported(mCameraId, extension, mCharacteristicsMapNative)) {
+                throw new IllegalArgumentException("Unsupported extension");
+            }
+
+            if (areAdvancedExtensionsSupported()) {
+                IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+                extender.init(mCameraId, mCharacteristicsMapNative);
+                CameraMetadataNative metadata =
+                        extender.getAvailableCharacteristicsKeyValues(mCameraId);
+                if (metadata == null) {
+                    return Collections.emptySet();
+                }
+
+                int[] keys = metadata.get(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+                if (keys == null) {
+                    throw new AssertionError(
+                            "android.request.availableCharacteristicsKeys must be non-null"
+                                    + " in the characteristics");
+                }
+                CameraCharacteristics chars = new CameraCharacteristics(metadata);
+
+                Object key = CameraCharacteristics.Key.class;
+                Class<CameraCharacteristics.Key<?>> keyTyped =
+                        (Class<CameraCharacteristics.Key<?>>) key;
+
+                ret.addAll(chars.getAvailableKeyList(CameraCharacteristics.class, keyTyped, keys,
+                        /*includeSynthetic*/ true));
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to query the extension for all available keys! Extension "
+                    + "service does not respond!");
+        } finally {
+            unregisterClient(mContext, token);
+        }
+        return Collections.unmodifiableSet(ret);
+    }
+
+    /**
      * Checks for postview support of still capture.
      *
      * <p>A postview is a preview version of the still capture that is available before the final
diff --git a/core/java/android/hardware/camera2/extension/AdvancedExtender.java b/core/java/android/hardware/camera2/extension/AdvancedExtender.java
index fb2df54..6653577 100644
--- a/core/java/android/hardware/camera2/extension/AdvancedExtender.java
+++ b/core/java/android/hardware/camera2/extension/AdvancedExtender.java
@@ -27,6 +27,7 @@
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.CaptureCallback;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Size;
 
 import com.android.internal.camera.flags.Flags;
@@ -56,6 +57,7 @@
     private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>();
     private final CameraManager mCameraManager;
 
+    private CameraUsageTracker mCameraUsageTracker;
     private static final String TAG = "AdvancedExtender";
 
     @FlaggedApi(Flags.FLAG_CONCERT_MODE)
@@ -81,6 +83,10 @@
         }
     }
 
+    void setCameraUsageTracker(CameraUsageTracker tracker) {
+        mCameraUsageTracker = tracker;
+    }
+
     @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     public long getMetadataVendorId(@NonNull String cameraId) {
         long vendorId = mMetadataVendorIdMap.containsKey(cameraId) ?
@@ -222,6 +228,23 @@
     public abstract List<CaptureResult.Key> getAvailableCaptureResultKeys(
             @NonNull String cameraId);
 
+    /**
+     * Returns a list of {@link CameraCharacteristics} key/value pairs for apps to use when
+     * querying the Extensions specific {@link CameraCharacteristics}.
+     *
+     * <p>To ensure the correct {@link CameraCharacteristics} are used when an extension is
+     * enabled, an application should prioritize the value returned from the list if the
+     * {@link CameraCharacteristics} key is present. If the key doesn't exist in the returned list,
+     * then the application should query the value using
+     * {@link CameraCharacteristics#get(CameraCharacteristics.Key)}.
+     *
+     * <p>For example, an extension may limit the zoom ratio range. In this case, an OEM can return
+     * a new zoom ratio range for the key {@link CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE}.
+     */
+    @FlaggedApi(Flags.FLAG_CAMERA_EXTENSIONS_CHARACTERISTICS_GET)
+    @NonNull
+    public abstract List<Pair<CameraCharacteristics.Key, Object>>
+            getAvailableCharacteristicsKeyValues();
 
     private final class AdvancedExtenderImpl extends IAdvancedExtenderImpl.Stub {
         @Override
@@ -264,7 +287,9 @@
 
         @Override
         public ISessionProcessorImpl getSessionProcessor() {
-            return AdvancedExtender.this.getSessionProcessor().getSessionProcessorBinder();
+            SessionProcessor processor =AdvancedExtender.this.getSessionProcessor();
+            processor.setCameraUsageTracker(mCameraUsageTracker);
+            return processor.getSessionProcessorBinder();
         }
 
         @Override
@@ -322,6 +347,33 @@
             // Feature is currently unsupported
             return false;
         }
+
+        @FlaggedApi(Flags.FLAG_CAMERA_EXTENSIONS_CHARACTERISTICS_GET)
+        @Override
+        public CameraMetadataNative getAvailableCharacteristicsKeyValues(String cameraId) {
+            List<Pair<CameraCharacteristics.Key, Object>> entries =
+                    AdvancedExtender.this.getAvailableCharacteristicsKeyValues();
+
+            if ((entries != null) && !entries.isEmpty()) {
+                CameraMetadataNative ret = new CameraMetadataNative();
+                long vendorId = mMetadataVendorIdMap.containsKey(cameraId)
+                        ? mMetadataVendorIdMap.get(cameraId) : Long.MAX_VALUE;
+                ret.setVendorId(vendorId);
+                int[] characteristicsKeyTags = new int[entries.size()];
+                int i = 0;
+                for (Pair<CameraCharacteristics.Key, Object> entry : entries) {
+                    int tag = CameraMetadataNative.getTag(entry.first.getName(), vendorId);
+                    characteristicsKeyTags[i++] = tag;
+                    ret.set(entry.first, entry.second);
+                }
+                ret.set(CameraCharacteristics.REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
+                        characteristicsKeyTags);
+
+                return ret;
+            }
+
+            return null;
+        }
     }
 
     @NonNull IAdvancedExtenderImpl getAdvancedExtenderBinder() {
diff --git a/core/java/android/hardware/camera2/extension/CameraExtensionService.java b/core/java/android/hardware/camera2/extension/CameraExtensionService.java
index 1426d7b..fa0d14a 100644
--- a/core/java/android/hardware/camera2/extension/CameraExtensionService.java
+++ b/core/java/android/hardware/camera2/extension/CameraExtensionService.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.app.AppOpsManager;
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
@@ -29,6 +30,11 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.camera.flags.Flags;
 
+interface CameraUsageTracker {
+    void startCameraOperation();
+    void finishCameraOperation();
+}
+
 /**
  * Base service class that extension service implementations must extend.
  *
@@ -38,8 +44,33 @@
 @FlaggedApi(Flags.FLAG_CONCERT_MODE)
 public abstract class CameraExtensionService extends Service {
     private static final String TAG = "CameraExtensionService";
+    private CameraUsageTracker mCameraUsageTracker;
     private static Object mLock = new Object();
 
+    private final class CameraTracker implements CameraUsageTracker {
+
+        private final AppOpsManager mAppOpsService = getApplicationContext().getSystemService(
+                AppOpsManager.class);
+        private final String mPackageName = getPackageName();
+        private final String mAttributionTag = getAttributionTag();
+        private int mUid = getApplicationInfo().uid;
+
+        @Override
+        public void startCameraOperation() {
+            if (mAppOpsService != null) {
+                mAppOpsService.startOp(AppOpsManager.OPSTR_CAMERA, mUid, mPackageName,
+                        mAttributionTag, "Camera extensions");
+            }
+        }
+
+        @Override
+        public void finishCameraOperation() {
+            if (mAppOpsService != null) {
+                mAppOpsService.finishOp(AppOpsManager.OPSTR_CAMERA, mUid, mPackageName,
+                        mAttributionTag);
+            }
+        }
+    }
     @GuardedBy("mLock")
     private static IInitializeSessionCallback mInitializeCb = null;
 
@@ -49,16 +80,22 @@
             synchronized (mLock) {
                 mInitializeCb = null;
             }
+            if (mCameraUsageTracker != null) {
+                mCameraUsageTracker.finishCameraOperation();
+            }
         }
     };
 
     @FlaggedApi(Flags.FLAG_CONCERT_MODE)
-    protected CameraExtensionService() {}
+    protected CameraExtensionService() { }
 
     @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     @Override
     @NonNull
     public IBinder onBind(@Nullable Intent intent) {
+        if (mCameraUsageTracker == null) {
+            mCameraUsageTracker = new CameraTracker();
+        }
         return new CameraExtensionServiceImpl();
     }
 
@@ -132,8 +169,10 @@
         @Override
         public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType)
                 throws RemoteException {
-            return CameraExtensionService.this.onInitializeAdvancedExtension(
-                    extensionType).getAdvancedExtenderBinder();
+            AdvancedExtender extender =  CameraExtensionService.this.onInitializeAdvancedExtension(
+                    extensionType);
+            extender.setCameraUsageTracker(mCameraUsageTracker);
+            return extender.getAdvancedExtenderBinder();
         }
     }
 
diff --git a/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
index 101442f..3071f0d 100644
--- a/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
@@ -38,4 +38,5 @@
     CameraMetadataNative getAvailableCaptureResultKeys(in String cameraId);
     boolean isCaptureProcessProgressAvailable();
     boolean isPostviewAvailable();
+    CameraMetadataNative getAvailableCharacteristicsKeyValues(in String cameraId);
 }
diff --git a/core/java/android/hardware/camera2/extension/SessionProcessor.java b/core/java/android/hardware/camera2/extension/SessionProcessor.java
index 6ed0c14..9c5136b 100644
--- a/core/java/android/hardware/camera2/extension/SessionProcessor.java
+++ b/core/java/android/hardware/camera2/extension/SessionProcessor.java
@@ -76,10 +76,15 @@
 @FlaggedApi(Flags.FLAG_CONCERT_MODE)
 public abstract class SessionProcessor {
     private static final String TAG = "SessionProcessor";
+    private CameraUsageTracker mCameraUsageTracker;
 
     @FlaggedApi(Flags.FLAG_CONCERT_MODE)
     protected SessionProcessor() {}
 
+    void setCameraUsageTracker(CameraUsageTracker tracker) {
+        mCameraUsageTracker = tracker;
+    }
+
     /**
      * Callback for notifying the status of {@link
      * #startCapture} and {@link #startRepeating}.
@@ -379,12 +384,18 @@
         @Override
         public void onCaptureSessionStart(IRequestProcessorImpl requestProcessor, String statsKey)
                 throws RemoteException {
+            if (mCameraUsageTracker != null) {
+                mCameraUsageTracker.startCameraOperation();
+            }
             SessionProcessor.this.onCaptureSessionStart(
                     new RequestProcessor(requestProcessor, mVendorId), statsKey);
         }
 
         @Override
         public void onCaptureSessionEnd() throws RemoteException {
+            if (mCameraUsageTracker != null) {
+                mCameraUsageTracker.finishCameraOperation();
+            }
             SessionProcessor.this.onCaptureSessionEnd();
         }
 
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 7bea9ae..1f54959 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -67,6 +67,9 @@
 
     KeyCharacterMap getKeyCharacterMap(String layoutDescriptor);
 
+    // Returns the mouse pointer speed.
+    int getMousePointerSpeed();
+
     // Temporarily changes the pointer speed.
     void tryPointerSpeed(int speed);
 
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 4ebbde7..744dfae9 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,9 +16,11 @@
 
 package android.hardware.input;
 
+import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API;
 import static com.android.hardware.input.Flags.keyboardLayoutPreviewFlag;
 
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -26,6 +28,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
@@ -294,6 +297,23 @@
     }
 
     /**
+     * Gets the {@link InputDevice.ViewBehavior} of the input device with a given {@code id}.
+     *
+     * <p>Use this API to query a fresh view behavior instance whenever the input device
+     * changes.
+     *
+     * @param deviceId the id of the input device whose view behavior is being requested.
+     * @return the view behavior of the input device with the provided id, or {@code null} if there
+     *      is not input device with the provided id.
+     */
+    @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
+    @Nullable
+    public InputDevice.ViewBehavior getInputDeviceViewBehavior(int deviceId) {
+        InputDevice device = getInputDevice(deviceId);
+        return device == null ? null : device.getViewBehavior();
+    }
+
+    /**
      * Gets information about the input device with the specified descriptor.
      * @param descriptor The input device descriptor.
      * @return The input device or null if not found.
@@ -838,6 +858,28 @@
     }
 
     /**
+     * Returns the mouse pointer speed.
+     *
+     * <p>The pointer speed is a value between {@link InputSettings#MIN_POINTER_SPEED} and
+     * {@link InputSettings#MAX_POINTER_SPEED}, the default value being
+     * {@link InputSettings#DEFAULT_POINTER_SPEED}.
+     *
+     * <p> Note that while setting the mouse pointer speed, it's possible that the input reader has
+     * only received this value and has not yet completed reconfiguring itself with this value.
+     *
+     * @hide
+     */
+    @SuppressLint("UnflaggedApi") // TestApi without associated feature.
+    @TestApi
+    public int getMousePointerSpeed() {
+        try {
+            return mIm.getMousePointerSpeed();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Changes the mouse pointer speed temporarily, but does not save the setting.
      * <p>
      * Requires {@link android.Manifest.permission#SET_POINTER_SPEED}.
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index 89fa5fb..54e34ec 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -54,8 +54,8 @@
 
     /**
      * Pointer Speed: The default pointer speed (0).
-     * @hide
      */
+    @SuppressLint("UnflaggedApi") // TestApi without associated feature.
     public static final int DEFAULT_POINTER_SPEED = 0;
 
     /**
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 536ef31..a459aaa 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -412,7 +413,9 @@
      * Returns the base directory for per-user system directory, device encrypted.
      * {@hide}
      */
-    public static File getDataSystemDeDirectory() {
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY)
+    public static @NonNull File getDataSystemDeDirectory() {
         return buildPath(getDataDirectory(), "system_de");
     }
 
diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl
index b7649ba..650aead 100644
--- a/core/java/android/os/ISystemConfig.aidl
+++ b/core/java/android/os/ISystemConfig.aidl
@@ -17,6 +17,8 @@
 package android.os;
 
 import android.content.ComponentName;
+import android.os.Bundle;
+import android.content.pm.SignedPackageParcel;
 
 /**
   * Binder interface to query SystemConfig in the system server.
@@ -57,4 +59,14 @@
      * @see SystemConfigManager#getPreventUserDisablePackages
      */
     List<String> getPreventUserDisablePackages();
+
+    /**
+     * @see SystemConfigManager#getEnhancedConfirmationTrustedPackages
+     */
+    List<SignedPackageParcel> getEnhancedConfirmationTrustedPackages();
+
+    /**
+     * @see SystemConfigManager#getEnhancedConfirmationTrustedInstallers
+     */
+    List<SignedPackageParcel> getEnhancedConfirmationTrustedInstallers();
 }
diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java
index 21ffbf1..13bc398 100644
--- a/core/java/android/os/SystemConfigManager.java
+++ b/core/java/android/os/SystemConfigManager.java
@@ -16,12 +16,15 @@
 package android.os;
 
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.SignedPackage;
+import android.content.pm.SignedPackageParcel;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -29,6 +32,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 
 /**
@@ -175,4 +179,69 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+
+    /**
+     * Returns a set of signed packages, represented as (packageName, certificateDigest) pairs, that
+     * should be considered "trusted packages" by ECM (Enhanced Confirmation Mode).
+     *
+     * <p>"Trusted packages" are exempt from ECM (i.e., they will never be considered "restricted").
+     *
+     * <p>A package will be considered "trusted package" if and only if it *matches* least one of
+     * the (*packageName*, *certificateDigest*) pairs in this set, where *matches* means satisfying
+     * both of the following:
+     *
+     * <ol>
+     *   <li>The package's name equals *packageName*
+     *   <li>The package is, or was ever, signed by *certificateDigest*, according to the package's
+     *       {@link android.content.pm.SigningDetails}
+     * </ol>
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+    @RequiresPermission(Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES)
+    @NonNull
+    public Set<SignedPackage> getEnhancedConfirmationTrustedPackages() {
+        try {
+            List<SignedPackageParcel> parcels = mInterface.getEnhancedConfirmationTrustedPackages();
+            return parcels.stream().map(SignedPackage::new).collect(Collectors.toSet());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns a set of signed packages, represented as (packageName, certificateDigest) pairs, that
+     * should be considered "trusted installers" by ECM (Enhanced Confirmation Mode).
+     *
+     * <p>"Trusted installers", and all apps installed by a trusted installer, are exempt from ECM
+     * (i.e., they will never be considered "restricted").
+     *
+     * <p>A package will be considered a "trusted installer" if and only if it *matches* least one
+     * of the (*packageName*, *certificateDigest*) pairs in this set, where *matches* means
+     * satisfying both of the following:
+     *
+     * <ol>
+     *   <li>The package's name equals *packageName*
+     *   <li>The package is, or was ever, signed by *certificateDigest*, according to the package's
+     *       {@link android.content.pm.SigningDetails}
+     * </ol>
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+    @RequiresPermission(Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES)
+    @NonNull
+    public Set<SignedPackage> getEnhancedConfirmationTrustedInstallers() {
+        try {
+            List<SignedPackageParcel> parcels =
+                    mInterface.getEnhancedConfirmationTrustedInstallers();
+            return parcels.stream().map(SignedPackage::new).collect(Collectors.toSet());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index d6df8d9..ad0f940 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3438,6 +3438,7 @@
     }
 
     /**
+     * @see #isVisibleBackgroundUsersSupported()
      * @hide
      */
     public static boolean isVisibleBackgroundUsersEnabled() {
@@ -3447,14 +3448,21 @@
     }
 
     /**
-     * Returns whether the device allows (full) users to be started in background visible in a given
+     * Returns whether the device allows full users to be started in background visible in a given
      * display (which would allow them to launch activities in that display).
      *
-     * @return {@code false} for most devices, except on automotive builds for vehiches with
+     * Note that this is specifically about allowing <b>full</b> users to be background visible.
+     * Even if it is false, there can still be background visible users.
+     *
+     * In particular, the Communal Profile is a background visible user, and it can be supported
+     * unrelated to the value of this method.
+     *
+     * @return {@code false} for most devices, except on automotive builds for vehicles with
      * passenger displays.
      *
      * @hide
      */
+    // TODO(b/310249114): Rename to isVisibleBackgroundFullUsersSupported
     @TestApi
     public boolean isVisibleBackgroundUsersSupported() {
         return isVisibleBackgroundUsersEnabled();
@@ -3470,12 +3478,13 @@
     }
 
     /**
-     * Returns whether the device allows (full) users to be started in background visible in the
+     * Returns whether the device allows full users to be started in background visible in the
      * {@link android.view.Display#DEFAULT_DISPLAY default display}.
      *
      * @return {@code false} for most devices, except passenger-only automotive build (i.e., when
      * Android runs in a separate system in the back seat to manage the passenger displays).
      *
+     * @see #isVisibleBackgroundUsersSupported()
      * @hide
      */
     @TestApi
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 524b733..76fda06 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6015,8 +6015,10 @@
          *   +7 = fastest
          * @hide
          */
+        @SuppressLint({"NoSettingsProvider", "UnflaggedApi"}) // TestApi without associated feature.
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         @Readable
+        @TestApi
         public static final String POINTER_SPEED = "pointer_speed";
 
         /**
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
index 5978383..8aeaacf 100644
--- a/core/java/android/service/chooser/flags.aconfig
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -7,3 +7,9 @@
   bug: "268089816"
 }
 
+flag {
+  name: "chooser_payload_toggling"
+  namespace: "intentresolver"
+  description: "This flag controls content toggling in Chooser"
+  bug: "302691505"
+}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 0a813a3..d39c4ce 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -19,12 +19,15 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.SystemService;
 import android.compat.Compatibility;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Build;
 import android.os.RemoteException;
@@ -73,9 +76,15 @@
  * Limit API access to only carrier apps with certain permissions or apps running on
  * privileged UID.
  *
+ * TelephonyRegistryManager is intended for use on devices that implement
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices
+ * that do not implement this feature, the behavior is not reliable.
+ *
  * @hide
  */
 @SystemApi
+@SystemService(Context.TELEPHONY_REGISTRY_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY)
 @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
 public class TelephonyRegistryManager {
 
@@ -389,10 +398,11 @@
     }
 
     /**
-     * Notify call state changed on all subscriptions.
+     * Notify call state changed on all subscriptions, excluding over-the-top VOIP calls (otherwise
+     * known as self-managed calls in the Android Platform).
      *
      * @param state latest call state. e.g, offhook, ringing
-     * @param incomingNumber incoming phone number.
+     * @param incomingNumber incoming phone number or null in the case for OTT VOIP calls
      * @hide
      */
     @SystemApi
@@ -627,17 +637,20 @@
     }
 
     /**
-     * Notify outgoing emergency call.
+     * Notify outgoing emergency call to all applications that have registered a listener
+     * ({@link PhoneStateListener}) or a callback ({@link TelephonyCallback}) to monitor changes in
+     * telephony states.
      * @param simSlotIndex Sender phone ID.
-     * @param subId Sender subscription ID.
+     * @param subscriptionId Sender subscription ID.
      * @param emergencyNumber Emergency number.
      * @hide
      */
     @SystemApi
-    public void notifyOutgoingEmergencyCall(int simSlotIndex, int subId,
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void notifyOutgoingEmergencyCall(int simSlotIndex, int subscriptionId,
             @NonNull EmergencyNumber emergencyNumber) {
         try {
-            sRegistry.notifyOutgoingEmergencyCall(simSlotIndex, subId, emergencyNumber);
+            sRegistry.notifyOutgoingEmergencyCall(simSlotIndex, subscriptionId, emergencyNumber);
         } catch (RemoteException ex) {
             // system process is dead
             throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/tracing/perfetto/DataSourceInstance.java b/core/java/android/tracing/perfetto/DataSourceInstance.java
index 4994501..3710b4d 100644
--- a/core/java/android/tracing/perfetto/DataSourceInstance.java
+++ b/core/java/android/tracing/perfetto/DataSourceInstance.java
@@ -69,4 +69,8 @@
     public final void release() {
         mDataSource.releaseDataSourceInstance(mInstanceIndex);
     }
+
+    public final int getInstanceIndex() {
+        return mInstanceIndex;
+    }
 }
diff --git a/core/java/android/tracing/transition/TransitionDataSource.java b/core/java/android/tracing/transition/TransitionDataSource.java
index baece75..82559da 100644
--- a/core/java/android/tracing/transition/TransitionDataSource.java
+++ b/core/java/android/tracing/transition/TransitionDataSource.java
@@ -24,8 +24,8 @@
 import android.tracing.perfetto.StopCallbackArguments;
 import android.util.proto.ProtoInputStream;
 
-import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * @hide
@@ -38,6 +38,9 @@
     private final Runnable mOnFlushStaticCallback;
     private final Runnable mOnStopStaticCallback;
 
+    private final ConcurrentHashMap<Integer, ConcurrentHashMap<String, Integer>> mHandlerMappings =
+            new ConcurrentHashMap<>();
+
     public TransitionDataSource(Runnable onStart, Runnable onFlush, Runnable onStop) {
         super(DATA_SOURCE_NAME);
         this.mOnStartStaticCallback = onStart;
@@ -47,11 +50,16 @@
 
     @Override
     protected TlsState createTlsState(CreateTlsStateArgs<DataSourceInstance> args) {
-        return new TlsState();
+        return new TlsState(args.getDataSourceInstanceLocked().getInstanceIndex());
     }
 
     public class TlsState {
-        public final Map<String, Integer> handlerMapping = new HashMap<>();
+        public final Map<String, Integer> handlerMapping;
+
+        public TlsState(int instanceIndex) {
+            handlerMapping = mHandlerMappings
+                    .computeIfAbsent(instanceIndex, index -> new ConcurrentHashMap<>());
+        }
     }
 
     @Override
@@ -70,6 +78,7 @@
             @Override
             protected void onStop(StopCallbackArguments args) {
                 mOnStopStaticCallback.run();
+                mHandlerMappings.remove(instanceIndex);
             }
         };
     }
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index f2c3abc..891e2a2 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -16,7 +16,10 @@
 
 package android.view;
 
+import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API;
+
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -28,6 +31,7 @@
 import android.hardware.SensorManager;
 import android.hardware.input.HostUsiVersion;
 import android.hardware.input.InputDeviceIdentifier;
+import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerGlobal;
 import android.hardware.lights.LightsManager;
 import android.icu.util.ULocale;
@@ -90,6 +94,8 @@
     private final int mAssociatedDisplayId;
     private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
 
+    private final ViewBehavior mViewBehavior = new ViewBehavior(this);
+
     @GuardedBy("mMotionRanges")
     private Vibrator mVibrator; // guarded by mMotionRanges during initialization
 
@@ -539,6 +545,8 @@
             addMotionRange(in.readInt(), in.readInt(), in.readFloat(), in.readFloat(),
                     in.readFloat(), in.readFloat(), in.readFloat());
         }
+
+        mViewBehavior.mShouldSmoothScroll = in.readBoolean();
     }
 
     /**
@@ -571,6 +579,7 @@
         private int mUsiVersionMinor = -1;
         private int mAssociatedDisplayId = Display.INVALID_DISPLAY;
         private List<MotionRange> mMotionRanges = new ArrayList<>();
+        private boolean mShouldSmoothScroll;
 
         /** @see InputDevice#getId() */
         public Builder setId(int id) {
@@ -706,6 +715,16 @@
             return this;
         }
 
+        /**
+         * Sets the view behavior for smooth scrolling ({@code false} by default).
+         *
+         * @see ViewBehavior#shouldSmoothScroll(int, int)
+         */
+        public Builder setShouldSmoothScroll(boolean shouldSmoothScroll) {
+            mShouldSmoothScroll = shouldSmoothScroll;
+            return this;
+        }
+
         /** Build {@link InputDevice}. */
         public InputDevice build() {
             InputDevice device = new InputDevice(
@@ -745,6 +764,8 @@
                         range.getResolution());
             }
 
+            device.setShouldSmoothScroll(mShouldSmoothScroll);
+
             return device;
         }
     }
@@ -1123,6 +1144,22 @@
         return mMotionRanges;
     }
 
+    /**
+     * Provides the {@link ViewBehavior} for the device.
+     *
+     * <p>This behavior is designed to be obtained using the
+     * {@link InputManager#getInputDeviceViewBehavior(int)} API, to allow associating the behavior
+     * with a {@link Context} (since input device is not associated with a context).
+     * The ability to associate the behavior with a context opens capabilities like linking the
+     * behavior to user settings, for example.
+     *
+     * @hide
+     */
+    @NonNull
+    public ViewBehavior getViewBehavior() {
+        return mViewBehavior;
+    }
+
     // Called from native code.
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private void addMotionRange(int axis, int source,
@@ -1130,6 +1167,11 @@
         mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz, resolution));
     }
 
+    // Called from native code.
+    private void setShouldSmoothScroll(boolean shouldSmoothScroll) {
+        mViewBehavior.mShouldSmoothScroll = shouldSmoothScroll;
+    }
+
     /**
      * Returns the Bluetooth address of this input device, if known.
      *
@@ -1447,6 +1489,82 @@
         }
     }
 
+    /**
+     * Provides information on how views processing {@link MotionEvent}s generated by this input
+     * device should respond to the events. Use {@link InputManager#getInputDeviceViewBehavior(int)}
+     * to get an instance of the view behavior for an input device.
+     *
+     * <p>See an example below how a {@link View} can use this class to determine and apply the
+     * scrolling behavior for a generic {@link MotionEvent}.
+     *
+     * <pre>{@code
+     *     public boolean onGenericMotionEvent(MotionEvent event) {
+     *         InputManager manager = context.getSystemService(InputManager.class);
+     *         ViewBehavior viewBehavior = manager.getInputDeviceViewBehavior(event.getDeviceId());
+     *         // Assume a helper function that tells us which axis to use for scrolling purpose.
+     *         int axis = getScrollAxisForGenericMotionEvent(event);
+     *         int source = event.getSource();
+     *
+     *         boolean shouldSmoothScroll =
+     *                 viewBehavior != null && viewBehavior.shouldSmoothScroll(axis, source);
+     *         // Proceed to running the scrolling logic...
+     *     }
+     * }</pre>
+     *
+     * @see InputManager#getInputDeviceViewBehavior(int)
+     */
+    @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
+    public static final class ViewBehavior {
+        private static final boolean DEFAULT_SHOULD_SMOOTH_SCROLL = false;
+
+        private final InputDevice mInputDevice;
+
+        // TODO(b/246946631): implement support for InputDevices to adjust this configuration
+        // by axis and source. When implemented, the axis/source specific config will take
+        // precedence over this global config.
+        /** A global smooth scroll configuration applying to all motion axis and input source. */
+        private boolean mShouldSmoothScroll = DEFAULT_SHOULD_SMOOTH_SCROLL;
+
+        /** @hide */
+        public ViewBehavior(@NonNull InputDevice inputDevice) {
+            mInputDevice = inputDevice;
+        }
+
+        /**
+         * Returns whether a view should smooth scroll when scrolling due to a {@link MotionEvent}
+         * generated by the input device.
+         *
+         * <p>Smooth scroll in this case refers to a scroll that animates the transition between
+         * the starting and ending positions of the scroll. When this method returns {@code true},
+         * views should try to animate a scroll generated by this device at the given axis and with
+         * the given source to produce a good scroll user experience. If this method returns
+         * {@code false}, animating scrolls is not necessary.
+         *
+         * <p>If the input device does not have a {@link MotionRange} with the provided axis and
+         * source, this method returns {@code false}.
+         *
+         * @param axis the {@link MotionEvent} axis whose value is used to get the scroll extent.
+         * @param source the {link InputDevice} source from which the {@link MotionEvent} that
+         *      triggers the scroll came.
+         * @return {@code true} if smooth scrolling should be used for the scroll, or {@code false}
+         *      if smooth scrolling is not necessary, or if the provided axis and source combination
+         *      is not available for the input device.
+         */
+        @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
+        public boolean shouldSmoothScroll(int axis, int source) {
+            // Note: although we currently do not use axis and source in computing the return value,
+            // we will keep the API params to avoid further public API changes when we start
+            // supporting axis/source configuration. Also, having these params lets OEMs provide
+            // their custom implementation of the API that depends on axis and source.
+
+            // TODO(b/246946631): speed up computation using caching of results.
+            if (mInputDevice.getMotionRange(axis, source) == null) {
+                return false;
+            }
+            return mShouldSmoothScroll;
+        }
+    }
+
     @Override
     public void writeToParcel(Parcel out, int flags) {
         mKeyCharacterMap.writeToParcel(out, flags);
@@ -1484,6 +1602,8 @@
             out.writeFloat(range.mFuzz);
             out.writeFloat(range.mResolution);
         }
+
+        out.writeBoolean(mViewBehavior.mShouldSmoothScroll);
     }
 
     @Override
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 163dfa2..021bbf7 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -16,7 +16,6 @@
 
 package android.view;
 
-import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -197,7 +196,6 @@
     private Canvas mCanvas;
     private int mSaveCount;
 
-    @FloatRange(from = 0.0) float mFrameRate;
     @Surface.FrameRateCompatibility int mFrameRateCompatibility;
 
     private final Object[] mNativeWindowLock = new Object[0];
@@ -473,13 +471,13 @@
             mLayer.setSurfaceTexture(mSurface);
             mSurface.setDefaultBufferSize(getWidth(), getHeight());
             mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
-            if (Flags.toolkitSetFrameRate()) {
+            if (Flags.toolkitSetFrameRateReadOnly()) {
                 mSurface.setOnSetFrameRateListener(
                         (surfaceTexture, frameRate, compatibility, strategy) -> {
                             if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                                 Trace.instant(Trace.TRACE_TAG_VIEW, "setFrameRate: " + frameRate);
                             }
-                            mFrameRate = frameRate;
+                            setRequestedFrameRate(frameRate);
                             mFrameRateCompatibility = compatibility;
                         }, mAttachInfo.mHandler);
             }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c27b2b1..6534354 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -26,6 +26,7 @@
 import static android.view.InputDevice.SOURCE_CLASS_NONE;
 import static android.view.InsetsSource.ID_IME;
 import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
 import static android.view.View.PFLAG_DRAW_ANIMATION;
 import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
@@ -1013,8 +1014,10 @@
     // Used to check if there were any view invalidations in
     // the previous time frame (FRAME_RATE_IDLENESS_REEVALUATE_TIME).
     private boolean mHasInvalidation = false;
-    // Used to check if it is in the touch boosting period.
+    // Used to check if it is in the frame rate boosting period.
     private boolean mIsFrameRateBoosting = false;
+    // Used to check if it is in touch boosting period.
+    private boolean mIsTouchBoosting = false;
     // Used to check if there is a message in the message queue
     // for idleness handling.
     private boolean mHasIdledMessage = false;
@@ -1024,6 +1027,9 @@
     private static final int FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS = 500;
     // time for revaluating the idle status before lowering the frame rate.
     private static final int FRAME_RATE_IDLENESS_REEVALUATE_TIME = 500;
+    // time for evaluating the interval between current time and
+    // the time when frame rate was set previously.
+    private static final int FRAME_RATE_SETTING_REEVALUATE_TIME = 100;
 
     /*
      * the variables below are used to determine whther a dVRR feature should be enabled
@@ -4080,7 +4086,6 @@
         setPreferredFrameRate(mPreferredFrameRate);
         setPreferredFrameRateCategory(mPreferredFrameRateCategory);
         mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
-        mPreferredFrameRate = 0;
     }
 
     private void createSyncIfNeeded() {
@@ -6134,6 +6139,7 @@
     private static final int MSG_TOUCH_BOOST_TIMEOUT = 39;
     private static final int MSG_CHECK_INVALIDATION_IDLE = 40;
     private static final int MSG_REFRESH_POINTER_ICON = 41;
+    private static final int MSG_FRAME_RATE_SETTING = 42;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -6445,11 +6451,12 @@
                      * Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME).
                      */
                     mIsFrameRateBoosting = false;
+                    mIsTouchBoosting = false;
                     setPreferredFrameRateCategory(Math.max(mPreferredFrameRateCategory,
                             mLastPreferredFrameRateCategory));
                     break;
                 case MSG_CHECK_INVALIDATION_IDLE:
-                    if (!mHasInvalidation && !mIsFrameRateBoosting) {
+                    if (!mHasInvalidation && !mIsFrameRateBoosting && !mIsTouchBoosting) {
                         mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
                         setPreferredFrameRateCategory(mPreferredFrameRateCategory);
                         mHasIdledMessage = false;
@@ -6472,6 +6479,10 @@
                     }
                     updatePointerIcon(mPointerIconEvent);
                     break;
+                case MSG_FRAME_RATE_SETTING:
+                    mPreferredFrameRate = 0;
+                    setPreferredFrameRate(mPreferredFrameRate);
+                    break;
             }
         }
     }
@@ -7482,7 +7493,7 @@
             // For the variable refresh rate project
             if (handled && shouldTouchBoost(action, mWindowAttributes.type)) {
                 // set the frame rate to the maximum value.
-                mIsFrameRateBoosting = true;
+                mIsTouchBoosting = true;
                 setPreferredFrameRateCategory(mPreferredFrameRateCategory);
             }
             /**
@@ -7490,7 +7501,7 @@
              * MotionEvent.ACTION_CANCEL is detected.
              * Not using ACTION_MOVE to avoid checking and sending messages too frequently.
              */
-            if (mIsFrameRateBoosting && (action == MotionEvent.ACTION_UP
+            if (mIsTouchBoosting && (action == MotionEvent.ACTION_UP
                     || action == MotionEvent.ACTION_CANCEL)) {
                 mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
                 mHandler.sendEmptyMessageDelayed(MSG_TOUCH_BOOST_TIMEOUT,
@@ -12234,17 +12245,32 @@
             return;
         }
 
-        int frameRateCategory = mIsFrameRateBoosting || mInsetsAnimationRunning
-                ? FRAME_RATE_CATEGORY_HIGH : preferredFrameRateCategory;
+        int frameRateCategory = mIsTouchBoosting
+                ? FRAME_RATE_CATEGORY_HIGH_HINT : preferredFrameRateCategory;
+
+        // FRAME_RATE_CATEGORY_HIGH has a higher precedence than FRAME_RATE_CATEGORY_HIGH_HINT
+        // For now, FRAME_RATE_CATEGORY_HIGH_HINT is used for boosting with user interaction.
+        // FRAME_RATE_CATEGORY_HIGH is for boosting without user interaction
+        // (e.g., Window Initialization).
+        if (mIsFrameRateBoosting || mInsetsAnimationRunning) {
+            frameRateCategory = FRAME_RATE_CATEGORY_HIGH;
+        }
 
         try {
             if (mLastPreferredFrameRateCategory != frameRateCategory) {
+                if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                    Trace.traceBegin(
+                            Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRateCategory "
+                                + frameRateCategory);
+                }
                 mFrameRateTransaction.setFrameRateCategory(mSurfaceControl,
                         frameRateCategory, false).applyAsyncUnsafe();
                 mLastPreferredFrameRateCategory = frameRateCategory;
             }
         } catch (Exception e) {
             Log.e(mTag, "Unable to set frame rate category", e);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
         }
 
         if (mPreferredFrameRateCategory != FRAME_RATE_CATEGORY_NO_PREFERENCE && !mHasIdledMessage) {
@@ -12263,12 +12289,19 @@
 
         try {
             if (mLastPreferredFrameRate != preferredFrameRate) {
+                if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                    Trace.traceBegin(
+                            Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRate "
+                                + preferredFrameRate);
+                }
                 mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate,
                     Surface.FRAME_RATE_COMPATIBILITY_DEFAULT).applyAsyncUnsafe();
                 mLastPreferredFrameRate = preferredFrameRate;
             }
         } catch (Exception e) {
             Log.e(mTag, "Unable to set frame rate", e);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
         }
     }
 
@@ -12285,7 +12318,7 @@
 
     private boolean shouldSetFrameRate() {
         // use toolkitSetFrameRate flag to gate the change
-        return mPreferredFrameRate > 0 && sToolkitSetFrameRateReadOnlyFlagValue;
+        return sToolkitSetFrameRateReadOnlyFlagValue;
     }
 
     private boolean shouldTouchBoost(int motionEventAction, int windowType) {
@@ -12336,6 +12369,9 @@
         }
 
         mHasInvalidation = true;
+        mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
+        mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
+                FRAME_RATE_SETTING_REEVALUATE_TIME);
     }
 
     /**
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 1fdd1a5..f54ef38 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -425,12 +425,12 @@
     int mMotionViewNewTop;
 
     /**
-     * The X value associated with the the down motion event
+     * The X value associated with the down motion event
      */
     int mMotionX;
 
     /**
-     * The Y value associated with the the down motion event
+     * The Y value associated with the down motion event
      */
     @UnsupportedAppUsage
     int mMotionY;
@@ -7381,7 +7381,7 @@
 
             scrap.dispatchStartTemporaryDetach();
 
-            // The the accessibility state of the view may change while temporary
+            // the accessibility state of the view may change while temporary
             // detached and we do not allow detached views to fire accessibility
             // events. So we are announcing that the subtree changed giving a chance
             // to clients holding on to a view in this subtree to refresh it.
@@ -7750,7 +7750,7 @@
     }
 
     /**
-     * Abstract positon scroller used to handle smooth scrolling.
+     * Abstract position scroller used to handle smooth scrolling.
      */
     static abstract class AbsPositionScroller {
         public abstract void start(int position);
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index 76e97ad..3b7e1e9 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -170,7 +170,7 @@
      * @see android.view.View#measure(int, int)
      *
      * Figure out the dimensions of this Spinner. The width comes from
-     * the widthMeasureSpec as Spinnners can't have their width set to
+     * the widthMeasureSpec as Spinners can't have their width set to
      * UNSPECIFIED. The height is based on the height of the selected item
      * plus padding.
      */
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 1de77f6..82067de 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -66,3 +66,14 @@
   bug: "314952133"
   is_fixed_read_only: true
 }
+
+flag {
+  name: "app_compat_refactoring"
+  namespace: "large_screen_experiences_app_compat"
+  description: "Whether the changes about app compat refactoring are enabled./n"
+                 "The goal is to simplify code readability unblocking the implementation of /n"
+                 "app compat feature like reachability, animations and others related to/n"
+                 "freeform windowing mode."
+  bug: "309593314"
+  is_fixed_read_only: true
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index f234637..14fb17c 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -84,6 +84,14 @@
 }
 
 flag {
+  name: "delegate_unhandled_drags"
+  namespace: "multitasking"
+  description: "Enables delegating unhandled drags to SystemUI"
+  bug: "320797628"
+  is_fixed_read_only: true
+}
+
+flag {
   name: "insets_decoupled_configuration"
   namespace: "windowing_frontend"
   description: "Configuration decoupled from insets"
diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java
index 627e877..e591327 100644
--- a/core/java/com/android/internal/widget/LockSettingsInternal.java
+++ b/core/java/com/android/internal/widget/LockSettingsInternal.java
@@ -171,11 +171,11 @@
      * Register a LockSettingsStateListener
      * @param listener The listener to be registered
      */
-    public abstract void registerLockSettingsStateListener(ILockSettingsStateListener listener);
+    public abstract void registerLockSettingsStateListener(LockSettingsStateListener listener);
 
     /**
      * Unregister a LockSettingsStateListener
      * @param listener The listener to be unregistered
      */
-    public abstract void unregisterLockSettingsStateListener(ILockSettingsStateListener listener);
+    public abstract void unregisterLockSettingsStateListener(LockSettingsStateListener listener);
 }
diff --git a/core/java/com/android/internal/widget/ILockSettingsStateListener.aidl b/core/java/com/android/internal/widget/LockSettingsStateListener.java
similarity index 91%
rename from core/java/com/android/internal/widget/ILockSettingsStateListener.aidl
rename to core/java/com/android/internal/widget/LockSettingsStateListener.java
index 25e3003..869e676 100644
--- a/core/java/com/android/internal/widget/ILockSettingsStateListener.aidl
+++ b/core/java/com/android/internal/widget/LockSettingsStateListener.java
@@ -5,7 +5,7 @@
  * 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
+ *      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,
@@ -21,7 +21,7 @@
  * state of primary authentication (i.e. PIN/pattern/password).
  * @hide
  */
-oneway interface ILockSettingsStateListener {
+public interface LockSettingsStateListener {
     /**
      * Defines behavior in response to a successful authentication
      * @param userId The user Id for the requested authentication
@@ -33,4 +33,4 @@
      * @param userId The user Id for the requested authentication
      */
     void onAuthenticationFailed(int userId);
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/OWNERS b/core/java/com/android/internal/widget/remotecompose/OWNERS
new file mode 100644
index 0000000..61724ec
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/OWNERS
@@ -0,0 +1,6 @@
+nicolasroard@google.com
+hoford@google.com
+jnichol@google.com
+sihua@google.com
+sunnygoyal@google.com
+oscarad@google.com
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 239c626..aae0da9 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -14,17 +14,16 @@
  * limitations under the License.
  */
 
-#include <input/Input.h>
+#include "android_view_InputDevice.h"
 
 #include <android_runtime/AndroidRuntime.h>
+#include <com_android_input_flags.h>
+#include <input/Input.h>
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
-
 #include <nativehelper/ScopedLocalRef.h>
 
-#include "android_view_InputDevice.h"
 #include "android_view_KeyCharacterMap.h"
-
 #include "core_jni_helpers.h"
 
 namespace android {
@@ -34,6 +33,7 @@
 
     jmethodID ctor;
     jmethodID addMotionRange;
+    jmethodID setShouldSmoothScroll;
 } gInputDeviceClassInfo;
 
 jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& deviceInfo) {
@@ -103,6 +103,18 @@
         }
     }
 
+    if (com::android::input::flags::input_device_view_behavior_api()) {
+        const InputDeviceViewBehavior& viewBehavior = deviceInfo.getViewBehavior();
+        std::optional<bool> defaultSmoothScroll = viewBehavior.shouldSmoothScroll;
+        if (defaultSmoothScroll.has_value()) {
+            env->CallVoidMethod(inputDeviceObj.get(), gInputDeviceClassInfo.setShouldSmoothScroll,
+                                *defaultSmoothScroll);
+            if (env->ExceptionCheck()) {
+                return NULL;
+            }
+        }
+    }
+
     return env->NewLocalRef(inputDeviceObj.get());
 }
 
@@ -118,6 +130,8 @@
 
     gInputDeviceClassInfo.addMotionRange =
             GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "addMotionRange", "(IIFFFFF)V");
+    gInputDeviceClassInfo.setShouldSmoothScroll =
+            GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "setShouldSmoothScroll", "(Z)V");
     return 0;
 }
 
diff --git a/core/res/assets/geoid_height_map/README.md b/core/res/assets/geoid_height_map/README.md
deleted file mode 100644
index 849d32e..0000000
--- a/core/res/assets/geoid_height_map/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-These binary protos are generated at runtime from the text protos in ../../geoid_height_map_assets
-and using aprotoc.
\ No newline at end of file
diff --git a/core/res/assets/geoid_map/README.md b/core/res/assets/geoid_map/README.md
new file mode 100644
index 0000000..5d480c1
--- /dev/null
+++ b/core/res/assets/geoid_map/README.md
@@ -0,0 +1,2 @@
+These binary protos are generated at runtime from the text protos in ../../geoid_map_assets and
+using aprotoc.
\ No newline at end of file
diff --git a/core/res/assets/geoid_map/expiration-distance-disk-tile-1.pb b/core/res/assets/geoid_map/expiration-distance-disk-tile-1.pb
new file mode 100644
index 0000000..c2ec5d9
--- /dev/null
+++ b/core/res/assets/geoid_map/expiration-distance-disk-tile-1.pb
Binary files differ
diff --git a/core/res/assets/geoid_map/expiration-distance-disk-tile-3.pb b/core/res/assets/geoid_map/expiration-distance-disk-tile-3.pb
new file mode 100644
index 0000000..e4b3b0c
--- /dev/null
+++ b/core/res/assets/geoid_map/expiration-distance-disk-tile-3.pb
Binary files differ
diff --git a/core/res/assets/geoid_map/expiration-distance-disk-tile-5.pb b/core/res/assets/geoid_map/expiration-distance-disk-tile-5.pb
new file mode 100644
index 0000000..95f54c2
--- /dev/null
+++ b/core/res/assets/geoid_map/expiration-distance-disk-tile-5.pb
Binary files differ
diff --git a/core/res/assets/geoid_map/expiration-distance-disk-tile-7.pb b/core/res/assets/geoid_map/expiration-distance-disk-tile-7.pb
new file mode 100644
index 0000000..896bbc1
--- /dev/null
+++ b/core/res/assets/geoid_map/expiration-distance-disk-tile-7.pb
Binary files differ
diff --git a/core/res/assets/geoid_map/expiration-distance-disk-tile-9.pb b/core/res/assets/geoid_map/expiration-distance-disk-tile-9.pb
new file mode 100644
index 0000000..ddc687d
--- /dev/null
+++ b/core/res/assets/geoid_map/expiration-distance-disk-tile-9.pb
Binary files differ
diff --git a/core/res/assets/geoid_map/expiration-distance-disk-tile-b.pb b/core/res/assets/geoid_map/expiration-distance-disk-tile-b.pb
new file mode 100644
index 0000000..eb405f6
--- /dev/null
+++ b/core/res/assets/geoid_map/expiration-distance-disk-tile-b.pb
Binary files differ
diff --git a/core/res/assets/geoid_map/expiration-distance-params.pb b/core/res/assets/geoid_map/expiration-distance-params.pb
new file mode 100644
index 0000000..e160120
--- /dev/null
+++ b/core/res/assets/geoid_map/expiration-distance-params.pb
Binary files differ
diff --git a/core/res/assets/geoid_height_map/tile-1.pb b/core/res/assets/geoid_map/geoid-height-disk-tile-1.pb
similarity index 100%
rename from core/res/assets/geoid_height_map/tile-1.pb
rename to core/res/assets/geoid_map/geoid-height-disk-tile-1.pb
Binary files differ
diff --git a/core/res/assets/geoid_height_map/tile-3.pb b/core/res/assets/geoid_map/geoid-height-disk-tile-3.pb
similarity index 100%
rename from core/res/assets/geoid_height_map/tile-3.pb
rename to core/res/assets/geoid_map/geoid-height-disk-tile-3.pb
Binary files differ
diff --git a/core/res/assets/geoid_height_map/tile-5.pb b/core/res/assets/geoid_map/geoid-height-disk-tile-5.pb
similarity index 100%
rename from core/res/assets/geoid_height_map/tile-5.pb
rename to core/res/assets/geoid_map/geoid-height-disk-tile-5.pb
Binary files differ
diff --git a/core/res/assets/geoid_height_map/tile-7.pb b/core/res/assets/geoid_map/geoid-height-disk-tile-7.pb
similarity index 100%
rename from core/res/assets/geoid_height_map/tile-7.pb
rename to core/res/assets/geoid_map/geoid-height-disk-tile-7.pb
Binary files differ
diff --git a/core/res/assets/geoid_height_map/tile-9.pb b/core/res/assets/geoid_map/geoid-height-disk-tile-9.pb
similarity index 100%
rename from core/res/assets/geoid_height_map/tile-9.pb
rename to core/res/assets/geoid_map/geoid-height-disk-tile-9.pb
Binary files differ
diff --git a/core/res/assets/geoid_height_map/tile-b.pb b/core/res/assets/geoid_map/geoid-height-disk-tile-b.pb
similarity index 100%
rename from core/res/assets/geoid_height_map/tile-b.pb
rename to core/res/assets/geoid_map/geoid-height-disk-tile-b.pb
Binary files differ
diff --git a/core/res/assets/geoid_height_map/map-params.pb b/core/res/assets/geoid_map/geoid-height-params.pb
similarity index 100%
rename from core/res/assets/geoid_height_map/map-params.pb
rename to core/res/assets/geoid_map/geoid-height-params.pb
Binary files differ
diff --git a/core/res/geoid_height_map_assets/README.md b/core/res/geoid_map_assets/README.md
similarity index 100%
rename from core/res/geoid_height_map_assets/README.md
rename to core/res/geoid_map_assets/README.md
diff --git a/core/res/geoid_map_assets/expiration-distance-disk-tile-1.textpb b/core/res/geoid_map_assets/expiration-distance-disk-tile-1.textpb
new file mode 100644
index 0000000..a4170e6
--- /dev/null
+++ b/core/res/geoid_map_assets/expiration-distance-disk-tile-1.textpb
@@ -0,0 +1,2 @@
+tile_key: "1"
+byte_png: "\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\000@\000\000\000@\b\000\000\000\000\217\002.\002\000\000\v\361IDATx^\035WYo\034Ir\256\256\252\274\357\314:\262\316\276\233\315S$E\211\244\244\341H\262$\256V\243\3359W\322\314\216\027Z\f\026\006\326\360\313b\001\277\032~1\f\333\257\376\t\376\233\216\232\006\371\322\310\214\216\214\370\342\373\276H^~\367\037\377\365\343\325\361\320\307\252\352\207\276\237o\327]w~zr~\262\026Xe\271\t\224*\257\352\3233\253\257\317\317W\265\337\305e\325\354\272X9k\271L\256_\376\353\377~z\264\031\372\246\254\272E\333\265\355vX\274y\376\360\226s\225\347i\232et?r\226#\032\312\217\377p\273)|\027\027\355\3204}\033\242\310E\322\275\372\374\267g\347\273\305\242\201\200E\263\034\347\235v\217\317\037.\373\276\330\342\024\"\220\343\315|\254,\301t}\376\207C\347\\\025\302f\fq\331\264\002\311$^\235\277\177|u\260j\272\262\366E]\327\373m=\177y\276\"\234 \256\226.\307\354v=\216\273\001\347DJiL\251\255\032\206\255\026m\355\030\226\311\345\343\375\301uU\314\245d\314\225\363\272\033\026\266\352{.p\236\271j\377\305\273\273\273\367\317\317v\333M\236s\257\363\034\033]\354[c\004\215\215\"9M\212b\277\252\215\366%Ms\241\275[\217\205=\350\021\211\224z\234\243\347w\217\317\316\036\270\233\'\373\363\262\222\230\"\312\225\365\255\022\234\370\261T\210&\262\210ma\031!\2311U\260\306\327\205\320\273\262\334P\302Q\216\324\227\257ONN\355\356\365\333\037\357;iJ\2458\227\\\325N\223\320\365\216\242\204\037VJ2\244T\325\325c\335-J\277h\254\332=;\333s\212\020!\264\177\365\366\313\353G_\276\376\366FR*\030\306\224b\214KK\nhD\215\022RW\222Jf\v\353\252]U\257v\273\335v.\352\373o\276~F\363\034!\210\342N\256\037=\277\270Xs\316\021\245\324\300W\310\030B\353\266\245y\302\245\307H.\252J\n3T\325\260^\257\016O\255n?\377r9\245\200q\236\273\3475\272|x\244\324\004\251\002\242d\024\v\217\3110o\263<!\232\321~uRE@\201\017\313\305f\263Z\227\326\276\372\373\377\374\346\351r\036%\244\301\337\f\362\346\341R(\316\nk\332\016\nF\030\000t\230w9K\000\037\230-\240\227\'CQ\270\242k\206\361h\241\324\307\377\373\317\237\337|\361t_j\226\323\313\363w\357\026\255n\2056\206\005G1\200CJ\347\232\np \210a\f\253\027w\267\217\226M\b}\323l\326\313\260\375\360O\277|\377\273\267O\216N\217;D\212\352\315\317sc\234\320\256\f\212\301\313x\200\000\b\033B \000\315YF]\374\355\267\227\243\325\306\205aQ\371\360\325\307\367/\256\276\370\342v3\366\232\241\374\372\273^k\313\353UQ*\306\b\346^J\236cII\022\004C8KS\230\226\243\273y\f\316\373\022\336\362\325\217\367O\316\326C\327-\326\253R\220\366\266S\232[\331\350\252l\265\244\\HB\255\343B%\326\230\250R\n\030#\b\337n\327\313U\277,\030\343\337\274{\275\335U]\327\036,\312\202\242S\350\222\206\264\213X;\3079F\n\263Fc\302\222\312\2320\r\335\315\315WU\375\351d\273\202\000}d\354\247\177\377\343\315~5^\235mG\2559UU\201\275Q\316\311\340\205\224yn9a\022\347i\262\232j\230e\352\305\375\037\276\377\360\303\3610\214\355\342\240\256\207\357\376\355\277_\234_\337=~r\fE\200\251\320V;\003\237\322a\")\312\t\003@`F\222BC\000\024\353\243W\177\375\376\307\'\253\266\353\233\345B\252\323g\227\177\377\370\364\370\260\276zy\266\\\356e\216\204\016ee]\343\001\2370w\204\331\n0\226%\232\023\301\371\372\240\264\277\375\353\037\317\226]\273\030\227\203\224\2671\036\331\agg\333\353O\357.\317\317\216\f\343&l\233PuNA\301`\256\265s\204\247i\242\005Q\212\001\312\252\235x\362hy8\366m\003Q\236\235X\234\271p\377\315\317\037\177\370\352\351\305\221\221\210\273v4f\t\224D\020t\016\273\300h\n\031(\350N\016\300\242\356\360\351\243\343]?\f\233\332\356\277y\342Lk\364\376\376\273\037~\372\363\207\017\227\000x\331\372B\312\256*\203\311Q\216I\027\020\274#\341\212k\231+\026\335\321\347_\316\317\017\026\243\2130K\217\237/\004\242\230l\337\376\360\347\337\377\356\356\346\264f\332\020m\255\365Ji\201S\344\"B\314W\211\022\006Per67\027\257n6c\315\3750D\021\027\341\031eK\202\312\373\237_==\336\f\212rBli\005gB(\234f\250@\244\036\253$X)\006S d\271zv\270\352-\005}\210\315\260\360\',\000\027\347\375O\257\237\356\227\205\207\031\240\2057BcLr`\373\214\bQ\214CR\303\273\204\26087kwz8\326\3327\274!j\275\334_\\?\210\221g\370\371\277\274\277\3314^B\000\243\225\344\224\300m\v\275\307\322\037\306\244,\225T\312\020\276\235o\340~\251t\031\243c\276\256\216\257_\275X\223<\257\257\276\275\177\030\f\a|s8\313\215\247\316\002\236\201\360\344|\225h\v\365Uy\206\351|\263\265]\3356M\354\326>\324E\r\2742\027@J2\336\003-0-!\202p\241A\310\353\236\003;2\336m\022ea<s\224ehy\260[\217UW5\303r\345\233\373\233\365`UW\032\t\334\265P@xAs\030(UD\240\264\302\243,\205b\2641a\224\021\224Swp\262Z\265Pc.\250\250\273z\371\351\353\177\376\374\217\237o\035\345p\016\006\330h#\245\226\302\215pW\351\036\000\r\331\371\022\002PH`:B!\213,\233\002`&n\277\376\345\303\357\377\364\351\315\233\301\v`q\216\271\001\366\347J\355;\21105!\317\250\226\210T\t\002zq\276m\v\a\332\006\001r\370c\f\b\357\376\371\335\207\277\275\177s$\303\031\312\004\234\221\202\251r\034Z#2n\f(\237\362\004\253\004M}\005\232b\224P_\272\240\200\236r\220\016\346\317\216\037\335\\\375\345\323\361\303\337\344)\343\032\002pS\017CiU\232\351<C9\267\001\243D\224\205\026\206K\355\005k+]\233)\006Dc\246l\226\'Ww\037\236\235\035\027\0316\023\004\204T^k]\344\251\230\332\306\214\205\000\276\357u,CQxyr~tq\321UUY(\023\255\244\312r\036\016\017j\306L\312\314\257\252\b\315\320a\250\2630=X\206\322\362d\021\035\020\245\v\222\210\376d\267]\305xryr\2727\030\224\025\230\a\2031\341\302f)e\034\304\031h\251\177\262h\025\241\360h\306}\341\023\005<Y\026\336{1?\326\240\032\321\025\017\256\326\f\223\246\206\252\023\002\322\206\244\225\030[\a\327\234/\264\367]e%\313\031(\302\302\'@T\021<\213Sj\354\r\364\220\270\366\270\202\206:)\232\346\242\001\n\205\237\016s\201\tUF\031\b`l\347z\r\336\201a\352}R\324\261\264zRmi\254\323\032$\234\242\f\340k\325\341\246\357\275o\036\200\376P2M\214\323!\024\306\354\306jY\021\202\322\224)\233\204\222\302\\\201\356V6\314\227\313\000\272\207\177\305\277sc\177pX\201_hAL\t\b\216\205Y\016U\335\306\020j\270\003\307\250\220@i\224 \230\275\252j\232!\004\353\n\016=\243f\330-\373.\356\367c\250|\\\fu\214!\026N\311\020\274\325\241 D\020`$\316 \300\204P@\272\203\361+\003\0302\231;\307t\327\035=]tm\035\373\"\366\273\323\355\252\033\326\261\356\242\322p\243\360\226L\211\303x&`B0\017\261\254\212\002\322h\332\345\240\362**\277\a\273V\v \301f\230\257\267\333\261,\227\307\203\265Q\262\322\201v\226\r$\316\255\222\tB\200\242\276.\n\253\224\367\263\031\352\025\251[\356\273q\230\357\025\030\002kO\327\213yl\332\341\374\240.\332\2713UU\270\261\342\031\210\023\322\t\341\215\373\265@Pd/\263Y\006\001\000tE7\276\275:X\037\201+\260\363\323\226\272P\327M,\233E\337\2128\200D\367,\233l\006\001\233W\200\244S\351\255\247U\226fxs\004-\221\252x\360\362\305\227 \254\240\030E\327\022k\353\246\261\266\005\301\232\017]]\373\025\320\020\206\366\'\n\222\a\315\005(r\3329\273Z_J*\240\030\253f\274\273*K\306J\003\364&Z0p0\250b\263:\214q\\T\332\026$+{\312\223\326;\037@\256\234\t\213\365|9\237_p*c\324u3\202?(\313\342`\tz\016\361\243\3078#\355a\023\233n\327T5\340\260\251\021N`<\240\002\360q\325f\034\227\3077\003\307Z\201g+\333\"n\273\262\b\001\b\325KW\031msjw24GkW\004\230\324&\315I\002\371\033\353\'s\324\235\354\346\353\265\303\036\340\021\240\253\315<\266uo\200\'\v\3707m\031F-MW\024m\333\307\326\001\364iJp\342\nm\001\3406T\363#\350\367\246A9 \3054\020\266o+\230\327((\210\231\\\215\253\312\205\t\207e_\307C\017_\211,c`\266\341\370\364\000\200FW\223f\025\201\243g\251lm\230Z\v\302Y\273\336\272ry\321\3250\036\301\273X\265 \333\225r\255\206\005\202\'\240k\022\350Jy\230\220\272\356\373\276Ig\263Y\306&\036\000/\006\303\\m\316\206\330\\,\232X\204\241/\344<\364\026s\006b\006\236\217\'\002\370\006\234\2323\316\227\343|9\324M\232\242,\237\364s\n\000\203\"T7V\256l\343\256\f][J_\0160\377 G,\002\235&R\201W\361Jh\355\2624\036\rCU\v\020#\006\002\nH\003s\016Dh\333\302\000Zb\333\004 K1\275-\a\006\200{Z\'\030\224\325Z L\256\263\324\271\266Wt\006\213\226I\'\225\311\375D\370\004\244E\256\226e\321F\340BHO\030\224\247\324.\n+mBA\260\004\237\350\306\000\221I\v\030\303<\313R\002{\005X\224T\t\006\313\037\202\031t\214)\224O\231\375\332\001\002\364\315\031O\200\235\341G\024\224Q\346`\031\246ta\337B\263\351 \254X \273\323\035\316 PJ\300\241\202\'\245B\030\223\206iR\201\017&\021\202{\234Y\0209PZx\266\244\031\232a\340c\f\244CA\375a\371,5\312f\222\344\320\241Y*`G@\\O\223j\022\006\v\"T\032\312\n\002\2073\304\b\2255\005\357\312*\247\240Z\006\030\276\346\024{\221\315R\232\315f\371,\231eS\247\241w\300N\t\345\034\004\036\274#\370\357<\317\230\201j\002e\202t\201;e\006\222\2438\202|\002\315\247\263\3515\f\315\222d\006/\341\240\367@\252\223\302Lp\221\300\274\210p\300%`\317Z\006\313\200\001#\bN\217\360\250\263\334\341i\a\313&\271\302\2239O\341q\23239\001\t~E\022\246\b\330\377XX\320/\251\'\242\304\320G:\351\axs\020\032\260e\be\b\002z\230\2114\005\356\206\276\210dB\233\202E\024\224O\214\233\276\204\212h\312 \327\251\\\031\355\300u\n\232M\276\016\201\'\310\t&\260n\321\276\201&C\3219\375\177\264\213Dt\003\247\373\213\000\000\000\000IEND\256B`\202"
diff --git a/core/res/geoid_map_assets/expiration-distance-disk-tile-3.textpb b/core/res/geoid_map_assets/expiration-distance-disk-tile-3.textpb
new file mode 100644
index 0000000..684f848
--- /dev/null
+++ b/core/res/geoid_map_assets/expiration-distance-disk-tile-3.textpb
@@ -0,0 +1,2 @@
+tile_key: "3"
+byte_png: "\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\000@\000\000\000@\b\000\000\000\000\217\002.\002\000\000\nKIDATx^-\227\351r\334\326\222\204\321\000\316\276c\355\225-\262%Q\242e[\226=\023s\035w\376\315<\302\274\377\253\314W\224\021\022\311\350\006\352\324\222\231\225\350.9\2720\244\266?=\335?\377\366\353\257\277?\266\323\375\345\345\345~:\356\333\365r\332\267m\235\226u\331\266\375|\272<=\276\375\370\365\227\267\177\377\353\307\375\323\307O\331\253\261\253\245\346\320\353\232\333\343\345\333\357\337~\273\177\270\235\326\333\313\371t.q;\235\217+\327<M\353\266\357\307\343\361\345\323\217\377\370\376\371\351\327\307\365|\334\2753\312w\271\315%\253\301\304\370\366\343\317\317\237\277_\256\267\333:=_\326\375\030\333:\267y\221\213\237\004\270^_\036\277\377\361\\S\276\234\317k\265\326\030\327\265\251\225\354\265\n\351\374\367\377\374\365\365\355\371\3065\307\351\264\315\307\224f\276n\004X\313r<\236\316\257\363Zk\2529W;\216\243\266\326\271\316\247\\r\364F\353\351\371\374\372\361\345z\275=\335\357)\305\343\266\257\363\334\262\344\277\254\373v\332\256)Y\233\3628h5\216\303h\254\367\316w6\3449\325\350\3061\247\355r\273]>\\n\347\263\317>\255\253D(\265\266\266\256\324\337\234\3621\0305*m\346Z\366\205\032\274\357|))M\261\270\321\370\363v\272]\256\227\215\303\213w\333:\257$_\033u\320Nr\f\3319\247\207u\231\225M\353\322\2345\241\313\311\232\312\025\2656e\335./\267\023\331\226Vb\\\346u\233\032\001\326m\273*\345\252q\24193<\355\307\311\306\326\30282\005\357\\\\j\245\rvp\247\323\351z\341\374\2472\325\342B\235\246\245\265i\t6\270\336\224`\326x\016z\240\2711\347l\006\002\204\316j}<m\323\266\2478\230\371r9S\355\266\022r\362\332\245:\255-*:fM\324\346\357\377{\373B\367\227\331\247Z\3658\322\022\335\031m\246\345\366t\2716o\2142\361|</\265\3442M\245j\343<\335\035c=?\331I\333\277\277\177\340v?\371@~\306f;\364#\001t.\3556=\036{p\301\030\263\357\205\004\351\3754\027\346\024\322\3246\372\374\232\036\371\3655Z\347\223\347\344\032|`\230c\350\264V\246\224f\355~j5\246`|H\2450\274y\231C\320\336O\241A\027\377\343\217\377\345\303hcM\216(6\020\\\006\323)\257\3241\323\320v\234\353\266V\372S\tX\005\200\233\323\241l\347k>U\333\376s\032M\n\256\272\030Rt6\371`m\bd\340\364x\334\303\3410\026\206>M-\346\024K\256\251\021o\266`z\177\334\336\356\326\236\325h\346\350L.\211,\271\311\333\030c\356\254r\332\335\250\3460f_\'\322\3659\223\202\0241Mn\252\347\327\267\327\327\257\317$\352\335\f\032\rh4\201\216S\r\207u\203R\306<\335N\266\357\025\370\246i\021v\200\254R\251C\307v}\373\366\366\375\323\251\030\337\246\b\370\225v\316\222\211\3211s\\\247\264\002\t`\vTYg|\"\257\230J\232B\3661\372\330\226/\277\277\375\327\027\357\366\375>Qs\320Z[\320\347\034E\004\237\273\301\214F\257\273Y\\?\250@^!\270\030K\230\005\315\204\234S\234\257\217\353\2162\355\313\334\254Q\0262\305\034  \371\344\256\'u5\327\251&\376\360<\021\234\v1E\232\220\321\t\355\266h\323\224R\233\270%\025\v\270`\215\241H\222`\344\235\006M\343\232i\317\330k9\222\001\323\005`$\200Dr\246\225qE\310\237\224BB\024\263\244\212\362s\214y\351fm\225:\241]K\033\r\375%j\200) \241\026\370n\034\277\350:\337(\351\367;\001h\2406i\"\325\\\273\241\357\315\250S\323\220[Ec8\337\225w(\002\206\310\304KA\200\220O-B\002\365\2143}O\336\203\314,\227N\037z\37024\327\254\032\231\016\205\t\t\247\372N\031[L*^\217<\3073\374\327c\337\037\270h]?\360q\352\3740h;\f\v\002\251\031\022\303\f&\320\261i\231Y\026\261\0017\243-\223\367\326jy\362\237\377\\\350A\257\272\017M\353\3630\304q\360\016\322E\215\276\2326K\027\3624\307HO\351\031\245 \177]w u\216>\034h\346\330K\244\356\313\307\264\337\206^\217>4\3211\023\36480\002`\224\352\342b\0024\3223cx\\)Z\357#\220\355G\272\"\271t\257\327\371\374\351Hm*5&\237\265\347k\221s0M\001\376]Gy\\\221\262@#\346\"Z\006x\224\264c\354N\267\260\277\035\3350\000\240\234J#\003=\242\270)\204\352\337ik\2048#i\243Ss\211.* \'\225\310\245\273u9\316\237n\254\306\242ucz\336\347F\200\t\244\227@\302\321\312\006\223\261\225\005\211.M\311\337#\317\313$\206\241k\3554\177\374\364z\254\227\251\262\267\002\210k\352tZr\244\243<\034\344y\305\371\242\321%\006\320e\373\303 \r\355G\272\320M\323\275}\371\353-\3733hd\346\256 \330\363\207OW\000\031\377\311\300\312\201!\325\":\226\253S\222|w\240\023\374*\365>}\375\353kV\363\372\256y\001\021\320\323\343\363S\220\376y\331\177\326\364`\021\365\251\311z\220H\003\016\335\241cE\252\330wFT\366\257\257\237t-,\342\311;Zo\362/_\276\325\020x\026|y\326\302\240\223,a>1\022\200\342\031 \264h\266\023\364\253\360\362\366\262-\313\004l\310\322\350\374#\337\317\305\000@\300\312\363\207\301jH\204\334CG\002\"+T?V\223\367\256\220\207\262\341\365\204\207@\"b&\200\232\252\247\215\034\346$\000\250\265Q\231,P\315,\037\253\347RPa\355\224u\335\354\371\251\307G\314GXD\241i\325\372\243\207\"\357\v\300\272L\253\006\266\210\003\002!Z\n\207\230E\213\020e\304\255\023\263\241.(\217GbR\016\271m\260T\353a\240m\310\247O\303\240\244\260\224\250/ \b#\372;\312\030uT\030\fF3\016\331\216i5L\276\324u\277\264&\232\253k\f41z\264\222A\220=\002\202\036\222\200\021.v\207p4\252\357\210\313\214\026b\t{Xh\373\222\263S\305\340B\002\024\026\2615\304\201&\"\305\026T\213+\350\207\256\257-\333C\027 \3400\224\204\334\343\251`\321\262\004$G8\335\320\2078\271\236\305\201\266@O-\v\234\347\025P\354\273a\237\202\004\000\360\330\275\261\037l\201\305\204\340.\f\324\250\346\211\246\346\t\236\3118\304\316\35018\005Yu\031u\327\251+\256Kw\322\027>\034\374\240\300\321\304\277\314\234\210\235J\264`gNj@\355\213\243\243`\006`\215Jd\264\353,\226rg\n\000\216\274\006H\252Y\206S\362\2633-r\222i\354\226TV\255*6*\240\346#c\025D\365\a\335\003\243\375t{\262\035\000\003\233 \230T\341+\367\221p\016\3163HKW\247gv\2213\301\266du\320\360J\t\262\244\342\355x\272\263\332\320&\245\262K\366p\300\017\261BD\363e\005q\247fIj\271b\322@0#MZ\224\350\320\365v\336\326\333\275\305\016\025\347\004\211;\036f\331\236\332j\251_\240/{\200x\243\032\003\304P5(\330\355\255d\240|\251,\327e\241\004.\347M\204\335\036c\240D\303!q\360\340\300\344\373\307\244\330}X\203\240\004\231\340\3219\304\210N\370\020\327-\201\003t\322\317\333\344!\035\033@\205\202S\202\273,\372:\227\307\243(\200\340\021we3V\212\304R\355\a\276/\367\313\355\342;\027B\331\237\177y\333\3430\034\000m\000\333\016j\037q\0232\265\201~\t\366\234/\016\253)\253\327\207~\264e===\177xD\234\252\257\307\307\237\'\3618\350u\336\321c\343\360\327%\tc:\331@4\b\347\360\3442\273\323\b?`\242\366\363\307/?~\334;\fcZ_\376\273\262+\304\221*[\353\202\016\002\261\237\227\260\206\030\270\002\334\021_\200G\223\230\002\366s\373\376\307\237\277\211\321\364\355\371\273\353\031\226/bj\323\374!\305\303\373\303\357^\206\241\217\006\2760?\353\212\370\v\314\037U\341\356\277\355\027\\\232\366\365\305\t\0270?Z\344\276\355N\236GR\347\272n\307$\253Y\226\001P\323\r\v\002,\2128Kp\025\r\001\214+Jt\033<)/\001R\2265\030\352\276c\025q\357\373)\023\263?\340\364U\364\342L0\000Y\020\005\273:\006\344\211F\000=\312\224\315a\320\224=\232\270my\332\2666\037\357\217|\274\264(\376F\211=\301@c\360\210 \306\216\000A\354\225\321=6\026\v\034\336\267\277\366\370\373\232\347#\372\222\004Y\3503\257\227\303\0302 \003\'\274Ol\200\267\312\033K\362\357\365\243\261\002kY\372h\336\273\203\350\255\000\341\247\027Aa\034\n\217\274\002\234\234dKji$\001j\337Ch#/n\2622\031\273\260E\026\027?{\001\001\257;\210){R\314N@\372\n\257\023\204 l\355bR\207^\001\016G;\3317\034\357\320;Z\237\262\037d\203\276\257!\222\250\220\201C!N\nXapI\320\326\201*\314\216A\326q[!\312\306~\277\177\320\310\t\344\025\025$1x\235\331?J\2746D\203\256y.\204s\330\274Q\017\302OV\315v\333\225\314Q\206\322u\222\r\241C2!?>P7\246\206V\330\214\367`\214\336\204\331*\333\211d\2767\3540,\274\t*\375\263\213\377x)\311\205\031\211K`M\263\0224\320\267\031?\305\213\036\236T\333nTRx\210\200l?6\270&f\346=\310\317AH\376\300\\d\231W \322\207P\354s\\\234\227\265\341\376\037O\340\276\251\345{,\253\000\000\000\000IEND\256B`\202"
diff --git a/core/res/geoid_map_assets/expiration-distance-disk-tile-5.textpb b/core/res/geoid_map_assets/expiration-distance-disk-tile-5.textpb
new file mode 100644
index 0000000..387bbdf
--- /dev/null
+++ b/core/res/geoid_map_assets/expiration-distance-disk-tile-5.textpb
@@ -0,0 +1,2 @@
+tile_key: "5"
+byte_png: "\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\000@\000\000\000@\b\000\000\000\000\217\002.\002\000\000\n\321IDATx^\035W\331n\034\327\025\354\351\345\356\373\332\353\364,$E\312\262eYN\340\004\b\022 H\200<\345)y\310\377\177H\352z$\200\304\260\373\334\263\324\251\252\333\r\303p\271\020:pa\274\325BP\252V\311\331\320_\372~\230(\031\206~\030\307\2110\356\202\025BI\251\230\225B2\374&\204\024\3350\210\313\3452NBh\217\317,\204O)Y\301\373\021\257\222\021/O#\002\311\024\360-^\221\332z\211s\264\261Bq\322!8R\350G%\215I5\347\325*_\222\346|\034h;}\232\2504\316\030c\255U\222s)\255w\0229R\'\270$}G\b\233\220\0023\306;\263\247T\274k\037\211T\351H(\245\023ERQ\031\'\244UJI\245\2037\222\021z0A\306\251\233\210\230\372\3762m\b\020pzL\336k\245\263\215\206M\212SJF\202s\225\261J \023\305\225O\316\021\374q\341B\022\004\020\222\242cC\310\321\aA\2716\305\332\020l\216\316K$L\31041:\216\375\200\323\245A\023|\b\224\2502\261\273\245S\337i\206\363Q\202m\265\033\212\b\331ZcB}\256\321%B\2317\255\363|\030\230t\355W\031\202\"$\032\356\320\335\313\245\313S\337\232(\275\224Q3J\031\367i\311!\326d]\n\204\350\2424\343\312![T\201\367\215\211\322G)\270\306{\227\241\323\343@.=\221\2221L\bEse\274\317\271.\347\313\227\212\202\244v&\205(\306Q\002\'R\271\020]\016x\234\221\276G\023\345\200aO\n\337p\f\211\v\005@\270RkJ\363\333Ok\314\316\343\273\270l\023\006\n\374h\035\262r\263\021\030\204\354\273\201v\300\3330L\321jF\bN\307\a/\204\\\312\272\034\333\222\225\322\202)\357+&:R\207\371X\340\360TZ+A.\335 \273\221+\264P0\3464\227\316\031\035$\362A\204\224\352\226}\233\274\213\245\324@\350\324\340jP\005\002\006\215\351t]\357;\f\211\231\3409_f\340\a\030\260\025C\262\321\307\230\213\327\022\370\325\353q_7dH8N\002$\262vR\0231\"@\355(\302\342M\316\317-\3703\006k\023\233\260)*\2259\205y\251\301\250\354k\3354E\217(a\322\370yc,\3232t\227\276t\350\277N\3006w\253s\325\270T\243\003\274\215\ni\306<\2637\016p\002\222\221\201\3268\316\355%z\306\3537\004\350I@\200q\342Q\243RlK\002\n\263\266\250\227\331\020\327T\003\n6\336x\355\"&\254\214\301>\334fi\343\017\347\251\373\256O\b0\364\224T\f\305\274\027\233\262\301\256\242\217\300.\323\347\271g\r\340\350\274\254\331\246H\031\023\202\a\357YY~x\276\242\005\223J\000\322\210\366z\246\3463\247\325\257{\260\0163\002N\364\272\037\017\311\265\326V\352\315\' wD\031W\245j\231\327\0053\030\274(\035M\321\003\"\000H\315\327\325\324Z\202\326\036\375\310\347-\227\031\033,\230\021z7\323\210\305\234\250\306d\347\307\327c\356\273\v\t\321wT\337%\266\336\316aG\325.\327b\271\325sT\363\313\236r\335@%&\273\307*\247)i\037\255;\364<\247\354Dw\031S\b\241\303\366e\n\214\316\031T\220\313\262Zp\225FS\000\337\353\016\fY4u\373t:\356\025\021:f\273\311\3031\n\f`/c\001\241p\251\251H\261\350\342\264{\0244\336\315\311`/\\.\332,\005\240\215\327\347uI\206b`N\345h%x\3402\201\000\347\243S#cm_\254\223\331\351\370\251l\321\206\224b\006\353Qn]\215\326\272uY\346e\251\025}\004\r\001j}+\000\200\366\241\033zD\305x\215\301\237r\315\t\345$P\t\bA\213h@\216&\256~\aKm\327\343~\r\306\201\033\030\210`T@\313\0000\r\323\202\247\202\307\256\203\254\021Er\254\202\v\350&\202\362\236\331|\004\303\365\276\275\274|\376\274\204\300$\260\323u#\003\357\203\221@&q\2432z\360zKZ\240\335`4\353\234\002\251s>I3\227\022l\375\351?\377\375\355\313\373\025\260\360\303\340\372\016\370\301\003\330\207Q\250*\315\001\226\002\243\000i\2145Z\000\232\220\037\aU\006\347\327\305hu>\377\371\333\307\016\262\213\001Y\311\3412\200p\255G&!ke\347\b\342 \2244\362\034\301\201\034\324\001!\313\331H\236\367\245||\256\373\277\376q\265\353\\\226\271G\365\364\202\001\222$:n\326\303\224u}(I\'\256\344d\025\301\331\200\226\02018.\364\022\342\2535\267\373\313/\337\357\321\317>\223\236\3228\\\2721\351y\355\244)\217\347\363\361\372\260\320\f\r\3162K\242d\302?\272Y=\030_9Hh1\366\361\375\333\367\017\264#*F\336~\261\340\343\236\356\327{g\322\355\375\353G\254\333\262\326\240\005\200n,\345\214pB\002z\032\b\2045`\321\324\355\373_\317O\261\240K\343\364\370+\277H\214r=_\273\262]\313\275\306P\227\nj\021\272\361\201\223\236P\264\001Z63Hc\331\316y\271\237\277\236[\314\2336\343\270\376\252\240&\335p?\216\356\374\355\036R\024\302\004\220\212\a\232\231\213N@L\024\265\244TNh9\224|\375\364ct\217k\235\363q{>P_\201\236u\303~\273u\354\347%@\361\204\242 B\020\263\3454y\201.\203%(\264\210\031\177\b\377i\261I\306\363\353\227O\037\307\037J\036\311G\316\320\263\375~\353\210\311\240\035\031L\333zA,\304\224\031\002\222\201\f\030\327T\300%?\257-\276\316_\377\3748_\337k\232\006\366\"\273\313\360\274_;Ld) @\r\032\220\006\v\v\345\204\016\202:\360\237\0064\035b\026V\006\321t\347\237\376v\034oo\337\236\274\357o\023(\365\231\317\016\310\335Jz\333\r\332\276;CY5\212M\200\223\2021\020^kc\300\357\205B4Y\2126\226\374\271^\177\240\203\206\250\311u\333:NG\a\031kk\240\017FD\333c,(P$\247\021z\310\245\307>\307\345\372\351d\251i\263\311\363\376\305\022\215)\232u\237;.F\352C!v\"\362\235\303n\350\276G\000\370\"K\306\2364\320*\025\303\266?\036\302\300\244\300+\335o\277|\301.\\\206\373\272\272fq\230\327\261%\24061\255B\f=\021D\203\t\246\2619<LCs\235\036\217D\024\245\316\302\312\325\363s\343dqn\233\353\bS\002-7\020\226|\354\233\267\313@\032A\301A\300#\"\000\003:a\225\324+,\025q\331V\b\330\233nk|;\257\313\334\021\237\257\213\211\320e\377~}\024\374\200\247T\026K\335\354\325\000\345\204\037\204\254I\243\246Q\201L\b\277\375\314\246\3762\272s\001\323u\023\315\v\3100K\236\316\375H\300\003\244\025v\004\026\223\264.\362\006/\346Tss2S\314\a\313\214}\035\371|\234\327}\2076\272#{\301\264\370x\277\302\300a\232\232\343m\006\217:\342\034\n\216\263\312\020\206\246R\005!\355G\344\006\253\260\316\327s_\266\316\305\274\276\336\035\277}\375;\002\271\310&\312\301\024\214\2148\3532\001?\214Y\240t\302\020aaF\006\252\000\021\313|\255q\313y\353t*\361\330\266e\377\337_\016\255\334\332\234k\363\307xyl\346\003\006GYX\003\252\034\322RS3V\200(,-\244tC\tT\224X\217E\313\267\177kx\027d>\301\341\2168\037\001xK@\231\320\354\2230=\261r\274\364hpS\235\230\363\262\264\000t;L\000\237\001\rJ%\336J\'\315f\343ui\031\\\2327\271\315Eil\031\347\227K\005\227\032\201\304S)\307\326\255\353\341a\001\274\237\b\350L\260\t\375\341\b\000\262\266\001\302\256|\330\022\024\336Y\016Di\336_\nq\024N2\305T\353\262v\373\331\354\271\017p\3050\324L4\267\b\310\264\372\261\231\315tBs`M\300\f\fh\a\363\216\274$E\361N\252\245\334\272\027@\023\"\202*\030of\000M\227\034\345\203\vy\320\316\266\345\t\306\307\002j\025\020b\314\200\315p\3331\304\245n\307\336\025\a\345H\360\217\b\000%\300\032\312\001\307s\017K\027\363\022-lo\366\241\031\263\306\266\022\312\301f\v\017\345\363z\\\257o\235\306M\210\206\200\214\220?t\221\300|C\362\261y\214\032\034q\334\326:\033W\022\207Tk\351\030\357\207\232\275\214qC\200\333\263kv\002F\023E0,\001o\327\003\\G\230\005\305JW\217\307c\376\275F\357\271\366\t>\026No\2341\200\274m\353\365\361,\235\001\361q\301\201U\2600\003\337\302\236\201\312Q\274\267\371\343\207\367+\304\020w\261\371jK\205\3278\024 \022\204\317\333\276\\\257\307\223w \016`\177\262\300\017\372\213\342\'A\235w\311\241\266u\373\214\273\213\365\326-\271\306\265\270F.Xr\3643\347\373\375v\234\216v\220k`\2259\034\215\344\261dp&\020{\330\237\365\276=vx\356\202\025\263\266\304\274k\320\016\240\312\337\245\\\326\217\327\347\313\025^\t\306]\033\335,$F\300\a\240\tXb\277g\275/0\230\355*\345\004s\270\202\354\226R\220\317\300\326\323\3477\004x\205\355\354\004n((\002\003F\v\241\354\231\214\226\363\022\340\223\2269\302\345z\017\303NS^p\363\304\245L4\323~+\371\307\337>\037\270\234<;\b\242\006\327\215m\005\031\247~\265\243\344\246@\356\"4\023\001\000\006\334cay*\236\244\0327\233f\203\365\217\267s\021b[:\270\0214\001\3149\265\335Wa\211\2362L&n>>\301u\0338\264x\\\037\316Sb\345\024\310\002I\320\363\r\367.\361\361\366\271\003\3341rJ`6q\321\004\355G\300,\315 <\037\236\031\240\026i\251\256\372t\336\223F\005p\337x\234Vp\234Y\326\237\337[\000l\232\342\006\227qBc\211\021\303\202E\203q.\245\235E\345z=p\213H+\\\231\024V\227E\2206\257p\256\267?|oc\204\362\340\256d|\301\315\t\350\270\303\272\342\276u\274\024\334\221\005b\324\375\226\332\025\022\202C<\020&\232\016\310y\275\375\361\227o\337\376\017\034\345\351>\bI\311Z\000\000\000\000IEND\256B`\202"
diff --git a/core/res/geoid_map_assets/expiration-distance-disk-tile-7.textpb b/core/res/geoid_map_assets/expiration-distance-disk-tile-7.textpb
new file mode 100644
index 0000000..371e9ec
--- /dev/null
+++ b/core/res/geoid_map_assets/expiration-distance-disk-tile-7.textpb
@@ -0,0 +1,2 @@
+tile_key: "7"
+byte_png: "\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\000@\000\000\000@\b\000\000\000\000\217\002.\002\000\000\v\366IDATx^\035Wi\217\033Ir-\326\221WefeUf\335\367A\026o6\373RkZ\323\032i\244\031Y\353\321`\327\213Y\333\203\005\f\333\037\0260\f\370\323\002\376\356\037\356\250n\200\r\202dFF\274x\357E\224E\034\227\307\rG\036&\n3\021V\233\306D\230\272\016W\346rIl\333u\354\025N\353$\020\001\027\201\220B\006\234 J%\245\2142d!\a\251 \b\302\204\370\204\a\375fh\207\276T\276e\371Eys3i\027\002X\250\254\313<\tC-\204\362\205\020\234P\001\247\024c\330\362<\027c\314\202&\321\252)\212\276\236\306`\265ZY\253@\266\367\267\247\320u<\307r\213\266\310b\245u\030\006\234\371\214R!\002\202\ba\334\362\034\033a\354ka\302\270\216\005\347e\340\257V\266\307\022\271\276\273^2\265\004\260\315P\025\"4\252\f\245`\f\002@\322K\034\346[\266mY\360\317qm\333\017\363\276\331n\367\215\263\262]\027\a\373\375u\277m\345\n\257l3\347B\206J\206\220C\310|\312\003\035\004\234+E-\033\322\265^_\020Ep\345+%mk\005\230\266\233\355T\2175\266=w\345\326\261\222\034\362\206\3735\367\001H\315\031\225y@\254\005f\370{\r\000\021\204\037H\t)aB\342y=\024\225\353\n\f\371\311T\"\017q.1\026\320\f\025\302/#c$\265\260\a_\344\271\362\035\aJ\267\021\300\vE\301\255\2104\327\241\031\034\307\307\253\225\313\b\203\337I\036P\3423\310]2\306\303(\"\334\n\212:g\332\204\304u\\\3575\035\033Z\200\260CI=\2366\265\v\367\332+{\345\320\bc\"\341b\037\316\252\000\370\300\002\204\250\225\313$%\236\313(vl\202 \ndbCM.\203.\303\275\256\207\005\204\264lG\'J\022m\000\n*#m|\237\272\b\031+\226*\361=\327\v\211g\273\236\01788.\362\201=\216\347A\213\001c\034\"@\331vd\026\032\252CH\037\002\b_P\212<\a[\261\016\363Dk\024C$\307\021p\320\226\302\037 gD\025\\o\257<\271\202^\001\267\v\225\2624OC\310\vN\373\v\366(\263L\234\026\271NQj !\a/\314\265\223|Cm\333\343&\t\031]0\201^y\216\250\241(\225%I\nU\340\245wI\354\373\226\311\322\024\302\246\245\353r\274\\\004iUu!\035(!L\323X\331\356+\323\210\203\363*\004\372s\017\200Z\216\307I\000\\\264\242\254(\333)\003\032\253H\272\310\361\b\360\201$\312\261)aY\020T9\301P\002\206vJ\343c\212\\\017H\v\360x\032)\337O,S5U7\366\371\020\344\201D\020\230\247hA\022\030C\002\255u\222$\001\265\035P\275 \212\003\377\005\v\274\245N\300KI\251\255\270\254\233\"M\265\016\f\021K\000GRH_\204\256M\002\223\306)\004\b^\333*\025\205n\271DJ\001\tp%D\034\a\322\312\313nHu\026\252\"\3174\360\300q\215\v\327\273 Q\270L\307F+B\\\240\222\343b\272t\026\000\000\vp\b\362#\235\305\211\225u\323<wy\224\344\271\314)\244\340\202\033-8!\344\363\250L\301F\200\370\340\020\200N\300\200zQ\030FI\022\a\334W&\016#+k\327=\257\213\".s\311)\206\b\b\356X\364m\034\251\313\301H\002\225C\177\034\365z\261\200\262tb\f\030\v\242\222\t\253\354\352\232\247\355\330\224\215V\212\260\305_\210\r\025\240\330\017\223v*\300\351$\363\220\215\000\177\344\330<JU\004\257@P\304\245\344\326i\236\323\315\361\374\207\276\316\022\024A\'\201\320hQ\225[B\215\331d\350b\177\016\202\"VD{+[i\031)\342\005\332\347\234\tbi\223\232\350x\377\353\313\241\017\301+\"\327A.\206\343\266\303\220\211MN\2120\213\002@\177\265\250\035 \362\021 \f\355\320\320R\306l\v\a\202\233\254\352\373\355]\344IN\210/\201\345\256\a\036\347\270@$\223\304U\035\270\216/@Z\213\246\204\324\230 \206p\b\375\024`^\313\247+\307\t\262b\312\263\fd\206\004%ha\212\343W`\200\240\262\f\354\306%\311\240(H\035\371`\t\004\234\\*\025x\236\365\032\326^B\230\260\355\213\210\270.[l\323\003Y\262\004h\020\345q\026\n\220\202\254\347\347\306\261\203(\242\f\006\021\230,\006\223\262\300 \302\252\360V\256M\351\335P\b\344\272\0043?\037C\320\005P\022\306N\232@/\301\026\213\3762\256VI\000\212`\034#I\340{l\261fw=\236z\035\300\251\247MDaha\237\245E\271N`z\001\a\235@\230h\251AVQ\221\331\356\020\004\221\240\212\222E\372\016\2678\225w\317\337\017\025\363\221\237]#p\243\240\344\030\2479\363`6`\360&\214\\\026H\027\314\b\212v\344\371L\022 B\340/\302!\324Z\v\321\275\177\274\3139\'\352pl\221Gi\323EC\b(-\204\b]\0271\327\365!r\342/\236\331\036oN\323iN94\n\344\302\254\315)\b~\270\333]\204@\264\377\330&\036%\244\243\024\\`\333\355n\266\003\345u\203\020R6a\320\033\327\230Sw\254F\243@0\204\004>\265v\363V\326w\273\301\207\301\241\036\037\214\217PP\270nH\366\327\251\251\347d4\231\300@\201\305k\001\372\354~\206\241(\"\240*!*\344\304\032\242\310g\277\274\277\2251\f\372\341\232QN\375\224\223r\267=\314\365\310\351&3\245\037a\350\252\207\021\321i\252\200\200\257\214\242\221\346!\266\272$g\230\315-\371\256\265m\246\312\"\364I\232\232i\274V\227&dMi\342\006\320\023 \321@h\2356\031s\026]xD)\030\264\310\352B\363\214I\"\231\274\001\362\365=\005\032\362\244\234\372\2428\233\020b\353\333\271\216\242\b\364%\3454g\257\232\000s\240Z0%\205k\245\252\373p\242\006\023O2\333\215\272\327\241\257\362\247k\b\236\274O|\330}\250)\f,\032\266\320\022Y\313(\267\035\254\264y\335x\\K\211\361\355ou\2312p+\272B\351\f>\001\323o\374v\366Y<|z\027x\030j\027\322[d\000\307-\210\344\211\310\204\341\"g\216\255\324d\017\177\330\375\365\247\207\2200\300\026\027\330\367=\302\374\340\223\241\261\326\227K\311=h \270\032\334m\301\2145zq&f\264\f\300\326\251\025\267\335\365\224\375\372\361\274\216\250\267Z\325\224\320\034F;fo\277\277\275\257\304\216s\327\215Z\204\300\324l\317\366sE}/\004\2150X\024L\b\001\262\252}h\364\327\227\323\224\004d\265\002_\005\272x=\346\333\367OO\037\276l\375\f\372\235C\363\"\310\334\363L\304}\304L\no]\016C\324\aS\335\336\253$y|\334\200\323\332v\264`\341\322\230\370\347O\335\347o\337\376\344:\337\375\372\241\031M\0369\204x\036K\005\327a\226\226\315R\207\017\333L7\354\036\032\251e8d\006\bb\263h\v\275c\204l\177|8\234\177\177\304hx\336\327u\227\372\230A\366qV\024\221\n\362\242*`\332\201\301[\315\270\335?\223\241\016\353L\033\360\377\344\323Ow\030a\317\r\363\254\3041\246\017\260\234v\275pi\003n*\362\274.\263\\\253</X\35092\006[\357n2\366\242\003\216u\334\332\253\247\317_.\227\f\374\302#\323H\250K~\221\222r_\226y`r\332u-,X0\000\312\274\314[\030y\332*\207\365\273<\377\334\373\024\023\352\333\316\323\371!2\225\241\036\f\241\254\244\364\351\347\215$@\317.\244\252@c\267\036\272\256\033\273\315\241\355\206.\002Kk\372\323\307\351\335\373\036\306\317k\000\357\374$\373\302\003\006z\024+J\263\227\257;@\204xJ\251\315~\\\017u=\232\271\352\367\363\361<M\205k\325}\237_\376\3742Dc\004s\002\255\274\315\341\361\002\213\000\362\370\342m\214\252+\354C\304\213\342\346f\n`b%Qg\352\252\336\314\323N\244io\325\2722\367\377\374\262\237\207D\371)0\0012\255a\332{\005-\030\301\"?=\207b\366\300\031\313\227\3073\270T\035\365\335\270-\273y\322A\252\215\325\036\313\352\370\365\237^\316y\036\207<\002\262\213\3656\355\327&2\361\236\300\200\224IDi\342\373\317\317\206\217\022E\211\330v\335y{\272\036\016Q\320u\326\266\251\252\313\345\315\343\017M\225\3022\017\214\257\373\252\032\347u_\027o\267\233\247P\200_S\231\252\362\361\3100=2\221>\236\177\376\362\307\317\327\3635\n\306\321\352\232\246\233\017\303\372\373\244\252\264\n\224\215\2735\004\030\247\272\236\267\333\246}\363\3764&\346\232\212\354\315\004\217\bqHy\365\366\333\227\367\315\375\365r\177\332\214\226R\311\346\334\367\323\224\314c\236$\371\264\335\314\035D\205y?u\305\364\360\371\353}\2479\330/\321\023 \340jLw\325\370\017\247\346\376\341\315a?\367\226T\325\371i\250`S:\315\323\220\227\323|}\231\306\016\306\3550\365\365\372\341\360\365\347A)\2148\314\034h-b`0t\275\237n>\276\177\270\207\307\t\213%\331x\031+\243\217w\247\r4\371|\\\247f\267\353\212\262\200M\346\346p\363\365\307Q\353n\006\371\202\312m\312\017\260\"\005\273\346z\177s\205G\242\275\305\362z\272L\231P\363\345\274)\233qsX\aj\277]7Y<\304\351\256\031\276{7f\315x\231G\030\352\016\246|\247`=\310\273c\267\271\336\277{woA\213\306\313V\262b:\2346%<\262\265kU\314\353y\275n\212l\3276\347\207\267\333\264\336^\242<I\227\r,\352\006H\206\234/\333\215\276{{}\003k^U\237\267\360\254\323\355\200\344m\337\266*\0306\343\272\357\273<\337\256\247\335\371q\277\333\357Ng\370\304\'`v<\204G3\017?\037\367\233\247g\bPt\335\356\224\250\034\"w]\277n\253^\210v\275^\217\375\030\367\303p\234\367\217\207\375\343<\034.C\b\236\200s?\252\236\300\273\365\2737Oo~\370\341\027k<\356\217\263\t\373\256\005\241M}[\305\242lZ\310\240\322z\354\272\303a}>\337<\315\204\356\206\242\027\332C~~\374\320\003\347>\374\362\345\343\277\177\376h\355\016\347c]\224\033\310\244\a\016\f\225\f\272\266m\247>\325\3618\r\343M\\\336?\274\257\240\374|J\313\030\212\240\364\363wm#\304\257\177\374\333\337>}\263\266\207=\220`\a\246\3255\303t\032r\2215uS\217e\031V\303\270\356N&\233\357\036\207e\205-\356\302d6\034v\263\337^NB\374\364\347\377\375\257/\237 @_4\331~\000\362\255\207ah\205(\3726\a0\263\244\354\341Qz\275[_\316\267\267\267\260\242\347\2730\034G\001\0327\277}\272\273=\377\313\177\374\337_\376jm6\345\202\3434\364M\335\017]\315\343\242)\263\242j\222\f>\337\214\233\371p\271}\270\273\275+U\276?\353\307\207sH\224\234~\377\323\315\371\307\177\375\373\357\177\261|Q\f}\267\333\326y\\7}\247\2022\352\212\242,\001\227\022\366p>\3177\217\367\017\267w\367o\263\354\374>\311\376\361\366\201s\217\374\333\177~}\371\370\333\337\377\373\177\376\037\341QF\003\316-\374\237\000\000\000\000IEND\256B`\202"
diff --git a/core/res/geoid_map_assets/expiration-distance-disk-tile-9.textpb b/core/res/geoid_map_assets/expiration-distance-disk-tile-9.textpb
new file mode 100644
index 0000000..59f386c
--- /dev/null
+++ b/core/res/geoid_map_assets/expiration-distance-disk-tile-9.textpb
@@ -0,0 +1,2 @@
+tile_key: "9"
+byte_png: "\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\000@\000\000\000@\b\000\000\000\000\217\002.\002\000\000\r[IDATx^5Wi\217#\327u-\262\226W\373\253}/\026\311b\261\212\373\316f\223\275\261\327\351\236\356\236\255g\353\2215[\317\264\244Y\002[\036\317\b\032[\262#X\260\244\004v\004\003\006\002Y\261\215\bH\020\177\n\034(\210\201$\310\017\310\347\374\201|\310\207\374\205\344r\202\024\032\315\356b\275S\367\235s\357\271\367\021v9\311\274\240\352\031Q\330(\205\236lxa\241\340\273\236\027\024\303\260\022\363\270>h\264F\375\301`\326J\306\263\357tO\317\256\246\032\305\336\373\344\371\326\356[70&\246u\216K\313AR\216\263F3\364\024\354\373^!\f\035\307\213\213Q\034W\332i\332joL\247k\263\225\255\315\235\017\276\367\303\337\235~\250i\301\a\377\360\335\323\353\327\353\234H\354\257!6\255\005QV)\226\322^\325\346#?\b\202\320s\355\250\024\224\253\365N\2559\233\314V\246;\307G\317~\370\352\344\352\223o~\367\331N\\}\347\263O\316&\206\b\000[\373SQk\325\242\254T\324\rl\271f1*D\205\330\327u\277X,\307q\226e\263\331\374\362\361\255\243\363\217\276\370\323\253\'\357\334\371\350\267\333\242\370\340\343\363-\214\305\332c\242\377\326\275fk\263\337n5\353Y\022\225\375b\245\222D\205B\301\266\254(.\306q\265^mLgW.\037\037\177\367\337\376\353\253\2337\357\274\375\342\225$\341G/\256o\031\262x\364gDxx\375\b\261{\275V\253]7\263\310\217\232q\\.\024\002X_J\313\345z-\255\246\323\325\243\203\345\235\367\377\365?\277\272q\343\376\323\323G<\277\372\344\301}\001\253\352\325\367\b\247R\277f\232\223Ng8JM3\254Em\340\263\\\364\\7\212\312\245R\265\326h4F\223\265\265\345\351\367\277\375\303/\256\\\3329\270\371|\272\367\370\301\307\027x\314\362\203\267\b\241\230\244\353\273\343\321t\270\232Zx\031WG\255,.\227=\327\017+\225\254\004,v\232\343\345\311\326\372\336\367\276\376\342\213m}y\266vOn\234}\370\325S\211a\351\372\207\004E!\2771\331.5\216g+aa\273\3409\353Y#\251\304\236\347\201\262\245RVk\267\226\016/\366]w\371G/^\354k\323\311\344\244r\371\326\213/\233\b\245\224\362\212\240\031:\317z-\303\033+\322\225\375\253\363\336\326\326\264\331\250\305\216\027\226\223z\271\334h$\315\346\305y\2672\330\334??\277u\375h2\306f\353\352\273\337Ld\272Ij\237\0214M\347\021\315\272\245zEn<|v\255\323\275\340\212i\263\354\004q\234f\265j\255Vi\265\326\346\235\366hc\343\370\370\354\372\321\312\216\026\215\266^~\275*\321}m\363SB\346)\222$9\006\313eM]I\333\206\276]gq;\016\213I\265\232ei\257Wk.\215\a\265\332\260\331\334?~\373\356\325\375}3\234\035?x\376P\022g\351\315\227\204 \n$E\321\034f\264\314\262\246\375\272\251\254otZI\241\224\266:Y\326\207\374h72\000\350\342f\351\364\354\335G\207G]<>\275s\366\035I\330\377\350\313\217\tI\342a=\r?$\203P#K\315b\271\325l\271n\263\035\247\203\341\270\327nw\a\235NT+\371\355\335\323\363\027\217\037^.\340\321\335\207\357?\341\330\316\355\317\177L\3602/2\034\017\313I\022\211I\232\262\272\323H\nZ\320\356\325\352\355\366\240\333m\265\246\355\222_i8\311\375\363\037\374\352\316\366\266e\325o\\>\277\205E\376\312\3133\202\323%\216bX\021\277\371P\v\301\"\217\323B\030\025\374f\243\327\353-\365\373\343\236\343\304\265\376\362\205w_}\375\347smo\307K/\236_\2724\226\233\247\257\317\b\236Eo.I\323x\236a\n>g\362\274\345`\323Q\232\315f\177<\031\216\333P\330\215\341\322\366\205W\277\370\303\247E\376\356js~\347\331n\372\'\361\203\237~\372K\002BG\274\310!N\221%I`\220\303\231\262J!N/hP`\375\321h8\364\313\335vky~x\373\365_\377\363\317.\237>\027\304\225my\347\306\347O_\377\376\037\377H\220\034M#V\346y\236e\020\'\3506S\346|\327\246\2644\031\366\006\323\351\372t\2206W\227\226\326V\217\357\274\367\344\357\177\365\345\227\317\020\332\334\022\006\233\177\371\373o~\375\233o\t\032!P\200AJ`\b\240\005\304#\250\222dRl\326\036M\273\335\265\255\371\2606\\]]\032\3166\217V\367\377\346\237\376\345\357$)=8\273W\362\036\377\350\247\257\376\3427\004\313\200\212\360rJ\r\\\215\241\251\034I\353\262\343\331F\022\330KK\223\303\033\a\313\275\336\336\336p\320\235\205t\353o\377\347\277\277\020\330`\351\336\275r\264uv\366\352\'\237\020\210a\030\212\345\005\201\2255\323\341h\212\314\363N\350DA\254\362\343\371\315\273o\235\256\257\357\356\316*q\255\035\222K\337\376\307\277\277\322\225\332\360\306\353\206\233\234\\\277\330|B0\262c\250\222\240\v\002\306*V\025\236\3163\256\356\224\223\2701\030M\227\017\236\237\335\276}R\213*\365\345F\273}\364\321\037?\337\344\315\321\225g?\351E\303\223\023O\275F\b\300[h\250*\306:fi\321\320\0312/D\305,\213[\235\221\353\036\234\277\376\371\'\247k\275\245\361r\265\334\374\301\213\237\235\v\254=\277t\377\323\241>\272\375t\264:%D\321\365\322\202\312aY\321$\212\242\030\206&\251b\322\254\325{\243v\253\266\371\350\303_\377\325\367o\034\017\207\255V\375\321\263w\236>\344\271z\260u\357\243\211\236%\005\256\232\022\026v<\307)\262\202*\b\232L/ (\0227\032\315l0\252\367\202\361\335w\177\374\333\3077\257\354l\256\016\252\357]?99\342\270yp\341\326\343-/I0I#B\2678\026\271\021\317+\252\253\360,\317\347I2\307W\333\355l4\033\266\312\315;\367\037\177\360\313\314\236ot\033\223\273\307{\'\207\310\335\336n]\275q\242w\032\001\217\004BW\031F\nC\216u\303\032\206\232@\240C\216I\332\235\341\322\244Q/\225\016o\335>\377\371dP\233u\272;\253\263\371\316\026\207\366\326g\343\v\227}\273\337o\016SBE4StM\216u\034K\026\260\242\212\024\324%\327\031\367FK\215\206m\367\347\363\313\357?\036)\223\331\265K\256R_\217X\2661\356\325\372\273\205\352\325a{V#\030\222\212\352\246\215\241\'\232XPE\222\024\251<IW\'\275\336\250\336\t\303di\351\360\316{\233\363\235\265\333\207\243\301\332\301!\317E\355\226\273\262\267\273\262\262\334\336\274\004~ 7B\313Q\203\330\267\025\335\'\363\244L\346\363$^\356\r\373q]\247\362T6>89YYY;\270\270\2661\337?\340\370\203V\327\335~\371r\377B\275\267w\221\020$<\016\203\240\033\227\035G\261\003\222rl\rx\244{\375^1\256\327(\222J{\263\365\275\331ly\272\273\2666\037-q\334\315\365\336\356\375\227\017\036\337\213\212k+\204`\325\227</L\n\005M\020\255|^\320\r\021\264\244\300\020\255\300\355\0264\272\336\356O66V\246\313\233\233\033\353\235u\206\331\336\330>8|\373\322\245{~X\353\020f\251Rr\242$q\034\215\345q>oBy\223 \005SOeA\217\352\252\030\307\315\316dyc\343\322\366\346\312\372n\233\221\306\303\313\233;+;\223ke\243V#T;)8\232\203\261\355\vH@4IR\271\034\v\205m\030\222\350\'\rU\215\343\244\326\233\254m\316W\267&\223\251\3070\243\341\346\366|l\364g\2111\034\001\200\241\204\032/\033\330u\301\027\240GH\371<\a\326 \b\266b\206a\030\024\313\225j\265=\234\255\365z\223I\237C\364\366x2\036\225\304~\346\330\315.a\031\272nK\034\247\342\202\312/\354]\244sH1\205<2\rE\324tE\325M/N\222r\251Q\253\266\352\"\313\242Aw\324\357\370\270\227\205N\334$*\266-I.B\206\353\253\252\203)\232g\362\274!\260y\222b\031N\341E\225\027\005\245\030\333a\251X\256z\nES\265\356R\307\257d\255\206g\307\036\221\264l[7Y\326\326uW\222\305E\177@,\317 H\006\212\226d\0268\311\347 9t\337\363\213%\v\f\024\325kI\235R\262V\315+4\034\242*i\206l\361\212\214ea\341\254\024\223gX$\36292\217\2400\350\305\362\305\305\b\226\345\030\262\352\245\264\254\2242J\252\267\352z\320\342\bC\322\261$\211\022\v\216\fmV\240\250\034B\264,\345\310\034\aa\344\340\202`\026 \274a8qV)\033v \227H\266\226&v\230I\204\256\351\222\"+2Z\004\235#\311<\221c)J\342\026+\t\202\200\350\363t~\201\000\030zP\210\334\202\256k\305\200\346\3234q\213\231O@O\223\024\f;\345\200\236\034<\a\000\214\0046\235_\000\344(\017\213\242\310\0024\000\346\215B\340)\262\252\206\241a\201<\340\245\210\020y\254\260,\t\357\024\\\323z\0233\022\305\034\221\177\003@9E}\001\200\026\\\300\277\276\242\b\252\351\232\nb ,\020\035\021\f\257 d\031~Q\221-\313F\213\307h\016\345\341\345\260\001$\341\310/\006*x6\005%J@\225\261\314\302\371\027\033\202~D3\fa\033\b\331e\337M{^P\362\235E\0044hG/\366/\210\"\326\213\206\241\a\272&\347\t\366\377y\201\337\377\a\300\212\"\341\250H\260\312^\034\204\361\240\227\306\034\354\226\221\241?\345)]\221d\350\231:h\204\r\325b\240D\026\b9\002</\267x\214\341\355\000\023&\315\270\336b:\207\361\274\332o\2339\350\3272\231\313\2034\264\310\261\234\252\2100\331\252\212\252Bp`6\020\034\363f\a\210\346\004M\023\bf\001\340\332\212\342\205\225l\330\347\340\005\202I\021y^\000\365\030\0309\f\003\026K\274\0007r\204\240a\235\002\206I\232\325t#\264E\000\340\030\023\000r9\277\022W6VX\370\026\033t\216b\270\005I4m(2\v\\\262\254 *y\320\006\362$O\220<B\262\356\205\256\253\023\252N\t\016F\224dY\305\356d\b\3743\220\201@\001\202\254\206\334v\f\314s\242 \t\260\223EF\345i\006v\304q\234\244\326=\'s\t\0171\310\320\220k\233\226\327\350\017\021I\344@s\000`\363\210\341e\310\177\210A\020$\340C\223\260\246\311\330\367\242\002\334\265J\315\310\315\nD\bC\205\t\246\bY\3444z\211F\202N \021\303\210y\206\023Dlj\006\213\241q\202\021\360\274\210E\216\263l\017\232a\026\265\266\002\325.\020\216\315p \227\016\230NRv\035(\300|\216\345i\312\242\031Q\226\025\254\251\334\342S\002\3258\216\027E\311\002\325\312A\351xR\vU3\"\300\rD\004\226bi\246\346E\216f\"\n\b\202\351\023\251\002\'\233\340\321\"/*0\177-\254\232\203H\004H/\331\264\315`\354\353\226U \260\241a\244j\246\251)\262a\352\272\203(lTL\021\261\034\317\361A\344p\234 I2\017^\bB\300p\t\224Bj\351~X\322u\025\313\004\206\r\312\n\374\255\302>4WW\0051(\245)b\221\ni\350\225\r\221\227\3404\006\"\202\313\300jN\340\004Y\226|`Q\024\025\216\203i\035r\004\313\252\002e*\033j\200E8)\264j\220\347\n\f-\020\221!\b\226\347\330JQ\326T\025\024\0211V4M\003r8(S\226\220)\222\226EPY\204\344\325\261\255H\\\220e\253\"\f\177\252j\351z\002/\212\003\253\0001\300\005Ly\005\005\212\003.Ma9Q%$\030\362\336\254\207\200D,\353\222\310\332~\273;V9\232\321\f]Q\324bQs\364EY\211`\006\222\022T -\025\211\246$\240\t\353\000@\211\210\227%pT\260&\360F\226\225p\251\232$.\213t\003\016\271`\327@\273,[XT\004^\342%\253\314B\222\222\024\202!_\367\t\004-\b\350\021\200r\360\177\036\000\200}\257\3408\256$X\274\345/\316\260\262]\006\211\0240^I\325\260\350c\226\001$\206\201\246\224\021\213u\034\224\032\214\230\022\270:\3148\220\373\256g\331\201\252\332\210\346\035M\325e\253\344\273\v\32689\322T\350\036<\'-\306\251(\2126\b(\024\216\005u`\342\205[0n\210\222\300q\006\f<V!\320(\312\021 N\327vAe\320\003\a\232\001;Ep\346\304\232\td\315\377\027US\b\376Q\205\227\315\000\000\000\000IEND\256B`\202"
diff --git a/core/res/geoid_map_assets/expiration-distance-disk-tile-b.textpb b/core/res/geoid_map_assets/expiration-distance-disk-tile-b.textpb
new file mode 100644
index 0000000..e65c433
--- /dev/null
+++ b/core/res/geoid_map_assets/expiration-distance-disk-tile-b.textpb
@@ -0,0 +1,2 @@
+tile_key: "b"
+byte_png: "\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\000@\000\000\000@\b\000\000\000\000\217\002.\002\000\000\f\246IDATx^\035W[\217\034\307un\356tw]\272\272\252\253\253\252\357\327\271\317\354\314\354\354\314\354}\271\313%E\212\022\245\345M\261CF\026M\213\022\035;J\240\004\326C,\3312\002\b\016\024\304Q\002\333@\000?\344\321\017\t\220<$O\201\003\377\202\374\240<\344\214\v\v,\260\330:]u\316w+\353\343W\337y\377\331\aO\357^l\206M3\354\016c\335\241q\024G\nS\3548\210PJ=\337u\361\216\205#\023&a\230r\237\270J:\333\325\331\331\261\256\277\377\352\365\313\017\336~\360\344j4\355\017\273\032\333<I\213\266T<\240\036,\036\312@*\344:\0262q\231\006y\3059\241\310\266]B\220G\250u\375\345_\276z\364\255\323[\267\036\237\314\247\343E\200;q\335M\212\266\212t\030*\3440\250\021\033\352\302\367\250(e\320d\260\261\323\261\211\312\222$Ic\353\325\307?\371\344O\036\235\237^^^\234\036\356\356!D\213*K\212\274\310\263\210\375\341\032\224\371Q\354\271;\256\027\232\244\256)\305P@N\016\372\335\254lz\326\313\227\037~\365\321{o\335|\363\215\343\243\335\315~(\313\262*\262,\317\313X3\027{\310q=!d\350\334pYh\312\272\241\236A\366\364\250\031\317\206e\2577\264~\374\352\243\257?\371\344\331\203\367\337\275y\274\020Xe&\321:\323Y\246\265\024\001\\_\270.\017\003\321\331\261Ud\2469cR\372\aom\366\027\263i\333\033\355Z\277\374\217\257\276\376\352\363[E1{|\266\2448\315L\024\310L\253Xi\225\306\320\b\311\035\233R\206\350\r\326\016\017\030\363et~}y\260\331\237\017\207\363\365\306\372\337\377\374\371\317\177\363\301\3030\224\'\327GI\234T\025\v\362<\326J\'Y\032\307F\033\351\332\016\241.\276\341U&\225A\232\256\207\303\363\263\343\365\341r\357\360\370\330\372\375\377|\363\243\037}\364\302\230\372\344\2155T\357k\246\3132\366\0057\2216\211\201\341k\317q0\362o\220\222\207=c\232~;\274}qv<[\035\036\037\036Z\277\377\267?\377\344\263\357>\274{xx;\223\204\017\206\224\226\320\002\311\230\021\302\030\023\307q\030\n\2372\262\223\026EAy\262\314\373\367\357\337\271:\231NO\217V\a\326\027\237\177\362\027\177\375\342\342[\213\305\323\232\272\274\325\272\312t\b\337U\234\347A\270\355C\226D\222\241\235\235\270jK\252f&?\276\177\375\356\331\301\374\346\301jub}\374\342\263/\377\354\351\336j\271w\f\005p\035\364\212\272PZ\006\312\204Q\256T\232\304\275\312\243\324\336q\374(\017a:\311\336\315\353\3537\316N/.N\217\217\247\326\353\027?\370\341\367\357\355\255\327\207\353\034\341\2249mY$E\242T\022\372Q\331DY*\220\213P`\333\236\'\343D\t=X\335{v\275Z,\366OON\216f\326O\276\374\364\207_~\373\376\311\362p\025\243\246Ki\006W\315b\255\203 \316\353\321(A\030c\025(\025p\316u\244D\177y|\377\275\273w\356\\\236\235\236\034\034\034[\177\365\017\277\372\342\357\236]]\355o\326\221\335\317\240I2\2118\221\222\005I\"\201t\000\356 2>\254@EQ\334\233\256.\336z\370\340\361\345xx\262Y\002\016>\375\346\353_\377\355\207Ww\316\226{\330\256]WDId\002\306\003\251\225\364:\035GaL\t\361=*#\025De;>\270\371\350\217\036\234u\307\213\311\376l2\261\376\3767\377\370\253\177\372\336\367\336\276s$)\315\267\005B-}.\225\366C\017u\\\204\t\331\036\203P\356y\304\317\253\341\352\366\335\213[\253n\253\273\375~\277g\375\356\267\277\375\367\037\277\376\350\365\2433\201\363\2242\243\000\376~ \302\220\205\036\365\335,%\230`\004\324\307\036\264!\256&\363\325\371\371r}\030\351n\267\251+\353\277~\367\177\377\372d\361\374\365\273\207\324-3\354\233P\026\000\001\005\3437>\363|\311\260\2131\v\002!\240R`L9\201~/\227\323\262i\353\"N\255o~\361\337\377\362Vr\365\342\366\024\3434!\332\230\240\210\245.\253r>\034\017=\337\'\202\261Pk\317\023I\210\210H\263f2_\356\016\2065p%1\251\365\305\207?\373\347\'\253w^\034\016\b\312\025\253\373y\221\304i^tg\363i+\204\247\"\356P\035C\003|\277$(\310\223\004X\\\265\2756\213\322,M\255\017\177\360\371\337\374\354\326\305\323\303\222\3406\225\331`\\\032/2i\224\324%c\214J\031`\3004\363\b\347\036A8\212\343\254i\252\246\327M\245\214@\322\236\376\361\263\227\337\275{\361\370\264\360\360\356$-gsD\\\315@E\341\356\274\322Jy\314O\205\357\021\341Q\217:4\326qQ\225m\267\fu\004\262a]^^\335{xu\276Yw9\313\247\243\341l\202l\037\3336\362|\023A#\241\200\n\225\020A\bZb\244\203\203\300\310\274\004b\2524RZ[\213\361\351\311\333\253\325b>b^\bm\033\f<\2121\310\001\363U]\267U\325\024\202+\nl\30042\n\376\3145+\267\005\322\n*\030k\301vO\201\226\253\275\232`9\237\f\3522r]bL$d\244\363\242\314\244\317\004\005,\206\210\202<\b\307\023*\313\n\231V\371\226r\306\"\336\350h\275\332\270\330\347,\231\257\207\335\n\271\000C\245b\020D\035\v\317\205\376\203\305\300\017X\004\020\203\372\306d \330y\235\'q\034Xlrx~uk\317Eu\025\244\323\275\321\250v\034_\203\254\307)T!\016\200\333\367=\214\335-\236a\321\216\243\303\004Q\267(\213<\217\204\345\317\216\216\036=\216\202\242\024<\352O\206\243\310\rd\230\344i\242\r\243\330&q\022\202\227`\327\205Z\310\303\302vd\250\2024.\313<\317Sc\371\233\325\362\374\r#J\342$iY\002`\355\004\006d@\246\003\020\"RT\302\'P\000l\020#\360)\346\272\241\202\0236M\236gA\230Z\263\345\260\036\037\224\246\326^\225\2266\354\267\325\326S$ \b\272R\026!\a\fl\vx\214\201M\371\276\220A\230\345eS\306\240=\231\325N\274z\264\230\251@J^\360\216\323\351\270J)\237r]\005A\325tc\016\256\002\222\3401\356sJ\f\361\214\224:-\300\377\212\n\016a\345\211\337m\247k\2603\346\305}\a \350\202)\205\201N\353\241\347E\025\210\000\314\0202\002\343\202\3630\206yJ\035\205U]d\025\024(\254\243d5\351\215\346\275\270\210}\335/R\215\267n\300y\326\214\a\333\001b8:\024\360Y\240\022\316M\212\020P\302gmS\302)\312\242\264\312\375\305\270\327\353\366t\030g&MR\201\271\017\212\304\263\272`\004\222\t\310\310v\263\002:\307\200\342\032\n\000\320\251\204\t\024u\223fV\332\237\f&m^WJ7i\2524w\2740\304Xd\261\330\026\240>\245\234\v\005\247\221J\206\265p=\037CBA\016-\300\353\201\316\224t\347\223(\364\202:\315@\313\300\212\271\016\b\021Q \240\257&+)`TT)cQ\024\004>E~\004\234B\330Ei\331VIj\251Q\177>l\233\314\000(\244\340\304\366\211\307\t\220A\212L\206\206\373\b1\256\"\3511\nj\300\020\311\262P\021\217\270\016\257\2522)-\321\355\315g\275A\257\n#\207E1\340\330g\200}\352\a\251\342>\364\017#\225l\351,\340\340\230\021\256{\231\240\222\0030\241\215um\341\244\233B\346\002\301\361\034\b\037.\374k\000\362A}\002\212\312\231\307\224\311\245T*\227@\361\314uT\006\361K%\2328;Y^\024\201\345+\300\216\275\263s\003\f\034\003N\001h.\2061\370\220\217<\026\000\214@@dd\222-4\267\005\306\375\214\023\245m\326\301\271\b\032\213\271;P@0\310\214@:\210r\022\344$\364!S\000jA\204\240\300\326\024U\226\202[F\210\325u\323\3476\204.\233\330\221\027\344\026\206\000\323q9s\311\016\270\250M\005B\232\'Q\200\221K\030e\324\v\005\360Y\372\n\320 b\024\017F\303\222\332l\340\332\340\272(\212\255m\022\304\254U\004\232\r\001\320w\0211R\233(\f\267\262\352\362\255\222\200\034\000\023<\037\001\216\367G\203\032\344r\223:\016\301T\265\026\306\016\341qQp\314B\210\240@z?\224*\f\033\02586\000\t>\203\200\214\\\303n\004P\\\357\257./\217\306\304\023\016p\254(,\300\216\'FE!\034\307#\247vG\021\2601P\203\250\351\"\2332\016_\247\340\225\304v)\213\253\272\267\\,\366\232^\004\373\035\020\3562\262\240Qy\3357\322\a\'G=\262\323\261=\0004\310M\275[\340\255\fz\002N1\033x\216\315\222\301\254\005\354\224\2521\f,\0330\252j\v\220\337^w\v-;;\304\t\332\316\215\035\204\303 Lz\343\223\253\241\221\245\226E\244\322\246j\225\r\023\3304e\222j-<\221\303\261hP\314,\226d\317{\303$3\004\206\351\031\300\207\323\201\301\311\274\232\235\034\354O\002_Va\200L;\3547I\331\353\017\247u\336\351\220\320\317z\322\204\371d\317J\346\267!K\a\261\362\341\360\244\002\241\332\366\016C\236\031\235L&\3730C\024\v\001Tm\362\272l\233\246-\323\216\335AHM\353\254j\366\346\326d\005-\026\324e\002Y;N\324n\272\333h\337A\240D\252\350\357GL\326!\260\027^\a\345\260\256\363,6\021\274T\306\373\273\323\0062=\204\355\nb\003(\260\353z\330\202\373;~]\214m\000\247\213\231\257\343\351f\005\342\035!\264_0oP7\031\350\241c\343\342\374\341\361xR\346iVY\r\034 \2326\005T\340\235\033\026\220\302\207\337\035\016\023`2\206\320:\352\326uU\245=\243\023 \217\321\312\261\365\360\374]@\342\266@fQ\327O\027\213YQ\"\004/\213\033\360\212\202\265\343\205`E G\002\2744\020m\253\266\316\fQ\245\256\323\304\201\334\264nl\333i\267\252j\271n\260\\\036\334;l\023\307\021\322w\211\v\324tU\257\206\247\032\240\035t\325\363\303\030\b\r\357\f\327\005\220\a\262\030S\222\330\266\335\213UUY\230N\216\317n\336|1j\024\t\3008@\274]\207\362\252\255\343\214\001\321t\000\352\245\3456\"xn\247CM\024\247\203\201\313M\2344\020\350\n\v\313\365\275\247\227w\327\273-${\001)\"\020ntro\261;\311\263\256t\021p\306v<\276\365f\006\202\351`\316{\314\261\355\274\035O\024\v\245E\207\307\267f\263\305b3\356\305\261&\310\305f4[\354\206\273\253\275\335\274{\362\246A^\207\330dk\r<\t8r:6\356\330\324\357\217g\213ZPf\245\343\313\223\371|q\262\\\036\227\2311\240\006q\336\037\227$.\027\367\317\357\235\254\020C\341\032\366\2410\212K\023\372x\a\026\a\227D\302OkE\254j\363\350\370p}x\377p\267\017\266\020\301kI\207\331\240\354U\020M\252M\372\agv1\310L\030\325\373F\244\001E\2003\240\222\335\001=\300\304\332}\362\336\267\337y\347\346f5h\341\221\224\024E\336\326:\025\243\204\261\264\032\000\177\027\021u\201\313H\226\325\336\305\301\365\036 \312\335\032\216v8\001)\262\356>\177\376\374\263\a\347\233\301 R\220\034\213,-\253n#\260\201h0\335_\236\336\\\267\333\356\273>I\232\325\315w>\373\352l.\266\262\317|\354n\245\336\272~\365\362O\177\372\346\301\254\237\201\n\304\020\001\301\261\262\25071\030\321l}\177\021^\0344\020\227\021#A6\334\177\362\301\373o.\227\0201\003\220[W\362PX\337\371\364\247\277x\276Y\316\341\245\253!\274\345y\267\216\223\371x\272\267\036g~\020q\244\300\256\274z\274\337\204bw\266\351v\207\220\004w\223\b\002\f\260^\350\377\a/i\247w\337\243[\370\000\000\000\000IEND\256B`\202"
diff --git a/core/res/geoid_map_assets/expiration-distance-params.textpb b/core/res/geoid_map_assets/expiration-distance-params.textpb
new file mode 100644
index 0000000..b3bbddb
--- /dev/null
+++ b/core/res/geoid_map_assets/expiration-distance-params.textpb
@@ -0,0 +1,6 @@
+map_s2_level: 6
+cache_tile_s2_level: 2
+disk_tile_s2_level: 0
+model_a_meters: 322622.0
+model_b_meters: 2828.0
+model_rmse_meters: 0.707
diff --git a/core/res/geoid_height_map_assets/tile-1.textpb b/core/res/geoid_map_assets/geoid-height-disk-tile-1.textpb
similarity index 100%
rename from core/res/geoid_height_map_assets/tile-1.textpb
rename to core/res/geoid_map_assets/geoid-height-disk-tile-1.textpb
diff --git a/core/res/geoid_height_map_assets/tile-3.textpb b/core/res/geoid_map_assets/geoid-height-disk-tile-3.textpb
similarity index 100%
rename from core/res/geoid_height_map_assets/tile-3.textpb
rename to core/res/geoid_map_assets/geoid-height-disk-tile-3.textpb
diff --git a/core/res/geoid_height_map_assets/tile-5.textpb b/core/res/geoid_map_assets/geoid-height-disk-tile-5.textpb
similarity index 100%
rename from core/res/geoid_height_map_assets/tile-5.textpb
rename to core/res/geoid_map_assets/geoid-height-disk-tile-5.textpb
diff --git a/core/res/geoid_height_map_assets/tile-7.textpb b/core/res/geoid_map_assets/geoid-height-disk-tile-7.textpb
similarity index 100%
rename from core/res/geoid_height_map_assets/tile-7.textpb
rename to core/res/geoid_map_assets/geoid-height-disk-tile-7.textpb
diff --git a/core/res/geoid_height_map_assets/tile-9.textpb b/core/res/geoid_map_assets/geoid-height-disk-tile-9.textpb
similarity index 100%
rename from core/res/geoid_height_map_assets/tile-9.textpb
rename to core/res/geoid_map_assets/geoid-height-disk-tile-9.textpb
diff --git a/core/res/geoid_height_map_assets/tile-b.textpb b/core/res/geoid_map_assets/geoid-height-disk-tile-b.textpb
similarity index 100%
rename from core/res/geoid_height_map_assets/tile-b.textpb
rename to core/res/geoid_map_assets/geoid-height-disk-tile-b.textpb
diff --git a/core/res/geoid_height_map_assets/map-params.textpb b/core/res/geoid_map_assets/geoid-height-params.textpb
similarity index 100%
rename from core/res/geoid_height_map_assets/map-params.textpb
rename to core/res/geoid_map_assets/geoid-height-params.textpb
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 238f242..e006b9d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2998,12 +2998,13 @@
          will be locked. -->
     <bool name="config_multiuserDelayUserDataLocking">false</bool>
 
-    <!-- Whether the device allows users to start in background visible on displays.
+    <!-- Whether the device allows full users to start in background visible on displays.
+         Note: this flag does NOT control the Communal Profile, which is not a full user.
          Should be false for all devices in production. Can be enabled only for development use
          in automotive vehicles with passenger displays. -->
     <bool name="config_multiuserVisibleBackgroundUsers">false</bool>
 
-    <!-- Whether the device allows users to start in background visible on the default display.
+    <!-- Whether the device allows full users to start in background visible on the default display.
          Should be false for all devices in production. Can be enabled only for development use
          in passenger-only automotive build (i.e., when Android runs in a separate system in the
          back seat to manage the passenger displays).
@@ -6943,4 +6944,7 @@
 
     <!-- Name of the starting activity for DisplayCompat host. specific to automotive.-->
     <string name="config_defaultDisplayCompatHostActivity" translatable="false"></string>
+
+    <!-- Whether to use file hashes cache in watchlist-->
+    <bool name="config_watchlistUseFileHashesCache">false</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 603b902..b8a399b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5346,4 +5346,6 @@
   <java-symbol type="string" name="satellite_notification_open_message" />
   <java-symbol type="string" name="satellite_notification_how_it_works" />
   <java-symbol type="drawable" name="ic_satellite_alt_24px" />
+
+  <java-symbol type="bool" name="config_watchlistUseFileHashesCache" />
 </resources>
diff --git a/core/tests/coretests/src/android/app/NotificationChannelTest.java b/core/tests/coretests/src/android/app/NotificationChannelTest.java
index d8305f0..56ab034 100644
--- a/core/tests/coretests/src/android/app/NotificationChannelTest.java
+++ b/core/tests/coretests/src/android/app/NotificationChannelTest.java
@@ -19,6 +19,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
@@ -42,6 +45,8 @@
 import android.os.Parcel;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.os.VibrationEffect;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.MediaStore.Audio.AudioColumns;
 import android.test.mock.MockContentResolver;
 import android.util.Xml;
@@ -55,6 +60,7 @@
 import com.google.common.base.Strings;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -63,10 +69,15 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.function.Consumer;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class NotificationChannelTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private final String CLASS = "android.app.NotificationChannel";
 
     Context mContext;
@@ -294,28 +305,12 @@
         NotificationChannel channel = new NotificationChannel("id", "name", 3);
         channel.setSound(uriToBeRestoredCanonicalized, mAudioAttributes);
 
-        TypedXmlSerializer serializer = Xml.newFastSerializer();
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
-        serializer.startDocument(null, true);
-
         // mock the canonicalize in writeXmlForBackup -> getSoundForBackup
         when(mIContentProvider.canonicalize(any(), eq(uriToBeRestoredUncanonicalized)))
                 .thenReturn(uriToBeRestoredCanonicalized);
         when(mIContentProvider.canonicalize(any(), eq(uriToBeRestoredCanonicalized)))
                 .thenReturn(uriToBeRestoredCanonicalized);
 
-        channel.writeXmlForBackup(serializer, mContext);
-        serializer.endDocument();
-        serializer.flush();
-
-        TypedXmlPullParser parser = Xml.newFastPullParser();
-        byte[] byteArray = baos.toByteArray();
-        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
-        parser.nextTag();
-
-        NotificationChannel targetChannel = new NotificationChannel("id", "name", 3);
-
         MatrixCursor cursor = new MatrixCursor(new String[] {"_id"});
         cursor.addRow(new Object[] {100L});
 
@@ -350,7 +345,263 @@
         when(mIContentProvider.canonicalize(any(), eq(uriAfterRestoredUncanonicalized)))
                 .thenReturn(uriAfterRestoredCanonicalized);
 
-        targetChannel.populateFromXmlForRestore(parser, true, mContext);
-        assertThat(targetChannel.getSound()).isEqualTo(uriAfterRestoredCanonicalized);
+        NotificationChannel restoredChannel = backUpAndRestore(channel);
+        assertThat(restoredChannel.getSound()).isEqualTo(uriAfterRestoredCanonicalized);
+    }
+
+    @Test
+    public void testVibrationGetters_nonPatternBasedVibrationEffect_waveform() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        // Note that the amplitude used (1) is not the default amplitude, meaning that this effect
+        // does not have an equivalent pattern based effect.
+        VibrationEffect effect = VibrationEffect.createOneShot(123, 1);
+
+        channel.setVibrationEffect(effect);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+            assertNull(testedChannel.getVibrationPattern());
+            assertTrue(testedChannel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_nonPatternBasedVibrationEffect_nonWaveform() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        VibrationEffect effect =
+                VibrationEffect
+                        .startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .compose();
+
+        channel.setVibrationEffect(effect);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+            assertNull(testedChannel.getVibrationPattern()); // amplitude not default.
+            assertTrue(testedChannel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_patternBasedVibrationEffect_nonRepeating() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        long[] pattern = new long[] {1, 2};
+        VibrationEffect effect = VibrationEffect.createWaveform(pattern, /* repeatIndex= */ -1);
+
+        channel.setVibrationEffect(effect);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+            assertTrue(Arrays.equals(pattern, testedChannel.getVibrationPattern()));
+            assertTrue(testedChannel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_patternBasedVibrationEffect_wholeRepeating() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        long[] pattern = new long[] {1, 2};
+        VibrationEffect effect = VibrationEffect.createWaveform(pattern, /* repeatIndex= */ 0);
+
+        channel.setVibrationEffect(effect);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+            assertNull(testedChannel.getVibrationPattern());
+            assertTrue(testedChannel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_patternBasedVibrationEffect_partialRepeating()
+            throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        long[] pattern = new long[] {1, 2, 3, 4};
+        VibrationEffect effect = VibrationEffect.createWaveform(pattern, /* repeatIndex= */ 2);
+
+        channel.setVibrationEffect(effect);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+            assertNull(testedChannel.getVibrationPattern());
+            assertTrue(testedChannel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_nullVibrationEffect() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+
+        channel.setVibrationEffect(null);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertNull(channel.getVibrationEffect());
+            assertNull(channel.getVibrationPattern());
+            assertFalse(channel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_nullPattern() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+
+        channel.setVibrationPattern(null);
+
+        Consumer<NotificationChannel> assertions = (testedChannel) -> {
+            assertThat(testedChannel.getVibrationEffect()).isNull();
+            assertNull(testedChannel.getVibrationPattern());
+            assertFalse(testedChannel.shouldVibrate());
+        };
+        assertions.accept(channel);
+        assertions.accept(writeToAndReadFromParcel(channel));
+        assertions.accept(backUpAndRestore(channel));
+    }
+
+    @Test
+    public void testVibrationGetters_setEffectOverridesSetPattern() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        VibrationEffect effect =
+                VibrationEffect.createOneShot(123, VibrationEffect.DEFAULT_AMPLITUDE);
+
+        channel.setVibrationPattern(new long[] {60, 80});
+        channel.setVibrationEffect(effect);
+
+        assertThat(channel.getVibrationEffect()).isEqualTo(effect);
+        assertTrue(Arrays.equals(new long[] {0, 123}, channel.getVibrationPattern()));
+        assertTrue(channel.shouldVibrate());
+
+        channel.setVibrationEffect(null);
+
+        assertNull(channel.getVibrationEffect());
+        assertNull(channel.getVibrationPattern());
+        assertFalse(channel.shouldVibrate());
+    }
+
+    @Test
+    public void testVibrationGetters_setPatternOverridesSetEffect() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        long[] pattern = new long[] {0, 123};
+
+        channel.setVibrationEffect(
+                VibrationEffect.createOneShot(123, VibrationEffect.DEFAULT_AMPLITUDE));
+        channel.setVibrationPattern(pattern);
+
+        assertThat(channel.getVibrationEffect())
+                .isEqualTo(VibrationEffect.createWaveform(pattern, -1));
+        assertTrue(Arrays.equals(pattern, channel.getVibrationPattern()));
+        assertTrue(channel.shouldVibrate());
+
+        channel.setVibrationPattern(null);
+
+        assertNull(channel.getVibrationEffect());
+        assertNull(channel.getVibrationPattern());
+        assertFalse(channel.shouldVibrate());
+    }
+
+    @Test
+    public void testEqualityDependsOnVibration() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel1 = new NotificationChannel("id", "name", 3);
+        NotificationChannel channel2 = new NotificationChannel("id", "name", 3);
+        assertThat(channel1).isEqualTo(channel2);
+
+        VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP);
+        long[] pattern = new long[] {1, 2, 3};
+        channel1.setVibrationEffect(effect);
+        channel2.setVibrationEffect(effect);
+        assertThat(channel1).isEqualTo(channel2);
+
+        channel1.setVibrationPattern(pattern);
+        channel2.setVibrationPattern(pattern);
+        assertThat(channel1).isEqualTo(channel2);
+
+        channel1.setVibrationPattern(pattern);
+        channel2.setVibrationEffect(VibrationEffect.createWaveform(pattern, /* repeat= */ -1));
+        // Channels should still be equal, because the pattern and the effect set are equivalent.
+        assertThat(channel1).isEqualTo(channel2);
+
+        channel1.setVibrationEffect(effect);
+        channel2.setVibrationPattern(pattern);
+        assertThat(channel1).isNotEqualTo(channel2);
+    }
+
+    @Test
+    public void testSetVibrationPattern_flagOn_setsEffect() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        long[] pattern = new long[] {1, 2, 3};
+
+        channel.setVibrationPattern(pattern);
+
+        assertThat(channel.getVibrationEffect())
+                .isEqualTo(VibrationEffect.createWaveform(pattern, -1));
+    }
+
+    @Test
+    public void testSetVibrationPattern_flagNotOn_doesNotSetEffect() throws Exception {
+        mSetFlagsRule.disableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+
+        channel.setVibrationPattern(new long[] {1, 2, 3});
+
+        assertNull(channel.getVibrationEffect());
+    }
+
+    /** Backs up a given channel to an XML, and returns the channel read from the XML. */
+    private NotificationChannel backUpAndRestore(NotificationChannel channel) throws Exception {
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        serializer.startDocument(null, true);
+
+        channel.writeXmlForBackup(serializer, mContext);
+        serializer.endDocument();
+        serializer.flush();
+
+        TypedXmlPullParser parser = Xml.newFastPullParser();
+        byte[] byteArray = baos.toByteArray();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
+        parser.nextTag();
+
+        NotificationChannel restoredChannel =
+                new NotificationChannel("default_id", "default_name", 3);
+        restoredChannel.populateFromXmlForRestore(parser, true, mContext);
+
+        return restoredChannel;
+    }
+
+    private NotificationChannel writeToAndReadFromParcel(NotificationChannel channel) {
+        Parcel parcel = Parcel.obtain();
+        channel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        return NotificationChannel.CREATOR.createFromParcel(parcel);
     }
 }
diff --git a/core/tests/coretests/src/android/os/HandlerThreadTest.java b/core/tests/coretests/src/android/os/HandlerThreadTest.java
index 1ad71da..3237812 100644
--- a/core/tests/coretests/src/android/os/HandlerThreadTest.java
+++ b/core/tests/coretests/src/android/os/HandlerThreadTest.java
@@ -126,7 +126,7 @@
     public void testUncaughtExceptionFails() throws Exception {
         // For the moment we can only test Ravenwood; on a physical device uncaught exceptions
         // are detected, but reported as test failures at a higher level where we can't inspect
-        Assume.assumeTrue(RavenwoodRule.isOnRavenwood());
+        Assume.assumeTrue(false); // TODO: re-enable
         mThrown.expect(IllegalStateException.class);
 
         final HandlerThread thread = new HandlerThread("HandlerThreadTest");
diff --git a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
index bd2f36f..d57f1fc 100644
--- a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
+++ b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
@@ -658,6 +658,52 @@
         Truth.assertThat(matchingPackets).hasSize(1);
     }
 
+    @Test
+    public void canTraceOnFlush() throws InvalidProtocolBufferException, InterruptedException {
+        final int singleIntValue = 101;
+        sInstanceProvider = (ds, idx, config) ->
+                new TestDataSource.TestDataSourceInstance(
+                        ds,
+                        idx,
+                        (args) -> {},
+                        (args) -> sTestDataSource.trace(ctx -> {
+                            final ProtoOutputStream protoOutputStream = ctx.newTracePacket();
+                            long forTestingToken = protoOutputStream.start(FOR_TESTING);
+                            long payloadToken = protoOutputStream.start(PAYLOAD);
+                            protoOutputStream.write(SINGLE_INT, singleIntValue);
+                            protoOutputStream.end(payloadToken);
+                            protoOutputStream.end(forTestingToken);
+
+                            ctx.flush();
+                        }),
+                        (args) -> {}
+                );
+
+        final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+                        .setName(sTestDataSource.name).build()).build();
+
+        try {
+            traceMonitor.start();
+        } finally {
+            traceMonitor.stop(mWriter);
+        }
+
+        final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+        final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL);
+        assert rawProtoFromFile != null;
+        final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace
+                .parseFrom(rawProtoFromFile);
+
+        Truth.assertThat(trace.getPacketCount()).isGreaterThan(0);
+        final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList()
+                .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList();
+        final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream()
+                .filter(it -> it.getForTesting().getPayload().getSingleInt()
+                        == singleIntValue).toList();
+        Truth.assertThat(matchingPackets).hasSize(1);
+    }
+
     interface RunnableCreator {
         Runnable create(int state, AtomicInteger stateOut);
     }
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 60769c7..52e996c 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -20,6 +20,7 @@
 import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
 import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY;
 import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
 import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
@@ -669,8 +670,13 @@
             assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
             viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
             assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
+            viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH_HINT);
+            assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+                    FRAME_RATE_CATEGORY_HIGH_HINT);
             viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH);
             assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+            viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH_HINT);
+            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
             viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
             assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
             viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW);
@@ -796,6 +802,33 @@
     }
 
     /**
+     * Test votePreferredFrameRate_voteFrameRateTimeOut
+     * If no frame rate is voted in 100 milliseconds, the value of
+     * mPreferredFrameRate should be set to 0.
+     */
+    @Test
+    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+    public void votePreferredFrameRate_voteFrameRateTimeOut() throws InterruptedException {
+        final long delay = 200L;
+
+        View view = new View(sContext);
+        attachViewToWindow(view);
+        sInstrumentation.waitForIdleSync();
+        ViewRootImpl viewRootImpl = view.getViewRootImpl();
+
+        sInstrumentation.runOnMainSync(() -> {
+            assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
+            viewRootImpl.votePreferredFrameRate(24);
+            assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
+            view.invalidate();
+            assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
+        });
+
+        Thread.sleep(delay);
+        assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
+    }
+
+    /**
      * Test the logic of infrequent layer:
      * - NORMAL for infrequent update: FT2-FT1 > 100 && FT3-FT2 > 100.
      * - HIGH/NORMAL based on size for frequent update: (FT3-FT2) + (FT2 - FT1) < 100.
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index ade20d2..1fd1003 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -66,6 +66,12 @@
     src: "preinstalled-packages-strict-signature.xml",
 }
 
+prebuilt_etc {
+    name: "enhanced-confirmation.xml",
+    sub_dir: "sysconfig",
+    src: "enhanced-confirmation.xml",
+}
+
 // Privapp permission whitelist files
 
 prebuilt_etc {
diff --git a/data/etc/enhanced-confirmation.xml b/data/etc/enhanced-confirmation.xml
new file mode 100644
index 0000000..4a9dd2f
--- /dev/null
+++ b/data/etc/enhanced-confirmation.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 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.
+ -->
+
+<!--
+This XML defines an allowlist of packages that should be exempt from ECM (Enhanced Confirmation
+Mode).
+
+Example usage:
+
+    <enhanced-confirmation-trusted-installer
+         package="com.example.app"
+         signature="E9:7A:BC:2C:D1:CA:8D:58:6A:57:0B:8C:F8:60:AA:D2:8D:13:30:2A:FB:C9:00:2C:5D:53:B2:6C:09:A4:85:A0"/>
+
+This indicates that "com.example.app" should be exempt from ECM, and that, if "com.example.app" is
+an installer, all packages installed via "com.example.app" will also be exempt from ECM.
+-->
+
+<config></config>
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index dd82fed..50b167e 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -489,7 +489,7 @@
             @Surface.ChangeFrameRateStrategy int changeFrameRateStrategy) {
         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "postOnSetFrameRateEventFromNative");
         try {
-            if (Flags.toolkitSetFrameRate()) {
+            if (Flags.toolkitSetFrameRateReadOnly()) {
                 SurfaceTexture st = weakSelf.get();
                 if (st != null) {
                     Handler handler = st.mOnSetFrameRateHandler;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index d0db708..a43a951 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -259,7 +259,7 @@
     /** One handed mode controller to register transition listener. */
     private final Optional<OneHandedController> mOneHandedOptional;
     /** Drag and drop controller to register listener for onDragStarted. */
-    private final Optional<DragAndDropController> mDragAndDropController;
+    private final DragAndDropController mDragAndDropController;
     /** Used to send bubble events to launcher. */
     private Bubbles.BubbleStateListener mBubbleStateListener;
 
@@ -285,7 +285,7 @@
             BubblePositioner positioner,
             DisplayController displayController,
             Optional<OneHandedController> oneHandedOptional,
-            Optional<DragAndDropController> dragAndDropController,
+            DragAndDropController dragAndDropController,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler,
             @ShellBackgroundThread ShellExecutor bgExecutor,
@@ -463,7 +463,7 @@
                 });
 
         mOneHandedOptional.ifPresent(this::registerOneHandedState);
-        mDragAndDropController.ifPresent(controller -> controller.addListener(this::collapseStack));
+        mDragAndDropController.addListener(this::collapseStack);
 
         // Clear out any persisted bubbles on disk that no longer have a valid user.
         List<UserInfo> users = mUserManager.getAliveUsers();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt
index e1dea3b..33b61b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt
@@ -17,18 +17,19 @@
 package com.android.wm.shell.bubbles.properties
 
 import android.os.SystemProperties
+import com.android.wm.shell.Flags
 
 /** Provides bubble properties in production. */
 object ProdBubbleProperties : BubbleProperties {
 
-    // TODO(b/256873975) Should use proper flag when available to shell/launcher
-    private var _isBubbleBarEnabled =
+    private var _isBubbleBarEnabled = Flags.enableBubbleBar() ||
             SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false)
 
     override val isBubbleBarEnabled
         get() = _isBubbleBarEnabled
 
     override fun refresh() {
-        _isBubbleBarEnabled = SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false)
+        _isBubbleBarEnabled = Flags.enableBubbleBar() ||
+                SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false)
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index b52a118..d4ed017 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -83,7 +83,6 @@
             DisplayController displayController,
             DisplayImeController displayImeController,
             DisplayInsetsController displayInsetsController,
-            Optional<DragAndDropController> dragAndDropController,
             Transitions transitions,
             TransactionPool transactionPool,
             IconProvider iconProvider,
@@ -94,8 +93,8 @@
             SystemWindows systemWindows) {
         return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
                 shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
-                displayImeController, displayInsetsController, dragAndDropController, transitions,
-                transactionPool, iconProvider, recentTasks, launchAdjacentController, mainExecutor,
-                mainHandler, systemWindows);
+                displayImeController, displayInsetsController, transitions, transactionPool,
+                iconProvider, recentTasks, launchAdjacentController, mainExecutor, mainHandler,
+                systemWindows);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index fc97c798..0d6a852 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -78,7 +78,6 @@
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
-import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.freeform.FreeformComponents;
 import com.android.wm.shell.fullscreen.FullscreenTaskListener;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
@@ -203,20 +202,6 @@
 
     @WMSingleton
     @Provides
-    static Optional<DragAndDropController> provideDragAndDropController(Context context,
-            ShellInit shellInit,
-            ShellController shellController,
-            ShellCommandHandler shellCommandHandler,
-            DisplayController displayController,
-            UiEventLogger uiEventLogger,
-            IconProvider iconProvider,
-            @ShellMainThread ShellExecutor mainExecutor) {
-        return Optional.ofNullable(DragAndDropController.create(context, shellInit, shellController,
-                shellCommandHandler, displayController, uiEventLogger, iconProvider, mainExecutor));
-    }
-
-    @WMSingleton
-    @Provides
     static ShellTaskOrganizer provideShellTaskOrganizer(
             Context context,
             ShellInit shellInit,
@@ -911,7 +896,6 @@
             DisplayController displayController,
             DisplayImeController displayImeController,
             DisplayInsetsController displayInsetsController,
-            Optional<DragAndDropController> dragAndDropControllerOptional,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<BubbleController> bubblesOptional,
             Optional<SplitScreenController> splitScreenOptional,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 36f06e8..ead5ad2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -172,7 +172,7 @@
             BubblePositioner positioner,
             DisplayController displayController,
             @DynamicOverride Optional<OneHandedController> oneHandedOptional,
-            Optional<DragAndDropController> dragAndDropController,
+            DragAndDropController dragAndDropController,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler,
             @ShellBackgroundThread ShellExecutor bgExecutor,
@@ -338,7 +338,7 @@
             DisplayController displayController,
             DisplayImeController displayImeController,
             DisplayInsetsController displayInsetsController,
-            Optional<DragAndDropController> dragAndDropController,
+            DragAndDropController dragAndDropController,
             Transitions transitions,
             TransactionPool transactionPool,
             IconProvider iconProvider,
@@ -553,6 +553,24 @@
     }
 
     //
+    // Drag and drop
+    //
+
+    @WMSingleton
+    @Provides
+    static DragAndDropController provideDragAndDropController(Context context,
+            ShellInit shellInit,
+            ShellController shellController,
+            ShellCommandHandler shellCommandHandler,
+            DisplayController displayController,
+            UiEventLogger uiEventLogger,
+            IconProvider iconProvider,
+            @ShellMainThread ShellExecutor mainExecutor) {
+        return new DragAndDropController(context, shellInit, shellController,
+                shellCommandHandler, displayController, uiEventLogger, iconProvider, mainExecutor);
+    }
+
+    //
     // Misc
     //
 
@@ -562,6 +580,7 @@
     @ShellCreateTriggerOverride
     @Provides
     static Object provideIndependentShellComponentsToCreate(
+            DragAndDropController dragAndDropController,
             DefaultMixedHandler defaultMixedHandler) {
         return new Object();
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index f82212d..7c8fcbb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -248,6 +248,12 @@
 
         // Check if count changed
         if (prevCount != newCount) {
+            KtProtoLog.d(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopTaskRepo: visibleTaskCount has changed from %d to %d",
+                prevCount,
+                newCount
+            )
             notifyVisibleTaskListeners(displayId, newCount)
         }
     }
@@ -262,6 +268,11 @@
      * Get number of tasks that are marked as visible on given [displayId]
      */
     fun getVisibleTaskCount(displayId: Int): Int {
+        KtProtoLog.d(
+            WM_SHELL_DESKTOP_MODE,
+            "DesktopTaskRepo: visibleTaskCount= %d",
+            displayData[displayId]?.visibleTasks?.size ?: 0
+        )
         return displayData[displayId]?.visibleTasks?.size ?: 0
     }
 
@@ -290,6 +301,10 @@
             taskId
         )
         freeformTasksInZOrder.remove(taskId)
+        KtProtoLog.d(
+            WM_SHELL_DESKTOP_MODE,
+            "DesktopTaskRepo: remaining freeform tasks: " + freeformTasksInZOrder.toDumpString()
+        )
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index fdfb6f3..269c369 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -105,25 +105,7 @@
         void onDragStarted();
     }
 
-    /**
-     * Creates {@link DragAndDropController}. Returns {@code null} if the feature is disabled.
-     */
-    public static DragAndDropController create(Context context,
-            ShellInit shellInit,
-            ShellController shellController,
-            ShellCommandHandler shellCommandHandler,
-            DisplayController displayController,
-            UiEventLogger uiEventLogger,
-            IconProvider iconProvider,
-            ShellExecutor mainExecutor) {
-        if (!context.getResources().getBoolean(R.bool.config_enableShellDragDrop)) {
-            return null;
-        }
-        return new DragAndDropController(context, shellInit, shellController, shellCommandHandler,
-                displayController, uiEventLogger, iconProvider, mainExecutor);
-    }
-
-    DragAndDropController(Context context,
+    public DragAndDropController(Context context,
             ShellInit shellInit,
             ShellController shellController,
             ShellCommandHandler shellCommandHandler,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 70cb2fc..1b124c2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -184,7 +184,7 @@
     private final DisplayController mDisplayController;
     private final DisplayImeController mDisplayImeController;
     private final DisplayInsetsController mDisplayInsetsController;
-    private final Optional<DragAndDropController> mDragAndDropController;
+    private final DragAndDropController mDragAndDropController;
     private final Transitions mTransitions;
     private final TransactionPool mTransactionPool;
     private final IconProvider mIconProvider;
@@ -214,7 +214,7 @@
             DisplayController displayController,
             DisplayImeController displayImeController,
             DisplayInsetsController displayInsetsController,
-            Optional<DragAndDropController> dragAndDropController,
+            DragAndDropController dragAndDropController,
             Transitions transitions,
             TransactionPool transactionPool,
             IconProvider iconProvider,
@@ -290,7 +290,7 @@
         mDisplayController = displayController;
         mDisplayImeController = displayImeController;
         mDisplayInsetsController = displayInsetsController;
-        mDragAndDropController = Optional.of(dragAndDropController);
+        mDragAndDropController = dragAndDropController;
         mTransitions = transitions;
         mTransactionPool = transactionPool;
         mIconProvider = iconProvider;
@@ -328,7 +328,9 @@
             // TODO: Multi-display
             mStageCoordinator = createStageCoordinator();
         }
-        mDragAndDropController.ifPresent(controller -> controller.setSplitScreenController(this));
+        if (mDragAndDropController != null) {
+            mDragAndDropController.setSplitScreenController(this);
+        }
         mWindowDecorViewModel.ifPresent(viewModel -> viewModel.setSplitScreenController(this));
         mDesktopTasksController.ifPresent(controller -> controller.setSplitScreenController(this));
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index e8894a83..b60e361 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -451,6 +451,8 @@
             mPendingResize.onConsumed(aborted);
             mPendingResize = null;
         }
+
+        // TODO: handle transition consumed for active remote handler
     }
 
     void onFinish(WindowContainerTransaction wct) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index c101425..aec4d11 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -74,7 +74,6 @@
             DisplayController displayController,
             DisplayImeController displayImeController,
             DisplayInsetsController displayInsetsController,
-            Optional<DragAndDropController> dragAndDropController,
             Transitions transitions,
             TransactionPool transactionPool,
             IconProvider iconProvider,
@@ -85,7 +84,7 @@
             SystemWindows systemWindows) {
         super(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer,
                 syncQueue, rootTDAOrganizer, displayController, displayImeController,
-                displayInsetsController, dragAndDropController, transitions, transactionPool,
+                displayInsetsController, null, transitions, transactionPool,
                 iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
                 Optional.empty(), mainExecutor);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 4355ed2..94519a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -74,6 +74,9 @@
             @Override
             public void onTransitionFinished(WindowContainerTransaction wct,
                     SurfaceControl.Transaction sct) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                        "Finished one-shot remote transition %s for (#%d).", mRemote,
+                        info.getDebugId());
                 if (mRemote.asBinder() != null) {
                     mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
                 }
@@ -82,8 +85,8 @@
                 }
                 mMainExecutor.execute(() -> {
                     finishCallback.onTransitionFinished(wct);
+                    mRemote = null;
                 });
-                mRemote = null;
             }
         };
         Transitions.setRunningRemoteTransitionDelegate(mRemote.getAppThread());
@@ -115,17 +118,24 @@
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Merging registered One-shot remote"
+                + " transition %s for (#%d).", mRemote, info.getDebugId());
         IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
             @Override
             public void onTransitionFinished(WindowContainerTransaction wct,
                     SurfaceControl.Transaction sct) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                        "Finished merging one-shot remote transition %s for (#%d).", mRemote,
+                        info.getDebugId());
                 // We have merged, since we sent the transaction over binder, the one in this
                 // process won't be cleared if the remote applied it. We don't actually know if the
                 // remote applied the transaction, but applying twice will break surfaceflinger
                 // so just assume the worst-case and clear the local transaction.
                 t.clear();
-                mMainExecutor.execute(() -> finishCallback.onTransitionFinished(wct));
-                mRemote = null;
+                mMainExecutor.execute(() -> {
+                    finishCallback.onTransitionFinished(wct);
+                    mRemote = null;
+                });
             }
         };
         try {
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index 72ddecc..3d7e559 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -1,6 +1,13 @@
 package: "com.android.graphics.hwui.flags"
 
 flag {
+  name: "clip_shader"
+  namespace: "core_graphics"
+  description: "API for canvas shader clipping operations"
+  bug: "280116960"
+}
+
+flag {
   name: "matrix_44"
   namespace: "core_graphics"
   description: "API for 4x4 matrix and related canvas functions"
diff --git a/location/java/com/android/internal/location/altitude/GeoidMap.java b/location/java/com/android/internal/location/altitude/GeoidMap.java
index 9bf5689..df9ca97 100644
--- a/location/java/com/android/internal/location/altitude/GeoidMap.java
+++ b/location/java/com/android/internal/location/altitude/GeoidMap.java
@@ -51,6 +51,10 @@
  */
 public final class GeoidMap {
 
+    private static final String GEOID_HEIGHT_PREFIX = "geoid-height";
+
+    private static final String EXPIRATION_DISTANCE_PREFIX = "expiration-distance";
+
     private static final Object GEOID_HEIGHT_PARAMS_LOCK = new Object();
 
     private static final Object EXPIRATION_DISTANCE_PARAMS_LOCK = new Object();
@@ -82,8 +86,7 @@
     public static MapParamsProto getGeoidHeightParams(@NonNull Context context) throws IOException {
         synchronized (GEOID_HEIGHT_PARAMS_LOCK) {
             if (sGeoidHeightParams == null) {
-                // TODO: b/304375846 - Configure with disk tile prefix once resources are updated.
-                sGeoidHeightParams = parseParams(context);
+                sGeoidHeightParams = parseParams(context, GEOID_HEIGHT_PREFIX);
             }
             return sGeoidHeightParams;
         }
@@ -99,17 +102,17 @@
             throws IOException {
         synchronized (EXPIRATION_DISTANCE_PARAMS_LOCK) {
             if (sExpirationDistanceParams == null) {
-                // TODO: b/304375846 - Configure with disk tile prefix once resources are updated.
-                sExpirationDistanceParams = parseParams(context);
+                sExpirationDistanceParams = parseParams(context, EXPIRATION_DISTANCE_PREFIX);
             }
             return sExpirationDistanceParams;
         }
     }
 
     @NonNull
-    private static MapParamsProto parseParams(@NonNull Context context) throws IOException {
+    private static MapParamsProto parseParams(@NonNull Context context, @NonNull String prefix)
+            throws IOException {
         try (InputStream is = context.getApplicationContext().getAssets().open(
-                "geoid_height_map/map-params.pb")) {
+                "geoid_map/" + prefix + "-params.pb")) {
             return MapParamsProto.parseFrom(is.readAllBytes());
         }
     }
@@ -267,7 +270,8 @@
     @NonNull
     public double[] readGeoidHeights(@NonNull MapParamsProto params, @NonNull Context context,
             @NonNull long[] s2CellIds) throws IOException {
-        return readMapValues(params, context, s2CellIds, mGeoidHeightCacheTiles);
+        return readMapValues(params, context, s2CellIds, mGeoidHeightCacheTiles,
+                GEOID_HEIGHT_PREFIX);
     }
 
     /**
@@ -278,7 +282,8 @@
     @NonNull
     public double[] readExpirationDistances(@NonNull MapParamsProto params,
             @NonNull Context context, @NonNull long[] s2CellIds) throws IOException {
-        return readMapValues(params, context, s2CellIds, mExpirationDistanceCacheTiles);
+        return readMapValues(params, context, s2CellIds, mExpirationDistanceCacheTiles,
+                EXPIRATION_DISTANCE_PREFIX);
     }
 
     /**
@@ -288,15 +293,16 @@
      */
     @NonNull
     private static double[] readMapValues(@NonNull MapParamsProto params, @NonNull Context context,
-            @NonNull long[] s2CellIds, @NonNull LruCache<Long, S2TileProto> cacheTiles)
-            throws IOException {
+            @NonNull long[] s2CellIds, @NonNull LruCache<Long, S2TileProto> cacheTiles,
+            @NonNull String prefix) throws IOException {
         validate(params, s2CellIds);
         double[] mapValuesMeters = new double[s2CellIds.length];
         if (getMapValues(params, cacheTiles::get, s2CellIds, mapValuesMeters)) {
             return mapValuesMeters;
         }
 
-        TileFunction loadedTiles = loadFromCacheAndDisk(params, context, s2CellIds, cacheTiles);
+        TileFunction loadedTiles = loadFromCacheAndDisk(params, context, s2CellIds, cacheTiles,
+                prefix);
         if (getMapValues(params, loadedTiles, s2CellIds, mapValuesMeters)) {
             return mapValuesMeters;
         }
@@ -338,7 +344,8 @@
     @NonNull
     private static TileFunction loadFromCacheAndDisk(@NonNull MapParamsProto params,
             @NonNull Context context, @NonNull long[] s2CellIds,
-            @NonNull LruCache<Long, S2TileProto> cacheTiles) throws IOException {
+            @NonNull LruCache<Long, S2TileProto> cacheTiles, @NonNull String prefix)
+            throws IOException {
         int len = s2CellIds.length;
 
         // Enable batch loading by finding all cache keys upfront.
@@ -374,7 +381,7 @@
 
             S2TileProto tile;
             try (InputStream is = context.getApplicationContext().getAssets().open(
-                    "geoid_height_map/tile-" + diskTokens[i] + ".pb")) {
+                    "geoid_map/" + prefix + "-disk-tile-" + diskTokens[i] + ".pb")) {
                 tile = S2TileProto.parseFrom(is.readAllBytes());
             }
             mergeFromDiskTile(params, tile, cacheKeys, diskTokens, i, loadedTiles, cacheTiles);
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 84c197d..be93abb 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -111,7 +111,7 @@
     void resumeRecording(in IBinder sessionToken, in Bundle params, int userId);
 
     // For playback control
-    void startPlayback(in IBinder sessionToken, int userId);
+    void resumePlayback(in IBinder sessionToken, int userId);
     void stopPlayback(in IBinder sessionToken, int mode, int userId);
 
     // For broadcast info
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 7b20c39..6b247cc 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -63,7 +63,7 @@
     void timeShiftSetMode(int mode);
     void timeShiftEnablePositionTracking(boolean enable);
 
-    void startPlayback();
+    void resumePlayback();
     void stopPlayback(int mode);
 
     // For the recording session
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 999e2cf..d145397 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -80,7 +80,7 @@
     private static final int DO_SET_TV_MESSAGE_ENABLED = 31;
     private static final int DO_NOTIFY_TV_MESSAGE = 32;
     private static final int DO_STOP_PLAYBACK = 33;
-    private static final int DO_START_PLAYBACK = 34;
+    private static final int DO_RESUME_PLAYBACK = 34;
     private static final int DO_SET_VIDEO_FROZEN = 35;
     private static final int DO_NOTIFY_AD_SESSION_DATA = 36;
 
@@ -295,8 +295,8 @@
                 mTvInputSessionImpl.stopPlayback(msg.arg1);
                 break;
             }
-            case DO_START_PLAYBACK: {
-                mTvInputSessionImpl.startPlayback();
+            case DO_RESUME_PLAYBACK: {
+                mTvInputSessionImpl.resumePlayback();
                 break;
             }
             case DO_SET_VIDEO_FROZEN: {
@@ -523,8 +523,8 @@
     }
 
     @Override
-    public void startPlayback() {
-        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_PLAYBACK));
+    public void resumePlayback() {
+        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RESUME_PLAYBACK));
     }
 
 
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 8720bfe..672f58b 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -17,6 +17,7 @@
 package android.media.tv;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -35,6 +36,7 @@
 import android.media.AudioPresentation;
 import android.media.PlaybackParams;
 import android.media.tv.ad.TvAdManager;
+import android.media.tv.flags.Flags;
 import android.media.tv.interactive.TvInteractiveAppManager;
 import android.net.Uri;
 import android.os.Binder;
@@ -131,7 +133,8 @@
             VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION, VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING,
             VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD, VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE,
             VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID, VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT,
-            VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING, VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN})
+            VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING, VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN,
+            VIDEO_UNAVAILABLE_REASON_STOPPED})
     public @interface VideoUnavailableReason {}
 
     /** Indicates that this TV message contains watermarking data */
@@ -344,9 +347,9 @@
     /**
      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
-     * it has been stopped by stopPlayback.
-     * @hide
+     * it has been stopped by {@link TvView#stopPlayback(int)}.
      */
+    @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
     public static final int VIDEO_UNAVAILABLE_REASON_STOPPED = 19;
 
     /** @hide */
@@ -3616,13 +3619,13 @@
             }
         }
 
-        void startPlayback() {
+        void resumePlayback() {
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
                 return;
             }
             try {
-                mService.startPlayback(mToken, mUserId);
+                mService.resumePlayback(mToken, mUserId);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index a022b1c..6b03041 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.MainThread;
@@ -35,6 +36,7 @@
 import android.media.AudioPresentation;
 import android.media.PlaybackParams;
 import android.media.tv.ad.TvAdManager;
+import android.media.tv.flags.Flags;
 import android.media.tv.interactive.TvInteractiveAppService;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -1615,20 +1617,20 @@
          * <p> Note that this is different form {@link #timeShiftPause()} as should release the
          * stream, making it impossible to resume from this position again.
          * @param mode
-         * @hide
          */
+        @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
         public void onStopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) {
         }
 
         /**
-         * Starts playback of the Audio, Video, and CC streams.
+         * Resumes playback of the Audio, Video, and CC streams.
          *
          * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be
-         * used after stopping playback. This is used to restart playback from the current position
+         * used after stopping playback. This is used to resume playback from the current position
          * in the live broadcast.
-         * @hide
          */
-        public void onStartPlayback() {
+        @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
+        public void onResumePlayback() {
         }
 
         /**
@@ -2112,10 +2114,10 @@
         }
 
         /**
-         * Calls {@link #onStartPlayback()}.
+         * Calls {@link #onResumePlayback()}.
          */
-        void startPlayback() {
-            onStartPlayback();
+        void resumePlayback() {
+            onResumePlayback();
         }
 
         /**
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index cb45661..ffc121e 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -37,6 +38,7 @@
 import android.media.tv.TvInputManager.Session;
 import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
 import android.media.tv.TvInputManager.SessionCallback;
+import android.media.tv.flags.Flags;
 import android.media.tv.interactive.TvInteractiveAppService;
 import android.net.Uri;
 import android.os.Bundle;
@@ -650,10 +652,10 @@
      * <p>The metadata that will continue to be filtered includes the PSI
      * (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1.
      *
-     * <p> Note that this is different form {@link #timeShiftPause()} as this completely drops
+     * <p> Note that this is different from {@link #timeShiftPause()} as this completely drops
      * the stream, making it impossible to resume from this position again.
-     * @hide
      */
+    @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
     public void stopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) {
         if (mSession != null) {
             mSession.stopPlayback(mode);
@@ -661,16 +663,19 @@
     }
 
     /**
-     * Starts playback of the Audio, Video, and CC streams.
+     * Resumes playback of the Audio, Video, and CC streams.
      *
-     * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be
-     * used after stopping playback. This is used to restart playback from the current position
-     * in the live broadcast.
-     * @hide
+     * <p> Note that this is different from {@link #timeShiftResume()} as this is intended to
+     * be used after {@link #stopPlayback(int)} has been called. This is used to resume
+     * playback from the current position in the live broadcast.
+
+     * <p> If this is the first time playback should begin, you will need to use
+     * {@link #tune(String, Uri, Bundle)} to begin playback.
      */
-    public void startPlayback() {
+    @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
+    public void resumePlayback() {
         if (mSession != null) {
-            mSession.startPlayback();
+            mSession.resumePlayback();
         }
     }
 
diff --git a/media/java/android/media/tv/ad/TvAdManager.java b/media/java/android/media/tv/ad/TvAdManager.java
index 02c0d75..f373bed 100644
--- a/media/java/android/media/tv/ad/TvAdManager.java
+++ b/media/java/android/media/tv/ad/TvAdManager.java
@@ -437,11 +437,10 @@
     }
 
     /**
-     * Returns the complete list of TV AD service on the system.
+     * Returns the complete list of TV AD services on the system.
      *
      * @return List of {@link TvAdServiceInfo} for each TV AD service that describes its meta
      * information.
-     * @hide
      */
     @NonNull
     public List<TvAdServiceInfo> getTvAdServiceList() {
@@ -1174,8 +1173,7 @@
     }
 
     /**
-     * Callback used to monitor status of the TV AD service.
-     * @hide
+     * Callback used to monitor status of the TV advertisement service.
      */
     public abstract static class TvAdServiceCallback {
         /**
diff --git a/media/java/android/media/tv/ad/TvAdService.java b/media/java/android/media/tv/ad/TvAdService.java
index 4d8f5c8b..953b5cf 100644
--- a/media/java/android/media/tv/ad/TvAdService.java
+++ b/media/java/android/media/tv/ad/TvAdService.java
@@ -17,6 +17,7 @@
 package android.media.tv.ad;
 
 import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -32,6 +33,7 @@
 import android.media.tv.TvInputManager;
 import android.media.tv.TvTrackInfo;
 import android.media.tv.TvView;
+import android.media.tv.flags.Flags;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -61,8 +63,8 @@
 
 /**
  * The TvAdService class represents a TV client-side advertisement service.
- * @hide
  */
+@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
 public abstract class TvAdService extends Service {
     private static final boolean DEBUG = false;
     private static final String TAG = "TvAdService";
@@ -73,7 +75,6 @@
      * Name under which a TvAdService component publishes information about itself. This meta-data
      * must reference an XML resource containing an
      * <code>&lt;{@link android.R.styleable#TvAdService tv-ad-service}&gt;</code> tag.
-     * @hide
      */
     public static final String SERVICE_META_DATA = "android.media.tv.ad.service";
 
@@ -92,7 +93,7 @@
 
     @Override
     @Nullable
-    public final IBinder onBind(@NonNull Intent intent) {
+    public final IBinder onBind(@Nullable Intent intent) {
         ITvAdService.Stub tvAdServiceBinder = new ITvAdService.Stub() {
             @Override
             public void registerCallback(ITvAdServiceCallback cb) {
@@ -398,6 +399,7 @@
          * @param data the original bytes to be signed.
          *
          * @see #onSigningResult(String, byte[])
+         * @hide
          */
         @CallSuper
         public void requestSigning(@NonNull String signingId, @NonNull String algorithm,
@@ -421,22 +423,22 @@
         }
 
         @Override
-        public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
+        public boolean onKeyDown(int keyCode, @Nullable KeyEvent event) {
             return false;
         }
 
         @Override
-        public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) {
+        public boolean onKeyLongPress(int keyCode, @Nullable KeyEvent event) {
             return false;
         }
 
         @Override
-        public boolean onKeyMultiple(int keyCode, int count, @NonNull KeyEvent event) {
+        public boolean onKeyMultiple(int keyCode, int count, @Nullable KeyEvent event) {
             return false;
         }
 
         @Override
-        public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
+        public boolean onKeyUp(int keyCode, @Nullable KeyEvent event) {
             return false;
         }
 
@@ -484,6 +486,7 @@
          * @param top Top position in pixels, relative to the overlay view.
          * @param right Right position in pixels, relative to the overlay view.
          * @param bottom Bottom position in pixels, relative to the overlay view.
+         *
          */
         @CallSuper
         public void layoutSurface(final int left, final int top, final int right,
diff --git a/media/java/android/media/tv/ad/TvAdServiceInfo.java b/media/java/android/media/tv/ad/TvAdServiceInfo.java
index 45dc89d..bac14e7 100644
--- a/media/java/android/media/tv/ad/TvAdServiceInfo.java
+++ b/media/java/android/media/tv/ad/TvAdServiceInfo.java
@@ -16,6 +16,7 @@
 
 package android.media.tv.ad;
 
+import android.annotation.FlaggedApi;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
@@ -26,6 +27,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.media.tv.flags.Flags;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
@@ -42,8 +44,8 @@
 
 /**
  * This class is used to specify meta information of a TV AD service.
- * @hide
  */
+@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
 public final class TvAdServiceInfo implements Parcelable {
     private static final boolean DEBUG = false;
     private static final String TAG = "TvAdServiceInfo";
@@ -95,6 +97,7 @@
         in.readStringList(mTypes);
     }
 
+    @NonNull
     public static final Creator<TvAdServiceInfo> CREATOR = new Creator<TvAdServiceInfo>() {
         @Override
         public TvAdServiceInfo createFromParcel(Parcel in) {
diff --git a/media/java/android/media/tv/ad/TvAdView.java b/media/java/android/media/tv/ad/TvAdView.java
index 604dbd5..be88506 100644
--- a/media/java/android/media/tv/ad/TvAdView.java
+++ b/media/java/android/media/tv/ad/TvAdView.java
@@ -17,6 +17,7 @@
 package android.media.tv.ad;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -29,6 +30,7 @@
 import android.media.tv.TvTrackInfo;
 import android.media.tv.TvView;
 import android.media.tv.ad.TvAdManager.Session.FinishedInputEventCallback;
+import android.media.tv.flags.Flags;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -48,9 +50,9 @@
 import java.util.concurrent.Executor;
 
 /**
- * Displays contents of TV AD services.
- * @hide
+ * Displays contents of TV advertisement services.
  */
+@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
 public class TvAdView extends ViewGroup {
     private static final String TAG = "TvAdView";
     private static final boolean DEBUG = false;
@@ -182,14 +184,12 @@
         return true;
     }
 
-    /** @hide */
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
         createSessionMediaView();
     }
 
-    /** @hide */
     @Override
     public void onDetachedFromWindow() {
         removeSessionMediaView();
@@ -381,6 +381,7 @@
      * @param event The input event.
      * @return If you handled the event, return {@code true}. If you want to allow the event to be
      *         handled by the next receiver, return {@code false}.
+     * @hide
      */
     public boolean onUnhandledInputEvent(@NonNull InputEvent event) {
         return false;
@@ -422,7 +423,7 @@
     }
 
     @Override
-    public boolean dispatchKeyEvent(@NonNull KeyEvent event) {
+    public boolean dispatchKeyEvent(@Nullable KeyEvent event) {
         if (super.dispatchKeyEvent(event)) {
             return true;
         }
@@ -465,6 +466,7 @@
 
     /**
      * Stops the AD service.
+     * @hide
      */
     public void stopAdService() {
         if (DEBUG) {
@@ -479,6 +481,7 @@
      * Resets the AD service.
      *
      * <p>This releases the resources of the corresponding {@link TvAdService.Session}.
+     * @hide
      */
     public void resetAdService() {
         if (DEBUG) {
@@ -493,6 +496,7 @@
      * Sends current video bounds to related TV AD service.
      *
      * @param bounds the rectangle area for rendering the current video.
+     * @hide
      */
     public void sendCurrentVideoBounds(@NonNull Rect bounds) {
         if (DEBUG) {
@@ -508,6 +512,7 @@
      *
      * @param channelUri The current channel URI; {@code null} if there is no currently tuned
      *                   channel.
+     * @hide
      */
     public void sendCurrentChannelUri(@Nullable Uri channelUri) {
         if (DEBUG) {
@@ -520,6 +525,7 @@
 
     /**
      * Sends track info list to related TV AD service.
+     * @hide
      */
     public void sendTrackInfoList(@Nullable List<TvTrackInfo> tracks) {
         if (DEBUG) {
@@ -536,6 +542,7 @@
      * @param inputId The current TV input ID whose channel is tuned. {@code null} if no channel is
      *                tuned.
      * @see android.media.tv.TvInputInfo
+     * @hide
      */
     public void sendCurrentTvInputId(@Nullable String inputId) {
         if (DEBUG) {
@@ -577,6 +584,7 @@
      *     can also be added to the params.
      *
      * @see #ERROR_KEY_METHOD_NAME
+     * @hide
      */
     public void notifyError(@NonNull String errMsg, @NonNull Bundle params) {
         if (DEBUG) {
@@ -599,6 +607,7 @@
      *             {@link TvInputManager#TV_MESSAGE_KEY_RAW_DATA}.
      *             See {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE} for more information on
      *             how to parse this data.
+     * @hide
      */
     public void notifyTvMessage(@NonNull @TvInputManager.TvMessageType int type,
             @NonNull Bundle data) {
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 7b6dc38..6b0620c 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -17,6 +17,7 @@
 package android.media.tv.interactive;
 
 import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.MainThread;
 import android.annotation.NonNull;
@@ -44,6 +45,7 @@
 import android.media.tv.TvRecordingInfo;
 import android.media.tv.TvTrackInfo;
 import android.media.tv.TvView;
+import android.media.tv.flags.Flags;
 import android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback;
 import android.net.Uri;
 import android.net.http.SslCertificate;
@@ -959,12 +961,12 @@
 
         /**
          * Called when the TV App sends the selected track info as a response to
-         * requestSelectedTrackInfo.
+         * {@link #requestSelectedTrackInfo()}
          *
-         * @param tracks
-         * @hide
+         * @param tracks A list of {@link TvTrackInfo} that are currently selected
          */
-        public void onSelectedTrackInfo(List<TvTrackInfo> tracks) {
+        @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
+        public void onSelectedTrackInfo(@NonNull List<TvTrackInfo> tracks) {
         }
 
         @Override
@@ -1373,13 +1375,13 @@
         }
 
         /**
-         * Requests the currently selected {@link TvTrackInfo} from the TV App.
+         * Requests a list of the currently selected {@link TvTrackInfo} from the TV App.
          *
          * <p> Normally, track info cannot be synchronized until the channel has
-         * been changed. This is used when the session of the TIAS is newly
-         * created and the normal synchronization has not happened yet.
-         * @hide
+         * been changed. This is used when the session of the {@link TvInteractiveAppService}
+         * is newly created and the normal synchronization has not happened yet.
          */
+        @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
         @CallSuper
         public void requestSelectedTrackInfo() {
             executeOrPostRunnableOnMainThread(() -> {
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 3b29574..584ea84 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -17,6 +17,7 @@
 package android.media.tv.interactive;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -30,6 +31,7 @@
 import android.media.tv.TvRecordingInfo;
 import android.media.tv.TvTrackInfo;
 import android.media.tv.TvView;
+import android.media.tv.flags.Flags;
 import android.media.tv.interactive.TvInteractiveAppManager.Session;
 import android.media.tv.interactive.TvInteractiveAppManager.Session.FinishedInputEventCallback;
 import android.media.tv.interactive.TvInteractiveAppManager.SessionCallback;
@@ -585,8 +587,9 @@
     /**
      * Sends the currently selected track info to the TV Interactive App.
      *
-     * @hide
+     * @param tracks list of {@link TvTrackInfo} of the currently selected track(s)
      */
+    @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
     public void sendSelectedTrackInfo(@Nullable List<TvTrackInfo> tracks) {
         if (DEBUG) {
             Log.d(TAG, "sendSelectedTrackInfo");
@@ -1248,8 +1251,8 @@
          * called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         * @hide
          */
+        @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
         public void onRequestSelectedTrackInfo(@NonNull String iAppServiceId) {
         }
 
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java b/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
index 70202463..c18a2de 100644
--- a/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
+++ b/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
@@ -41,7 +41,7 @@
 
 
     public EffectsTest() {
-        Log.d(TAG, "contructor");
+        Log.d(TAG, "constructor");
     }
 
     @Override
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index 9742d46..9f94ef9 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -98,12 +98,12 @@
     field public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
     field public static final String EXTRA_TAG = "android.nfc.extra.TAG";
     field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_DISABLE = 0; // 0x0
-    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_KEEP = -1; // 0xffffffff
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_KEEP = -2147483648; // 0x80000000
     field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_A = 1; // 0x1
     field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_B = 2; // 0x2
     field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_F = 4; // 0x4
     field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_DISABLE = 0; // 0x0
-    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_KEEP = -1; // 0xffffffff
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_KEEP = -2147483648; // 0x80000000
     field public static final int FLAG_READER_NFC_A = 1; // 0x1
     field public static final int FLAG_READER_NFC_B = 2; // 0x2
     field public static final int FLAG_READER_NFC_BARCODE = 16; // 0x10
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 55506a1..5b917a1 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -416,18 +416,18 @@
     /**
      * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
      * <p>
-     * Setting this flag makes listening to use current flags.
+     * Setting this flag makes listening to keep the current technology configuration.
      */
     @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
-    public static final int FLAG_LISTEN_KEEP = -1;
+    public static final int FLAG_LISTEN_KEEP = 0x80000000;
 
     /**
      * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
      * <p>
-     * Setting this flag makes polling to use current flags.
+     * Setting this flag makes polling to keep the current technology configuration.
      */
     @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
-    public static final int FLAG_READER_KEEP = -1;
+    public static final int FLAG_READER_KEEP = 0x80000000;
 
     /** @hide */
     public static final int FLAG_USE_ALL_TECH = 0xff;
@@ -1785,6 +1785,8 @@
      *
      * Use {@link #FLAG_READER_KEEP} to keep current polling technology.
      * Use {@link #FLAG_LISTEN_KEEP} to keep current listenig technology.
+     * (if the _KEEP flag is specified the other technology flags shouldn't be set
+     * and are quietly ignored otherwise).
      * Use {@link #FLAG_READER_DISABLE} to disable polling.
      * Use {@link #FLAG_LISTEN_DISABLE} to disable listening.
      * Also refer to {@link #resetDiscoveryTechnology(Activity)} to restore these changes.
@@ -1816,6 +1818,15 @@
     @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
     public void setDiscoveryTechnology(@NonNull Activity activity,
             @PollTechnology int pollTechnology, @ListenTechnology int listenTechnology) {
+
+        // A special treatment of the _KEEP flags
+        if ((listenTechnology & FLAG_LISTEN_KEEP) != 0) {
+            listenTechnology = -1;
+        }
+        if ((pollTechnology & FLAG_READER_KEEP) != 0) {
+            pollTechnology = -1;
+        }
+
         if (listenTechnology == FLAG_LISTEN_DISABLE) {
             synchronized (sLock) {
                 if (!sHasNfcFeature) {
@@ -1842,10 +1853,10 @@
     }
 
     /**
-     * Restore the poll/listen technologies of NFC controller,
+     * Restore the poll/listen technologies of NFC controller to its default state,
      * which were changed by {@link #setDiscoveryTechnology(Activity , int , int)}
      *
-     * @param activity The Activity that requests to changed technologies.
+     * @param activity The Activity that requested to change technologies.
      */
 
     @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
diff --git a/packages/CrashRecovery/aconfig/flags.aconfig b/packages/CrashRecovery/aconfig/flags.aconfig
index 5636266..572a669 100644
--- a/packages/CrashRecovery/aconfig/flags.aconfig
+++ b/packages/CrashRecovery/aconfig/flags.aconfig
@@ -6,4 +6,11 @@
     description: "Feature flag for recoverability detection"
     bug: "310236690"
     is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+    name: "enable_crashrecovery"
+    namespace: "crashrecovery"
+    description: "Enables various dependencies of crashrecovery module"
+    bug: "289203818"
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 6cafcf7..bd9d2e6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -32,6 +32,7 @@
 import android.os.Bundle
 import android.os.ResultReceiver
 import android.util.Log
+import android.view.autofill.AutofillManager
 import com.android.credentialmanager.createflow.DisabledProviderInfo
 import com.android.credentialmanager.createflow.EnabledProviderInfo
 import com.android.credentialmanager.createflow.RequestDisplayInfo
@@ -80,9 +81,10 @@
                     CreateCredentialProviderData::class.java
                 ) ?: emptyList()
             RequestInfo.TYPE_GET ->
-                intent.extras?.getParcelableArrayList(
-                    ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
-                    GetCredentialProviderData::class.java
+                getEnabledProviderDataList(
+                    intent
+                ) ?: getEnabledProviderDataListFromAuthExtras(
+                    intent
                 ) ?: emptyList()
             else -> {
                 Log.d(
@@ -238,6 +240,24 @@
         )
     }
 
+    private fun getEnabledProviderDataList(intent: Intent): List<GetCredentialProviderData>? {
+        return intent.extras?.getParcelableArrayList(
+            ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
+            GetCredentialProviderData::class.java
+        )
+    }
+
+    private fun getEnabledProviderDataListFromAuthExtras(
+        intent: Intent
+    ): List<GetCredentialProviderData>? {
+        return intent.getBundleExtra(
+            AutofillManager.EXTRA_AUTH_STATE
+        ) ?.getParcelableArrayList(
+            ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
+            GetCredentialProviderData::class.java
+        )
+    }
+
     // IMPORTANT: new invocation should be mindful that this method can throw.
     private fun getCreateProviderEnableListInitialUiState(): List<EnabledProviderInfo> {
         return CreateFlowUtils.toEnabledProviderList(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 1f1d236..07f1fa3 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -16,22 +16,24 @@
 
 package com.android.credentialmanager.autofill
 
-import android.R
+import android.app.PendingIntent
 import android.app.assist.AssistStructure
 import android.content.Context
-import android.app.PendingIntent
-import android.credentials.GetCredentialResponse
-import android.credentials.GetCredentialRequest
-import android.credentials.GetCandidateCredentialsResponse
-import android.credentials.GetCandidateCredentialsException
+import android.credentials.Credential
+import android.credentials.CredentialManager
 import android.credentials.CredentialOption
+import android.credentials.GetCandidateCredentialsException
+import android.credentials.GetCandidateCredentialsResponse
+import android.credentials.GetCredentialRequest
+import android.credentials.GetCredentialResponse
+import android.credentials.selection.Entry
 import android.credentials.selection.GetCredentialProviderData
+import android.credentials.selection.ProviderData
 import android.graphics.drawable.Icon
 import android.os.Bundle
 import android.os.CancellationSignal
 import android.os.OutcomeReceiver
 import android.provider.Settings
-import android.credentials.Credential
 import android.service.autofill.AutofillService
 import android.service.autofill.Dataset
 import android.service.autofill.Field
@@ -44,12 +46,11 @@
 import android.service.autofill.SaveRequest
 import android.service.credentials.CredentialProviderService
 import android.util.Log
+import android.view.autofill.AutofillId
 import android.view.autofill.AutofillValue
 import android.view.autofill.IAutoFillManagerClient
-import android.view.autofill.AutofillId
-import android.widget.inline.InlinePresentationSpec
-import android.credentials.CredentialManager
 import android.widget.RemoteViews
+import android.widget.inline.InlinePresentationSpec
 import androidx.autofill.inline.v1.InlineSuggestionUi
 import androidx.credentials.provider.CustomCredentialEntry
 import androidx.credentials.provider.PasswordCredentialEntry
@@ -61,10 +62,9 @@
 import com.android.credentialmanager.ktx.credentialEntry
 import com.android.credentialmanager.model.CredentialType
 import com.android.credentialmanager.model.get.CredentialEntryInfo
-import com.android.credentialmanager.model.get.ProviderInfo
+import java.util.concurrent.Executors
 import org.json.JSONException
 import org.json.JSONObject
-import java.util.concurrent.Executors
 
 
 class CredentialAutofillService : AutofillService() {
@@ -153,8 +153,7 @@
                     return
                 }
 
-                val fillResponse = convertToFillResponse(result, request,
-                        this@CredentialAutofillService)
+                val fillResponse = convertToFillResponse(result, request)
                 if (fillResponse != null) {
                     callback.onSuccess(fillResponse)
                 } else {
@@ -231,7 +230,7 @@
     }
 
     private fun getEntryToIconMap(
-            candidateProviderDataList: MutableList<GetCredentialProviderData>
+            candidateProviderDataList: List<GetCredentialProviderData>
     ): Map<String, Icon> {
         val entryIconMap: MutableMap<String, Icon> = mutableMapOf()
         candidateProviderDataList.forEach { provider ->
@@ -261,20 +260,16 @@
 
     private fun convertToFillResponse(
             getCredResponse: GetCandidateCredentialsResponse,
-            filLRequest: FillRequest,
-            context: Context
+            filLRequest: FillRequest
     ): FillResponse? {
-        val providerList = GetFlowUtils.toProviderList(
-                getCredResponse.candidateProviderDataList,
-                context)
-        if (providerList.isEmpty()) {
+        val candidateProviders = getCredResponse.candidateProviderDataList
+        if (candidateProviders.isEmpty()) {
             return null
         }
 
-        val entryIconMap: Map<String, Icon> =
-                getEntryToIconMap(getCredResponse.candidateProviderDataList)
-        val autofillIdToProvidersMap: Map<AutofillId, List<ProviderInfo>> =
-                mapAutofillIdToProviders(providerList)
+        val entryIconMap: Map<String, Icon> = getEntryToIconMap(candidateProviders)
+        val autofillIdToProvidersMap: Map<AutofillId, ArrayList<GetCredentialProviderData>> =
+                mapAutofillIdToProviders(candidateProviders)
         val fillResponseBuilder = FillResponse.Builder()
         var validFillResponse = false
         autofillIdToProvidersMap.forEach { (autofillId, providers) ->
@@ -292,11 +287,14 @@
     private fun processProvidersForAutofillId(
             filLRequest: FillRequest,
             autofillId: AutofillId,
-            providerList: List<ProviderInfo>,
+            providerDataList: ArrayList<GetCredentialProviderData>,
             entryIconMap: Map<String, Icon>,
             fillResponseBuilder: FillResponse.Builder,
             bottomSheetPendingIntent: PendingIntent?
     ): Boolean {
+        val providerList = GetFlowUtils.toProviderList(
+            providerDataList,
+            this@CredentialAutofillService)
         if (providerList.isEmpty()) {
             return false
         }
@@ -340,7 +338,7 @@
                 return@usernameLoop
             }
             if (i >= maxInlineItemCount && i >= lastDropdownDatasetIndex) {
-                return@usernameLoop;
+                return@usernameLoop
             }
             val icon: Icon = if (primaryEntry.icon == null) {
                 // The empty entry icon has non-null icon reference but null drawable reference.
@@ -398,19 +396,20 @@
                 inlinePresentationSpecsCount)
         if (datasetAdded && bottomSheetPendingIntent != null && pinnedSpec != null) {
             addPinnedInlineSuggestion(bottomSheetPendingIntent, pinnedSpec, autofillId,
-                    fillResponseBuilder)
+                    fillResponseBuilder, providerDataList)
         }
         return datasetAdded
     }
 
-    private fun createInlinePresentation(primaryEntry: CredentialEntryInfo,
-                                         pendingIntent: PendingIntent,
-                                         icon: Icon,
-                                         spec: InlinePresentationSpec,
-                                         duplicateDisplayNameForPasskeys: MutableMap<String, Boolean>):
-            InlinePresentation {
-        val displayName: String = if (primaryEntry.credentialType == CredentialType.PASSKEY
-                && primaryEntry.displayName != null) {
+    private fun createInlinePresentation(
+        primaryEntry: CredentialEntryInfo,
+        pendingIntent: PendingIntent,
+        icon: Icon,
+        spec: InlinePresentationSpec,
+        duplicateDisplayNameForPasskeys: MutableMap<String, Boolean>
+    ): InlinePresentation {
+        val displayName: String = if (primaryEntry.credentialType == CredentialType.PASSKEY &&
+            primaryEntry.displayName != null) {
             primaryEntry.displayName!!
         } else {
             primaryEntry.userName
@@ -430,7 +429,8 @@
     private fun addDropdownMoreOptionsPresentation(
             bottomSheetPendingIntent: PendingIntent,
             autofillId: AutofillId,
-            fillResponseBuilder: FillResponse.Builder) {
+            fillResponseBuilder: FillResponse.Builder
+    ) {
         val presentationBuilder = Presentations.Builder()
                 .setMenuPresentation(RemoteViewsFactory.createMoreSignInOptionsPresentation(this))
 
@@ -460,7 +460,8 @@
             bottomSheetPendingIntent: PendingIntent,
             spec: InlinePresentationSpec,
             autofillId: AutofillId,
-            fillResponseBuilder: FillResponse.Builder
+            fillResponseBuilder: FillResponse.Builder,
+            providerDataList: ArrayList<GetCredentialProviderData>
     ) {
         val dataSetBuilder = Dataset.Builder()
         val sliceBuilder = InlineSuggestionUi
@@ -471,6 +472,10 @@
                 .setInlinePresentation(InlinePresentation(
                         sliceBuilder.build().slice, spec, /* pinned= */ true))
 
+        val extraBundle = Bundle()
+        extraBundle.putParcelableArrayList(
+                        ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, providerDataList)
+
         fillResponseBuilder.addDataset(
                 dataSetBuilder
                         .setField(
@@ -479,6 +484,7 @@
                                         presentationBuilder.build())
                                         .build())
                         .setAuthentication(bottomSheetPendingIntent.intentSender)
+                        .setAuthenticationExtras(extraBundle)
                         .build()
         )
     }
@@ -514,16 +520,16 @@
      *     }
      */
     private fun mapAutofillIdToProviders(
-            providerList: List<ProviderInfo>
-    ): Map<AutofillId, List<ProviderInfo>> {
-        val autofillIdToProviders: MutableMap<AutofillId, MutableList<ProviderInfo>> =
-                mutableMapOf()
+        providerList: List<GetCredentialProviderData>
+    ): Map<AutofillId, ArrayList<GetCredentialProviderData>> {
+        val autofillIdToProviders: MutableMap<AutofillId, ArrayList<GetCredentialProviderData>> =
+            mutableMapOf()
         providerList.forEach { provider ->
             val autofillIdToCredentialEntries:
-                    MutableMap<AutofillId, MutableList<CredentialEntryInfo>> =
-                    mapAutofillIdToCredentialEntries(provider.credentialEntryList)
+                    MutableMap<AutofillId, ArrayList<Entry>> =
+                mapAutofillIdToCredentialEntries(provider.credentialEntries)
             autofillIdToCredentialEntries.forEach { (autofillId, entries) ->
-                autofillIdToProviders.getOrPut(autofillId) { mutableListOf() }
+                autofillIdToProviders.getOrPut(autofillId) { ArrayList() }
                         .add(copyProviderInfo(provider, entries))
             }
         }
@@ -531,13 +537,13 @@
     }
 
     private fun mapAutofillIdToCredentialEntries(
-            credentialEntryList: List<CredentialEntryInfo>
-    ): MutableMap<AutofillId, MutableList<CredentialEntryInfo>> {
+            credentialEntryList: List<Entry>
+    ): MutableMap<AutofillId, ArrayList<Entry>> {
         val autofillIdToCredentialEntries:
-                MutableMap<AutofillId, MutableList<CredentialEntryInfo>> = mutableMapOf()
+                MutableMap<AutofillId, ArrayList<Entry>> = mutableMapOf()
         credentialEntryList.forEach entryLoop@{ credentialEntry ->
             val autofillId: AutofillId? = credentialEntry
-                    .fillInIntent
+                    .frameworkExtrasIntent
                     ?.getParcelableExtra(
                             CredentialProviderService.EXTRA_AUTOFILL_ID,
                             AutofillId::class.java)
@@ -546,24 +552,22 @@
                         " Integration might be disabled.")
                 return@entryLoop
             }
-            autofillIdToCredentialEntries.getOrPut(autofillId) { mutableListOf() }
+            autofillIdToCredentialEntries.getOrPut(autofillId) { ArrayList() }
                     .add(credentialEntry)
         }
         return autofillIdToCredentialEntries
     }
 
     private fun copyProviderInfo(
-            providerInfo: ProviderInfo,
-            credentialList: List<CredentialEntryInfo>
-    ): ProviderInfo {
-        return ProviderInfo(
-                providerInfo.id,
-                providerInfo.icon,
-                providerInfo.displayName,
-                credentialList,
-                providerInfo.authenticationEntryList,
-                providerInfo.remoteEntry,
-                providerInfo.actionEntryList
+            providerInfo: GetCredentialProviderData,
+            credentialList: List<Entry>
+    ): GetCredentialProviderData {
+        return GetCredentialProviderData(
+            providerInfo.providerFlattenedComponentName,
+            credentialList,
+            providerInfo.actionChips,
+            providerInfo.authenticationEntries,
+            providerInfo.remoteEntry
         )
     }
 
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 6f9556f..ec519ca 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
 
 allprojects {
     extra["androidTop"] = androidTop
-    extra["jetpackComposeVersion"] = "1.6.0-rc01"
+    extra["jetpackComposeVersion"] = "1.7.0-alpha01"
 }
 
 subprojects {
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 1f78a9c..f6fbc02 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,11 +15,11 @@
 #
 
 [versions]
-agp = "8.2.1"
-compose-compiler = "1.5.1"
+agp = "8.2.2"
+compose-compiler = "1.5.8"
 dexmaker-mockito = "2.28.3"
 jvm = "17"
-kotlin = "1.9.0"
+kotlin = "1.9.22"
 truth = "1.1.5"
 
 [libraries]
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 618dc37..08a8797 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -57,13 +57,13 @@
     api("androidx.slice:slice-builders:1.1.0-alpha02")
     api("androidx.slice:slice-core:1.1.0-alpha02")
     api("androidx.slice:slice-view:1.1.0-alpha02")
-    api("androidx.compose.material3:material3:1.2.0-beta02")
+    api("androidx.compose.material3:material3:1.2.0-rc01")
     api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
     api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
     api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
     api("androidx.lifecycle:lifecycle-livedata-ktx")
     api("androidx.lifecycle:lifecycle-runtime-compose")
-    api("androidx.navigation:navigation-compose:2.7.6")
+    api("androidx.navigation:navigation-compose:2.8.0-alpha01")
     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:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index fd2f9bd..bab6781 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -7,3 +7,13 @@
     bug: "314812750"
 }
 
+flag {
+    name: "enable_cached_bluetooth_device_dedup"
+    namespace: "bluetooth"
+    description: "Enable dedup in CachedBluetoothDevice"
+    bug: "319197962"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index c97445f..647fcb9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -16,6 +16,8 @@
 
 package com.android.settingslib.bluetooth;
 
+import static com.android.settingslib.flags.Flags.enableCachedBluetoothDeviceDedup;
+
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothCsipSetCoordinator;
@@ -377,6 +379,10 @@
                 cachedDevice = mDeviceManager.addDevice(device);
             }
 
+            if (enableCachedBluetoothDeviceDedup() && bondState == BluetoothDevice.BOND_BONDED) {
+                mDeviceManager.removeDuplicateInstanceForIdentityAddress(device);
+            }
+
             for (BluetoothCallback callback : mCallbacks) {
                 callback.onDeviceBondStateChanged(cachedDevice, bondState);
             }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 560bc46..bcdb64d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1745,12 +1745,14 @@
         final BluetoothDevice tmpDevice = mDevice;
         final short tmpRssi = mRssi;
         final boolean tmpJustDiscovered = mJustDiscovered;
+        final HearingAidInfo tmpHearingAidInfo = mHearingAidInfo;
 
         // Set main device from sub device
         release();
         mDevice = newMainDevice.mDevice;
         mRssi = newMainDevice.mRssi;
         mJustDiscovered = newMainDevice.mJustDiscovered;
+        mHearingAidInfo = newMainDevice.mHearingAidInfo;
         fillData();
 
         // Set sub device from backup
@@ -1758,6 +1760,7 @@
         newMainDevice.mDevice = tmpDevice;
         newMainDevice.mRssi = tmpRssi;
         newMainDevice.mJustDiscovered = tmpJustDiscovered;
+        newMainDevice.mHearingAidInfo = tmpHearingAidInfo;
         newMainDevice.fillData();
 
         // Add the sub device back into mMemberDevices with correct hash
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 89fe268..32eec7e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -340,6 +340,20 @@
         }
     }
 
+    synchronized void removeDuplicateInstanceForIdentityAddress(BluetoothDevice device) {
+        String identityAddress = device.getIdentityAddress();
+        if (identityAddress == null || identityAddress.equals(device.getAddress())) {
+            return;
+        }
+        mCachedDevices.removeIf(d -> {
+            boolean shouldRemove = d.getDevice().getAddress().equals(identityAddress);
+            if (shouldRemove) {
+                Log.d(TAG, "Remove instance for identity address " + d);
+            }
+            return shouldRemove;
+        });
+    }
+
     public synchronized boolean onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice
             cachedDevice, int state, int profileId) {
         if (profileId == BluetoothProfile.HEARING_AID) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 1d2f790..6ee403d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -21,6 +21,7 @@
 import android.annotation.CallbackExecutor;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeAudioContentMetadata;
 import android.bluetooth.BluetoothLeBroadcast;
@@ -62,6 +63,7 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.Collectors;
 
 /**
  * LocalBluetoothLeBroadcast provides an interface between the Settings app and the functionality of
@@ -88,6 +90,8 @@
                         Settings.Secure.BLUETOOTH_LE_BROADCAST_IMPROVE_COMPATIBILITY),
             };
 
+    private final Context mContext;
+    private final CachedBluetoothDeviceManager mDeviceManager;
     private BluetoothLeBroadcast mServiceBroadcast;
     private BluetoothLeBroadcastAssistant mServiceBroadcastAssistant;
     private BluetoothLeAudioContentMetadata mBluetoothLeAudioContentMetadata;
@@ -256,8 +260,19 @@
     private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
             new BluetoothLeBroadcastAssistant.Callback() {
                 @Override
-                public void onSourceAdded(
-                        @NonNull BluetoothDevice sink, int sourceId, int reason) {}
+                public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId, int reason) {
+                    if (DEBUG) {
+                        Log.d(
+                                TAG,
+                                "onSourceAdded(), sink = "
+                                        + sink
+                                        + ", reason = "
+                                        + reason
+                                        + ", sourceId = "
+                                        + sourceId);
+                    }
+                    updateFallbackActiveDeviceIfNeeded();
+                }
 
                 @Override
                 public void onSearchStarted(int reason) {}
@@ -301,6 +316,7 @@
                                         + ", sourceId = "
                                         + sourceId);
                     }
+                    updateFallbackActiveDeviceIfNeeded();
                 }
 
                 @Override
@@ -348,7 +364,9 @@
         }
     }
 
-    LocalBluetoothLeBroadcast(Context context) {
+    LocalBluetoothLeBroadcast(Context context, CachedBluetoothDeviceManager deviceManager) {
+        mContext = context;
+        mDeviceManager = deviceManager;
         mExecutor = Executors.newSingleThreadExecutor();
         mBuilder = new BluetoothLeAudioContentMetadata.Builder();
         mContentResolver = context.getContentResolver();
@@ -430,49 +448,6 @@
         mServiceBroadcast.startBroadcast(settings);
     }
 
-    /**
-     * Start the private Broadcast for personal audio sharing or qr code sharing.
-     *
-     * <p>The broadcast will use random string for both broadcast name and subgroup program info;
-     * The broadcast will use random string for broadcast code; The broadcast will only have one
-     * subgroup due to system limitation; The subgroup language will be null.
-     *
-     * <p>If the system started the LE Broadcast, then the system calls the corresponding callback
-     * {@link BluetoothLeBroadcast.Callback}.
-     */
-    public void startPrivateBroadcast(int quality) {
-        mNewAppSourceName = "Sharing audio";
-        if (mServiceBroadcast == null) {
-            Log.d(TAG, "The BluetoothLeBroadcast is null when starting the private broadcast.");
-            return;
-        }
-        if (mServiceBroadcast.getAllBroadcastMetadata().size()
-                >= mServiceBroadcast.getMaximumNumberOfBroadcasts()) {
-            Log.d(TAG, "Skip starting the broadcast due to number limit.");
-            return;
-        }
-        String programInfo = getProgramInfo();
-        if (DEBUG) {
-            Log.d(TAG, "startBroadcast: language = null ,programInfo = " + programInfo);
-        }
-        // Current broadcast framework only support one subgroup
-        BluetoothLeBroadcastSubgroupSettings subgroupSettings =
-                buildBroadcastSubgroupSettings(
-                        /* language= */ null,
-                        programInfo,
-                        /* improveCompatibility= */
-                        BluetoothLeBroadcastSubgroupSettings.QUALITY_STANDARD == quality);
-        BluetoothLeBroadcastSettings settings =
-                buildBroadcastSettings(
-                        true, // TODO: set to false after framework fix
-                        TextUtils.isEmpty(programInfo) ? null : programInfo,
-                        (mBroadcastCode != null && mBroadcastCode.length > 0)
-                                ? mBroadcastCode
-                                : null,
-                        ImmutableList.of(subgroupSettings));
-        mServiceBroadcast.startBroadcast(settings);
-    }
-
     private BluetoothLeBroadcastSettings buildBroadcastSettings(
             boolean isPublic,
             @Nullable String broadcastName,
@@ -1027,4 +1002,80 @@
             }
         }
     }
+
+    /** Update fallback active device if needed. */
+    public void updateFallbackActiveDeviceIfNeeded() {
+        if (!isEnabled(null)) {
+            Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded due to no ongoing broadcast");
+            return;
+        }
+        if (mServiceBroadcastAssistant == null) {
+            Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded due to assistant profile is null");
+            return;
+        }
+        List<BluetoothDevice> connectedDevices = mServiceBroadcastAssistant.getConnectedDevices();
+        List<BluetoothDevice> devicesInSharing =
+                connectedDevices.stream()
+                        .filter(
+                                bluetoothDevice -> {
+                                    List<BluetoothLeBroadcastReceiveState> sourceList =
+                                            mServiceBroadcastAssistant.getAllSources(
+                                                    bluetoothDevice);
+                                    return !sourceList.isEmpty();
+                                })
+                        .collect(Collectors.toList());
+        if (devicesInSharing.isEmpty()) {
+            Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded due to no sinks in broadcast");
+            return;
+        }
+        List<BluetoothDevice> devices =
+                BluetoothAdapter.getDefaultAdapter().getMostRecentlyConnectedDevices();
+        BluetoothDevice targetDevice = null;
+        // Find the earliest connected device in sharing session.
+        int targetDeviceIdx = -1;
+        for (BluetoothDevice device : devicesInSharing) {
+            if (devices.contains(device)) {
+                int idx = devices.indexOf(device);
+                if (idx > targetDeviceIdx) {
+                    targetDeviceIdx = idx;
+                    targetDevice = device;
+                }
+            }
+        }
+        if (targetDevice == null) {
+            Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded, target is null");
+            return;
+        }
+        Log.d(
+                TAG,
+                "updateFallbackActiveDeviceIfNeeded, set active device: "
+                        + targetDevice.getAnonymizedAddress());
+        CachedBluetoothDevice targetCachedDevice = mDeviceManager.findDevice(targetDevice);
+        if (targetCachedDevice == null) {
+            Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded, fail to find cached bt device");
+            return;
+        }
+        int fallbackActiveGroupId = getFallbackActiveGroupId();
+        if (targetCachedDevice.getGroupId() == fallbackActiveGroupId) {
+            Log.d(
+                    TAG,
+                    "Skip updateFallbackActiveDeviceIfNeeded, already is fallback: "
+                            + fallbackActiveGroupId);
+            return;
+        }
+        targetCachedDevice.setActive();
+    }
+
+    private boolean isDecryptedSource(BluetoothLeBroadcastReceiveState state) {
+        return state.getPaSyncState() == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED
+                && state.getBigEncryptionState()
+                        == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING;
+    }
+
+    private int getFallbackActiveGroupId() {
+        return Settings.Secure.getInt(
+                mContext.getContentResolver(),
+                "bluetooth_le_broadcast_fallback_active_group_id",
+                BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManagerExt.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManagerExt.kt
new file mode 100644
index 0000000..5dc0237
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManagerExt.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 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.bluetooth
+
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.launch
+
+/** Returns a [Flow] that emits a [Unit] whenever the headset audio mode changes. */
+val LocalBluetoothManager.headsetAudioModeChanges: Flow<Unit>
+    get() {
+        return callbackFlow {
+            val callback =
+                object : BluetoothCallback {
+                    override fun onAudioModeChanged() {
+                        launch { send(Unit) }
+                    }
+                }
+
+            eventManager.registerCallback(callback)
+            awaitClose { eventManager.unregisterCallback(callback) }
+        }
+    }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 119aef6..79e4c37 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -257,7 +257,7 @@
             if (DEBUG) {
                 Log.d(TAG, "Adding local LE_AUDIO_BROADCAST profile");
             }
-            mLeAudioBroadcast = new LocalBluetoothLeBroadcast(mContext);
+            mLeAudioBroadcast = new LocalBluetoothLeBroadcast(mContext, mDeviceManager);
             // no event handler for the LE boradcast.
             mProfileNameMap.put(LocalBluetoothLeBroadcast.NAME, mLeAudioBroadcast);
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
new file mode 100644
index 0000000..1597b77
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 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.volume.data.repository
+
+import com.android.settingslib.media.LocalMediaManager
+import com.android.settingslib.media.MediaDevice
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.stateIn
+
+/** Repository providing data about connected media devices. */
+interface LocalMediaRepository {
+
+    /** Available devices list */
+    val mediaDevices: StateFlow<Collection<MediaDevice>>
+
+    /** Currently connected media device */
+    val currentConnectedDevice: StateFlow<MediaDevice?>
+}
+
+class LocalMediaRepositoryImpl(
+    private val localMediaManager: LocalMediaManager,
+    coroutineScope: CoroutineScope,
+    backgroundContext: CoroutineContext,
+) : LocalMediaRepository {
+
+    private val deviceUpdates: Flow<DevicesUpdate> = callbackFlow {
+        val callback =
+            object : LocalMediaManager.DeviceCallback {
+                override fun onDeviceListUpdate(newDevices: List<MediaDevice>?) {
+                    trySend(DevicesUpdate.DeviceListUpdate(newDevices ?: emptyList()))
+                }
+
+                override fun onSelectedDeviceStateChanged(
+                    device: MediaDevice?,
+                    state: Int,
+                ) {
+                    trySend(DevicesUpdate.SelectedDeviceStateChanged)
+                }
+
+                override fun onDeviceAttributesChanged() {
+                    trySend(DevicesUpdate.DeviceAttributesChanged)
+                }
+            }
+        localMediaManager.registerCallback(callback)
+        localMediaManager.startScan()
+
+        awaitClose {
+            localMediaManager.stopScan()
+            localMediaManager.unregisterCallback(callback)
+        }
+    }
+
+    override val mediaDevices: StateFlow<Collection<MediaDevice>> =
+        deviceUpdates
+            .mapNotNull {
+                if (it is DevicesUpdate.DeviceListUpdate) {
+                    it.newDevices ?: emptyList()
+                } else {
+                    null
+                }
+            }
+            .flowOn(backgroundContext)
+            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyList())
+
+    override val currentConnectedDevice: StateFlow<MediaDevice?> =
+        deviceUpdates
+            .map { localMediaManager.currentConnectedDevice }
+            .stateIn(
+                coroutineScope,
+                SharingStarted.WhileSubscribed(),
+                localMediaManager.currentConnectedDevice
+            )
+
+    private sealed interface DevicesUpdate {
+
+        data class DeviceListUpdate(val newDevices: List<MediaDevice>?) : DevicesUpdate
+
+        data object SelectedDeviceStateChanged : DevicesUpdate
+
+        data object DeviceAttributesChanged : DevicesUpdate
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
new file mode 100644
index 0000000..93aa90d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 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.volume.data.repository
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.media.AudioManager
+import android.media.session.MediaController
+import android.media.session.MediaSessionManager
+import android.media.session.PlaybackState
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.bluetooth.headsetAudioModeChanges
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+/** Provides controllers for currently active device media sessions. */
+interface MediaControllerRepository {
+
+    /** Current [MediaController]. Null is emitted when there is no active [MediaController]. */
+    val activeMediaController: StateFlow<MediaController?>
+}
+
+class MediaControllerRepositoryImpl(
+    private val context: Context,
+    private val mediaSessionManager: MediaSessionManager,
+    localBluetoothManager: LocalBluetoothManager?,
+    coroutineScope: CoroutineScope,
+    backgroundContext: CoroutineContext,
+) : MediaControllerRepository {
+
+    private val devicesChanges: Flow<Unit> =
+        callbackFlow {
+                val receiver =
+                    object : BroadcastReceiver() {
+                        override fun onReceive(context: Context?, intent: Intent?) {
+                            if (AudioManager.STREAM_DEVICES_CHANGED_ACTION == intent?.action) {
+                                launch { send(Unit) }
+                            }
+                        }
+                    }
+                context.registerReceiver(
+                    receiver,
+                    IntentFilter(AudioManager.STREAM_DEVICES_CHANGED_ACTION)
+                )
+
+                awaitClose { context.unregisterReceiver(receiver) }
+            }
+            .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), replay = 0)
+
+    override val activeMediaController: StateFlow<MediaController?> =
+        combine(
+                localBluetoothManager?.headsetAudioModeChanges?.onStart { emit(Unit) }
+                    ?: emptyFlow(),
+                devicesChanges.onStart { emit(Unit) },
+            ) { _, _ ->
+                getActiveLocalMediaController()
+            }
+            .flowOn(backgroundContext)
+            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
+
+    private fun getActiveLocalMediaController(): MediaController? {
+        var localController: MediaController? = null
+        val remoteMediaSessionLists: MutableList<String> = ArrayList()
+        for (controller in mediaSessionManager.getActiveSessions(null)) {
+            val playbackInfo: MediaController.PlaybackInfo = controller.playbackInfo ?: continue
+            val playbackState = controller.playbackState ?: continue
+            if (inactivePlaybackStates.contains(playbackState.state)) {
+                continue
+            }
+            when (playbackInfo.playbackType) {
+                MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE -> {
+                    if (localController?.packageName.equals(controller.packageName)) {
+                        localController = null
+                    }
+                    if (!remoteMediaSessionLists.contains(controller.packageName)) {
+                        remoteMediaSessionLists.add(controller.packageName)
+                    }
+                }
+                MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL -> {
+                    if (
+                        localController == null &&
+                            !remoteMediaSessionLists.contains(controller.packageName)
+                    ) {
+                        localController = controller
+                    }
+                }
+            }
+        }
+        return localController
+    }
+
+    private companion object {
+        val inactivePlaybackStates =
+            setOf(PlaybackState.STATE_STOPPED, PlaybackState.STATE_NONE, PlaybackState.STATE_ERROR)
+    }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
new file mode 100644
index 0000000..d106bce
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 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.volume.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.media.LocalMediaManager
+import com.android.settingslib.media.MediaDevice
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class LocalMediaRepositoryImplTest {
+
+    @Mock private lateinit var localMediaManager: LocalMediaManager
+    @Mock private lateinit var mediaDevice1: MediaDevice
+    @Mock private lateinit var mediaDevice2: MediaDevice
+
+    @Captor
+    private lateinit var deviceCallbackCaptor: ArgumentCaptor<LocalMediaManager.DeviceCallback>
+
+    private val testScope = TestScope()
+
+    private lateinit var underTest: LocalMediaRepository
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        underTest =
+            LocalMediaRepositoryImpl(
+                localMediaManager,
+                testScope.backgroundScope,
+                testScope.testScheduler,
+            )
+    }
+
+    @Test
+    fun mediaDevices_areUpdated() {
+        testScope.runTest {
+            var mediaDevices: Collection<MediaDevice>? = null
+            underTest.mediaDevices.onEach { mediaDevices = it }.launchIn(backgroundScope)
+            runCurrent()
+            verify(localMediaManager).registerCallback(deviceCallbackCaptor.capture())
+            deviceCallbackCaptor.value.onDeviceListUpdate(listOf(mediaDevice1, mediaDevice2))
+            runCurrent()
+
+            assertThat(mediaDevices).hasSize(2)
+            assertThat(mediaDevices).contains(mediaDevice1)
+            assertThat(mediaDevices).contains(mediaDevice2)
+        }
+    }
+
+    @Test
+    fun deviceListUpdated_currentConnectedDeviceUpdated() {
+        testScope.runTest {
+            var currentConnectedDevice: MediaDevice? = null
+            underTest.currentConnectedDevice
+                .onEach { currentConnectedDevice = it }
+                .launchIn(backgroundScope)
+            runCurrent()
+
+            `when`(localMediaManager.currentConnectedDevice).thenReturn(mediaDevice1)
+            verify(localMediaManager).registerCallback(deviceCallbackCaptor.capture())
+            deviceCallbackCaptor.value.onDeviceListUpdate(listOf(mediaDevice1, mediaDevice2))
+            runCurrent()
+
+            assertThat(currentConnectedDevice).isEqualTo(mediaDevice1)
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
new file mode 100644
index 0000000..f07b1bff
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2024 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.volume.data.repository
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.media.AudioManager
+import android.media.session.MediaController
+import android.media.session.MediaController.PlaybackInfo
+import android.media.session.MediaSessionManager
+import android.media.session.PlaybackState
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.BluetoothCallback
+import com.android.settingslib.bluetooth.BluetoothEventManager
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class MediaControllerRepositoryImplTest {
+
+    @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
+    @Captor private lateinit var callbackCaptor: ArgumentCaptor<BluetoothCallback>
+
+    @Mock private lateinit var context: Context
+    @Mock private lateinit var mediaSessionManager: MediaSessionManager
+    @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
+    @Mock private lateinit var eventManager: BluetoothEventManager
+
+    @Mock private lateinit var stoppedMediaController: MediaController
+    @Mock private lateinit var statelessMediaController: MediaController
+    @Mock private lateinit var errorMediaController: MediaController
+    @Mock private lateinit var remoteMediaController: MediaController
+    @Mock private lateinit var localMediaController: MediaController
+
+    @Mock private lateinit var remotePlaybackInfo: PlaybackInfo
+    @Mock private lateinit var localPlaybackInfo: PlaybackInfo
+
+    private val testScope = TestScope()
+
+    private lateinit var underTest: MediaControllerRepository
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        `when`(localBluetoothManager.eventManager).thenReturn(eventManager)
+
+        `when`(stoppedMediaController.playbackState).thenReturn(stateStopped)
+        `when`(stoppedMediaController.packageName).thenReturn("test.pkg.stopped")
+        `when`(statelessMediaController.playbackState).thenReturn(stateNone)
+        `when`(statelessMediaController.packageName).thenReturn("test.pkg.stateless")
+        `when`(errorMediaController.playbackState).thenReturn(stateError)
+        `when`(errorMediaController.packageName).thenReturn("test.pkg.error")
+        `when`(remoteMediaController.playbackState).thenReturn(statePlaying)
+        `when`(remoteMediaController.playbackInfo).thenReturn(remotePlaybackInfo)
+        `when`(remoteMediaController.packageName).thenReturn("test.pkg.remote")
+        `when`(localMediaController.playbackState).thenReturn(statePlaying)
+        `when`(localMediaController.playbackInfo).thenReturn(localPlaybackInfo)
+        `when`(localMediaController.packageName).thenReturn("test.pkg.local")
+
+        `when`(remotePlaybackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+        `when`(localPlaybackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
+
+        underTest =
+            MediaControllerRepositoryImpl(
+                context,
+                mediaSessionManager,
+                localBluetoothManager,
+                testScope.backgroundScope,
+                testScope.testScheduler,
+            )
+    }
+
+    @Test
+    fun playingMediaDevicesAvailable_sessionIsActive() {
+        testScope.runTest {
+            `when`(mediaSessionManager.getActiveSessions(any()))
+                .thenReturn(
+                    listOf(
+                        stoppedMediaController,
+                        statelessMediaController,
+                        errorMediaController,
+                        remoteMediaController,
+                        localMediaController
+                    )
+                )
+            var mediaController: MediaController? = null
+            underTest.activeMediaController
+                .onEach { mediaController = it }
+                .launchIn(backgroundScope)
+            runCurrent()
+
+            triggerDevicesChange()
+            triggerOnAudioModeChanged()
+            runCurrent()
+
+            assertThat(mediaController).isSameInstanceAs(localMediaController)
+        }
+    }
+
+    @Test
+    fun noPlayingMediaDevicesAvailable_sessionIsInactive() {
+        testScope.runTest {
+            `when`(mediaSessionManager.getActiveSessions(any()))
+                .thenReturn(
+                    listOf(
+                        stoppedMediaController,
+                        statelessMediaController,
+                        errorMediaController,
+                    )
+                )
+            var mediaController: MediaController? = null
+            underTest.activeMediaController
+                .onEach { mediaController = it }
+                .launchIn(backgroundScope)
+            runCurrent()
+
+            triggerDevicesChange()
+            triggerOnAudioModeChanged()
+            runCurrent()
+
+            assertThat(mediaController).isNull()
+        }
+    }
+
+    private fun triggerDevicesChange() {
+        verify(context).registerReceiver(receiverCaptor.capture(), any())
+        receiverCaptor.value.onReceive(context, Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION))
+    }
+
+    private fun triggerOnAudioModeChanged() {
+        verify(eventManager).registerCallback(callbackCaptor.capture())
+        callbackCaptor.value.onAudioModeChanged()
+    }
+
+    private companion object {
+        val statePlaying =
+            PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0, 0f).build()
+        val stateError = PlaybackState.Builder().setState(PlaybackState.STATE_ERROR, 0, 0f).build()
+        val stateStopped =
+            PlaybackState.Builder().setState(PlaybackState.STATE_STOPPED, 0, 0f).build()
+        val stateNone = PlaybackState.Builder().setState(PlaybackState.STATE_NONE, 0, 0f).build()
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 9db8b47..461ecf5d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -1769,8 +1769,10 @@
     public void switchMemberDeviceContent_switchMainDevice_switchesSuccessful() {
         mCachedDevice.mRssi = RSSI_1;
         mCachedDevice.mJustDiscovered = JUSTDISCOVERED_1;
+        mCachedDevice.setHearingAidInfo(getLeftAshaHearingAidInfo());
         mSubCachedDevice.mRssi = RSSI_2;
         mSubCachedDevice.mJustDiscovered = JUSTDISCOVERED_2;
+        mSubCachedDevice.setHearingAidInfo(getRightAshaHearingAidInfo());
         mCachedDevice.addMemberDevice(mSubCachedDevice);
 
         mCachedDevice.switchMemberDeviceContent(mSubCachedDevice);
@@ -1778,10 +1780,12 @@
         assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_2);
         assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
         assertThat(mCachedDevice.mDevice).isEqualTo(mSubDevice);
+        assertThat(mCachedDevice.getDeviceSide()).isEqualTo(HearingAidInfo.DeviceSide.SIDE_RIGHT);
         verify(mCachedDevice).fillData();
         assertThat(mSubCachedDevice.mRssi).isEqualTo(RSSI_1);
         assertThat(mSubCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
         assertThat(mSubCachedDevice.mDevice).isEqualTo(mDevice);
+        assertThat(mSubCachedDevice.getDeviceSide()).isEqualTo(HearingAidInfo.DeviceSide.SIDE_LEFT);
         verify(mSubCachedDevice).fillData();
         assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
     }
diff --git a/packages/SettingsLib/tests/unit/Android.bp b/packages/SettingsLib/tests/unit/Android.bp
index e2eda4f..6d6e2ff 100644
--- a/packages/SettingsLib/tests/unit/Android.bp
+++ b/packages/SettingsLib/tests/unit/Android.bp
@@ -32,7 +32,5 @@
         "androidx.test.ext.junit",
         "androidx.test.runner",
         "truth",
-        "kotlinx_coroutines_test",
-        "mockito-target-minus-junit4",
     ],
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 6f3c88f..ae71cec 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -612,12 +612,19 @@
             String packageName) {
         List<String> changedKeys = new ArrayList<>();
         final Iterator<Map.Entry<String, Setting>> iterator = mSettings.entrySet().iterator();
+        int index = prefix.lastIndexOf('/');
+        String namespace = index < 0 ? "" : prefix.substring(0, index);
+        Map<String, String> trunkFlagMap =
+                mNamespaceDefaults.get(namespace);
         // Delete old keys with the prefix that are not part of the new set.
+        // trunk flags will not be configured with restricted propagation
+        // trunk flags will be explicitly set, so not removing them here
         while (iterator.hasNext()) {
             Map.Entry<String, Setting> entry = iterator.next();
             final String key = entry.getKey();
             final Setting oldState = entry.getValue();
-            if (key != null && key.startsWith(prefix) && !keyValues.containsKey(key)) {
+            if (key != null && (trunkFlagMap == null || !trunkFlagMap.containsKey(key))
+                    && key.startsWith(prefix) && !keyValues.containsKey(key)) {
                 iterator.remove();
 
                 FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, key,
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index efdbfdb..055252b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -353,7 +353,7 @@
 
         /**
          * Return the first [GradientDrawable] found in [drawable], or null if none is found. If
-         * [drawable] is a [LayerDrawable], this will return the first layer that is a
+         * [drawable] is a [LayerDrawable], this will return the first layer that has a
          * [GradientDrawable].
          */
         fun findGradientDrawable(drawable: Drawable): GradientDrawable? {
@@ -367,8 +367,8 @@
 
             if (drawable is LayerDrawable) {
                 for (i in 0 until drawable.numberOfLayers) {
-                    val maybeGradient = drawable.getDrawable(i)
-                    if (maybeGradient is GradientDrawable) {
+                    val maybeGradient = findGradientDrawable(drawable.getDrawable(i))
+                    if (maybeGradient != null) {
                         return maybeGradient
                     }
                 }
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
index 3c32594..9a34d6f 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -17,7 +17,6 @@
 
 package com.android.systemui.compose
 
-import android.app.Dialog
 import android.content.Context
 import android.view.View
 import android.view.WindowInsets
@@ -28,12 +27,13 @@
 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import com.android.systemui.people.ui.viewmodel.PeopleViewModel
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
 import com.android.systemui.scene.shared.model.Scene
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.StateFlow
@@ -90,10 +90,10 @@
         throwComposeUnavailableError()
     }
 
-    override fun createStickyKeysDialog(
-        dialogFactory: SystemUIDialogFactory,
+    override fun createStickyKeysIndicatorContent(
+        context: Context,
         viewModel: StickyKeysIndicatorViewModel
-    ): Dialog {
+    ): View {
         throwComposeUnavailableError()
     }
 
@@ -114,6 +114,12 @@
         dialogFactory: BouncerDialogFactory,
     ): View = throwComposeUnavailableError()
 
+    override fun createLockscreen(
+        context: Context,
+        viewModel: LockscreenContentViewModel,
+        blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
+    ): View = throwComposeUnavailableError()
+
     private fun throwComposeUnavailableError(): Nothing {
         error(
             "Compose is not available. Make sure to check isComposeAvailable() before calling any" +
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/LockscreenSceneModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
index 725aef2..fc3912e 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
@@ -16,6 +16,16 @@
 
 package com.android.systemui.scene
 
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
 import dagger.Module
+import dagger.Provides
 
-@Module interface LockscreenSceneModule
+@Module
+interface LockscreenSceneModule {
+    companion object {
+        @Provides
+        fun providesLockscreenBlueprints(): Set<LockscreenSceneBlueprint> {
+            return emptySet()
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
index afb860e..4cc7332 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -16,13 +16,14 @@
 
 package com.android.systemui.compose
 
-import android.app.Dialog
 import android.content.Context
 import android.graphics.Point
 import android.view.View
 import android.view.WindowInsets
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
@@ -39,8 +40,12 @@
 import com.android.systemui.communal.ui.compose.CommunalHub
 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
 import com.android.systemui.communal.widgets.WidgetConfigurator
-import com.android.systemui.keyboard.stickykeys.ui.view.StickyKeysIndicator
+import com.android.systemui.keyboard.stickykeys.ui.view.createStickyKeyIndicatorView
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
+import com.android.systemui.keyguard.ui.composable.LockscreenContent
+import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import com.android.systemui.people.ui.compose.PeopleScreen
 import com.android.systemui.people.ui.viewmodel.PeopleViewModel
 import com.android.systemui.qs.footer.ui.compose.FooterActions
@@ -50,8 +55,6 @@
 import com.android.systemui.scene.ui.composable.ComposableScene
 import com.android.systemui.scene.ui.composable.SceneContainer
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
-import com.android.systemui.statusbar.phone.create
 import com.android.systemui.volume.panel.ui.composable.VolumePanelRoot
 import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
 import kotlinx.coroutines.CoroutineScope
@@ -140,11 +143,11 @@
         }
     }
 
-    override fun createStickyKeysDialog(
-        dialogFactory: SystemUIDialogFactory,
+    override fun createStickyKeysIndicatorContent(
+        context: Context,
         viewModel: StickyKeysIndicatorViewModel
-    ): Dialog {
-        return dialogFactory.create { StickyKeysIndicator(viewModel) }
+    ): View {
+        return createStickyKeyIndicatorView(context, viewModel)
     }
 
     override fun createCommunalView(
@@ -214,4 +217,19 @@
             setContent { PlatformTheme { BouncerContent(viewModel, dialogFactory) } }
         }
     }
+
+    override fun createLockscreen(
+        context: Context,
+        viewModel: LockscreenContentViewModel,
+        blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
+    ): View {
+        val sceneBlueprints =
+            blueprints.mapNotNull { it as? ComposableLockscreenSceneBlueprint }.toSet()
+        return ComposeView(context).apply {
+            setContent {
+                LockscreenContent(viewModel = viewModel, blueprints = sceneBlueprints)
+                    .Content(modifier = Modifier.fillMaxSize())
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
index cbf2496..f5dc154 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
@@ -20,8 +20,10 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.KeyguardViewConfigurator
 import com.android.systemui.keyguard.qualifiers.KeyguardRootView
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.composable.LockscreenScene
 import com.android.systemui.keyguard.ui.composable.LockscreenSceneBlueprintModule
+import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
 import com.android.systemui.scene.shared.model.Scene
 import dagger.Binds
 import dagger.Module
@@ -51,5 +53,12 @@
         ): () -> View {
             return { configurator.get().getKeyguardRootView() }
         }
+
+        @Provides
+        fun providesLockscreenBlueprints(
+            blueprints: Set<@JvmSuppressWildcards ComposableLockscreenSceneBlueprint>
+        ): Set<LockscreenSceneBlueprint> {
+            return blueprints
+        }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
index 071433e..dd86646 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
@@ -16,23 +16,42 @@
 
 package com.android.systemui.keyboard.stickykeys.ui.view
 
+import android.content.Context
+import android.view.View
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.LocalContentColor
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
+import com.android.compose.theme.PlatformTheme
 import com.android.systemui.keyboard.stickykeys.shared.model.Locked
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
 
+fun createStickyKeyIndicatorView(context: Context, viewModel: StickyKeysIndicatorViewModel): View {
+    return ComposeView(context).apply {
+        setContent {
+            PlatformTheme {
+                val defaultContentColor = MaterialTheme.colorScheme.onSurfaceVariant
+                CompositionLocalProvider(LocalContentColor provides defaultContentColor) {
+                    StickyKeysIndicator(viewModel)
+                }
+            }
+        }
+    }
+}
+
 @Composable
 fun StickyKeysIndicator(viewModel: StickyKeysIndicatorViewModel) {
     val stickyKeys by viewModel.indicatorContent.collectAsState(emptyMap())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
index 2cb0034..b5499b7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
@@ -25,7 +25,7 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayout
 import com.android.compose.animation.scene.transitions
-import com.android.systemui.keyguard.ui.composable.blueprint.LockscreenSceneBlueprint
+import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import javax.inject.Inject
 
@@ -39,10 +39,10 @@
 @Inject
 constructor(
     private val viewModel: LockscreenContentViewModel,
-    private val blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
+    private val blueprints: Set<@JvmSuppressWildcards ComposableLockscreenSceneBlueprint>,
 ) {
 
-    private val sceneKeyByBlueprint: Map<LockscreenSceneBlueprint, SceneKey> by lazy {
+    private val sceneKeyByBlueprint: Map<ComposableLockscreenSceneBlueprint, SceneKey> by lazy {
         blueprints.associateWith { blueprint -> SceneKey(blueprint.id) }
     }
     private val sceneKeyByBlueprintId: Map<String, SceneKey> by lazy {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
index 86124c6..6b210af 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/CommunalBlueprint.kt
@@ -36,7 +36,7 @@
 @Inject
 constructor(
     private val viewModel: LockscreenContentViewModel,
-) : LockscreenSceneBlueprint {
+) : ComposableLockscreenSceneBlueprint {
 
     override val id: String = "communal"
 
@@ -59,5 +59,5 @@
 
 @Module
 interface CommunalBlueprintModule {
-    @Binds @IntoSet fun blueprint(blueprint: CommunalBlueprint): LockscreenSceneBlueprint
+    @Binds @IntoSet fun blueprint(blueprint: CommunalBlueprint): ComposableLockscreenSceneBlueprint
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ComposableLockscreenSceneBlueprint.kt
similarity index 87%
rename from packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt
rename to packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ComposableLockscreenSceneBlueprint.kt
index 6d9cba4..cb73983 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ComposableLockscreenSceneBlueprint.kt
@@ -19,13 +19,10 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
 
 /** Defines interface for classes that can render the content for a specific blueprint/layout. */
-interface LockscreenSceneBlueprint {
-
-    /** The ID that uniquely identifies this blueprint across all other blueprints. */
-    val id: String
-
+interface ComposableLockscreenSceneBlueprint : LockscreenSceneBlueprint {
     /** Renders the content of this blueprint. */
     @Composable fun SceneScope.Content(modifier: Modifier)
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index bf02d8a..a07ab4a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -20,10 +20,12 @@
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.IntRect
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
@@ -38,6 +40,7 @@
 import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
 import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
@@ -61,7 +64,7 @@
     private val bottomAreaSection: BottomAreaSection,
     private val settingsMenuSection: SettingsMenuSection,
     private val clockInteractor: KeyguardClockInteractor,
-) : LockscreenSceneBlueprint {
+) : ComposableLockscreenSceneBlueprint {
 
     override val id: String = "default"
 
@@ -84,6 +87,7 @@
                         with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
                         with(clockSection) {
                             SmallClock(
+                                burnInParams = burnIn.parameters,
                                 onTopChanged = burnIn.onSmallClockTopChanged,
                                 modifier = Modifier.fillMaxWidth(),
                             )
@@ -95,7 +99,13 @@
                                 modifier =
                                     Modifier.fillMaxWidth()
                                         .padding(
-                                            top = { viewModel.getSmartSpacePaddingTop(resources) }
+                                            top = { viewModel.getSmartSpacePaddingTop(resources) },
+                                        )
+                                        .padding(
+                                            bottom =
+                                                dimensionResource(
+                                                    R.dimen.keyguard_status_view_bottom_margin
+                                                ),
                                         ),
                             )
                         }
@@ -214,5 +224,5 @@
 
 @Module
 interface DefaultBlueprintModule {
-    @Binds @IntoSet fun blueprint(blueprint: DefaultBlueprint): LockscreenSceneBlueprint
+    @Binds @IntoSet fun blueprint(blueprint: DefaultBlueprint): ComposableLockscreenSceneBlueprint
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index d0aa444..b035e42 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -20,10 +20,12 @@
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.IntRect
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
@@ -38,6 +40,7 @@
 import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
 import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.res.R
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
@@ -61,7 +64,7 @@
     private val bottomAreaSection: BottomAreaSection,
     private val settingsMenuSection: SettingsMenuSection,
     private val clockInteractor: KeyguardClockInteractor,
-) : LockscreenSceneBlueprint {
+) : ComposableLockscreenSceneBlueprint {
 
     override val id: String = "shortcuts-besides-udfps"
 
@@ -86,6 +89,7 @@
                             SmallClock(
                                 onTopChanged = burnIn.onSmallClockTopChanged,
                                 modifier = Modifier.fillMaxWidth(),
+                                burnInParams = burnIn.parameters,
                             )
                         }
                         with(smartSpaceSection) {
@@ -96,6 +100,12 @@
                                     Modifier.fillMaxWidth()
                                         .padding(
                                             top = { viewModel.getSmartSpacePaddingTop(resources) }
+                                        )
+                                        .padding(
+                                            bottom =
+                                                dimensionResource(
+                                                    R.dimen.keyguard_status_view_bottom_margin
+                                                )
                                         ),
                             )
                         }
@@ -222,5 +232,5 @@
 interface ShortcutsBesideUdfpsBlueprintModule {
     @Binds
     @IntoSet
-    fun blueprint(blueprint: ShortcutsBesideUdfpsBlueprint): LockscreenSceneBlueprint
+    fun blueprint(blueprint: ShortcutsBesideUdfpsBlueprint): ComposableLockscreenSceneBlueprint
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
index 616a7b4..660fc5a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
@@ -72,7 +72,7 @@
     private val settingsMenuSection: SettingsMenuSection,
     private val clockInteractor: KeyguardClockInteractor,
     private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
-) : LockscreenSceneBlueprint {
+) : ComposableLockscreenSceneBlueprint {
 
     override val id: String = "split-shade"
 
@@ -109,7 +109,14 @@
                                                 .padding(
                                                     top = {
                                                         viewModel.getSmartSpacePaddingTop(resources)
-                                                    }
+                                                    },
+                                                )
+                                                .padding(
+                                                    bottom =
+                                                        dimensionResource(
+                                                            R.dimen
+                                                                .keyguard_status_view_bottom_margin
+                                                        )
                                                 ),
                                     )
                                 }
@@ -237,5 +244,7 @@
 
 @Module
 interface SplitShadeBlueprintModule {
-    @Binds @IntoSet fun blueprint(blueprint: SplitShadeBlueprint): LockscreenSceneBlueprint
+    @Binds
+    @IntoSet
+    fun blueprint(blueprint: SplitShadeBlueprint): ComposableLockscreenSceneBlueprint
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt
index 8f21879..fa07baf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt
@@ -33,7 +33,10 @@
 import com.android.keyguard.KeyguardClockSwitch
 import com.android.systemui.customization.R as customizationR
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
 import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
+import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
+import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import javax.inject.Inject
 
@@ -42,10 +45,12 @@
 constructor(
     private val viewModel: KeyguardClockViewModel,
     private val clockInteractor: KeyguardClockInteractor,
+    private val aodBurnInViewModel: AodBurnInViewModel,
 ) {
 
     @Composable
     fun SceneScope.SmallClock(
+        burnInParams: BurnInParameters,
         onTopChanged: (top: Float?) -> Unit,
         modifier: Modifier = Modifier,
     ) {
@@ -89,7 +94,11 @@
                                     dimensionResource(customizationR.dimen.clock_padding_start)
                             )
                             .padding(top = { viewModel.getSmallClockTopMargin(view.context) })
-                            .onTopPlacementChanged(onTopChanged),
+                            .onTopPlacementChanged(onTopChanged)
+                            .burnInAware(
+                                viewModel = aodBurnInViewModel,
+                                params = burnInParams,
+                            ),
                     update = {
                         val newClockView = checkNotNull(currentClock).smallClock.view
                         it.removeAllViews()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index 5e27d82..6a8da10 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -19,6 +19,11 @@
 import android.content.Context
 import android.view.ViewGroup
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.dagger.SysUISingleton
@@ -40,56 +45,82 @@
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.DisposableHandle
 
 @SysUISingleton
 class NotificationSection
 @Inject
 constructor(
-    @Application context: Context,
+    @Application private val context: Context,
     private val viewModel: NotificationsPlaceholderViewModel,
-    controller: NotificationStackScrollLayoutController,
-    sceneContainerFlags: SceneContainerFlags,
-    sharedNotificationContainer: SharedNotificationContainer,
-    sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
-    stackScrollLayout: NotificationStackScrollLayout,
-    notificationStackAppearanceViewModel: NotificationStackAppearanceViewModel,
-    ambientState: AmbientState,
-    notificationStackSizeCalculator: NotificationStackSizeCalculator,
-    @Main mainDispatcher: CoroutineDispatcher,
+    private val controller: NotificationStackScrollLayoutController,
+    private val sceneContainerFlags: SceneContainerFlags,
+    private val sharedNotificationContainer: SharedNotificationContainer,
+    private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+    private val stackScrollLayout: NotificationStackScrollLayout,
+    private val notificationStackAppearanceViewModel: NotificationStackAppearanceViewModel,
+    private val ambientState: AmbientState,
+    private val notificationStackSizeCalculator: NotificationStackSizeCalculator,
+    @Main private val mainDispatcher: CoroutineDispatcher,
 ) {
-    init {
-        if (!KeyguardShadeMigrationNssl.isUnexpectedlyInLegacyMode()) {
-            // This scene container section moves the NSSL to the SharedNotificationContainer. This
-            //  also requires that SharedNotificationContainer gets moved to the SceneWindowRootView
-            //  by the SceneWindowRootViewBinder.
-            // Prior to Scene Container, but when the KeyguardShadeMigrationNssl flag is enabled,
-            //  NSSL is moved into this container by the NotificationStackScrollLayoutSection.
-            (stackScrollLayout.parent as? ViewGroup)?.removeView(stackScrollLayout)
-            sharedNotificationContainer.addNotificationStackScrollLayout(stackScrollLayout)
+    @Composable
+    fun SceneScope.Notifications(modifier: Modifier = Modifier) {
+        if (KeyguardShadeMigrationNssl.isUnexpectedlyInLegacyMode()) {
+            // This scene container section moves the NSSL to the SharedNotificationContainer.
+            // This also requires that SharedNotificationContainer gets moved to the
+            // SceneWindowRootView by the SceneWindowRootViewBinder. Prior to Scene Container,
+            // but when the KeyguardShadeMigrationNssl flag is enabled, NSSL is moved into this
+            // container by the NotificationStackScrollLayoutSection.
+            return
+        }
 
-            SharedNotificationContainerBinder.bind(
-                sharedNotificationContainer,
-                sharedNotificationContainerViewModel,
-                sceneContainerFlags,
-                controller,
-                notificationStackSizeCalculator,
-                mainDispatcher,
+        var isBound by remember { mutableStateOf(false) }
+
+        DisposableEffect(Unit) {
+            val disposableHandles: MutableList<DisposableHandle> = mutableListOf()
+
+            // Ensure stackScrollLayout is a child of sharedNotificationContainer.
+            if (stackScrollLayout.parent != sharedNotificationContainer) {
+                (stackScrollLayout.parent as? ViewGroup)?.removeView(stackScrollLayout)
+                sharedNotificationContainer.addNotificationStackScrollLayout(stackScrollLayout)
+            }
+
+            disposableHandles.add(
+                SharedNotificationContainerBinder.bind(
+                    sharedNotificationContainer,
+                    sharedNotificationContainerViewModel,
+                    sceneContainerFlags,
+                    controller,
+                    notificationStackSizeCalculator,
+                    mainDispatcher,
+                )
             )
 
             if (sceneContainerFlags.flexiNotifsEnabled()) {
-                NotificationStackAppearanceViewBinder.bind(
-                    context,
-                    sharedNotificationContainer,
-                    notificationStackAppearanceViewModel,
-                    ambientState,
-                    controller,
+                disposableHandles.add(
+                    NotificationStackAppearanceViewBinder.bind(
+                        context,
+                        sharedNotificationContainer,
+                        notificationStackAppearanceViewModel,
+                        ambientState,
+                        controller,
+                    )
                 )
             }
-        }
-    }
 
-    @Composable
-    fun SceneScope.Notifications(modifier: Modifier = Modifier) {
+            isBound = true
+
+            onDispose {
+                disposableHandles.forEach { it.dispose() }
+                disposableHandles.clear()
+                isBound = false
+            }
+        }
+
+        if (!isBound) {
+            return
+        }
+
         NotificationStack(
             viewModel = viewModel,
             modifier = modifier,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
index dcd22fe..a7ec93f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
@@ -19,6 +19,7 @@
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
+import androidx.compose.material3.Slider
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
@@ -33,6 +34,7 @@
         modifier = modifier,
         verticalArrangement = Arrangement.spacedBy(20.dp),
     ) {
+        Slider(0.5f, {})
         for (component in components) {
             AnimatedVisibility(component.isVisible) {
                 with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index a910bca..e40f6b6 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -26,6 +26,7 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.isUnspecified
 import androidx.compose.ui.geometry.lerp
+import androidx.compose.ui.graphics.CompositingStrategy
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
 import androidx.compose.ui.graphics.drawscope.scale
 import androidx.compose.ui.layout.IntermediateMeasureScope
@@ -473,7 +474,8 @@
             placeable.place(offset)
         } else {
             placeable.placeWithLayer(offset) {
-                this.alpha = elementAlpha(layoutImpl, element, scene)
+                alpha = elementAlpha(layoutImpl, element, scene)
+                compositingStrategy = CompositingStrategy.ModulateAlpha
             }
         }
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
new file mode 100644
index 0000000..693de55
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.NotificationContainerBounds
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationsPlaceholderViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val underTest = kosmos.notificationsPlaceholderViewModel
+    @Test
+    fun onBoundsChanged_setsNotificationContainerBounds() {
+        underTest.onBoundsChanged(left = 5f, top = 5f, right = 5f, bottom = 5f)
+        assertThat(kosmos.keyguardInteractor.notificationContainerBounds.value)
+            .isEqualTo(NotificationContainerBounds(left = 5f, top = 5f, right = 5f, bottom = 5f))
+        assertThat(kosmos.notificationStackAppearanceInteractor.stackBounds.value)
+            .isEqualTo(NotificationContainerBounds(left = 5f, top = 5f, right = 5f, bottom = 5f))
+    }
+    @Test
+    fun onContentTopChanged_setsContentTop() {
+        underTest.onContentTopChanged(padding = 5f)
+        assertThat(kosmos.notificationStackAppearanceInteractor.contentTop.value).isEqualTo(5f)
+    }
+}
diff --git a/packages/SystemUI/res/values-xxhdpi/dimens.xml b/packages/SystemUI/res/values-xxhdpi/dimens.xml
index 26c8437..9bff422 100644
--- a/packages/SystemUI/res/values-xxhdpi/dimens.xml
+++ b/packages/SystemUI/res/values-xxhdpi/dimens.xml
@@ -22,4 +22,8 @@
          fraction of a pixel.-->
     <fraction name="battery_subpixel_smoothing_left">33%</fraction>
     <fraction name="battery_subpixel_smoothing_right">33%</fraction>
+
+    <!-- Biometrics fingerprint icon size for full resolution.-->
+    <dimen name="biometric_dialog_fingerprint_icon_width">120dp</dimen>
+    <dimen name="biometric_dialog_fingerprint_icon_height">120dp</dimen>
 </resources>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 4e04af6..6bf4906 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -54,7 +54,7 @@
         "SystemUIUnfoldLib",
         "SystemUISharedLib-Keyguard",
         "WindowManager-Shell-shared",
-        "tracinglib",
+        "tracinglib-platform",
         "androidx.dynamicanimation_dynamicanimation",
         "androidx.concurrent_concurrent-futures",
         "androidx.lifecycle_lifecycle-runtime-ktx",
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index dfeb1f3..d821f19 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -33,7 +33,7 @@
 public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
     protected View mEcaView;
 
-    // To avoid accidental lockout due to events while the device in in the pocket, ignore
+    // To avoid accidental lockout due to events while the device in the pocket, ignore
     // any passwords with length less than or equal to this length.
     protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
     private KeyDownListener mKeyDownListener;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index bc12aee..ce03072 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -132,7 +132,7 @@
     boolean shouldSubtleWindowAnimationsForUnlock();
 
     /**
-     * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
+     * Starts the animation before we dismiss Keyguard, i.e. a disappearing animation on the
      * security view of the bouncer.
      *
      * @param finishRunnable the runnable to be run after the animation finished, or {@code null} if
diff --git a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
index 5f5cca8..e8499d3 100644
--- a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
@@ -17,15 +17,11 @@
 package com.android.systemui
 
 import android.content.Context
-import android.content.res.Resources
 import android.graphics.Path
 import android.graphics.Rect
-import android.graphics.RectF
 import android.hardware.camera2.CameraManager
-import android.util.PathParser
 import com.android.systemui.res.R
 import java.util.concurrent.Executor
-import kotlin.math.roundToInt
 
 /**
  * Listens for usage of the Camera and controls the ScreenDecorations transition to show extra
@@ -163,89 +159,20 @@
     }
 
     companion object Factory {
-        fun build(context: Context, executor: Executor): CameraAvailabilityListener {
+        fun build(
+            context: Context,
+            executor: Executor,
+            cameraProtectionLoader: CameraProtectionLoader
+        ): CameraAvailabilityListener {
             val manager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
             val res = context.resources
-            val cameraProtectionInfoList = loadCameraProtectionInfoList(res)
+            val cameraProtectionInfoList = cameraProtectionLoader.loadCameraProtectionInfoList()
             val excluded = res.getString(R.string.config_cameraProtectionExcludedPackages)
 
             return CameraAvailabilityListener(manager, cameraProtectionInfoList, excluded, executor)
         }
-
-        private fun pathFromString(pathString: String): Path {
-            val spec = pathString.trim()
-            val p: Path
-            try {
-                p = PathParser.createPathFromPathData(spec)
-            } catch (e: Throwable) {
-                throw IllegalArgumentException("Invalid protection path", e)
-            }
-
-            return p
-        }
-
-        private fun loadCameraProtectionInfoList(res: Resources): List<CameraProtectionInfo> {
-            val list = mutableListOf<CameraProtectionInfo>()
-            val front =
-                loadCameraProtectionInfo(
-                    res,
-                    R.string.config_protectedCameraId,
-                    R.string.config_protectedPhysicalCameraId,
-                    R.string.config_frontBuiltInDisplayCutoutProtection
-                )
-            if (front != null) {
-                list.add(front)
-            }
-            val inner =
-                loadCameraProtectionInfo(
-                    res,
-                    R.string.config_protectedInnerCameraId,
-                    R.string.config_protectedInnerPhysicalCameraId,
-                    R.string.config_innerBuiltInDisplayCutoutProtection
-                )
-            if (inner != null) {
-                list.add(inner)
-            }
-            return list
-        }
-
-        private fun loadCameraProtectionInfo(
-            res: Resources,
-            cameraIdRes: Int,
-            physicalCameraIdRes: Int,
-            pathRes: Int
-        ): CameraProtectionInfo? {
-            val logicalCameraId = res.getString(cameraIdRes)
-            if (logicalCameraId.isNullOrEmpty()) {
-                return null
-            }
-            val physicalCameraId = res.getString(physicalCameraIdRes)
-            val protectionPath = pathFromString(res.getString(pathRes))
-            val computed = RectF()
-            protectionPath.computeBounds(computed)
-            val protectionBounds =
-                Rect(
-                    computed.left.roundToInt(),
-                    computed.top.roundToInt(),
-                    computed.right.roundToInt(),
-                    computed.bottom.roundToInt()
-                )
-            return CameraProtectionInfo(
-                logicalCameraId,
-                physicalCameraId,
-                protectionPath,
-                protectionBounds
-            )
-        }
     }
 
-    data class CameraProtectionInfo(
-        val logicalCameraId: String,
-        val physicalCameraId: String?,
-        val cutoutProtectionPath: Path,
-        val cutoutBounds: Rect,
-    )
-
     private data class OpenCameraInfo(
         val logicalCameraId: String,
         val packageId: String,
diff --git a/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt b/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt
new file mode 100644
index 0000000..bbab4de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import android.graphics.Path
+import android.graphics.Rect
+
+data class CameraProtectionInfo(
+    val logicalCameraId: String,
+    val physicalCameraId: String?,
+    val cutoutProtectionPath: Path,
+    val cutoutBounds: Rect,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/CameraProtectionLoader.kt b/packages/SystemUI/src/com/android/systemui/CameraProtectionLoader.kt
new file mode 100644
index 0000000..8fe9389
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/CameraProtectionLoader.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import android.content.Context
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.util.PathParser
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlin.math.roundToInt
+
+class CameraProtectionLoader @Inject constructor(private val context: Context) {
+
+    fun loadCameraProtectionInfoList(): List<CameraProtectionInfo> {
+        val list = mutableListOf<CameraProtectionInfo>()
+        val front =
+            loadCameraProtectionInfo(
+                R.string.config_protectedCameraId,
+                R.string.config_protectedPhysicalCameraId,
+                R.string.config_frontBuiltInDisplayCutoutProtection
+            )
+        if (front != null) {
+            list.add(front)
+        }
+        val inner =
+            loadCameraProtectionInfo(
+                R.string.config_protectedInnerCameraId,
+                R.string.config_protectedInnerPhysicalCameraId,
+                R.string.config_innerBuiltInDisplayCutoutProtection
+            )
+        if (inner != null) {
+            list.add(inner)
+        }
+        return list
+    }
+
+    private fun loadCameraProtectionInfo(
+        cameraIdRes: Int,
+        physicalCameraIdRes: Int,
+        pathRes: Int
+    ): CameraProtectionInfo? {
+        val logicalCameraId = context.getString(cameraIdRes)
+        if (logicalCameraId.isNullOrEmpty()) {
+            return null
+        }
+        val physicalCameraId = context.getString(physicalCameraIdRes)
+        val protectionPath = pathFromString(context.getString(pathRes))
+        val computed = RectF()
+        protectionPath.computeBounds(computed)
+        val protectionBounds =
+            Rect(
+                computed.left.roundToInt(),
+                computed.top.roundToInt(),
+                computed.right.roundToInt(),
+                computed.bottom.roundToInt()
+            )
+        return CameraProtectionInfo(
+            logicalCameraId,
+            physicalCameraId,
+            protectionPath,
+            protectionBounds
+        )
+    }
+
+    private fun pathFromString(pathString: String): Path {
+        return try {
+            PathParser.createPathFromPathData(pathString.trim())
+        } catch (e: Throwable) {
+            throw IllegalArgumentException("Invalid protection path", e)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index d6d5c26..3e03fb8 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -146,6 +146,7 @@
     private final ThreadFactory mThreadFactory;
     private final DecorProviderFactory mDotFactory;
     private final FaceScanningProviderFactory mFaceScanningFactory;
+    private final CameraProtectionLoader mCameraProtectionLoader;
     public final int mFaceScanningViewId;
 
     @VisibleForTesting
@@ -333,7 +334,8 @@
             FaceScanningProviderFactory faceScanningFactory,
             ScreenDecorationsLogger logger,
             FacePropertyRepository facePropertyRepository,
-            JavaAdapter javaAdapter) {
+            JavaAdapter javaAdapter,
+            CameraProtectionLoader cameraProtectionLoader) {
         mContext = context;
         mSecureSettings = secureSettings;
         mCommandRegistry = commandRegistry;
@@ -343,6 +345,7 @@
         mThreadFactory = threadFactory;
         mDotFactory = dotFactory;
         mFaceScanningFactory = faceScanningFactory;
+        mCameraProtectionLoader = cameraProtectionLoader;
         mFaceScanningViewId = com.android.systemui.res.R.id.face_scanning_anim;
         mLogger = logger;
         mFacePropertyRepository = facePropertyRepository;
@@ -981,7 +984,9 @@
         Resources res = mContext.getResources();
         boolean enabled = res.getBoolean(R.bool.config_enableDisplayCutoutProtection);
         if (enabled) {
-            mCameraListener = CameraAvailabilityListener.Factory.build(mContext, mExecutor);
+            mCameraListener =
+                    CameraAvailabilityListener.Factory.build(
+                            mContext, mExecutor, mCameraProtectionLoader);
             mCameraListener.addTransitionCallback(mCameraTransitionCallback);
             mCameraListener.startListening();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
index 11c7a31..ecbd3f9 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
@@ -69,7 +69,7 @@
         }
 
         val resolveInfo: ResolveInfo? = packageManager.resolveActivityAsUser(
-            getStartCameraIntent(),
+            getStartCameraIntent(selectedUserInteractor.getSelectedUserId()),
             PackageManager.MATCH_DEFAULT_ONLY,
             selectedUserInteractor.getSelectedUserId()
         )
@@ -85,7 +85,7 @@
      * @param source The source of the camera launch, to be passed to the camera app via [Intent]
      */
     fun launchCamera(source: Int) {
-        val intent: Intent = getStartCameraIntent()
+        val intent: Intent = getStartCameraIntent(selectedUserInteractor.getSelectedUserId())
         intent.putExtra(CameraIntents.EXTRA_LAUNCH_SOURCE, source)
         val wouldLaunchResolverActivity = activityIntentHelper.wouldLaunchResolverActivity(
             intent, selectedUserInteractor.getSelectedUserId()
@@ -143,13 +143,13 @@
      * Returns an [Intent] that can be used to start the camera app such that it occludes the
      * lock-screen, if needed.
      */
-    private fun getStartCameraIntent(): Intent {
+    private fun getStartCameraIntent(userId: Int): Intent {
         val isLockScreenDismissible = keyguardStateController.canDismissLockScreen()
         val isSecure = keyguardStateController.isMethodSecure
         return if (isSecure && !isLockScreenDismissible) {
-            cameraIntents.getSecureCameraIntent()
+            cameraIntents.getSecureCameraIntent(userId)
         } else {
-            cameraIntents.getInsecureCameraIntent()
+            cameraIntents.getInsecureCameraIntent(userId)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt
index 1e17059..1137586 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt
@@ -18,9 +18,11 @@
 
 import android.content.Context
 import android.content.Intent
+import android.content.pm.PackageManager
 import android.provider.MediaStore
 import android.text.TextUtils
 import com.android.systemui.res.R
+import android.util.Log
 
 class CameraIntents {
     companion object {
@@ -28,28 +30,33 @@
         val DEFAULT_INSECURE_CAMERA_INTENT_ACTION = MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA
         private val VIDEO_CAMERA_INTENT_ACTION = MediaStore.INTENT_ACTION_VIDEO_CAMERA
         const val EXTRA_LAUNCH_SOURCE = "com.android.systemui.camera_launch_source"
+        const val TAG = "CameraIntents"
 
         @JvmStatic
-        fun getOverrideCameraPackage(context: Context): String? {
-            context.resources.getString(R.string.config_cameraGesturePackage)?.let {
-                if (!TextUtils.isEmpty(it)) {
-                    return it
+        fun getOverrideCameraPackage(context: Context, userId: Int): String? {
+            val packageName = context.resources.getString(R.string.config_cameraGesturePackage)!!
+            try {
+                if (!TextUtils.isEmpty(packageName)
+                        && context.packageManager.getApplicationInfoAsUser(packageName, 0, userId).enabled ?: false) {
+                    return packageName
                 }
+            } catch (e: PackageManager.NameNotFoundException) {
+                Log.w(TAG, "Missing cameraGesturePackage $packageName", e)
             }
             return null
         }
 
         @JvmStatic
-        fun getInsecureCameraIntent(context: Context): Intent {
+        fun getInsecureCameraIntent(context: Context, userId: Int): Intent {
             val intent = Intent(DEFAULT_INSECURE_CAMERA_INTENT_ACTION)
-            getOverrideCameraPackage(context)?.let { intent.setPackage(it) }
+            getOverrideCameraPackage(context, userId)?.let { intent.setPackage(it) }
             return intent
         }
 
         @JvmStatic
-        fun getSecureCameraIntent(context: Context): Intent {
+        fun getSecureCameraIntent(context: Context, userId: Int): Intent {
             val intent = Intent(DEFAULT_SECURE_CAMERA_INTENT_ACTION)
-            getOverrideCameraPackage(context)?.let { intent.setPackage(it) }
+            getOverrideCameraPackage(context, userId)?.let { intent.setPackage(it) }
             return intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
         }
 
@@ -65,7 +72,7 @@
 
         /** Returns an [Intent] that can be used to start the camera in video mode. */
         @JvmStatic
-        fun getVideoCameraIntent(): Intent {
+        fun getVideoCameraIntent(userId: Int): Intent {
             return Intent(VIDEO_CAMERA_INTENT_ACTION)
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt
index a434617..b65c0d4 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt
@@ -29,22 +29,22 @@
 
     /**
      * Returns an [Intent] that can be used to start the camera, suitable for when the device is
-     * already unlocked
+     * locked
      */
-    fun getSecureCameraIntent(): Intent {
-        return CameraIntents.getSecureCameraIntent(context)
+    fun getSecureCameraIntent(userId: Int): Intent {
+        return CameraIntents.getSecureCameraIntent(context, userId)
     }
 
     /**
      * Returns an [Intent] that can be used to start the camera, suitable for when the device is not
      * already unlocked
      */
-    fun getInsecureCameraIntent(): Intent {
-        return CameraIntents.getInsecureCameraIntent(context)
+    fun getInsecureCameraIntent(userId: Int): Intent {
+        return CameraIntents.getInsecureCameraIntent(context, userId)
     }
 
     /** Returns an [Intent] that can be used to start the camera in video mode. */
-    fun getVideoCameraIntent(): Intent {
-        return CameraIntents.getVideoCameraIntent()
+    fun getVideoCameraIntent(userId: Int): Intent {
+        return CameraIntents.getVideoCameraIntent(userId)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
index c5dac77..80db535 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
@@ -19,14 +19,12 @@
 import android.app.smartspace.SmartspaceConfig
 import android.app.smartspace.SmartspaceManager
 import android.app.smartspace.SmartspaceSession
-import android.app.smartspace.SmartspaceTarget
 import android.content.Context
 import android.util.Log
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.BcSmartspaceDataPlugin
 import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
-import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
 import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_GLANCEABLE_HUB
 import com.android.systemui.smartspace.SmartspacePrecondition
 import com.android.systemui.smartspace.SmartspaceTargetFilter
@@ -64,11 +62,6 @@
     // A shadow copy of listeners is maintained to track whether the session should remain open.
     private var listeners = mutableSetOf<SmartspaceTargetListener>()
 
-    private var unfilteredListeners = mutableSetOf<SmartspaceTargetListener>()
-
-    // Smartspace can be used on multiple displays, such as when the user casts their screen
-    private var smartspaceViews = mutableSetOf<SmartspaceView>()
-
     var preconditionListener =
         object : SmartspacePrecondition.Listener {
             override fun onCriteriaChanged() {
@@ -101,9 +94,7 @@
         }
 
     private fun hasActiveSessionListeners(): Boolean {
-        return smartspaceViews.isNotEmpty() ||
-            listeners.isNotEmpty() ||
-            unfilteredListeners.isNotEmpty()
+        return listeners.isNotEmpty()
     }
 
     private fun connectSession() {
@@ -188,8 +179,4 @@
     private fun reloadSmartspace() {
         session?.requestSmartspaceUpdate()
     }
-
-    private fun onTargetsAvailableUnfiltered(targets: List<SmartspaceTarget>) {
-        unfilteredListeners.forEach { it.onSmartspaceTargetsUpdated(targets) }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
index 947cb02..9a4dfdd 100644
--- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
+++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
@@ -17,7 +17,6 @@
 
 package com.android.systemui.compose
 
-import android.app.Dialog
 import android.content.Context
 import android.view.View
 import android.view.WindowInsets
@@ -28,12 +27,13 @@
 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import com.android.systemui.people.ui.viewmodel.PeopleViewModel
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
 import com.android.systemui.scene.shared.model.Scene
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.StateFlow
@@ -96,11 +96,11 @@
         sceneByKey: Map<SceneKey, Scene>,
     ): View
 
-    /** Creates sticky key dialog presenting provided [viewModel] */
-    fun createStickyKeysDialog(
-        dialogFactory: SystemUIDialogFactory,
+    /** Creates sticky key indicator content presenting provided [viewModel] */
+    fun createStickyKeysIndicatorContent(
+        context: Context,
         viewModel: StickyKeysIndicatorViewModel
-    ): Dialog
+    ): View
 
     /** Create a [View] to represent [viewModel] on screen. */
     fun createCommunalView(
@@ -117,4 +117,11 @@
 
     /** Creates a container that hosts the communal UI and handles gesture transitions. */
     fun createCommunalContainer(context: Context, viewModel: BaseCommunalViewModel): View
+
+    /** Creates a [View] that represents the Lockscreen. */
+    fun createLockscreen(
+        context: Context,
+        viewModel: LockscreenContentViewModel,
+        blueprints: Set<@JvmSuppressWildcards LockscreenSceneBlueprint>,
+    ): View
 }
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index c93b8e1..1230156 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -292,7 +292,7 @@
 
     private fun <T> Flow<T>.debugLog(flowName: String): Flow<T> {
         return if (DEBUG) {
-            traceEach(flowName, logcat = true)
+            traceEach(flowName, logcat = true, traceEmissionCount = true)
         } else {
             this
         }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
index c331164..537cacd 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -93,7 +93,11 @@
             }
 
             mDockState = dockState;
-            if (isPulsing()) {
+            if (mMachine.isExecutingTransition() || isPulsing()) {
+                // If the device is in the middle of executing a transition or is pulsing,
+                // exit early instead of requesting a new state. DozeMachine
+                // will check the docked state and resolveIntermediateState in the next
+                // transition after pulse done.
                 return;
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt
new file mode 100644
index 0000000..3ed58a7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeyDialogFactory.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.stickykeys.ui
+
+import android.app.Dialog
+import android.content.Context
+import android.view.Gravity
+import android.view.Window
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND
+import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+import android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL
+import androidx.activity.ComponentDialog
+import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+@SysUISingleton
+class StickyKeyDialogFactory
+@Inject
+constructor(
+    @Application val context: Context,
+) {
+
+    fun create(viewModel: StickyKeysIndicatorViewModel): Dialog {
+        return createStickyKeyIndicator(viewModel)
+    }
+
+    private fun createStickyKeyIndicator(viewModel: StickyKeysIndicatorViewModel): Dialog {
+        return ComponentDialog(context, R.style.Theme_SystemUI_Dialog).apply {
+            // because we're requesting window feature it must be called before setting content
+            window?.setStickyKeyWindowAttributes()
+            setContentView(ComposeFacade.createStickyKeysIndicatorContent(context, viewModel))
+        }
+    }
+
+    private fun Window.setStickyKeyWindowAttributes() {
+        requestFeature(Window.FEATURE_NO_TITLE)
+        setType(TYPE_STATUS_BAR_SUB_PANEL)
+        addFlags(FLAG_NOT_FOCUSABLE or FLAG_NOT_TOUCHABLE)
+        clearFlags(FLAG_DIM_BEHIND)
+        setGravity(Gravity.TOP or Gravity.END)
+        attributes =
+            WindowManager.LayoutParams().apply {
+                copyFrom(attributes)
+                title = "StickyKeysIndicator"
+            }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt
index c3a618d..842fd04 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinator.kt
@@ -18,16 +18,11 @@
 
 import android.app.Dialog
 import android.util.Log
-import android.view.Gravity
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.view.Window
-import android.view.WindowManager
 import com.android.systemui.compose.ComposeFacade
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 import javax.inject.Inject
@@ -37,7 +32,7 @@
 @Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
-    private val dialogFactory: SystemUIDialogFactory,
+    private val stickyKeyDialogFactory: StickyKeyDialogFactory,
     private val viewModel: StickyKeysIndicatorViewModel,
     private val stickyKeysLogger: StickyKeysLogger,
 ) {
@@ -57,25 +52,10 @@
                     dialog?.dismiss()
                     dialog = null
                 } else if (dialog == null) {
-                    dialog = ComposeFacade.createStickyKeysDialog(dialogFactory, viewModel).apply {
-                        window?.setAttributes()
-                        show()
-                    }
+                    dialog = stickyKeyDialogFactory.create(viewModel)
+                    dialog?.show()
                 }
             }
         }
     }
-
-    private fun Window.setAttributes() {
-        setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
-        addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
-        addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
-        clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
-        setGravity(Gravity.TOP or Gravity.END)
-        attributes = WindowManager.LayoutParams().apply {
-            copyFrom(attributes)
-            width = WRAP_CONTENT
-            title = "StickyKeysIndicator"
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
index 9b83b75..ee3706a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
@@ -80,7 +80,7 @@
     }
 
     /**
-     * Click listener for messsage.
+     * Click listener for message.
      */
     public @Nullable View.OnClickListener getClickListener() {
         return mOnClickListener;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 7f43fac..abe49ee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -20,6 +20,12 @@
 import android.content.Context
 import android.view.LayoutInflater
 import android.view.View
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.keyguard.KeyguardStatusView
 import com.android.keyguard.KeyguardStatusViewController
@@ -29,10 +35,13 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.Flags.keyguardBottomAreaRefactor
 import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.compose.ComposeFacade
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.keyguard.shared.ComposeLockscreen
+import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
 import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
@@ -44,6 +53,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
@@ -88,6 +98,8 @@
     private val falsingManager: FalsingManager,
     private val aodAlphaViewModel: AodAlphaViewModel,
     private val keyguardClockViewModel: KeyguardClockViewModel,
+    private val lockscreenContentViewModel: LockscreenContentViewModel,
+    private val lockscreenSceneBlueprintsLazy: Lazy<Set<LockscreenSceneBlueprint>>,
 ) : CoreStartable {
 
     private var rootViewHandle: DisposableHandle? = null
@@ -115,11 +127,28 @@
         initializeViews()
 
         if (!SceneContainerFlag.isEnabled) {
-            KeyguardBlueprintViewBinder.bind(
-                keyguardRootView,
-                keyguardBlueprintViewModel,
-                keyguardClockViewModel
-            )
+            if (ComposeLockscreen.isEnabled) {
+                val composeView =
+                    ComposeFacade.createLockscreen(
+                        context = context,
+                        viewModel = lockscreenContentViewModel,
+                        blueprints = lockscreenSceneBlueprintsLazy.get(),
+                    )
+                composeView.id = View.generateViewId()
+                val cs = ConstraintSet()
+                cs.clone(keyguardRootView)
+                cs.connect(composeView.id, START, PARENT_ID, START)
+                cs.connect(composeView.id, END, PARENT_ID, END)
+                cs.connect(composeView.id, TOP, PARENT_ID, TOP)
+                cs.connect(composeView.id, BOTTOM, PARENT_ID, BOTTOM)
+                keyguardRootView.addView(composeView)
+            } else {
+                KeyguardBlueprintViewBinder.bind(
+                    keyguardRootView,
+                    keyguardBlueprintViewModel,
+                    keyguardClockViewModel,
+                )
+            }
         }
         keyguardBlueprintCommandListener.start()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index f085e88..4766a84 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -444,7 +444,7 @@
     /**
      * Whether a hide is pending and we are just waiting for #startKeyguardExitAnimation to be
      * called.
-     * */
+     */
     private boolean mHiding;
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
index bbdd903..3e6e3b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
@@ -50,14 +50,13 @@
     @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) : KeyguardQuickAffordanceConfig {
 
-    private val intent: Intent by lazy {
-        cameraIntents.getVideoCameraIntent().apply {
+    private val intent: Intent
+        get() = cameraIntents.getVideoCameraIntent(userTracker.userId).apply {
             putExtra(
                 CameraIntents.EXTRA_LAUNCH_SOURCE,
                 StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE,
             )
         }
-    }
 
     override val key: String
         get() = BuiltInKeyguardQuickAffordanceKeys.VIDEO_CAMERA
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/LockscreenSceneBlueprint.kt
similarity index 60%
copy from packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt
copy to packages/SystemUI/src/com/android/systemui/keyguard/shared/model/LockscreenSceneBlueprint.kt
index 6d9cba4..2eafb83 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/LockscreenSceneBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/LockscreenSceneBlueprint.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,18 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.keyguard.ui.composable.blueprint
+package com.android.systemui.keyguard.shared.model
 
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import com.android.compose.animation.scene.SceneScope
-
-/** Defines interface for classes that can render the content for a specific blueprint/layout. */
+/**
+ * Defines interface for classes that can render the content for a specific blueprint/layout.
+ *
+ * The actual rendering is done by a compose-aware sub-interface.
+ */
 interface LockscreenSceneBlueprint {
-
     /** The ID that uniquely identifies this blueprint across all other blueprints. */
     val id: String
-
-    /** Renders the content of this blueprint. */
-    @Composable fun SceneScope.Content(modifier: Modifier)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
index a651c10..52d94a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
@@ -57,7 +57,7 @@
     private val mainDispatcher: CoroutineDispatcher,
 ) : KeyguardSection() {
     private val placeHolderId = R.id.nssl_placeholder
-    private var disposableHandle: DisposableHandle? = null
+    private val disposableHandles: MutableList<DisposableHandle> = mutableListOf()
 
     /**
      * Align the notification placeholder bottom to the top of either the lock icon or the ambient
@@ -102,8 +102,9 @@
         if (!KeyguardShadeMigrationNssl.isEnabled) {
             return
         }
-        disposableHandle?.dispose()
-        disposableHandle =
+
+        disposeHandles()
+        disposableHandles.add(
             SharedNotificationContainerBinder.bind(
                 sharedNotificationContainer,
                 sharedNotificationContainerViewModel,
@@ -112,19 +113,28 @@
                 notificationStackSizeCalculator,
                 mainDispatcher,
             )
+        )
+
         if (sceneContainerFlags.flexiNotifsEnabled()) {
-            NotificationStackAppearanceViewBinder.bind(
-                context,
-                sharedNotificationContainer,
-                notificationStackAppearanceViewModel,
-                ambientState,
-                controller,
+            disposableHandles.add(
+                NotificationStackAppearanceViewBinder.bind(
+                    context,
+                    sharedNotificationContainer,
+                    notificationStackAppearanceViewModel,
+                    ambientState,
+                    controller,
+                )
             )
         }
     }
 
     override fun removeViews(constraintLayout: ConstraintLayout) {
-        disposableHandle?.dispose()
+        disposeHandles()
         constraintLayout.removeView(placeHolderId)
     }
+
+    private fun disposeHandles() {
+        disposableHandles.forEach { it.dispose() }
+        disposableHandles.clear()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
index d4ea728..9cf3c95 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
@@ -28,7 +28,6 @@
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.onStart
 
 /** Models UI state for the alpha of the AOD (always-on display). */
 @SysUISingleton
@@ -43,15 +42,13 @@
     /** The alpha level for the entire lockscreen while in AOD. */
     val alpha: Flow<Float> =
         combine(
-                keyguardTransitionInteractor.transitionValue(KeyguardState.GONE).onStart {
-                    emit(0f)
-                },
+                keyguardTransitionInteractor.currentKeyguardState,
                 merge(
                     keyguardInteractor.keyguardAlpha,
                     occludedToLockscreenTransitionViewModel.lockscreenAlpha,
                 )
-            ) { transitionToGone, alpha ->
-                if (transitionToGone == 1f) {
+            ) { currentKeyguardState, alpha ->
+                if (currentKeyguardState == KeyguardState.GONE) {
                     // Ensures content is not visible when in GONE state
                     0f
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index ba04fd3..f5e6135 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -67,6 +67,8 @@
             duration = 500.milliseconds,
             onStart = { 0f },
             onStep = { it },
+            onFinish = { 1f },
+            onCancel = { 1f },
         )
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt
index 785a1e8..1d3cfd2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt
@@ -30,7 +30,7 @@
     private val localBluetoothManager: LocalBluetoothManager?
 ) {
     /** Creates a [LocalMediaManager] for the given package. */
-    fun create(packageName: String): LocalMediaManager {
+    fun create(packageName: String?): LocalMediaManager {
         return InfoMediaManager.createInstance(context, packageName, null, localBluetoothManager)
             .run { LocalMediaManager(context, localBluetoothManager, this, packageName) }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 09e4e75..06ca3af 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2472,11 +2472,9 @@
             return 0;
         }
         if (!mKeyguardBypassController.getBypassEnabled()) {
-            if (migrateClocksToBlueprint()) {
-                View nsslPlaceholder = mView.getRootView().findViewById(R.id.nssl_placeholder);
-                if (!mSplitShadeEnabled && nsslPlaceholder != null) {
-                    return nsslPlaceholder.getTop();
-                }
+            if (migrateClocksToBlueprint() && !mSplitShadeEnabled) {
+                return (int) mKeyguardInteractor.getNotificationContainerBounds()
+                        .getValue().getTop();
             }
 
             return mClockPositionResult.stackScrollerPadding;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt
new file mode 100644
index 0000000..c74c396
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.interruption
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.util.Log
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+
+// Class to track avalanche trigger event time.
+@SysUISingleton
+class AvalancheProvider
+@Inject
+constructor(
+        private val broadcastDispatcher: BroadcastDispatcher,
+        private val logger: VisualInterruptionDecisionLogger,
+) {
+    val TAG = "AvalancheProvider"
+    val timeoutMs = 120000
+    var startTime: Long = 0L
+
+    private val avalancheTriggerIntents = mutableSetOf(
+            Intent.ACTION_AIRPLANE_MODE_CHANGED,
+            Intent.ACTION_BOOT_COMPLETED,
+            Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
+            Intent.ACTION_USER_SWITCHED
+    )
+
+    private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent) {
+            if (intent.action in avalancheTriggerIntents) {
+
+                // Ignore when airplane mode turned on
+                if (intent.action == Intent.ACTION_AIRPLANE_MODE_CHANGED
+                        && intent.getBooleanExtra(/* name= */ "state", /* defaultValue */ false)) {
+                    Log.d(TAG, "broadcastReceiver: ignore airplane mode on")
+                    return
+                }
+                Log.d(TAG, "broadcastReceiver received intent.action=" + intent.action)
+                startTime = System.currentTimeMillis()
+            }
+        }
+    }
+
+    fun register() {
+        val intentFilter = IntentFilter()
+        for (intent in avalancheTriggerIntents) {
+            intentFilter.addAction(intent)
+        }
+        broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index 8e82442..20c8add 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -16,7 +16,10 @@
 
 package com.android.systemui.statusbar.notification.interruption
 
+import android.app.Notification
 import android.app.Notification.BubbleMetadata
+import android.app.Notification.CATEGORY_EVENT
+import android.app.Notification.CATEGORY_REMINDER
 import android.app.Notification.VISIBILITY_PRIVATE
 import android.app.NotificationManager.IMPORTANCE_DEFAULT
 import android.app.NotificationManager.IMPORTANCE_HIGH
@@ -224,3 +227,68 @@
     override fun shouldSuppress(entry: NotificationEntry) =
         keyguardNotificationVisibilityProvider.shouldHideNotification(entry)
 }
+
+
+class AvalancheSuppressor(
+    private val avalancheProvider: AvalancheProvider,
+    private val systemClock: SystemClock,
+) : VisualInterruptionFilter(
+        types = setOf(PEEK, PULSE),
+        reason = "avalanche",
+    ) {
+    val TAG = "AvalancheSuppressor"
+
+    enum class State {
+        ALLOW_CONVERSATION_AFTER_AVALANCHE,
+        ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME,
+        ALLOW_CALLSTYLE,
+        ALLOW_CATEGORY_REMINDER,
+        ALLOW_CATEGORY_EVENT,
+        ALLOW_FSI_WITH_PERMISSION_ON,
+        ALLOW_COLORIZED,
+        SUPPRESS
+    }
+
+    override fun shouldSuppress(entry: NotificationEntry): Boolean {
+        val timeSinceAvalanche = systemClock.currentTimeMillis() - avalancheProvider.startTime
+        val isActive = timeSinceAvalanche < avalancheProvider.timeoutMs
+        val state = allow(entry)
+        val suppress = isActive && state == State.SUPPRESS
+        reason = "avalanche suppress=$suppress isActive=$isActive state=$state"
+        return suppress
+    }
+
+    fun allow(entry: NotificationEntry): State  {
+        if (
+            entry.ranking.isConversation &&
+                entry.sbn.notification.`when` > avalancheProvider.startTime
+        ) {
+            return State.ALLOW_CONVERSATION_AFTER_AVALANCHE
+        }
+
+        if (entry.channel?.isImportantConversation == true) {
+            return State.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME
+        }
+
+        if (entry.sbn.notification.isStyle(Notification.CallStyle::class.java)) {
+            return State.ALLOW_CALLSTYLE
+        }
+
+        if (entry.sbn.notification.category == CATEGORY_REMINDER) {
+            return State.ALLOW_CATEGORY_REMINDER
+        }
+
+        if (entry.sbn.notification.category == CATEGORY_EVENT) {
+            return State.ALLOW_CATEGORY_EVENT
+        }
+
+        if (entry.sbn.notification.fullScreenIntent != null) {
+            return State.ALLOW_FSI_WITH_PERMISSION_ON
+        }
+
+        if (entry.sbn.notification.isColorized) {
+            return State.ALLOW_COLORIZED
+        }
+        return State.SUPPRESS
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index 6878a1e..dabb18b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE
+import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.statusbar.policy.HeadsUpManager
@@ -45,22 +46,25 @@
 class VisualInterruptionDecisionProviderImpl
 @Inject
 constructor(
-    private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
-    private val batteryController: BatteryController,
-    deviceProvisionedController: DeviceProvisionedController,
-    private val eventLog: EventLog,
-    private val globalSettings: GlobalSettings,
-    private val headsUpManager: HeadsUpManager,
-    private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
-    keyguardStateController: KeyguardStateController,
-    private val logger: VisualInterruptionDecisionLogger,
-    @Main private val mainHandler: Handler,
-    private val powerManager: PowerManager,
-    private val statusBarStateController: StatusBarStateController,
-    private val systemClock: SystemClock,
-    private val uiEventLogger: UiEventLogger,
-    private val userTracker: UserTracker,
+        private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
+        private val batteryController: BatteryController,
+        deviceProvisionedController: DeviceProvisionedController,
+        private val eventLog: EventLog,
+        private val globalSettings: GlobalSettings,
+        private val headsUpManager: HeadsUpManager,
+        private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
+        keyguardStateController: KeyguardStateController,
+        private val logger: VisualInterruptionDecisionLogger,
+        @Main private val mainHandler: Handler,
+        private val powerManager: PowerManager,
+        private val statusBarStateController: StatusBarStateController,
+        private val systemClock: SystemClock,
+        private val uiEventLogger: UiEventLogger,
+        private val userTracker: UserTracker,
+        private val avalancheProvider: AvalancheProvider
+
 ) : VisualInterruptionDecisionProvider {
+
     init {
         check(!VisualInterruptionRefactor.isUnexpectedlyInLegacyMode())
     }
@@ -166,6 +170,10 @@
         addFilter(HunJustLaunchedFsiSuppressor())
         addFilter(AlertKeyguardVisibilitySuppressor(keyguardNotificationVisibilityProvider))
 
+        if (NotificationAvalancheSuppression.isEnabled) {
+            addFilter(AvalancheSuppressor(avalancheProvider, systemClock))
+            avalancheProvider.register()
+        }
         started = true
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
index ee79727..2f80c5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
@@ -85,7 +85,7 @@
 /** A reason why visual interruptions might be suppressed based on the notification. */
 abstract class VisualInterruptionFilter(
     override val types: Set<VisualInterruptionType>,
-    override val reason: String,
+    override var reason: String,
     override val uiEventId: UiEventEnum? = null,
     override val eventLogData: EventLogData? = null
 ) : VisualInterruptionSuppressor {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
index 6c2cbbe..50b08b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel
 import kotlin.math.roundToInt
+import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.launch
 
 /** Binds the shared notification container to its view-model. */
@@ -38,8 +39,8 @@
         viewModel: NotificationStackAppearanceViewModel,
         ambientState: AmbientState,
         controller: NotificationStackScrollLayoutController,
-    ) {
-        view.repeatWhenAttached {
+    ): DisposableHandle {
+        return view.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.CREATED) {
                 launch {
                     viewModel.stackBounds.collect { bounds ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index a436f17..8b723da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -20,6 +20,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.flag.SceneContainerFlags
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
@@ -40,9 +42,11 @@
     shadeInteractor: ShadeInteractor,
     flags: SceneContainerFlags,
     featureFlags: FeatureFlagsClassic,
+    private val keyguardInteractor: KeyguardInteractor,
 ) {
     /** DEBUG: whether the placeholder "Notifications" text should be shown. */
-    val isPlaceholderTextVisible: Boolean = !flags.flexiNotifsEnabled()
+    val isPlaceholderTextVisible: Boolean =
+        !flags.flexiNotifsEnabled() && SceneContainerFlag.isEnabled
 
     /** DEBUG: whether the placeholder should be made slightly visible for positional debugging. */
     val isVisualDebuggingEnabled: Boolean = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES)
@@ -64,7 +68,10 @@
         right: Float,
         bottom: Float,
     ) {
-        interactor.setStackBounds(NotificationContainerBounds(left, top, right, bottom))
+        val notificationContainerBounds =
+            NotificationContainerBounds(top = top, bottom = bottom, left = left, right = right)
+        keyguardInteractor.setNotificationContainerBounds(notificationContainerBounds)
+        interactor.setStackBounds(notificationContainerBounds)
     }
 
     /** The corner radius of the placeholder, in dp. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 39ca7b2..3669ba8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -352,7 +352,7 @@
         }
 
         if (!mKeyguardStateController.isShowing()) {
-            final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext);
+            final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext, mUserTracker.getUserId());
             cameraIntent.putExtra(CameraIntents.EXTRA_LAUNCH_SOURCE, source);
             mActivityStarter.startActivityDismissingKeyguard(cameraIntent,
                     false /* onlyProvisioned */, true /* dismissShade */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 64fcef5..2099361 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1803,10 +1803,10 @@
         }
 
         pw.println("Camera gesture intents:");
-        pw.println("   Insecure camera: " + CameraIntents.getInsecureCameraIntent(mContext));
-        pw.println("   Secure camera: " + CameraIntents.getSecureCameraIntent(mContext));
+        pw.println("   Insecure camera: " + CameraIntents.getInsecureCameraIntent(mContext, mUserTracker.getUserId()));
+        pw.println("   Secure camera: " + CameraIntents.getSecureCameraIntent(mContext, mUserTracker.getUserId()));
         pw.println("   Override package: "
-                + CameraIntents.getOverrideCameraPackage(mContext));
+                + CameraIntents.getOverrideCameraPackage(mContext, mUserTracker.getUserId()));
     }
 
     private void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 1a17e7c..665a571 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -32,7 +32,7 @@
 import com.android.systemui.statusbar.notification.stack.AnimationProperties
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.app.tracing.TraceUtils
+import com.android.app.tracing.namedRunnable
 import com.android.systemui.util.settings.GlobalSettings
 import javax.inject.Inject
 
@@ -125,7 +125,7 @@
     }
 
     // FrameCallback used to delay starting the light reveal animation until the next frame
-    private val startLightRevealCallback = TraceUtils.namedRunnable("startLightReveal") {
+    private val startLightRevealCallback = namedRunnable("startLightReveal") {
         lightRevealAnimationPlaying = true
         lightRevealAnimator.start()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
index 92a64a6..4dfd5a1 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
@@ -18,8 +18,8 @@
 
 import android.content.Context
 import android.util.Log
-import com.android.app.tracing.TraceUtils.instantForTrack
 import com.android.app.tracing.TraceUtils.traceAsync
+import com.android.app.tracing.instantForTrack
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -81,8 +81,8 @@
                 .pairwise()
                 .filter {
                     // Start tracking only when the foldable device is
-                    //folding(UNFOLDED/HALF_FOLDED -> FOLDED) or
-                    //unfolding(FOLDED -> HALF_FOLD/UNFOLDED)
+                    // folding(UNFOLDED/HALF_FOLDED -> FOLDED) or
+                    // unfolding(FOLDED -> HALF_FOLD/UNFOLDED)
                     foldableDeviceState ->
                     foldableDeviceState.previousValue == DeviceState.FOLDED ||
                         foldableDeviceState.newValue == DeviceState.FOLDED
@@ -172,7 +172,7 @@
         fromFoldableDeviceState: Int
     ): DisplaySwitchLatencyEvent {
         log { "fromFoldableDeviceState=$fromFoldableDeviceState" }
-        instantForTrack(TAG, "fromFoldableDeviceState=$fromFoldableDeviceState")
+        instantForTrack(TAG) { "fromFoldableDeviceState=$fromFoldableDeviceState" }
 
         return copy(fromFoldableDeviceState = fromFoldableDeviceState)
     }
@@ -187,7 +187,7 @@
                 "toState=$toState, " +
                 "latencyMs=$displaySwitchTimeMs"
         }
-        instantForTrack(TAG, "toFoldableDeviceState=$toFoldableDeviceState, toState=$toState")
+        instantForTrack(TAG) { "toFoldableDeviceState=$toFoldableDeviceState, toState=$toState" }
 
         return copy(
             toFoldableDeviceState = toFoldableDeviceState,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
new file mode 100644
index 0000000..2ff9af9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dagger
+
+import android.content.Context
+import android.media.session.MediaSessionManager
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.volume.data.repository.MediaControllerRepository
+import com.android.settingslib.volume.data.repository.MediaControllerRepositoryImpl
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import dagger.Module
+import dagger.Provides
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+
+@Module
+interface MediaDevicesModule {
+
+    companion object {
+
+        @Provides
+        @SysUISingleton
+        fun provideMediaDeviceSessionRepository(
+            @Application context: Context,
+            mediaSessionManager: MediaSessionManager,
+            localBluetoothManager: LocalBluetoothManager?,
+            @Application coroutineScope: CoroutineScope,
+            @Background backgroundContext: CoroutineContext,
+        ): MediaControllerRepository =
+            MediaControllerRepositoryImpl(
+                context,
+                mediaSessionManager,
+                localBluetoothManager,
+                coroutineScope,
+                backgroundContext,
+            )
+    }
+}
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 c842e5f..5cb6fa8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -62,6 +62,7 @@
 @Module(
         includes = {
                 AudioModule.class,
+                MediaDevicesModule.class
         },
         subcomponents = {
                 VolumePanelComponent.class
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
new file mode 100644
index 0000000..57ac435
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.volume.panel.component.mediaoutput.data.repository
+
+import com.android.settingslib.volume.data.repository.LocalMediaRepository
+import com.android.settingslib.volume.data.repository.LocalMediaRepositoryImpl
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.media.controls.pipeline.LocalMediaManagerFactory
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+
+class LocalMediaRepositoryFactory
+@Inject
+constructor(
+    private val localMediaManagerFactory: LocalMediaManagerFactory,
+    @Application private val coroutineScope: CoroutineScope,
+    @Background private val backgroundCoroutineContext: CoroutineContext,
+) {
+
+    fun create(packageName: String?): LocalMediaRepository =
+        LocalMediaRepositoryImpl(
+            localMediaManagerFactory.create(packageName),
+            coroutineScope,
+            backgroundCoroutineContext,
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
new file mode 100644
index 0000000..6c456f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import android.content.pm.PackageManager
+import android.util.Log
+import com.android.settingslib.media.MediaDevice
+import com.android.settingslib.volume.data.repository.LocalMediaRepository
+import com.android.settingslib.volume.data.repository.MediaControllerRepository
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSession
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.withContext
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@VolumePanelScope
+class MediaOutputInteractor
+@Inject
+constructor(
+    private val localMediaRepositoryFactory: LocalMediaRepositoryFactory,
+    private val packageManager: PackageManager,
+    @VolumePanelScope private val coroutineScope: CoroutineScope,
+    @Background private val backgroundCoroutineContext: CoroutineContext,
+    mediaControllerRepository: MediaControllerRepository
+) {
+
+    val mediaDeviceSession: Flow<MediaDeviceSession> =
+        mediaControllerRepository.activeMediaController.mapNotNull { mediaController ->
+            if (mediaController == null) {
+                MediaDeviceSession.Inactive
+            } else {
+                MediaDeviceSession.Active(
+                    appLabel = getApplicationLabel(mediaController.packageName)
+                            ?: return@mapNotNull null,
+                    packageName = mediaController.packageName,
+                    sessionToken = mediaController.sessionToken,
+                )
+            }
+        }
+    private val localMediaRepository: Flow<LocalMediaRepository> =
+        mediaDeviceSession
+            .map { (it as? MediaDeviceSession.Active)?.packageName }
+            .distinctUntilChanged()
+            .map { localMediaRepositoryFactory.create(it) }
+            .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), replay = 1)
+
+    val currentConnectedDevice: Flow<MediaDevice?> =
+        localMediaRepository.flatMapLatest { it.currentConnectedDevice }
+
+    val mediaDevices: Flow<Collection<MediaDevice>> =
+        localMediaRepository.flatMapLatest { it.mediaDevices }
+
+    private suspend fun getApplicationLabel(packageName: String): CharSequence? {
+        return try {
+            withContext(backgroundCoroutineContext) {
+                val appInfo =
+                    packageManager.getApplicationInfo(
+                        packageName,
+                        PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER
+                    )
+                appInfo.loadLabel(packageManager)
+            }
+        } catch (e: PackageManager.NameNotFoundException) {
+            Log.e(TAG, "Unable to find info for package: $packageName")
+            null
+        }
+    }
+
+    private companion object {
+        const val TAG = "MediaOutputInteractor"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaDeviceSession.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaDeviceSession.kt
new file mode 100644
index 0000000..f250308
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaDeviceSession.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.model
+
+import android.media.session.MediaSession
+
+/** Represents media playing on the connected device. */
+sealed interface MediaDeviceSession {
+
+    /** Media is playing. */
+    data class Active(
+        val appLabel: CharSequence,
+        val packageName: String,
+        val sessionToken: MediaSession.Token,
+    ) : MediaDeviceSession
+
+    /** Media is not playing. */
+    data object Inactive : MediaDeviceSession
+
+    /** Current media state is unknown yet. */
+    data object Unknown : MediaDeviceSession
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
index e921a59..64cd526 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/CameraAvailabilityListenerTest.kt
@@ -344,10 +344,15 @@
     }
 
     private fun createAndStartSut(): CameraAvailabilityListener {
-        return CameraAvailabilityListener.build(context, context.mainExecutor).also {
-            it.addTransitionCallback(cameraTransitionCallback)
-            it.startListening()
-        }
+        return CameraAvailabilityListener.build(
+                context,
+                context.mainExecutor,
+                CameraProtectionLoader((context))
+            )
+            .also {
+                it.addTransitionCallback(cameraTransitionCallback)
+                it.startListening()
+            }
     }
 
     private class TestCameraTransitionCallback :
diff --git a/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderTest.kt
new file mode 100644
index 0000000..238e5e9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui
+
+import android.graphics.Rect
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.res.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CameraProtectionLoaderTest : SysuiTestCase() {
+
+    private val loader = CameraProtectionLoader(context)
+
+    @Before
+    fun setUp() {
+        overrideResource(R.string.config_protectedCameraId, OUTER_CAMERA_LOGICAL_ID)
+        overrideResource(R.string.config_protectedPhysicalCameraId, OUTER_CAMERA_PHYSICAL_ID)
+        overrideResource(
+            R.string.config_frontBuiltInDisplayCutoutProtection,
+            OUTER_CAMERA_PROTECTION_PATH
+        )
+        overrideResource(R.string.config_protectedInnerCameraId, INNER_CAMERA_LOGICAL_ID)
+        overrideResource(R.string.config_protectedInnerPhysicalCameraId, INNER_CAMERA_PHYSICAL_ID)
+        overrideResource(
+            R.string.config_innerBuiltInDisplayCutoutProtection,
+            INNER_CAMERA_PROTECTION_PATH
+        )
+    }
+
+    @Test
+    fun loadCameraProtectionInfoList() {
+        val protectionInfos = loader.loadCameraProtectionInfoList().map { it.toTestableVersion() }
+
+        assertThat(protectionInfos)
+            .containsExactly(OUTER_CAMERA_PROTECTION_INFO, INNER_CAMERA_PROTECTION_INFO)
+    }
+
+    @Test
+    fun loadCameraProtectionInfoList_outerCameraIdEmpty_onlyReturnsInnerInfo() {
+        overrideResource(R.string.config_protectedCameraId, "")
+
+        val protectionInfos = loader.loadCameraProtectionInfoList().map { it.toTestableVersion() }
+
+        assertThat(protectionInfos).containsExactly(INNER_CAMERA_PROTECTION_INFO)
+    }
+
+    @Test
+    fun loadCameraProtectionInfoList_innerCameraIdEmpty_onlyReturnsOuterInfo() {
+        overrideResource(R.string.config_protectedInnerCameraId, "")
+
+        val protectionInfos = loader.loadCameraProtectionInfoList().map { it.toTestableVersion() }
+
+        assertThat(protectionInfos).containsExactly(OUTER_CAMERA_PROTECTION_INFO)
+    }
+
+    @Test
+    fun loadCameraProtectionInfoList_innerAndOuterCameraIdsEmpty_returnsEmpty() {
+        overrideResource(R.string.config_protectedCameraId, "")
+        overrideResource(R.string.config_protectedInnerCameraId, "")
+
+        val protectionInfos = loader.loadCameraProtectionInfoList().map { it.toTestableVersion() }
+
+        assertThat(protectionInfos).isEmpty()
+    }
+
+    private fun CameraProtectionInfo.toTestableVersion() =
+        TestableProtectionInfo(logicalCameraId, physicalCameraId, cutoutBounds)
+
+    /**
+     * "Testable" version, because the original version contains a Path property, which doesn't
+     * implement equals.
+     */
+    private data class TestableProtectionInfo(
+        val logicalCameraId: String,
+        val physicalCameraId: String?,
+        val cutoutBounds: Rect,
+    )
+
+    companion object {
+        private const val OUTER_CAMERA_LOGICAL_ID = "1"
+        private const val OUTER_CAMERA_PHYSICAL_ID = "11"
+        private const val OUTER_CAMERA_PROTECTION_PATH = "M 0,0 H 10,10 V 10,10 H 0,10 Z"
+        private val OUTER_CAMERA_PROTECTION_BOUNDS =
+            Rect(/* left = */ 0, /* top = */ 0, /* right = */ 10, /* bottom = */ 10)
+        private val OUTER_CAMERA_PROTECTION_INFO =
+            TestableProtectionInfo(
+                OUTER_CAMERA_LOGICAL_ID,
+                OUTER_CAMERA_PHYSICAL_ID,
+                OUTER_CAMERA_PROTECTION_BOUNDS
+            )
+
+        private const val INNER_CAMERA_LOGICAL_ID = "2"
+        private const val INNER_CAMERA_PHYSICAL_ID = "22"
+        private const val INNER_CAMERA_PROTECTION_PATH = "M 0,0 H 20,20 V 20,20 H 0,20 Z"
+        private val INNER_CAMERA_PROTECTION_BOUNDS =
+            Rect(/* left = */ 0, /* top = */ 0, /* right = */ 20, /* bottom = */ 20)
+        private val INNER_CAMERA_PROTECTION_INFO =
+            TestableProtectionInfo(
+                INNER_CAMERA_LOGICAL_ID,
+                INNER_CAMERA_PHYSICAL_ID,
+                INNER_CAMERA_PROTECTION_BOUNDS
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index c07148b..1f1fa72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -176,6 +176,8 @@
     private FakeFacePropertyRepository mFakeFacePropertyRepository =
             new FakeFacePropertyRepository();
     private List<DecorProvider> mMockCutoutList;
+    private final CameraProtectionLoader mCameraProtectionLoader =
+            new CameraProtectionLoader(mContext);
 
     @Before
     public void setup() {
@@ -247,7 +249,7 @@
                 mThreadFactory,
                 mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
                 new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
-                mFakeFacePropertyRepository, mJavaAdapter) {
+                mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader) {
             @Override
             public void start() {
                 super.start();
@@ -1243,7 +1245,7 @@
                 mDotViewController,
                 mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
                 new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
-                mFakeFacePropertyRepository, mJavaAdapter);
+                mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader);
         screenDecorations.start();
         when(mContext.getDisplay()).thenReturn(mDisplay);
         when(mDisplay.getDisplayInfo(any())).thenAnswer(new Answer<Boolean>() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
index 6d3cc4c..669795b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
@@ -81,10 +81,10 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        whenever(cameraIntents.getSecureCameraIntent()).thenReturn(
+        whenever(cameraIntents.getSecureCameraIntent(anyInt())).thenReturn(
             Intent(CameraIntents.DEFAULT_SECURE_CAMERA_INTENT_ACTION)
         )
-        whenever(cameraIntents.getInsecureCameraIntent()).thenReturn(
+        whenever(cameraIntents.getInsecureCameraIntent(anyInt())).thenReturn(
             Intent(CameraIntents.DEFAULT_INSECURE_CAMERA_INTENT_ACTION)
         )
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index af027e8..6d2df19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -63,6 +63,7 @@
         mDockHandler = new DozeDockHandler(mConfig, mDockManagerFake, mUserTracker);
         mDockHandler.setDozeMachine(mMachine);
 
+        when(mMachine.isExecutingTransition()).thenReturn(false);
         when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
         when(mMachine.getState()).thenReturn(State.DOZE_AOD);
         doReturn(true).when(mConfig).alwaysOnEnabled(anyInt());
@@ -148,4 +149,13 @@
 
         verify(mMachine, never()).requestState(any(State.class));
     }
+
+    @Test
+    public void onEvent_dockedWhileTransitioning_wontRequestStateChange() {
+        when(mMachine.isExecutingTransition()).thenReturn(true);
+
+        mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED_HIDE);
+
+        verify(mMachine, never()).requestState(any(State.class));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
index df73cc8..a992956 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyboard.stickykeys.ui
 
+import android.app.Dialog
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.compose.ComposeFacade
@@ -26,8 +27,6 @@
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
 import com.android.systemui.keyboard.stickykeys.ui.viewmodel.StickyKeysIndicatorViewModel
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.phone.ComponentSystemUIDialog
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -40,8 +39,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
 
@@ -53,15 +50,13 @@
     private lateinit var coordinator: StickyKeysIndicatorCoordinator
     private val testScope = TestScope(StandardTestDispatcher())
     private val stickyKeysRepository = FakeStickyKeysRepository()
-    private val dialog = mock<ComponentSystemUIDialog>()
+    private val dialog = mock<Dialog>()
 
     @Before
     fun setup() {
         Assume.assumeTrue(ComposeFacade.isComposeAvailable())
-        val dialogFactory = mock<SystemUIDialogFactory> {
-            whenever(applicationContext).thenReturn(context)
-            whenever(create(any(), anyInt(), anyBoolean())).thenReturn(dialog)
-        }
+        val dialogFactory = mock<StickyKeyDialogFactory>()
+        whenever(dialogFactory.create(any())).thenReturn(dialog)
         val keyboardRepository = Kosmos().keyboardRepository
         val viewModel = StickyKeysIndicatorViewModel(
                 stickyKeysRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 0831971..9517f82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -16,12 +16,14 @@
 
 package com.android.systemui.shade
 
+import android.content.Context
 import android.os.PowerManager
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.ViewUtils
 import android.view.MotionEvent
 import android.view.View
+import android.view.WindowManager
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -33,18 +35,22 @@
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.compose.ComposeFacade
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.After
 import org.junit.Assert.assertThrows
 import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.BeforeClass
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
@@ -52,12 +58,17 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@Ignore("b/323053208")
+@ExperimentalCoroutinesApi
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class GlanceableHubContainerControllerTest : SysuiTestCase() {
-    private val kosmos = testKosmos()
+    private val kosmos: Kosmos =
+        testKosmos().apply {
+            // UnconfinedTestDispatcher makes testing simpler due to CommunalInteractor flows using
+            // SharedFlow
+            testDispatcher = UnconfinedTestDispatcher()
+        }
 
     @Mock private lateinit var communalViewModel: CommunalViewModel
     @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@@ -104,17 +115,24 @@
             R.dimen.communal_bottom_edge_swipe_region_height,
             BOTTOM_SWIPE_REGION_WIDTH
         )
+
+        initAndAttachContainerView()
+    }
+
+    @After
+    fun tearDown() {
+        ViewUtils.detachView(parentView)
     }
 
     @Test
-    fun isEnabled_interactorEnabled_interceptsTouches() {
+    fun isEnabled_communalEnabled_returnsTrue() {
         communalRepository.setIsCommunalEnabled(true)
 
         assertThat(underTest.isEnabled()).isTrue()
     }
 
     @Test
-    fun isEnabled_interactorDisabled_doesNotIntercept() {
+    fun isEnabled_communalDisabled_returnsFalse() {
         communalRepository.setIsCommunalEnabled(false)
 
         assertThat(underTest.isEnabled()).isFalse()
@@ -124,11 +142,29 @@
     fun initView_notEnabled_throwsException() {
         communalRepository.setIsCommunalEnabled(false)
 
+        underTest =
+            GlanceableHubContainerController(
+                communalInteractor,
+                communalViewModel,
+                keyguardTransitionInteractor,
+                shadeInteractor,
+                powerManager,
+            )
+
         assertThrows(RuntimeException::class.java) { underTest.initView(context) }
     }
 
     @Test
     fun initView_calledTwice_throwsException() {
+        underTest =
+            GlanceableHubContainerController(
+                communalInteractor,
+                communalViewModel,
+                keyguardTransitionInteractor,
+                shadeInteractor,
+                powerManager,
+            )
+
         // First call succeeds.
         underTest.initView(context)
 
@@ -137,25 +173,20 @@
     }
 
     @Test
-    fun onTouchEvent_touchInsideGestureRegion_interceptsTouches() {
-        // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+    fun onTouchEvent_communalClosed_doesNotIntercept() {
+        // Communal is closed.
+        goToScene(CommunalSceneKey.Blank)
 
-        initAndAttachContainerView()
-
-        // Touch events are intercepted.
-        assertThat(underTest.onTouchEvent(DOWN_IN_RIGHT_SWIPE_REGION_EVENT)).isTrue()
+        assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
     }
 
     @Test
-    fun onTouchEvent_subsequentTouchesAfterGestureStart_interceptsTouches() {
-        // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+    fun onTouchEvent_openGesture_interceptsTouches() {
+        // Communal is closed.
+        goToScene(CommunalSceneKey.Blank)
 
-        initAndAttachContainerView()
-
-        // Initial touch down is intercepted, and so are touches outside of the region, until an up
-        // event is received.
+        // Initial touch down is intercepted, and so are touches outside of the region, until an
+        // up event is received.
         assertThat(underTest.onTouchEvent(DOWN_IN_RIGHT_SWIPE_REGION_EVENT)).isTrue()
         assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
         assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
@@ -165,34 +196,27 @@
     @Test
     fun onTouchEvent_communalOpen_interceptsTouches() {
         // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+        goToScene(CommunalSceneKey.Communal)
 
-        initAndAttachContainerView()
-        testableLooper.processAllMessages()
-
-        // Touch events are intercepted.
+        // Touch events are intercepted outside of any gesture areas.
         assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
         // User activity sent to PowerManager.
         verify(powerManager).userActivity(any(), any(), any())
     }
 
     @Test
-    fun onTouchEvent_topSwipeWhenHubOpen_returnsFalse() {
+    fun onTouchEvent_topSwipeWhenCommunalOpen_doesNotIntercept() {
         // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
-        initAndAttachContainerView()
+        goToScene(CommunalSceneKey.Communal)
 
         // Touch event in the top swipe reqgion is not intercepted.
         assertThat(underTest.onTouchEvent(DOWN_IN_TOP_SWIPE_REGION_EVENT)).isFalse()
     }
 
     @Test
-    fun onTouchEvent_bottomSwipeWhenHubOpen_returnsFalse() {
+    fun onTouchEvent_bottomSwipeWhenCommunalOpen_doesNotIntercept() {
         // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
-        initAndAttachContainerView()
+        goToScene(CommunalSceneKey.Communal)
 
         // Touch event in the bottom swipe reqgion is not intercepted.
         assertThat(underTest.onTouchEvent(DOWN_IN_BOTTOM_SWIPE_REGION_EVENT)).isFalse()
@@ -201,9 +225,7 @@
     @Test
     fun onTouchEvent_communalAndBouncerShowing_doesNotIntercept() {
         // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
-        initAndAttachContainerView()
+        goToScene(CommunalSceneKey.Communal)
 
         // Bouncer is visible.
         bouncerShowingFlow.value = true
@@ -218,9 +240,7 @@
     @Test
     fun onTouchEvent_communalAndShadeShowing_doesNotIntercept() {
         // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
-        initAndAttachContainerView()
+        goToScene(CommunalSceneKey.Communal)
 
         shadeShowingFlow.value = true
         testableLooper.processAllMessages()
@@ -232,10 +252,7 @@
     @Test
     fun onTouchEvent_containerViewDisposed_doesNotIntercept() {
         // Communal is open.
-        communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
-        initAndAttachContainerView()
-        testableLooper.processAllMessages()
+        goToScene(CommunalSceneKey.Communal)
 
         // Touch events are intercepted.
         assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
@@ -253,15 +270,24 @@
         parentView = FrameLayout(context)
         parentView.addView(containerView)
 
-        // Make view clickable so that dispatchTouchEvent returns true.
-        containerView.isClickable = true
-
         underTest.initView(containerView)
+
         // Attach the view so that flows start collecting.
         ViewUtils.attachView(parentView)
-        // Give the view a size so that determining if a touch starts at the right edge works.
-        parentView.layout(0, 0, CONTAINER_WIDTH, CONTAINER_HEIGHT)
-        containerView.layout(0, 0, CONTAINER_WIDTH, CONTAINER_HEIGHT)
+
+        // Give the view a fixed size to simplify testing for edge swipes.
+        val lp =
+            parentView.layoutParams.apply {
+                width = CONTAINER_WIDTH
+                height = CONTAINER_HEIGHT
+            }
+        val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+        wm.updateViewLayout(parentView, lp)
+    }
+
+    private fun goToScene(scene: CommunalSceneKey) {
+        communalRepository.setDesiredScene(scene)
+        testableLooper.processAllMessages()
     }
 
     companion object {
@@ -271,13 +297,17 @@
         private const val TOP_SWIPE_REGION_WIDTH = 20
         private const val BOTTOM_SWIPE_REGION_WIDTH = 20
 
+        /**
+         * A touch down event right in the middle of the screen, to avoid being in any of the swipe
+         * regions.
+         */
         private val DOWN_EVENT =
             MotionEvent.obtain(
                 0L,
                 0L,
                 MotionEvent.ACTION_DOWN,
-                CONTAINER_WIDTH.toFloat(),
-                CONTAINER_HEIGHT.toFloat(),
+                CONTAINER_WIDTH.toFloat() / 2,
+                CONTAINER_HEIGHT.toFloat() / 2,
                 0
             )
         private val DOWN_IN_RIGHT_SWIPE_REGION_EVENT =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
index da68d9c..5410864 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.statusbar.notification.interruption
 
+import android.app.Notification.CATEGORY_EVENT
+import android.app.Notification.CATEGORY_REMINDER
+import android.app.NotificationManager
 import android.platform.test.annotations.EnableFlags
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
@@ -47,6 +50,7 @@
             systemClock,
             uiEventLogger,
             userTracker,
+            avalancheProvider
         )
     }
 
@@ -70,6 +74,114 @@
         }
     }
 
+    // Avalanche tests are in VisualInterruptionDecisionProviderImplTest
+    // instead of VisualInterruptionDecisionProviderTestBase
+    // because avalanche code is based on the suppression refactor.
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowConversationFromAfterEvent() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_HIGH
+                isConversation = true
+                isImportantConversation = false
+                whenMs = whenAgo(5)
+            })
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_suppressConversationFromBeforeEvent() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldNotHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_DEFAULT
+                isConversation = true
+                isImportantConversation = false
+                whenMs = whenAgo(15)
+            })
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowHighPriorityConversation() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_HIGH
+                isImportantConversation = true
+            })
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowCall() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_HIGH
+                isCall = true
+            })
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowCategoryReminder() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_HIGH
+                category = CATEGORY_REMINDER
+            })
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowCategoryEvent() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_HIGH
+                category = CATEGORY_EVENT
+            })
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowFsi() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            assertFsiNotSuppressed()
+        }
+    }
+
+    @Test
+    fun testAvalancheFilter_duringAvalanche_allowColorized() {
+        avalancheProvider.startTime = whenAgo(10)
+
+        withFilter(AvalancheSuppressor(avalancheProvider, systemClock)) {
+            ensurePeekState()
+            assertShouldHeadsUp(buildEntry {
+                importance = NotificationManager.IMPORTANCE_HIGH
+                isColorized = true
+            })
+        }
+    }
+
     @Test
     fun testPeekCondition_suppressesOnlyPeek() {
         withCondition(TestCondition(types = setOf(PEEK)) { true }) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index 2ac0cb7..f89b9cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -19,7 +19,10 @@
 import android.app.ActivityManager
 import android.app.Notification
 import android.app.Notification.BubbleMetadata
+import android.app.Notification.EXTRA_COLORIZED
+import android.app.Notification.EXTRA_TEMPLATE
 import android.app.Notification.FLAG_BUBBLE
+import android.app.Notification.FLAG_CAN_COLORIZE
 import android.app.Notification.FLAG_FOREGROUND_SERVICE
 import android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED
 import android.app.Notification.FLAG_USER_INITIATED_JOB
@@ -50,6 +53,8 @@
 import com.android.internal.logging.UiEventLogger.UiEventEnum
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.broadcast.FakeBroadcastDispatcher
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogcatEchoTracker
 import com.android.systemui.log.core.LogLevel
@@ -122,6 +127,7 @@
     protected val systemClock = FakeSystemClock()
     protected val uiEventLogger = UiEventLoggerFake()
     protected val userTracker = FakeUserTracker()
+    protected val avalancheProvider: AvalancheProvider = mock()
 
     protected abstract val provider: VisualInterruptionDecisionProvider
 
@@ -1097,6 +1103,8 @@
         var whenMs: Long? = null
         var isGrouped = false
         var isGroupSummary = false
+        var isCall = false
+        var category: String? = null
         var groupAlertBehavior: Int? = null
         var hasBubbleMetadata = false
         var hasFsi = false
@@ -1106,10 +1114,12 @@
         var isUserInitiatedJob = false
         var isBubble = false
         var isStickyAndNotDemoted = false
+        var isColorized = false
 
         // Set on NotificationEntryBuilder:
         var importance = IMPORTANCE_DEFAULT
         var canBubble: Boolean? = null
+        var isImportantConversation = false
 
         // Set on NotificationEntry:
         var hasJustLaunchedFsi = false
@@ -1118,6 +1128,7 @@
         var packageSuspended = false
         var visibilityOverride: Int? = null
         var suppressedVisualEffects: Int? = null
+        var isConversation = false
 
         private fun buildBubbleMetadata(): BubbleMetadata {
             val builder =
@@ -1158,6 +1169,13 @@
                         nb.setGroupSummary(true)
                     }
 
+                    if (isCall) {
+                        nb.extras.putString(EXTRA_TEMPLATE, Notification.CallStyle::class.java.name)
+                    }
+
+                    if (category != null) {
+                        nb.setCategory(category)
+                    }
                     groupAlertBehavior?.let { nb.setGroupAlertBehavior(it) }
 
                     if (hasBubbleMetadata) {
@@ -1185,6 +1203,10 @@
                     if (isStickyAndNotDemoted) {
                         n.flags = n.flags or FLAG_FSI_REQUESTED_BUT_DENIED
                     }
+                    if (isColorized) {
+                        n.extras.putBoolean(EXTRA_COLORIZED, true)
+                        n.flags = n.flags or FLAG_CAN_COLORIZE
+                    }
                 }
                 .let { NotificationEntryBuilder().setNotification(it) }
                 .also { neb ->
@@ -1193,9 +1215,10 @@
                     neb.setTag(TEST_TAG)
 
                     neb.setImportance(importance)
-                    neb.setChannel(
-                        NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance)
-                    )
+                    val channel =
+                            NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance)
+                    channel.isImportantConversation = isImportantConversation
+                    neb.setChannel(channel)
 
                     canBubble?.let { neb.setCanBubble(it) }
                 }
@@ -1216,6 +1239,7 @@
                             }
                             visibilityOverride?.let { mrb.setVisibilityOverride(it) }
                             suppressedVisualEffects?.let { mrb.setSuppressedVisualEffects(it) }
+                            mrb.setIsConversation(isConversation)
                         }
                         .build()
                 }
@@ -1287,7 +1311,7 @@
         }
     }
 
-    private fun whenAgo(whenAgeMs: Long) = systemClock.currentTimeMillis() - whenAgeMs
+    protected fun whenAgo(whenAgeMs: Long) = systemClock.currentTimeMillis() - whenAgeMs
 }
 
 private const val TEST_CONTENT_TITLE = "Test Content Title"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
index 0356c2c..620ad9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
@@ -19,6 +19,7 @@
 import android.os.Handler
 import android.os.PowerManager
 import com.android.internal.logging.UiEventLogger
+import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.settings.UserTracker
@@ -49,7 +50,8 @@
         statusBarStateController: StatusBarStateController,
         systemClock: SystemClock,
         uiEventLogger: UiEventLogger,
-        userTracker: UserTracker
+        userTracker: UserTracker,
+        avalancheProvider: AvalancheProvider
     ): VisualInterruptionDecisionProvider {
         return if (VisualInterruptionRefactor.isEnabled) {
             VisualInterruptionDecisionProviderImpl(
@@ -67,7 +69,8 @@
                 statusBarStateController,
                 systemClock,
                 uiEventLogger,
-                userTracker
+                userTracker,
+                avalancheProvider
             )
         } else {
             NotificationInterruptStateProviderWrapper(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 849a13b..b048949 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -153,6 +153,7 @@
 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.AvalancheProvider;
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
@@ -313,6 +314,7 @@
     @Mock private CameraLauncher mCameraLauncher;
     @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
     @Mock private UserTracker mUserTracker;
+    @Mock private AvalancheProvider mAvalancheProvider;
     @Mock private FingerprintManager mFingerprintManager;
     @Mock IPowerManager mPowerManagerService;
     @Mock ActivityStarter mActivityStarter;
@@ -367,7 +369,8 @@
                         mStatusBarStateController,
                         mFakeSystemClock,
                         mock(UiEventLogger.class),
-                        mUserTracker);
+                        mUserTracker,
+                        mAvalancheProvider);
         mVisualInterruptionDecisionProvider.start();
 
         mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 3bde6e3..99c2dc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -315,6 +315,8 @@
 
     @After
     public void tearDown() {
+        // Detaching view stops flow collection and prevents memory leak.
+        ViewUtils.detachView(mScrimBehind);
         finishAnimationsImmediately();
         Arrays.stream(ScrimState.values()).forEach((scrim) -> {
             scrim.setAodFrontScrimAlpha(0f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
index ba34ce6..bda339f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.app.tracing
+package com.android.systemui.tracing
 
 import android.os.Handler
 import android.os.Looper
@@ -20,11 +20,13 @@
 import android.testing.AndroidTestingRunner
 import android.util.Log
 import androidx.test.filters.SmallTest
+import com.android.app.tracing.TraceUtils.traceRunnable
+import com.android.app.tracing.namedRunnable
+import com.android.app.tracing.traceSection
 import com.android.systemui.SysuiTestCase
 import org.junit.After
 import org.junit.Assert.assertThrows
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -68,7 +70,6 @@
     }
 
     @Test
-    @Ignore("b/267482189 - Enable once androidx.tracing >= 1.2.0-beta04")
     fun testLongTraceSection_doesNotThrow_whenUsingAndroidX() {
         androidx.tracing.Trace.beginSection(SECTION_NAME_THATS_TOO_LONG)
     }
@@ -84,17 +85,13 @@
     fun testLongTraceSection_doesNotThrow_whenUsedAsTraceNameSupplier() {
         Handler(Looper.getMainLooper())
             .runWithScissors(
-                TraceUtils.namedRunnable(SECTION_NAME_THATS_TOO_LONG) {
-                    Log.v(TAG, "TraceUtils.namedRunnable() block.")
-                },
+                namedRunnable(SECTION_NAME_THATS_TOO_LONG) { Log.v(TAG, "namedRunnable() block.") },
                 TEST_FAIL_TIMEOUT
             )
     }
 
     @Test
     fun testLongTraceSection_doesNotThrow_whenUsingTraceRunnable() {
-        TraceUtils.traceRunnable(SECTION_NAME_THATS_TOO_LONG) {
-            Log.v(TAG, "TraceUtils.traceRunnable() block.")
-        }
+        traceRunnable(SECTION_NAME_THATS_TOO_LONG) { Log.v(TAG, "traceRunnable() block.") }.run()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 9ea4142..f4b36659 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -96,6 +96,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
@@ -151,6 +152,7 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.statusbar.notification.interruption.AvalancheProvider;
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionLogger;
@@ -587,7 +589,9 @@
                         mock(StatusBarStateController.class),
                         mock(SystemClock.class),
                         mock(UiEventLogger.class),
-                        mock(UserTracker.class));
+                        mock(UserTracker.class),
+                        mock(AvalancheProvider.class)
+                        );
         interruptionDecisionProvider.start();
 
         mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class),
@@ -616,7 +620,7 @@
                 mPositioner,
                 mock(DisplayController.class),
                 mOneHandedOptional,
-                Optional.of(mock(DragAndDropController.class)),
+                mock(DragAndDropController.class),
                 syncExecutor,
                 mock(Handler.class),
                 mTaskViewTransitions,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index 9ad234e1..4a5ebd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -71,7 +71,7 @@
             BubblePositioner positioner,
             DisplayController displayController,
             Optional<OneHandedController> oneHandedOptional,
-            Optional<DragAndDropController> dragAndDropController,
+            DragAndDropController dragAndDropController,
             ShellExecutor shellMainExecutor,
             Handler shellMainHandler,
             TaskViewTransitions taskViewTransitions,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
index d7e948e..29faa58 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
 import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.scene.shared.flag.sceneContainerFlags
@@ -29,5 +30,6 @@
         shadeInteractor = shadeInteractor,
         flags = sceneContainerFlags,
         featureFlags = featureFlagsClassic,
+        keyguardInteractor = keyguardInteractor,
     )
 }
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 155c523..c134a4c 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -120,6 +120,7 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -142,11 +143,13 @@
     // Support for various latency improvements
     private static final String LATENCY_VERSION_PREFIX = "1.4";
     private static final String EFV_VERSION_PREFIX = "1.5";
+    private static final String GET_VERSION_PREFIX = "1.5";
     private static final String[] ADVANCED_VERSION_PREFIXES = {EFV_VERSION_PREFIX,
-            LATENCY_VERSION_PREFIX, ADVANCED_VERSION_PREFIX, RESULTS_VERSION_PREFIX };
+            LATENCY_VERSION_PREFIX, ADVANCED_VERSION_PREFIX, RESULTS_VERSION_PREFIX,
+            GET_VERSION_PREFIX};
     private static final String[] SUPPORTED_VERSION_PREFIXES = {EFV_VERSION_PREFIX,
             LATENCY_VERSION_PREFIX, RESULTS_VERSION_PREFIX, ADVANCED_VERSION_PREFIX, "1.1",
-            NON_INIT_VERSION_PREFIX};
+            NON_INIT_VERSION_PREFIX, GET_VERSION_PREFIX};
     private static final boolean EXTENSIONS_PRESENT = checkForExtensions();
     private static final String EXTENSIONS_VERSION = EXTENSIONS_PRESENT ?
             (new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION) : null;
@@ -156,6 +159,8 @@
                     (EXTENSIONS_VERSION.startsWith(EFV_VERSION_PREFIX)));
     private static final boolean EFV_SUPPORTED = EXTENSIONS_PRESENT &&
             (EXTENSIONS_VERSION.startsWith(EFV_VERSION_PREFIX));
+    private static final boolean GET_API_SUPPORTED = EXTENSIONS_PRESENT
+            && (EXTENSIONS_VERSION.startsWith(GET_VERSION_PREFIX));
     private static final boolean ADVANCED_API_SUPPORTED = checkForAdvancedAPI();
     private static final boolean INIT_API_SUPPORTED = EXTENSIONS_PRESENT &&
             (!EXTENSIONS_VERSION.startsWith(NON_INIT_VERSION_PREFIX));
@@ -777,6 +782,12 @@
                         public boolean isPostviewAvailable() {
                             return false;
                         }
+
+                        @Override
+                        public List<Pair<CameraCharacteristics.Key, Object>>
+                                getAvailableCharacteristicsKeyValues() {
+                            return Collections.emptyList();
+                        }
                     };
                 }
             }
@@ -1186,6 +1197,35 @@
 
             return false;
         }
+
+        @Override
+        public CameraMetadataNative getAvailableCharacteristicsKeyValues(String cameraId) {
+            if (GET_API_SUPPORTED) {
+                List<Pair<CameraCharacteristics.Key, Object>> entries =
+                        mAdvancedExtender.getAvailableCharacteristicsKeyValues();
+
+                if ((entries != null) && !entries.isEmpty()) {
+                    CameraMetadataNative ret = new CameraMetadataNative();
+                    long vendorId = mMetadataVendorIdMap.containsKey(cameraId)
+                            ? mMetadataVendorIdMap.get(cameraId) : Long.MAX_VALUE;
+                    ret.setVendorId(vendorId);
+                    int[] characteristicsKeyTags = new int[entries.size()];
+                    int i = 0;
+                    for (Pair<CameraCharacteristics.Key, Object> entry : entries) {
+                        int tag = CameraMetadataNative.getTag(entry.first.getName(), vendorId);
+                        characteristicsKeyTags[i++] = tag;
+                        ret.set(entry.first, entry.second);
+                    }
+                    ret.set(CameraCharacteristics.REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
+                            characteristicsKeyTags);
+
+                    return ret;
+                }
+            }
+
+            return null;
+        }
+
     }
 
     private class CaptureCallbackStub implements SessionProcessorImpl.CaptureCallback {
diff --git a/ravenwood/bulk_enable.py b/ravenwood/bulk_enable.py
index 83fda9e..aafaaff 100644
--- a/ravenwood/bulk_enable.py
+++ b/ravenwood/bulk_enable.py
@@ -21,7 +21,7 @@
 classes that have partial success.
 
 Typical usage:
-$ ENABLE_PROBE_IGNORED=1 atest MyTestsRavenwood
+$ RAVENWOOD_RUN_DISABLED_TESTS=1 atest MyTestsRavenwood
 $ cd /path/to/tests/root
 $ python bulk_enable.py /path/to/atest/output/host_log.txt
 """
diff --git a/ravenwood/coretest/Android.bp b/ravenwood/coretest/Android.bp
new file mode 100644
index 0000000..9b7f8f7
--- /dev/null
+++ b/ravenwood/coretest/Android.bp
@@ -0,0 +1,23 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_ravenwood_test {
+    name: "RavenwoodCoreTest",
+
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.test.rules",
+    ],
+
+    srcs: [
+        "test/**/*.java",
+    ],
+    sdk_version: "test_current",
+    auto_gen_config: true,
+}
diff --git a/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java b/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java
new file mode 100644
index 0000000..e58c282
--- /dev/null
+++ b/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 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.platform.test.ravenwood.coretest;
+
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assume;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for the test runner validator in RavenwoodRule.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodTestRunnerValidationTest {
+    // Note the following rules don't have a @Rule, because they need to be applied in a specific
+    // order. So we use a RuleChain instead.
+    private ExpectedException mThrown = ExpectedException.none();
+    private final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+    @Rule
+    public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood);
+
+    public RavenwoodTestRunnerValidationTest() {
+        Assume.assumeTrue(mRavenwood._ravenwood_private$isOptionalValidationEnabled());
+        // Because RavenwoodRule will throw this error before executing the test method,
+        // we can't do it in the test method itself.
+        // So instead, we initialize it here.
+        mThrown.expectMessage("Switch to androidx.test.ext.junit.runners.AndroidJUnit4");
+    }
+
+    @Test
+    public void testValidateTestRunner() {
+    }
+}
diff --git a/ravenwood/fix_test_runner.py b/ravenwood/fix_test_runner.py
new file mode 100755
index 0000000..99b7a1f
--- /dev/null
+++ b/ravenwood/fix_test_runner.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 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.
+
+"""
+Tool switch the deprecated jetpack test runner to the correct one.
+
+Typical usage:
+$ RAVENWOOD_OPTIONAL_VALIDATION=1 atest MyTestsRavenwood # Prepend RAVENWOOD_RUN_DISABLED_TESTS=1 as needed
+$ cd /path/to/tests/root
+$ python bulk_enable.py /path/to/atest/output/host_log.txt
+"""
+
+import collections
+import os
+import re
+import subprocess
+import sys
+
+re_result = re.compile("I/ModuleListener.+?null-device-0 (.+?)#(.+?) ([A-Z_]+)(.*)$")
+
+OLD_RUNNER = "androidx.test.runner.AndroidJUnit4"
+NEW_RUNNER = "androidx.test.ext.junit.runners.AndroidJUnit4"
+SED_ARG = r"s/%s/%s/g" % (OLD_RUNNER, NEW_RUNNER)
+
+target = collections.defaultdict()
+
+with open(sys.argv[1]) as f:
+    for line in f.readlines():
+        result = re_result.search(line)
+        if result:
+            clazz, method, state, msg = result.groups()
+            if NEW_RUNNER in msg:
+                target[clazz] = 1
+
+if len(target) == 0:
+    print("No tests need updating.")
+    sys.exit(0)
+
+num_fixed = 0
+for clazz in target.keys():
+    print("Fixing test runner", clazz)
+    clazz_match = re.compile("%s\.(kt|java)" % (clazz.split(".")[-1]))
+    found = False
+    for root, dirs, files in os.walk("."):
+        for f in files:
+            if clazz_match.match(f):
+                found = True
+                num_fixed += 1
+                path = os.path.join(root, f)
+                subprocess.run(["sed", "-i", "-E", SED_ARG, path])
+    if not found:
+        print(f"  Warining: tests {clazz} not found")
+
+
+print("Tests fixed", num_fixed)
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 8af561f..7b5932b 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -28,7 +28,10 @@
 
 import com.android.internal.os.RuntimeInit;
 
+import org.junit.Assert;
 import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
 
 import java.io.PrintStream;
 import java.util.Map;
@@ -55,6 +58,11 @@
     private static ScheduledFuture<?> sPendingTimeout;
 
     /**
+     * When enabled, attempt to detect uncaught exceptions from background threads.
+     */
+    private static final boolean ENABLE_UNCAUGHT_EXCEPTION_DETECTION = false;
+
+    /**
      * When set, an unhandled exception was discovered (typically on a background thread), and we
      * capture it here to ensure it's reported as a test failure.
      */
@@ -72,8 +80,10 @@
     }
 
     public static void init(RavenwoodRule rule) {
-        maybeThrowPendingUncaughtException(false);
-        Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
+        if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
+            maybeThrowPendingUncaughtException(false);
+            Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
+        }
 
         RuntimeInit.redirectLogStreams();
 
@@ -126,7 +136,9 @@
         android.os.Binder.reset$ravenwood();
         android.os.Process.reset$ravenwood();
 
-        maybeThrowPendingUncaughtException(true);
+        if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
+            maybeThrowPendingUncaughtException(true);
+        }
     }
 
     public static void logTestRunner(String label, Description description) {
@@ -167,4 +179,31 @@
             }
         }
     }
+
+    public static void validate(Statement base, Description description,
+            boolean enableOptionalValidation) {
+        validateTestRunner(base, description, enableOptionalValidation);
+    }
+
+    private static void validateTestRunner(Statement base, Description description,
+            boolean shouldFail) {
+        final var testClass = description.getTestClass();
+        final var runWith = testClass.getAnnotation(RunWith.class);
+        if (runWith == null) {
+            return;
+        }
+
+        // Due to build dependencies, we can't directly refer to androidx classes here,
+        // so just check the class name instead.
+        if (runWith.value().getCanonicalName().equals("androidx.test.runner.AndroidJUnit4")) {
+            var message = "Test " + testClass.getCanonicalName() + " uses deprecated"
+                    + " test runner androidx.test.runner.AndroidJUnit4."
+                    + " Switch to androidx.test.ext.junit.runners.AndroidJUnit4.";
+            if (shouldFail) {
+                Assert.fail(message);
+            } else {
+                System.err.println("Warning: " + message);
+            }
+        }
+    }
 }
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index daed457..764573d 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -89,6 +89,12 @@
     private static final boolean ENABLE_REALLY_DISABLE_PATTERN =
             !REALLY_DISABLE_PATTERN.pattern().isEmpty();
 
+    /**
+     * If true, enable optional validation on running tests.
+     */
+    private static final boolean ENABLE_OPTIONAL_VALIDATION = "1".equals(
+            System.getenv("RAVENWOOD_OPTIONAL_VALIDATION"));
+
     static {
         if (ENABLE_PROBE_IGNORED) {
             System.out.println("$RAVENWOOD_RUN_DISABLED_TESTS enabled: force running all tests");
@@ -275,6 +281,12 @@
         }
     }
 
+    private void commonPrologue(Statement base, Description description) {
+        RavenwoodRuleImpl.logTestRunner("started", description);
+        RavenwoodRuleImpl.validate(base, description, ENABLE_OPTIONAL_VALIDATION);
+        RavenwoodRuleImpl.init(RavenwoodRule.this);
+    }
+
     /**
      * Run the given {@link Statement} with no special treatment.
      */
@@ -284,8 +296,7 @@
             public void evaluate() throws Throwable {
                 Assume.assumeTrue(shouldEnableOnRavenwood(description));
 
-                RavenwoodRuleImpl.logTestRunner("started", description);
-                RavenwoodRuleImpl.init(RavenwoodRule.this);
+                commonPrologue(base, description);
                 try {
                     base.evaluate();
                     RavenwoodRuleImpl.logTestRunner("finished", description);
@@ -310,8 +321,7 @@
             public void evaluate() throws Throwable {
                 Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description));
 
-                RavenwoodRuleImpl.logTestRunner("started", description);
-                RavenwoodRuleImpl.init(RavenwoodRule.this);
+                commonPrologue(base, description);
                 try {
                     base.evaluate();
                 } catch (Throwable t) {
@@ -331,4 +341,11 @@
             }
         };
     }
+
+    /**
+     * Do not use it outside ravenwood core classes.
+     */
+    public boolean _ravenwood_private$isOptionalValidationEnabled() {
+        return ENABLE_OPTIONAL_VALIDATION;
+    }
 }
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 7d172f2..e951351b 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -17,6 +17,7 @@
 package android.platform.test.ravenwood;
 
 import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
 
 public class RavenwoodRuleImpl {
     public static boolean isOnRavenwood() {
@@ -34,4 +35,8 @@
     public static void logTestRunner(String label, Description description) {
         // No-op when running on a real device
     }
+
+    public static void validate(Statement base, Description description,
+            boolean enableOptionalValidation) {
+    }
 }
diff --git a/ravenwood/minimum-test/Android.bp b/ravenwood/minimum-test/Android.bp
index bf3583c..e4ed3d5 100644
--- a/ravenwood/minimum-test/Android.bp
+++ b/ravenwood/minimum-test/Android.bp
@@ -13,6 +13,7 @@
 
     static_libs: [
         "androidx.annotation_annotation",
+        "androidx.test.ext.junit",
         "androidx.test.rules",
     ],
 
diff --git a/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java b/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
index 7abfecf..03cfad5 100644
--- a/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
+++ b/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
@@ -18,7 +18,7 @@
 import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Assert;
 import org.junit.Rule;
diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md
index 5df827f..9179a62 100644
--- a/ravenwood/test-authors.md
+++ b/ravenwood/test-authors.md
@@ -101,7 +101,7 @@
 @RunWith(AndroidJUnit4.class)
 public class MyCodeTest {
     @Rule
-    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(SetFlagsRule.DefaultInitValueType.NULL_DEFAULT);
 
     @Test
     public void testEnabled() {
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index a493d7a..797a2e6 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -24,6 +24,8 @@
 import android.content.ComponentName;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.pm.SignedPackage;
 import android.os.Build;
 import android.os.CarrierAssociatedAppEntry;
 import android.os.Environment;
@@ -350,6 +352,16 @@
     // updated to avoid cached/potentially tampered results.
     private final Set<String> mPreinstallPackagesWithStrictSignatureCheck = new ArraySet<>();
 
+    // A set of packages that should be considered "trusted packages" by ECM (Enhanced
+    // Confirmation Mode). "Trusted packages" are exempt from ECM (i.e., they will never be
+    // considered "restricted").
+    private final ArraySet<SignedPackage> mEnhancedConfirmationTrustedPackages = new ArraySet<>();
+
+    // A set of packages that should be considered "trusted installers" by ECM (Enhanced
+    // Confirmation Mode). "Trusted installers", and all apps installed by a trusted installer, are
+    // exempt from ECM (i.e., they will never be considered "restricted").
+    private final ArraySet<SignedPackage> mEnhancedConfirmationTrustedInstallers = new ArraySet<>();
+
     /**
      * Map of system pre-defined, uniquely named actors; keys are namespace,
      * value maps actor name to package name.
@@ -560,6 +572,14 @@
         return mPreinstallPackagesWithStrictSignatureCheck;
     }
 
+    public ArraySet<SignedPackage> getEnhancedConfirmationTrustedPackages() {
+        return mEnhancedConfirmationTrustedPackages;
+    }
+
+    public ArraySet<SignedPackage> getEnhancedConfirmationTrustedInstallers() {
+        return mEnhancedConfirmationTrustedInstallers;
+    }
+
     /**
      * Only use for testing. Do NOT use in production code.
      * @param readPermissions false to create an empty SystemConfig; true to read the permissions.
@@ -1558,6 +1578,26 @@
                             }
                         }
                     } break;
+                    case "enhanced-confirmation-trusted-package": {
+                        if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()) {
+                            SignedPackage signedPackage = parseEnhancedConfirmationTrustedPackage(
+                                    parser, permFile, name);
+                            if (signedPackage != null) {
+                                mEnhancedConfirmationTrustedPackages.add(signedPackage);
+                            }
+                            break;
+                        }
+                    } // fall through if flag is not enabled
+                    case "enhanced-confirmation-trusted-installer": {
+                        if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()) {
+                            SignedPackage signedPackage = parseEnhancedConfirmationTrustedPackage(
+                                    parser, permFile, name);
+                            if (signedPackage != null) {
+                                mEnhancedConfirmationTrustedInstallers.add(signedPackage);
+                            }
+                            break;
+                        }
+                    } // fall through if flag is not enabled
                     default: {
                         Slog.w(TAG, "Tag " + name + " is unknown in "
                                 + permFile + " at " + parser.getPositionDescription());
@@ -1619,6 +1659,33 @@
         }
     }
 
+    private @Nullable SignedPackage parseEnhancedConfirmationTrustedPackage(XmlPullParser parser,
+            File permFile, String elementName) {
+        String pkgName = parser.getAttributeValue(null, "package");
+        if (TextUtils.isEmpty(pkgName)) {
+            Slog.w(TAG, "<" + elementName + "> without package " + permFile + " at "
+                    + parser.getPositionDescription());
+            return null;
+        }
+
+        String certificateDigestStr = parser.getAttributeValue(null, "sha256-cert-digest");
+        if (TextUtils.isEmpty(certificateDigestStr)) {
+            Slog.w(TAG, "<" + elementName + "> without sha256-cert-digest in " + permFile
+                    + " at " + parser.getPositionDescription());
+            return null;
+        }
+        byte[] certificateDigest = null;
+        try {
+            certificateDigest = new Signature(certificateDigestStr).toByteArray();
+        } catch (IllegalArgumentException e) {
+            Slog.w(TAG, "<" + elementName + "> with invalid sha256-cert-digest in "
+                    + permFile + " at " + parser.getPositionDescription());
+            return null;
+        }
+
+        return new SignedPackage(pkgName, certificateDigest);
+    }
+
     // This method only enables a new Android feature added in U and will not have impact on app
     // compatibility
     @SuppressWarnings("AndroidFrameworkCompatChange")
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 77cb08b..cb15abc 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -914,6 +914,9 @@
             }
 
             mOverrideRequestController.dumpInternal(pw);
+            pw.println();
+
+            mDeviceStatePolicy.dump(pw, /* args= */ null);
         }
     }
 
diff --git a/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java b/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java
index 5c4e2f3..65e5085 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStatePolicy.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.text.TextUtils;
+import android.util.Dumpable;
 
 import com.android.server.policy.DeviceStatePolicyImpl;
 
@@ -29,7 +30,7 @@
  *
  * @see DeviceStateManagerService
  */
-public abstract class DeviceStatePolicy {
+public abstract class DeviceStatePolicy implements Dumpable {
     protected final Context mContext;
 
     protected DeviceStatePolicy(@NonNull Context context) {
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index 50ab3f8..d5945f4 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -21,6 +21,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
+import android.util.Dumpable;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -31,7 +32,7 @@
  *
  * @see DeviceStatePolicy
  */
-public interface DeviceStateProvider {
+public interface DeviceStateProvider extends Dumpable {
     int SUPPORTED_DEVICE_STATES_CHANGED_DEFAULT = 0;
 
     /**
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 687def0..a61199a 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1392,6 +1392,11 @@
     }
 
     @Override // Binder call
+    public int getMousePointerSpeed() {
+        return mNative.getMousePointerSpeed();
+    }
+
+    @Override // Binder call
     public void tryPointerSpeed(int speed) {
         if (!checkCallingPermission(android.Manifest.permission.SET_POINTER_SPEED,
                 "tryPointerSpeed()")) {
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index e5f3484..b16df0f 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -119,6 +119,8 @@
      */
     boolean transferTouch(IBinder destChannelToken, int displayId);
 
+    int getMousePointerSpeed();
+
     void setPointerSpeed(int speed);
 
     void setMousePointerAccelerationEnabled(int displayId, boolean enabled);
@@ -364,6 +366,9 @@
         public native boolean transferTouch(IBinder destChannelToken, int displayId);
 
         @Override
+        public native int getMousePointerSpeed();
+
+        @Override
         public native void setPointerSpeed(int speed);
 
         @Override
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a06607b..7fb3e00 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -93,7 +93,6 @@
 import android.os.IBinder;
 import android.os.IProgressListener;
 import android.os.Process;
-import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -139,11 +138,11 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
 import com.android.internal.widget.ILockSettings;
-import com.android.internal.widget.ILockSettingsStateListener;
 import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
 import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.LockSettingsStateListener;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.RebootEscrowListener;
 import com.android.internal.widget.VerifyCredentialResponse;
@@ -185,6 +184,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.StringJoiner;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -332,8 +332,8 @@
 
     private HashMap<UserHandle, UserManager> mUserManagerCache = new HashMap<>();
 
-    private final RemoteCallbackList<ILockSettingsStateListener> mLockSettingsStateListeners =
-            new RemoteCallbackList<>();
+    private final CopyOnWriteArrayList<LockSettingsStateListener> mLockSettingsStateListeners =
+            new CopyOnWriteArrayList<>();
 
     // This class manages life cycle events for encrypted users on File Based Encryption (FBE)
     // devices. The most basic of these is to show/hide notifications about missing features until
@@ -2379,25 +2379,12 @@
     }
 
     private void notifyLockSettingsStateListeners(boolean success, int userId) {
-        int i = mLockSettingsStateListeners.beginBroadcast();
-        try {
-            while (i > 0) {
-                i--;
-                try {
-                    if (success) {
-                        mLockSettingsStateListeners.getBroadcastItem(i)
-                                .onAuthenticationSucceeded(userId);
-                    } else {
-                        mLockSettingsStateListeners.getBroadcastItem(i)
-                                .onAuthenticationFailed(userId);
-                    }
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Exception while notifying LockSettingsStateListener:"
-                            + " success = " + success + ", userId = " + userId, e);
-                }
+        for (LockSettingsStateListener listener : mLockSettingsStateListeners) {
+            if (success) {
+                listener.onAuthenticationSucceeded(userId);
+            } else {
+                listener.onAuthenticationFailed(userId);
             }
-        } finally {
-            mLockSettingsStateListeners.finishBroadcast();
         }
     }
 
@@ -3720,15 +3707,15 @@
         }
 
         @Override
-        public void registerLockSettingsStateListener(
-                @NonNull ILockSettingsStateListener listener) {
-            mLockSettingsStateListeners.register(listener);
+        public void registerLockSettingsStateListener(@NonNull LockSettingsStateListener listener) {
+            Objects.requireNonNull(listener, "listener cannot be null");
+            mLockSettingsStateListeners.add(listener);
         }
 
         @Override
         public void unregisterLockSettingsStateListener(
-                @NonNull ILockSettingsStateListener listener) {
-            mLockSettingsStateListeners.unregister(listener);
+                @NonNull LockSettingsStateListener listener) {
+            mLockSettingsStateListeners.remove(listener);
         }
     }
 
diff --git a/services/core/java/com/android/server/net/watchlist/FileHashCache.java b/services/core/java/com/android/server/net/watchlist/FileHashCache.java
new file mode 100644
index 0000000..f829bc6
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/FileHashCache.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2020 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.net.watchlist;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.HexDump;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @hide
+ * Utility class that keeps file hashes in cache. This cache is persistent across reboots.
+ * If requested hash does not exist in cache, it is calculated from the target file. Cache gets
+ * persisted once it is changed in deferred mode to prevent multiple savings per many small updates.
+ * Deleted files are detected and removed from the cache during the initial load. If file change is
+ * detected, it is hash is calculated during the next request.
+ * The synchronization is done using Handler. All requests for hashes must be done in context of
+ * handler thread.
+ */
+public class FileHashCache {
+    private static final String TAG = FileHashCache.class.getSimpleName();
+    private static final boolean DEBUG = false;
+    // Turns on the check that validates hash in cache matches one, calculated directly on the
+    // target file. Not to be used in production.
+    private static final boolean VERIFY = false;
+
+    // Used for logging wtf only once during load, see logWtfOnce()
+    private static boolean sLoggedWtf = false;
+
+    @VisibleForTesting
+    static String sPersistFileName = "/data/system/file_hash_cache";
+
+    static long sSaveDeferredDelayMillis = TimeUnit.SECONDS.toMillis(5);
+
+    private static class Entry {
+        public final long mLastModified;
+        public final byte[] mSha256Hash;
+
+        Entry(long lastModified, @NonNull byte[] sha256Hash) {
+            mLastModified = lastModified;
+            mSha256Hash = sha256Hash;
+        }
+    }
+
+    private Handler mHandler;
+    private final Map<File, Entry> mEntries = new HashMap<>();
+
+    private final Runnable mLoadTask = () -> {
+        load();
+    };
+    private final Runnable mSaveTask = () -> {
+        save();
+    };
+
+    /**
+     * @hide
+     */
+    public FileHashCache(@NonNull Handler handler) {
+        mHandler = handler;
+        mHandler.post(mLoadTask);
+    }
+
+    /**
+     * Requests sha256 for the provided file from the cache. If cache entry does not exist or
+     * file was modified, then null is returned.
+     * @hide
+    **/
+    @VisibleForTesting
+    @Nullable
+    byte[] getSha256HashFromCache(@NonNull File file) {
+        if (!mHandler.getLooper().isCurrentThread()) {
+            Slog.wtf(TAG, "Request from invalid thread", new Exception());
+            return null;
+        }
+
+        final Entry entry = mEntries.get(file);
+        if (entry == null) {
+            return null;
+        }
+
+        try {
+            if (entry.mLastModified == Os.stat(file.getAbsolutePath()).st_ctime) {
+                if (VERIFY) {
+                    try {
+                        if (!Arrays.equals(entry.mSha256Hash, DigestUtils.getSha256Hash(file))) {
+                            Slog.wtf(TAG, "Failed to verify entry for " + file);
+                        }
+                    } catch (NoSuchAlgorithmException | IOException e) { }
+                }
+
+                return entry.mSha256Hash;
+            }
+        } catch (ErrnoException e) { }
+
+        if (DEBUG) Slog.v(TAG, "Found stale cached entry for " + file);
+        mEntries.remove(file);
+        return null;
+    }
+
+    /**
+     * Requests sha256 for the provided file. If cache entry does not exist or file was modified,
+     * hash is calculated from the requested file. Otherwise hash from cache is returned.
+     * @hide
+    **/
+    @NonNull
+    public byte[] getSha256Hash(@NonNull File file) throws NoSuchAlgorithmException, IOException {
+        byte[] sha256Hash = getSha256HashFromCache(file);
+        if (sha256Hash != null) {
+            return sha256Hash;
+        }
+
+        try {
+            sha256Hash = DigestUtils.getSha256Hash(file);
+            mEntries.put(file, new Entry(Os.stat(file.getAbsolutePath()).st_ctime, sha256Hash));
+            if (DEBUG) Slog.v(TAG, "New cache entry is created for " + file);
+            scheduleSave();
+            return sha256Hash;
+        } catch (ErrnoException e) {
+            throw new IOException(e);
+        }
+    }
+
+    private static void closeQuietly(@Nullable Closeable closeable) {
+        try {
+            if (closeable != null) {
+                closeable.close();
+            }
+        } catch (IOException e) { }
+    }
+
+    /**
+     * Log an error as wtf only the first instance, then log as warning.
+     */
+    private static void logWtfOnce(@NonNull final String s, final Exception e) {
+        if (!sLoggedWtf) {
+            Slog.wtf(TAG, s, e);
+            sLoggedWtf = true;
+        } else {
+            Slog.w(TAG, s, e);
+        }
+    }
+
+    private void load() {
+        mEntries.clear();
+
+        final long startTime = SystemClock.currentTimeMicro();
+        final File file = new File(sPersistFileName);
+        if (!file.exists()) {
+            if (DEBUG) Slog.v(TAG, "Storage file does not exist. Starting from scratch.");
+            return;
+        }
+
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new FileReader(file));
+            // forEach rethrows IOException as UncheckedIOException
+            reader.lines().forEach((fileEntry)-> {
+                try {
+                    final StringTokenizer tokenizer = new StringTokenizer(fileEntry, ",");
+                    final File testFile = new File(tokenizer.nextToken());
+                    final long lastModified = Long.parseLong(tokenizer.nextToken());
+                    final byte[] sha256 = HexDump.hexStringToByteArray(tokenizer.nextToken());
+                    mEntries.put(testFile, new Entry(lastModified, sha256));
+                    if (DEBUG) Slog.v(TAG, "Loaded entry for " + testFile);
+                } catch (RuntimeException e) {
+                    // hexStringToByteArray can throw raw RuntimeException on invalid input.  Avoid
+                    // potentially reporting one error per line if the data is corrupt.
+                    logWtfOnce("Invalid entry for " + fileEntry, e);
+                    return;
+                }
+            });
+            if (DEBUG) {
+                Slog.i(TAG, "Loaded " + mEntries.size() + " entries in "
+                        + (SystemClock.currentTimeMicro() - startTime) + " mcs.");
+            }
+        } catch (IOException | UncheckedIOException e) {
+            Slog.e(TAG, "Failed to read storage file", e);
+        } finally {
+            closeQuietly(reader);
+        }
+    }
+
+    private void scheduleSave() {
+        mHandler.removeCallbacks(mSaveTask);
+        mHandler.postDelayed(mSaveTask, sSaveDeferredDelayMillis);
+    }
+
+    private void save() {
+        BufferedWriter writer = null;
+        final long startTime = SystemClock.currentTimeMicro();
+        try {
+            writer = new BufferedWriter(new FileWriter(sPersistFileName));
+            for (Map.Entry<File, Entry> entry : mEntries.entrySet()) {
+                writer.write(entry.getKey() + ","
+                             + entry.getValue().mLastModified + ","
+                             + HexDump.toHexString(entry.getValue().mSha256Hash) + "\n");
+            }
+            if (DEBUG) {
+                Slog.i(TAG, "Saved " + mEntries.size() + " entries in "
+                        + (SystemClock.currentTimeMicro() - startTime) + " mcs.");
+            }
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to save.", e);
+        } finally {
+            closeQuietly(writer);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
index 8ce7b57..c863cbf 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
@@ -83,6 +83,8 @@
     private final ConcurrentHashMap<Integer, byte[]> mCachedUidDigestMap =
             new ConcurrentHashMap<>();
 
+    private final FileHashCache mApkHashCache;
+
     private interface WatchlistEventKeys {
         String HOST = "host";
         String IP_ADDRESSES = "ipAddresses";
@@ -100,6 +102,13 @@
         mSettings = WatchlistSettings.getInstance();
         mDropBoxManager = mContext.getSystemService(DropBoxManager.class);
         mPrimaryUserId = getPrimaryUserId();
+        if (context.getResources().getBoolean(
+                com.android.internal.R.bool.config_watchlistUseFileHashesCache)) {
+            mApkHashCache = new FileHashCache(this);
+            Slog.i(TAG, "Using file hashes cache.");
+        } else {
+            mApkHashCache = null;
+        }
     }
 
     @Override
@@ -345,7 +354,9 @@
                             Slog.i(TAG, "Skipping incremental path: " + packageName);
                             continue;
                         }
-                        return DigestUtils.getSha256Hash(new File(apkPath));
+                        return mApkHashCache != null
+                                ? mApkHashCache.getSha256Hash(new File(apkPath))
+                                : DigestUtils.getSha256Hash(new File(apkPath));
                     } catch (NameNotFoundException | NoSuchAlgorithmException | IOException e) {
                         Slog.e(TAG, "Cannot get digest from uid: " + key
                                 + ",pkg: " + packageName, e);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1c9bbab..638382e 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7955,8 +7955,8 @@
                     && mTelecomManager != null) {
                 try {
                     return mTelecomManager.isInManagedCall()
-                            || mTelecomManager.isInSelfManagedCall(
-                            pkg, UserHandle.getUserHandleForUid(uid));
+                            || mTelecomManager.isInSelfManagedCall(pkg,
+                            UserHandle.getUserHandleForUid(uid), /* hasCrossUserAccess */ true);
                 } catch (IllegalStateException ise) {
                     // Telecom is not ready (this is likely early boot), so there are no calls.
                     return false;
@@ -8844,19 +8844,26 @@
         }
     }
 
+    private PendingIntent getNotificationTimeoutPendingIntent(NotificationRecord record,
+            int flags) {
+        flags |= PendingIntent.FLAG_IMMUTABLE;
+        return PendingIntent.getBroadcast(getContext(),
+                REQUEST_CODE_TIMEOUT,
+                new Intent(ACTION_NOTIFICATION_TIMEOUT)
+                        .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
+                        .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
+                                .appendPath(record.getKey()).build())
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                        .putExtra(EXTRA_KEY, record.getKey()),
+                flags);
+    }
+
     @VisibleForTesting
     @GuardedBy("mNotificationLock")
     void scheduleTimeoutLocked(NotificationRecord record) {
         if (record.getNotification().getTimeoutAfter() > 0) {
-            final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
-                    REQUEST_CODE_TIMEOUT,
-                    new Intent(ACTION_NOTIFICATION_TIMEOUT)
-                            .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
-                            .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
-                                    .appendPath(record.getKey()).build())
-                            .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
-                            .putExtra(EXTRA_KEY, record.getKey()),
-                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+            final PendingIntent pi = getNotificationTimeoutPendingIntent(
+                    record, PendingIntent.FLAG_UPDATE_CURRENT);
             mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                     SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
         }
@@ -8864,6 +8871,16 @@
 
     @VisibleForTesting
     @GuardedBy("mNotificationLock")
+    void cancelScheduledTimeoutLocked(NotificationRecord record) {
+        final PendingIntent pi = getNotificationTimeoutPendingIntent(
+                record, PendingIntent.FLAG_CANCEL_CURRENT);
+        if (pi != null) {
+            mAlarmManager.cancel(pi);
+        }
+    }
+
+    @VisibleForTesting
+    @GuardedBy("mNotificationLock")
     /**
      * Determine whether this notification should attempt to make noise, vibrate, or flash the LED
      * @return buzzBeepBlink - bitfield (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)
@@ -9894,21 +9911,7 @@
             int rank, int count, boolean wasPosted, String listenerName,
             @ElapsedRealtimeLong long cancellationElapsedTimeMs) {
         final String canceledKey = r.getKey();
-
-        // Get pending intent used to create alarm, use FLAG_NO_CREATE if PendingIntent
-        // does not already exist, then null will be returned.
-        final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
-                REQUEST_CODE_TIMEOUT,
-                new Intent(ACTION_NOTIFICATION_TIMEOUT)
-                        .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
-                                .appendPath(r.getKey()).build())
-                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
-                PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE);
-
-        // Cancel alarm corresponding to pi.
-        if (pi != null) {
-            mAlarmManager.cancel(pi);
-        }
+        cancelScheduledTimeoutLocked(r);
 
         // Record caller.
         recordCallerLocked(r);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 1786ac5..6ab4b99 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -308,31 +308,43 @@
         return light;
     }
 
+    private VibrationEffect getVibrationForChannel(
+            NotificationChannel channel, VibratorHelper helper, boolean insistent) {
+        if (!channel.shouldVibrate()) {
+            return null;
+        }
+
+        if (Flags.notificationChannelVibrationEffectApi()) {
+            final VibrationEffect vibration = channel.getVibrationEffect();
+            if (vibration != null && helper.areEffectComponentsSupported(vibration)) {
+                // Adjust the vibration's repeat behavior based on the `insistent` property.
+                return vibration.applyRepeatingIndefinitely(insistent, /* loopDelayMs= */ 0);
+            }
+        }
+
+        final long[] vibrationPattern = channel.getVibrationPattern();
+        if (vibrationPattern == null) {
+            return helper.createDefaultVibration(insistent);
+        }
+        return helper.createWaveformVibration(vibrationPattern, insistent);
+    }
+
     private VibrationEffect calculateVibration() {
         VibratorHelper helper = new VibratorHelper(mContext);
         final Notification notification = getSbn().getNotification();
         final boolean insistent = (notification.flags & Notification.FLAG_INSISTENT) != 0;
-        VibrationEffect defaultVibration = helper.createDefaultVibration(insistent);
-        VibrationEffect vibration;
-        if (getChannel().shouldVibrate()) {
-            vibration = getChannel().getVibrationPattern() == null
-                    ? defaultVibration
-                    : helper.createWaveformVibration(getChannel().getVibrationPattern(), insistent);
-        } else {
-            vibration = null;
-        }
+
         if (mPreChannelsNotification
                 && (getChannel().getUserLockedFields()
                 & NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
             final boolean useDefaultVibrate =
                     (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
             if (useDefaultVibrate) {
-                vibration = defaultVibration;
-            } else {
-                vibration = helper.createWaveformVibration(notification.vibrate, insistent);
+                return helper.createDefaultVibration(insistent);
             }
+            return  helper.createWaveformVibration(notification.vibrate, insistent);
         }
-        return vibration;
+        return getVibrationForChannel(getChannel(), helper, insistent);
     }
 
     private @NonNull AudioAttributes calculateAttributes() {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 1bafcfe..4f3cdbc 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1212,6 +1212,8 @@
         if ((conversation.getUserLockedFields() & NotificationChannel.USER_LOCKED_VIBRATION) == 0
                 && (!Arrays.equals(oldParent.getVibrationPattern(),
                 updatedParent.getVibrationPattern())
+                || !Objects.equals(
+                        oldParent.getVibrationEffect(), updatedParent.getVibrationEffect())
                 || oldParent.shouldVibrate() != updatedParent.shouldVibrate())) {
             // enableVibration must be 2nd because setVibrationPattern may toggle it.
             conversation.setVibrationPattern(updatedParent.getVibrationPattern());
@@ -1972,6 +1974,7 @@
             update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
         }
         if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
+                || !Objects.equals(original.getVibrationEffect(), update.getVibrationEffect())
                 || original.shouldVibrate() != update.shouldVibrate()) {
             update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
         }
diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java
index 7204d05..8a0e595 100644
--- a/services/core/java/com/android/server/notification/VibratorHelper.java
+++ b/services/core/java/com/android/server/notification/VibratorHelper.java
@@ -193,6 +193,11 @@
         return createWaveformVibration(mDefaultPattern, insistent);
     }
 
+    /** Returns if a given vibration can be played by the vibrator that does notification buzz. */
+    public boolean areEffectComponentsSupported(VibrationEffect effect) {
+        return mVibrator.areVibrationFeaturesSupported(effect);
+    }
+
     @Nullable
     private static float[] getFloatArray(Resources resources, int resId) {
         TypedArray array = resources.obtainTypedArray(resId);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 28f3d59..33f481c 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2204,6 +2204,9 @@
                 File appMetadataFile = new File(ps.getPath(), APP_METADATA_FILE_NAME);
                 if (appMetadataFile.exists()) {
                     ps.setAppMetadataFilePath(appMetadataFile.getAbsolutePath());
+                    if (Flags.aslInApkAppMetadataSource()) {
+                        ps.setAppMetadataSource(PackageManager.APP_METADATA_SOURCE_INSTALLER);
+                    }
                 } else {
                     ps.setAppMetadataFilePath(null);
                 }
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 339b1e7..dc97e5f 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -28,6 +28,7 @@
 import static android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_STATUS_UNSET;
+import static android.content.pm.PackageManager.DELETE_ALL_USERS;
 import static android.content.pm.PackageManager.DELETE_ARCHIVE;
 import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
 import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT;
@@ -182,62 +183,76 @@
         return Flags.archiving() || SystemProperties.getBoolean("pm.archiving.enabled", false);
     }
 
+    @VisibleForTesting
     void requestArchive(
             @NonNull String packageName,
             @NonNull String callerPackageName,
             @NonNull IntentSender intentSender,
             @NonNull UserHandle userHandle) {
+        requestArchive(packageName, callerPackageName, /*flags=*/ 0, intentSender, userHandle);
+    }
+
+    void requestArchive(
+            @NonNull String packageName,
+            @NonNull String callerPackageName,
+            int flags,
+            @NonNull IntentSender intentSender,
+            @NonNull UserHandle userHandle) {
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(callerPackageName);
         Objects.requireNonNull(intentSender);
         Objects.requireNonNull(userHandle);
 
         Computer snapshot = mPm.snapshotComputer();
-        int userId = userHandle.getIdentifier();
+        int binderUserId = userHandle.getIdentifier();
         int binderUid = Binder.getCallingUid();
         int binderPid = Binder.getCallingPid();
         if (!PackageManagerServiceUtils.isSystemOrRootOrShell(binderUid)) {
-            verifyCaller(snapshot.getPackageUid(callerPackageName, 0, userId), binderUid);
+            verifyCaller(snapshot.getPackageUid(callerPackageName, 0, binderUserId), binderUid);
         }
-        snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
-                "archiveApp");
+
+        final boolean deleteAllUsers = (flags & PackageManager.DELETE_ALL_USERS) != 0;
+        final int[] users = deleteAllUsers ? mPm.mInjector.getUserManagerInternal().getUserIds()
+                : new int[]{binderUserId};
+        for (int userId : users) {
+            snapshot.enforceCrossUserPermission(binderUid, userId,
+                    /*requireFullPermission=*/ true, /*checkShell=*/ true,
+                    "archiveApp");
+        }
         verifyUninstallPermissions();
 
-        CompletableFuture<ArchiveState> archiveStateFuture;
+        CompletableFuture<Void>[] archiveStateStored = new CompletableFuture[users.length];
         try {
-            archiveStateFuture = createArchiveState(packageName, userId);
+            for (int i = 0, size = users.length; i < size; ++i) {
+                archiveStateStored[i] = createAndStoreArchiveState(packageName, users[i]);
+            }
         } catch (PackageManager.NameNotFoundException e) {
             Slog.d(TAG, TextUtils.formatSimple("Failed to archive %s with message %s",
                     packageName, e.getMessage()));
             throw new ParcelableException(e);
         }
 
-        archiveStateFuture
-                .thenAccept(
-                        archiveState -> {
-                            // TODO(b/282952870) Should be reverted if uninstall fails/cancels
-                            try {
-                                storeArchiveState(packageName, archiveState, userId);
-                            } catch (PackageManager.NameNotFoundException e) {
-                                sendFailureStatus(intentSender, packageName, e.getMessage());
-                                return;
-                            }
+        final int deleteFlags = DELETE_ARCHIVE | DELETE_KEEP_DATA
+                | (deleteAllUsers ? DELETE_ALL_USERS : 0);
 
-                            mPm.mInstallerService.uninstall(
-                                    new VersionedPackage(packageName,
-                                            PackageManager.VERSION_CODE_HIGHEST),
-                                    callerPackageName,
-                                    DELETE_ARCHIVE | DELETE_KEEP_DATA,
-                                    intentSender,
-                                    userId,
-                                    binderUid,
-                                    binderPid);
-                        })
-                .exceptionally(
-                        e -> {
-                            sendFailureStatus(intentSender, packageName, e.getMessage());
-                            return null;
-                        });
+        CompletableFuture.allOf(archiveStateStored).thenAccept(ignored ->
+                mPm.mInstallerService.uninstall(
+                        new VersionedPackage(packageName,
+                                PackageManager.VERSION_CODE_HIGHEST),
+                        callerPackageName,
+                        deleteFlags,
+                        intentSender,
+                        binderUserId,
+                        binderUid,
+                        binderPid)
+        ).exceptionally(
+                e -> {
+                    Slog.d(TAG, TextUtils.formatSimple("Failed to archive %s with message %s",
+                            packageName, e.getMessage()));
+                    sendFailureStatus(intentSender, packageName, e.getMessage());
+                    return null;
+                }
+        );
     }
 
     /**
@@ -384,7 +399,7 @@
     }
 
     /** Creates archived state for the package and user. */
-    private CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId)
+    private CompletableFuture<Void> createAndStoreArchiveState(String packageName, int userId)
             throws PackageManager.NameNotFoundException {
         Computer snapshot = mPm.snapshotComputer();
         PackageStateInternal ps = getPackageState(packageName, snapshot,
@@ -399,17 +414,18 @@
 
         List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps.getPackageName(),
                 userId);
-        final CompletableFuture<ArchiveState> archiveState = new CompletableFuture<>();
+        final CompletableFuture<Void> archiveStateStored = new CompletableFuture<>();
         mPm.mHandler.post(() -> {
             try {
-                archiveState.complete(
-                        createArchiveStateInternal(packageName, userId, mainActivities,
-                                installerInfo.loadLabel(mContext.getPackageManager()).toString()));
-            } catch (IOException e) {
-                archiveState.completeExceptionally(e);
+                var archiveState = createArchiveStateInternal(packageName, userId, mainActivities,
+                        installerInfo.loadLabel(mContext.getPackageManager()).toString());
+                storeArchiveState(packageName, archiveState, userId);
+                archiveStateStored.complete(null);
+            } catch (IOException | PackageManager.NameNotFoundException e) {
+                archiveStateStored.completeExceptionally(e);
             }
         });
-        return archiveState;
+        return archiveStateStored;
     }
 
     @Nullable
@@ -803,6 +819,7 @@
      * <p> The icon is returned without any treatment/overlay. In the rare case the app had multiple
      * launcher activities, only one of the icons is returned arbitrarily.
      */
+    @Nullable
     public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user,
             String callingPackageName) {
         Objects.requireNonNull(packageName);
@@ -828,7 +845,7 @@
         // In the rare case the archived app defined more than two launcher activities, we choose
         // the first one arbitrarily.
         Bitmap icon = decodeIcon(archiveState.getActivityInfos().get(0));
-        if (getAppOpsManager().checkOp(
+        if (icon != null && getAppOpsManager().checkOp(
                 AppOpsManager.OP_ARCHIVE_ICON_OVERLAY, callingUid, callingPackageName)
                 == MODE_ALLOWED) {
             icon = includeCloudOverlay(icon);
@@ -884,6 +901,7 @@
         return bitmap;
     }
 
+    @Nullable
     Bitmap includeCloudOverlay(Bitmap bitmap) {
         Drawable cloudDrawable =
                 mContext.getResources()
@@ -904,7 +922,9 @@
         final int iconSize = mContext.getSystemService(
                 ActivityManager.class).getLauncherLargeIconSize();
         Bitmap appIconWithCloudOverlay = drawableToBitmap(layerDrawable, iconSize);
-        bitmap.recycle();
+        if (bitmap != null) {
+            bitmap.recycle();
+        }
         return appIconWithCloudOverlay;
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index c6d448d..abea56b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1672,9 +1672,11 @@
     public void requestArchive(
             @NonNull String packageName,
             @NonNull String callerPackageName,
+            int flags,
             @NonNull IntentSender intentSender,
             @NonNull UserHandle userHandle) {
-        mPackageArchiver.requestArchive(packageName, callerPackageName, intentSender, userHandle);
+        mPackageArchiver.requestArchive(packageName, callerPackageName, flags, intentSender,
+                userHandle);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b705e84..b8960da 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2559,12 +2559,20 @@
                         PackageSetting pkgSetting = mSettings.getPackageLPr(pkgName);
                         if (pkgSetting != null) {
                             pkgSetting.setAppMetadataFilePath(path);
+                            if (Flags.aslInApkAppMetadataSource()) {
+                                pkgSetting.setAppMetadataSource(
+                                        PackageManager.APP_METADATA_SOURCE_SYSTEM_IMAGE);
+                            }
                         } else {
                             Slog.w(TAG, "Cannot set app metadata file for nonexistent package "
                                     + pkgName);
                         }
                     } else {
                         disabledPkgSetting.setAppMetadataFilePath(path);
+                        if (Flags.aslInApkAppMetadataSource()) {
+                            disabledPkgSetting.setAppMetadataSource(
+                                    PackageManager.APP_METADATA_SOURCE_SYSTEM_IMAGE);
+                        }
                     }
                 }
             }
@@ -5231,6 +5239,21 @@
             return null;
         }
 
+        @android.annotation.EnforcePermission(android.Manifest.permission.GET_APP_METADATA)
+        @Override
+        public int getAppMetadataSource(String packageName, int userId) {
+            getAppMetadataSource_enforcePermission();
+            final int callingUid = Binder.getCallingUid();
+            final Computer snapshot = snapshotComputer();
+            final PackageStateInternal ps = snapshot.getPackageStateForInstalledAndFiltered(
+                    packageName, callingUid, userId);
+            if (ps == null) {
+                throw new ParcelableException(
+                        new PackageManager.NameNotFoundException(packageName));
+            }
+            return ps.getAppMetadataSource();
+        }
+
         @Override
         public String getPermissionControllerPackageName() {
             final int callingUid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 5c9c8c6..81f9d1b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -4651,6 +4651,7 @@
 
     private int runArchive() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
+        int flags = 0;
         int userId = UserHandle.USER_ALL;
 
         String opt;
@@ -4678,13 +4679,16 @@
             return 1;
         }
 
+        if (userId == UserHandle.USER_ALL) {
+            flags |= PackageManager.DELETE_ALL_USERS;
+        }
         final int translatedUserId =
                 translateUserId(userId, UserHandle.USER_SYSTEM, "runArchive");
         final LocalIntentReceiver receiver = new LocalIntentReceiver();
 
         try {
             mInterface.getPackageInstaller().requestArchive(packageName,
-                    /* callerPackageName= */ "", receiver.getIntentSender(),
+                    /* callerPackageName= */ "", flags, receiver.getIntentSender(),
                     new UserHandle(translatedUserId));
         } catch (Exception e) {
             pw.println("Failure [" + e.getMessage() + "]");
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index f474d32..12eb88e 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -224,6 +224,8 @@
     @Nullable
     private String mAppMetadataFilePath;
 
+    private int mAppMetadataSource = PackageManager.APP_METADATA_SOURCE_UNKNOWN;
+
     private int mTargetSdkVersion;
 
     @Nullable
@@ -716,6 +718,7 @@
         categoryOverride = other.categoryOverride;
         mDomainSetId = other.mDomainSetId;
         mAppMetadataFilePath = other.mAppMetadataFilePath;
+        mAppMetadataSource = other.mAppMetadataSource;
         mTargetSdkVersion = other.mTargetSdkVersion;
         mRestrictUpdateHash = other.mRestrictUpdateHash == null
                 ? null : other.mRestrictUpdateHash.clone();
@@ -1378,6 +1381,15 @@
         return this;
     }
 
+    /**
+     * @param source the source of the app metadata that is currently associated with
+     */
+    public PackageSetting setAppMetadataSource(int source) {
+        mAppMetadataSource = source;
+        onChanged();
+        return this;
+    }
+
     @NonNull
     @Override
     public long getVersionCode() {
@@ -1818,6 +1830,11 @@
     }
 
     @DataClass.Generated.Member
+    public int getAppMetadataSource() {
+        return mAppMetadataSource;
+    }
+
+    @DataClass.Generated.Member
     public int getTargetSdkVersion() {
         return mTargetSdkVersion;
     }
@@ -1828,10 +1845,10 @@
     }
 
     @DataClass.Generated(
-            time = 1702666890740L,
+            time = 1706698406378L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
-            inputSignatures = "private  int mBooleans\nprivate  int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable boolean[] usesSdkLibrariesOptional\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.internal.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate @android.annotation.Nullable java.util.LinkedHashSet<java.io.File> mOldPaths\nprivate  float mLoadingProgress\nprivate  long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate  int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic  com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  com.android.server.pm.PackageSetting setSharedUserAppId(int)\npublic  com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic  com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprivate  void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  boolean isRequestLegacyExternalStorage()\npublic  boolean isUserDataFragile()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic  com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  void setInstalled(boolean,int)\n  void setArchiveState(com.android.server.pm.pkg.ArchiveState,int)\n  boolean getInstalled(int)\n  boolean isArchived(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n  boolean isInstalledOrHasDataOnAnyOtherUser(int[],int)\n  int[] queryInstalledUsers(int[],boolean)\n  int[] queryUsersInstalledOrHasData(int[])\n  long getCeDataInode(int)\n  long getDeDataInode(int)\n  void setCeDataInode(long,int)\n  void setDeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\npublic  com.android.server.pm.PackageSetting setScannedAsStoppedSystemApp(boolean)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static  void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting addOldPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting removeOldPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isIncremental()\npublic  boolean isLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic  com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic  com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override boolean[] getUsesSdkLibrariesOptional()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesOptional(boolean[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic  com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic  com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\npublic @java.lang.Override boolean isScannedAsStoppedSystemApp()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final  int INSTALL_PERMISSION_FIXED\nprivate static final  int UPDATE_AVAILABLE\nprivate static final  int FORCE_QUERYABLE_OVERRIDE\nprivate static final  int SCANNED_AS_STOPPED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+            inputSignatures = "private  int mBooleans\nprivate  int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable boolean[] usesSdkLibrariesOptional\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.internal.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate @android.annotation.Nullable java.util.LinkedHashSet<java.io.File> mOldPaths\nprivate  float mLoadingProgress\nprivate  long mLoadingCompletedTime\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate @android.annotation.Nullable java.lang.String mAppMetadataFilePath\nprivate  int mAppMetadataSource\nprivate  int mTargetSdkVersion\nprivate @android.annotation.Nullable byte[] mRestrictUpdateHash\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  void setBoolean(int,boolean)\nprivate  boolean getBoolean(int)\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackage(java.lang.String,int)\npublic  com.android.server.pm.PackageSetting setUpdateOwnerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  com.android.server.pm.PackageSetting setSharedUserAppId(int)\npublic  com.android.server.pm.PackageSetting setTargetSdkVersion(int)\npublic  com.android.server.pm.PackageSetting setRestrictUpdateHash(byte[])\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprivate  void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  boolean isRequestLegacyExternalStorage()\npublic  boolean isUserDataFragile()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic  com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  void setInstalled(boolean,int)\n  void setArchiveState(com.android.server.pm.pkg.ArchiveState,int)\n  boolean getInstalled(int)\n  boolean isArchived(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n  boolean isInstalledOnAnyOtherUser(int[],int)\n  boolean hasDataOnAnyOtherUser(int[],int)\n  int[] queryInstalledUsers(int[],boolean)\n  int[] queryUsersInstalledOrHasData(int[])\n  long getCeDataInode(int)\n  long getDeDataInode(int)\n  void setCeDataInode(long,int)\n  void setDeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\npublic  com.android.server.pm.PackageSetting setScannedAsStoppedSystemApp(boolean)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<android.content.pm.UserPackage,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long,int,com.android.server.pm.pkg.ArchiveState)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n  com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  void restoreComponentSettings(int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\nprivate static  void writeArchiveState(android.util.proto.ProtoOutputStream,com.android.server.pm.pkg.ArchiveState)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting addOldPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting com.android.server.pm.PackageSetting removeOldPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isIncremental()\npublic  boolean isLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic  com.android.server.pm.PackageSetting setLoadingCompletedTime(long)\npublic  com.android.server.pm.PackageSetting setAppMetadataFilePath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setAppMetadataSource(int)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override boolean[] getUsesSdkLibrariesOptional()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getSharedLibraryDependencies()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getApexModuleName()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic  com.android.server.pm.PackageSetting setUsesSdkLibrariesOptional(boolean[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic  com.android.server.pm.PackageSetting setApexModuleName(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic  com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbi()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbi()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfo()\npublic @android.annotation.Nullable java.lang.String getPrimaryCpuAbiLegacy()\npublic @android.annotation.Nullable java.lang.String getSecondaryCpuAbiLegacy()\npublic @android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy @java.lang.Override int getHiddenApiEnforcementPolicy()\npublic @java.lang.Override boolean isApex()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isDefaultToDeviceProtectedStorage()\npublic @java.lang.Override boolean isPersistent()\npublic @java.lang.Override boolean isScannedAsStoppedSystemApp()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\nprivate static final  int INSTALL_PERMISSION_FIXED\nprivate static final  int UPDATE_AVAILABLE\nprivate static final  int FORCE_QUERYABLE_OVERRIDE\nprivate static final  int SCANNED_AS_STOPPED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c7ee649..04e8205 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -924,6 +924,7 @@
             ret.setUsesStaticLibrariesVersions(p.getUsesStaticLibrariesVersions());
             ret.setMimeGroups(p.getMimeGroups());
             ret.setAppMetadataFilePath(p.getAppMetadataFilePath());
+            ret.setAppMetadataSource(p.getAppMetadataSource());
             ret.getPkgState().setUpdatedSystemApp(false);
             ret.setTargetSdkVersion(p.getTargetSdkVersion());
             ret.setRestrictUpdateHash(p.getRestrictUpdateHash());
@@ -3122,6 +3123,9 @@
                     pkg.getAppMetadataFilePath());
         }
 
+        serializer.attributeInt(null, "appMetadataSource",
+                pkg.getAppMetadataSource());
+
         writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(),
                 pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesOptional());
 
@@ -3226,6 +3230,9 @@
         if (pkg.getAppMetadataFilePath() != null) {
             serializer.attribute(null, "appMetadataFilePath", pkg.getAppMetadataFilePath());
         }
+        serializer.attributeInt(null, "appMetadataSource",
+                pkg.getAppMetadataSource());
+
 
         writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(),
                 pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesOptional());
@@ -3942,6 +3949,8 @@
         }
 
         ps.setAppMetadataFilePath(parser.getAttributeValue(null, "appMetadataFilePath"));
+        ps.setAppMetadataSource(parser.getAttributeInt(null,
+                "appMetadataSource", PackageManager.APP_METADATA_SOURCE_UNKNOWN));
 
         int outerDepth = parser.getDepth();
         int type;
@@ -4021,6 +4030,7 @@
         long loadingCompletedTime = 0;
         UUID domainSetId;
         String appMetadataFilePath = null;
+        int appMetadataSource = PackageManager.APP_METADATA_SOURCE_UNKNOWN;
         int targetSdkVersion = 0;
         byte[] restrictUpdateHash = null;
         boolean isScannedAsStoppedSystemApp = false;
@@ -4066,6 +4076,9 @@
             categoryHint = parser.getAttributeInt(null, "categoryHint",
                     ApplicationInfo.CATEGORY_UNDEFINED);
             appMetadataFilePath = parser.getAttributeValue(null, "appMetadataFilePath");
+            appMetadataSource = parser.getAttributeInt(null, "appMetadataSource",
+                    PackageManager.APP_METADATA_SOURCE_UNKNOWN);
+
             isScannedAsStoppedSystemApp = parser.getAttributeBoolean(null,
                 "scannedAsStoppedSystemApp", false);
 
@@ -4214,6 +4227,7 @@
                     .setLoadingProgress(loadingProgress)
                     .setLoadingCompletedTime(loadingCompletedTime)
                     .setAppMetadataFilePath(appMetadataFilePath)
+                    .setAppMetadataSource(appMetadataSource)
                     .setTargetSdkVersion(targetSdkVersion)
                     .setRestrictUpdateHash(restrictUpdateHash)
                     .setScannedAsStoppedSystemApp(isScannedAsStoppedSystemApp);
@@ -5223,6 +5237,8 @@
         }
         pw.print(prefix); pw.print("  appMetadataFilePath=");
         pw.println(ps.getAppMetadataFilePath());
+        pw.print(prefix); pw.print("  appMetadataSource=");
+        pw.println(ps.getAppMetadataSource());
         if (ps.getVolumeUuid() != null) {
             pw.print(prefix); pw.print("  volumeUuid=");
                     pw.println(ps.getVolumeUuid());
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index f7603b5..85ea83a 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -116,4 +116,9 @@
 
     @Nullable
     Set<File> getOldPaths();
+
+    /**
+     * @return the source of the app metadata that is currently associated with the given package.
+     */
+    int getAppMetadataSource();
 }
diff --git a/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java b/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java
index 7754944..07cc775 100644
--- a/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java
@@ -17,11 +17,14 @@
 package com.android.server.policy;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 
 import com.android.server.devicestate.DeviceStatePolicy;
 import com.android.server.devicestate.DeviceStateProvider;
 
+import java.io.PrintWriter;
+
 /**
  * Default empty implementation of {@link DeviceStatePolicy}.
  *
@@ -43,4 +46,9 @@
     public void configureDeviceForState(int state, @NonNull Runnable onComplete) {
         onComplete.run();
     }
+
+    @Override
+    public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        mProvider.dump(writer, args);
+    }
 }
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 3644054..afcf5a0 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -58,6 +58,7 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.PrintWriter;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -503,6 +504,24 @@
         // Do nothing.
     }
 
+    @Override
+    public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        writer.println("DeviceStateProviderImpl");
+
+        synchronized (mLock) {
+            writer.println("  mLastReportedState = " + mLastReportedState);
+            writer.println("  mPowerSaveModeEnabled = " + mPowerSaveModeEnabled);
+            writer.println("  mThermalStatus = " + mThermalStatus);
+            writer.println("  mIsLidOpen = " + mIsLidOpen);
+            writer.println("  Sensor values:");
+
+            for (Sensor sensor : mLatestSensorEvent.keySet()) {
+                SensorEvent sensorEvent = mLatestSensorEvent.get(sensor);
+                writer.println("   - " + toSensorValueString(sensor, sensorEvent));
+            }
+        }
+    }
+
     /**
      * Implementation of {@link BooleanSupplier} that returns {@code true} if the expected lid
      * switch open state matches {@link #mIsLidOpen}.
@@ -669,14 +688,16 @@
         Slog.i(TAG, "Sensor values:");
         for (Sensor sensor : mLatestSensorEvent.keySet()) {
             SensorEvent sensorEvent = mLatestSensorEvent.get(sensor);
-            if (sensorEvent != null) {
-                Slog.i(TAG, sensor.getName() + ": " + Arrays.toString(sensorEvent.values));
-            } else {
-                Slog.i(TAG, sensor.getName() + ": null");
-            }
+            Slog.i(TAG, toSensorValueString(sensor, sensorEvent));
         }
     }
 
+    private String toSensorValueString(Sensor sensor, @Nullable SensorEvent event) {
+        String sensorString = sensor == null ? "null" : sensor.getName();
+        String eventValues = event == null ? "null" : Arrays.toString(event.values);
+        return sensorString + " : " + eventValues;
+    }
+
     /**
      * Tries to parse the provided file into a {@link DeviceStateConfig} object. Returns
      * {@code null} if the file could not be successfully parsed.
diff --git a/services/core/java/com/android/server/policy/TalkbackShortcutController.java b/services/core/java/com/android/server/policy/TalkbackShortcutController.java
index 906da2f..b05a421 100644
--- a/services/core/java/com/android/server/policy/TalkbackShortcutController.java
+++ b/services/core/java/com/android/server/policy/TalkbackShortcutController.java
@@ -59,6 +59,10 @@
         final Set<ComponentName> enabledServices =
                 AccessibilityUtils.getEnabledServicesFromSettings(mContext, userId);
         ComponentName componentName = getTalkbackComponent();
+        if (componentName == null) {
+            return false;
+        }
+
         boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName);
 
         if (isTalkBackShortcutGestureEnabled()) {
@@ -67,7 +71,7 @@
                     isTalkbackAlreadyEnabled);
 
             // log stem triple press telemetry if it's a talkback enabled event.
-            if (componentName != null && isTalkbackAlreadyEnabled) {
+            if (isTalkbackAlreadyEnabled) {
                 logStemTriplePressAccessibilityTelemetry(componentName);
             }
         }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 089a886..e0a8226 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2095,17 +2095,17 @@
         }
 
         @Override
-        public void startPlayback(IBinder sessionToken, int userId) {
+        public void resumePlayback(IBinder sessionToken, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "stopPlayback");
+                    userId, "resumePlayback");
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     try {
-                        getSessionLocked(sessionToken, callingUid, resolvedUserId).startPlayback();
+                        getSessionLocked(sessionToken, callingUid, resolvedUserId).resumePlayback();
                     } catch (RemoteException | SessionNotFoundException e) {
-                        Slog.e(TAG, "error in startPlayback()", e);
+                        Slog.e(TAG, "error in resumePlayback()", e);
                     }
                 }
             } finally {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 716aee3..e743172 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5150,6 +5150,15 @@
 
     /** @return the orientation of the display when it's rotation is ROTATION_0. */
     int getNaturalOrientation() {
+        return mBaseDisplayWidth <= mBaseDisplayHeight
+                ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+    }
+
+    /**
+     * Returns the orientation which is used for app's Configuration (excluding decor insets) when
+     * the display rotation is ROTATION_0.
+     */
+    int getNaturalConfigurationOrientation() {
         final Configuration config = getConfiguration();
         if (config.windowConfiguration.getDisplayRotation() == ROTATION_0) {
             return config.orientation;
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 34d7651..4204670 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -132,7 +132,7 @@
     void show(SurfaceControl.Transaction t, WindowContainer w) {
         t.show(mInputSurface);
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
-        t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1);
+        t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1 + w.getChildCount());
     }
 
     void show(SurfaceControl.Transaction t, int layer) {
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 27cc2d6..7fc61e1 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -66,7 +66,6 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.WeakHashMap;
-import java.util.function.Consumer;
 
 /**
  * Stores the TaskOrganizers associated with a given windowing mode and
@@ -102,11 +101,8 @@
      */
     private static class TaskOrganizerCallbacks {
         final ITaskOrganizer mTaskOrganizer;
-        final Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
 
-        TaskOrganizerCallbacks(ITaskOrganizer taskOrg,
-                Consumer<Runnable> deferTaskOrgCallbacksConsumer) {
-            mDeferTaskOrgCallbacksConsumer = deferTaskOrgCallbacksConsumer;
+        TaskOrganizerCallbacks(ITaskOrganizer taskOrg) {
             mTaskOrganizer = taskOrg;
         }
 
@@ -335,11 +331,7 @@
         private final int mUid;
 
         TaskOrganizerState(ITaskOrganizer organizer, int uid) {
-            final Consumer<Runnable> deferTaskOrgCallbacksConsumer =
-                    mDeferTaskOrgCallbacksConsumer != null
-                            ? mDeferTaskOrgCallbacksConsumer
-                            : mService.mWindowManager.mAnimator::addAfterPrepareSurfacesRunnable;
-            mOrganizer = new TaskOrganizerCallbacks(organizer, deferTaskOrgCallbacksConsumer);
+            mOrganizer = new TaskOrganizerCallbacks(organizer);
             mDeathRecipient = new DeathRecipient(organizer);
             mPendingEventsQueue = new TaskOrganizerPendingEventsQueue(this);
             try {
@@ -484,8 +476,6 @@
     // Set of organized tasks (by taskId) that dispatch back pressed to their organizers
     private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet<>();
 
-    private Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
-
     TaskOrganizerController(ActivityTaskManagerService atm) {
         mService = atm;
         mGlobalLock = atm.mGlobalLock;
@@ -502,15 +492,6 @@
     }
 
     /**
-     * Specifies the consumer to run to defer the task org callbacks. Can be overridden while
-     * testing to allow the callbacks to be sent synchronously.
-     */
-    @VisibleForTesting
-    public void setDeferTaskOrgCallbacksConsumer(Consumer<Runnable> consumer) {
-        mDeferTaskOrgCallbacksConsumer = consumer;
-    }
-
-    /**
      * Register a TaskOrganizer to manage tasks as they enter the a supported windowing mode.
      */
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 2d2857a..80894b2 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1589,7 +1589,7 @@
         if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
             // NOSENSOR means the display's "natural" orientation, so return that.
             if (mDisplayContent != null) {
-                return mDisplayContent.getNaturalOrientation();
+                return mDisplayContent.getNaturalConfigurationOrientation();
             }
         } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
             // LOCKED means the activity's orientation remains unchanged, so return existing value.
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index cc08488..df7fb99 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -31,5 +31,8 @@
 per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com
 per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS
 
+# Memory
+per-file com_android_server_am_OomConnection.cpp = file:/MEMORY_OWNERS
+
 # Bug component : 158088 = per-file *AnrTimer*
 per-file *AnrTimer* = file:/PERFORMANCE_OWNERS
diff --git a/services/core/jni/com_android_server_am_OomConnection.cpp b/services/core/jni/com_android_server_am_OomConnection.cpp
index e892d23..49a3ad3 100644
--- a/services/core/jni/com_android_server_am_OomConnection.cpp
+++ b/services/core/jni/com_android_server_am_OomConnection.cpp
@@ -22,13 +22,15 @@
 
 namespace android {
 
+using namespace ::android::bpf::memevents;
+
 // Used to cache the results of the JNI name lookup
 static struct {
     jclass clazz;
     jmethodID ctor;
 } sOomKillRecordInfo;
 
-static memevents::MemEventListener memevent_listener;
+static MemEventListener memevent_listener(MemEventClient::AMS);
 
 /**
  * Initialize listening and waiting for new out-of-memory (OOM) events to occur.
@@ -42,25 +44,20 @@
  * @throws java.lang.RuntimeException
  */
 static jobjectArray android_server_am_OomConnection_waitOom(JNIEnv* env, jobject) {
-    const memevents::MemEvent oom_event = memevents::MemEvent::OOM_KILL;
-    if (!memevent_listener.registerEvent(oom_event)) {
+    if (!memevent_listener.registerEvent(MEM_EVENT_OOM_KILL)) {
         memevent_listener.deregisterAllEvents();
         jniThrowRuntimeException(env, "listener failed to register to OOM events");
         return nullptr;
     }
 
-    memevents::MemEvent event_received;
-    do {
-        event_received = memevent_listener.listen();
-        if (event_received == memevents::MemEvent::ERROR) {
-            memevent_listener.deregisterAllEvents();
-            jniThrowRuntimeException(env, "listener received error event");
-            return nullptr;
-        }
-    } while (event_received != oom_event);
+    if (!memevent_listener.listen()) {
+        memevent_listener.deregisterAllEvents();
+        jniThrowRuntimeException(env, "listener failed waiting for OOM event");
+        return nullptr;
+    }
 
-    std::vector<memevents::OomKill> oom_events;
-    if (!memevent_listener.getOomEvents(oom_events)) {
+    std::vector<mem_event_t> oom_events;
+    if (!memevent_listener.getMemEvents(oom_events)) {
         memevent_listener.deregisterAllEvents();
         jniThrowRuntimeException(env, "Failed to get OOM events");
         return nullptr;
@@ -75,15 +72,23 @@
     }
 
     for (int i = 0; i < oom_events.size(); i++) {
-        const memevents::OomKill oom_event = oom_events[i];
-        jstring process_name = env->NewStringUTF(oom_event.process_name);
+        const mem_event_t mem_event = oom_events[i];
+        if (mem_event.type != MEM_EVENT_OOM_KILL) {
+            memevent_listener.deregisterAllEvents();
+            jniThrowRuntimeException(env, "Received invalid memory event");
+            return java_oom_array;
+        }
+
+        const auto oom_kill = mem_event.event_data.oom_kill;
+
+        jstring process_name = env->NewStringUTF(oom_kill.process_name);
         if (process_name == NULL) {
             memevent_listener.deregisterAllEvents();
             jniThrowRuntimeException(env, "Failed creating java string for process name");
         }
         jobject java_oom_kill = env->NewObject(sOomKillRecordInfo.clazz, sOomKillRecordInfo.ctor,
-                                               oom_event.timestamp_ms, oom_event.pid, oom_event.uid,
-                                               process_name, oom_event.oom_score_adj);
+                                               oom_kill.timestamp_ms, oom_kill.pid, oom_kill.uid,
+                                               process_name, oom_kill.oom_score_adj);
         if (java_oom_kill == NULL) {
             memevent_listener.deregisterAllEvents();
             jniThrowRuntimeException(env, "Failed to create OomKillRecord object");
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 4a6b31c..810090a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -285,6 +285,7 @@
     void setInputDispatchMode(bool enabled, bool frozen);
     void setSystemUiLightsOut(bool lightsOut);
     void setPointerDisplayId(int32_t displayId);
+    int32_t getMousePointerSpeed();
     void setPointerSpeed(int32_t speed);
     void setMousePointerAccelerationEnabled(int32_t displayId, bool enabled);
     void setTouchpadPointerSpeed(int32_t speed);
@@ -1219,6 +1220,11 @@
     }
 }
 
+int32_t NativeInputManager::getMousePointerSpeed() {
+    std::scoped_lock _l(mLock);
+    return mLocked.pointerSpeed;
+}
+
 void NativeInputManager::setPointerSpeed(int32_t speed) {
     { // acquire lock
         std::scoped_lock _l(mLock);
@@ -2211,6 +2217,12 @@
     }
 }
 
+static jint nativeGetMousePointerSpeed(JNIEnv* env, jobject nativeImplObj) {
+    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+
+    return static_cast<jint>(im->getMousePointerSpeed());
+}
+
 static void nativeSetPointerSpeed(JNIEnv* env, jobject nativeImplObj, jint speed) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
 
@@ -2865,6 +2877,7 @@
         {"transferTouchFocus", "(Landroid/os/IBinder;Landroid/os/IBinder;Z)Z",
          (void*)nativeTransferTouchFocus},
         {"transferTouch", "(Landroid/os/IBinder;I)Z", (void*)nativeTransferTouch},
+        {"getMousePointerSpeed", "()I", (void*)nativeGetMousePointerSpeed},
         {"setPointerSpeed", "(I)V", (void*)nativeSetPointerSpeed},
         {"setMousePointerAccelerationEnabled", "(IZ)V",
          (void*)nativeSetMousePointerAccelerationEnabled},
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 4203576..e8b32cd 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -178,7 +178,7 @@
         // intents
         return PendingIntent.getActivityAsUser(
                 mContext, /*requestCode=*/0, intent,
-                PendingIntent.FLAG_IMMUTABLE, /*options=*/null,
+                PendingIntent.FLAG_MUTABLE, /*options=*/null,
                 UserHandle.of(mUserId));
     }
 }
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index 7e709fe..1a9a0e6 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -116,7 +116,7 @@
                         mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
                         PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
                                 Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
-                providerDataList,
+                /*providerDataList=*/ null,
                 /*isRequestForAllOptions=*/ true);
 
         List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index fcaef9f..ca23d62 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -30,6 +30,7 @@
 import android.credentials.selection.Entry;
 import android.credentials.selection.GetCredentialProviderData;
 import android.credentials.selection.ProviderPendingIntentResponse;
+import android.os.Bundle;
 import android.os.ICancellationSignal;
 import android.service.autofill.Flags;
 import android.service.credentials.Action;
@@ -76,6 +77,9 @@
     @NonNull
     private final Map<String, CredentialOption> mBeginGetOptionToCredentialOptionMap;
 
+    @NonNull
+    private final Map<String, AutofillId> mCredentialEntryKeyToAutofilLIdMap;
+
 
     /** The complete request to be used in the second round. */
     private final android.credentials.GetCredentialRequest mCompleteRequest;
@@ -245,6 +249,7 @@
         mBeginGetOptionToCredentialOptionMap = new HashMap<>(beginGetOptionToCredentialOptionMap);
         mProviderResponseDataHandler = new ProviderResponseDataHandler(
                 ComponentName.unflattenFromString(hybridService));
+        mCredentialEntryKeyToAutofilLIdMap = new HashMap<>();
     }
 
     /** Called when the provider response has been updated by an external source. */
@@ -298,7 +303,7 @@
                     invokeCallbackOnInternalInvalidState();
                     return;
                 }
-                onCredentialEntrySelected(providerPendingIntentResponse);
+                onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
                 break;
             case ACTION_ENTRY_KEY:
                 Action actionEntry = mProviderResponseDataHandler.getActionEntry(entryKey);
@@ -307,7 +312,7 @@
                     invokeCallbackOnInternalInvalidState();
                     return;
                 }
-                onActionEntrySelected(providerPendingIntentResponse);
+                onActionEntrySelected(providerPendingIntentResponse, entryKey);
                 break;
             case AUTHENTICATION_ACTION_ENTRY_KEY:
                 Action authenticationEntry = mProviderResponseDataHandler
@@ -337,7 +342,7 @@
                 break;
             case REMOTE_ENTRY_KEY:
                 if (mProviderResponseDataHandler.getRemoteEntry(entryKey) != null) {
-                    onRemoteEntrySelected(providerPendingIntentResponse);
+                    onRemoteEntrySelected(providerPendingIntentResponse, entryKey);
                 } else {
                     Slog.i(TAG, "Unexpected remote entry key");
                     invokeCallbackOnInternalInvalidState();
@@ -376,7 +381,7 @@
         return null;
     }
 
-    private Intent setUpFillInIntentWithFinalRequest(@NonNull String id) {
+    private Intent setUpFillInIntentWithFinalRequest(@NonNull String id, String entryKey) {
         // TODO: Determine if we should skip this entry if entry id is not set, or is set
         // but does not resolve to a valid option. For now, not skipping it because
         // it may be possible that the provider adds their own extras and expects to receive
@@ -392,6 +397,7 @@
                 .getParcelable(CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class);
         if (autofillId != null && Flags.autofillCredmanIntegration()) {
             intent.putExtra(CredentialProviderService.EXTRA_AUTOFILL_ID, autofillId);
+            mCredentialEntryKeyToAutofilLIdMap.put(entryKey, autofillId);
         }
         return intent.putExtra(
                 CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
@@ -408,12 +414,13 @@
     }
 
     private void onRemoteEntrySelected(
-            ProviderPendingIntentResponse providerPendingIntentResponse) {
-        onCredentialEntrySelected(providerPendingIntentResponse);
+            ProviderPendingIntentResponse providerPendingIntentResponse, String entryKey) {
+        onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
     }
 
     private void onCredentialEntrySelected(
-            ProviderPendingIntentResponse providerPendingIntentResponse) {
+            ProviderPendingIntentResponse providerPendingIntentResponse,
+            String entryKey) {
         if (providerPendingIntentResponse == null) {
             invokeCallbackOnInternalInvalidState();
             return;
@@ -430,7 +437,18 @@
         GetCredentialResponse getCredentialResponse = PendingIntentResultHandler
                 .extractGetCredentialResponse(
                         providerPendingIntentResponse.getResultData());
-        if (getCredentialResponse != null) {
+        if (getCredentialResponse != null && getCredentialResponse.getCredential() != null) {
+            Bundle credentialData = getCredentialResponse.getCredential().getData();
+            AutofillId autofillId = mCredentialEntryKeyToAutofilLIdMap.get(entryKey);
+            if (Flags.autofillCredmanIntegration()
+                    && entryKey != null && autofillId != null && credentialData != null
+            ) {
+                Slog.d(TAG, "Adding autofillId to credential response: " + autofillId);
+                credentialData.putParcelable(
+                        CredentialProviderService.EXTRA_AUTOFILL_ID,
+                        mCredentialEntryKeyToAutofilLIdMap.get(entryKey)
+                );
+            }
             mCallbacks.onFinalResponseReceived(mComponentName,
                     getCredentialResponse);
             return;
@@ -514,9 +532,9 @@
 
     /** Returns true if either an exception or a response is found. */
     private void onActionEntrySelected(ProviderPendingIntentResponse
-            providerPendingIntentResponse) {
+            providerPendingIntentResponse, String entryKey) {
         Slog.i(TAG, "onActionEntrySelected");
-        onCredentialEntrySelected(providerPendingIntentResponse);
+        onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
     }
 
 
@@ -614,7 +632,7 @@
             Entry entry = new Entry(CREDENTIAL_ENTRY_KEY,
                     id, credentialEntry.getSlice(),
                     setUpFillInIntentWithFinalRequest(credentialEntry
-                            .getBeginGetCredentialOptionId()));
+                            .getBeginGetCredentialOptionId(), id));
             mUiCredentialEntries.put(id, new Pair<>(credentialEntry, entry));
             mCredentialEntryTypes.add(credentialEntry.getType());
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 519c9bb..74d544f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -23128,6 +23128,10 @@
                     mInjector.systemPropertiesSet(memtagProperty, "memtag");
                 } else if (flags == DevicePolicyManager.MTE_DISABLED) {
                     mInjector.systemPropertiesSet(memtagProperty, "memtag-off");
+                } else if (flags == DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
+                    if (admin.mtePolicy != DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
+                        mInjector.systemPropertiesSet(memtagProperty, "default");
+                    }
                 }
                 admin.mtePolicy = flags;
                 saveSettingsLocked(caller.getUserId());
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java
index d5a3cff..82d5247 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleClosedStatePredicate.java
@@ -35,6 +35,7 @@
 import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.util.ArraySet;
+import android.util.Dumpable;
 import android.view.Display;
 import android.view.Surface;
 
@@ -43,7 +44,9 @@
 import com.android.server.policy.BookStylePreferredScreenCalculator.StateTransition;
 import com.android.server.policy.BookStyleClosedStatePredicate.ConditionSensorListener.SensorSubscription;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Predicate;
@@ -56,7 +59,7 @@
  * See {@link BookStyleStateTransitions} for detailed description of the default behavior.
  */
 public class BookStyleClosedStatePredicate implements Predicate<FoldableDeviceStateProvider>,
-        DisplayManager.DisplayListener {
+        DisplayManager.DisplayListener, Dumpable {
 
     private final BookStylePreferredScreenCalculator mClosedStateCalculator;
     private final Handler mHandler = new Handler();
@@ -154,6 +157,14 @@
 
     }
 
+    @Override
+    public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        writer.println("  " + getDumpableName());
+
+        mPostureEstimator.dump(writer, args);
+        mClosedStateCalculator.dump(writer, args);
+    }
+
     public interface ClosedStateUpdatesListener {
         void onClosedStateUpdated();
     }
@@ -161,7 +172,7 @@
     /**
      * Estimates if the device is going to enter wedge/tent mode based on the sensor data
      */
-    private static class PostureEstimator implements SensorEventListener {
+    private static class PostureEstimator implements SensorEventListener, Dumpable {
 
 
         private static final int FLAT_INCLINATION_THRESHOLD_DEGREES = 8;
@@ -356,6 +367,23 @@
             mDeviceClosed = deviceClosed;
             mConditionedSensorListener.updateListeningState();
         }
+
+        @Override
+        public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+            writer.println("    " + getDumpableName());
+            writer.println("      isLikelyTentOrWedgeMode = " + isLikelyTentOrWedgeMode());
+            writer.println("      mScreenTurnedOn = " + mScreenTurnedOn);
+            writer.println("      mLastScreenRotation = " + mLastScreenRotation);
+            writer.println("      mDeviceClosed = " + mDeviceClosed);
+            writer.println("      mLeftGravityVector = " + Arrays.toString(mLeftGravityVector));
+            writer.println("      mRightGravityVector = " + Arrays.toString(mRightGravityVector));
+        }
+
+        @NonNull
+        @Override
+        public String getDumpableName() {
+            return "PostureEstimator";
+        }
     }
 
     /**
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
index ad938af..8b22718 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java
@@ -38,6 +38,7 @@
 import com.android.server.policy.feature.flags.FeatureFlags;
 import com.android.server.policy.feature.flags.FeatureFlagsImpl;
 
+import java.io.PrintWriter;
 import java.util.function.Predicate;
 
 /**
@@ -182,4 +183,9 @@
     public void configureDeviceForState(int state, @NonNull Runnable onComplete) {
         onComplete.run();
     }
+
+    @Override
+    public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        mProvider.dump(writer, args);
+    }
 }
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStylePreferredScreenCalculator.java b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStylePreferredScreenCalculator.java
index 8977422..69d793e 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStylePreferredScreenCalculator.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStylePreferredScreenCalculator.java
@@ -16,8 +16,14 @@
 
 package com.android.server.policy;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Build;
+import android.util.Dumpable;
+import android.util.Slog;
 
+
+import java.io.PrintWriter;
 import java.util.List;
 import java.util.Objects;
 
@@ -31,7 +37,12 @@
  *
  * See {@link BookStyleStateTransitions} for detailed description of the default behavior.
  */
-public class BookStylePreferredScreenCalculator {
+public class BookStylePreferredScreenCalculator implements Dumpable {
+
+    private static final String TAG = "BookStylePreferredScreenCalculator";
+
+    // TODO(b/322137477): disable by default on all builds after flag clean-up
+    private static final boolean DEBUG = Build.IS_USERDEBUG || Build.IS_ENG;
 
     /**
      * When calculating the new state we will re-calculate it until it settles down. We re-calculate
@@ -77,6 +88,9 @@
      */
     public PreferredScreen calculatePreferredScreen(HingeAngle angle, boolean likelyTentOrWedge,
             boolean likelyReverseWedge) {
+
+        final State oldState = mState;
+
         int attempts = 0;
         State newState = calculateNewState(mState, angle, likelyTentOrWedge, likelyReverseWedge);
         while (attempts < MAX_STATE_CHANGES && !Objects.equals(mState, newState)) {
@@ -92,7 +106,6 @@
                             + ", likelyReverseWedge = " + likelyReverseWedge);
         }
 
-        final State oldState = mState;
         mState = newState;
 
         if (mState.mPreferredScreen == PreferredScreen.INVALID) {
@@ -103,6 +116,13 @@
                             + oldState);
         }
 
+        if (DEBUG && !Objects.equals(oldState, newState)) {
+            Slog.d(TAG, "Moving to state " + mState
+                    + " (hingeAngle = " + angle
+                    + ", likelyTentOrWedge = " + likelyTentOrWedge
+                    + ", likelyReverseWedge = " + likelyReverseWedge + ")");
+        }
+
         return mState.mPreferredScreen;
     }
 
@@ -129,6 +149,18 @@
                         + likelyReverseWedge);
     }
 
+    @Override
+    public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        writer.println("    " + getDumpableName());
+        writer.println("      mState = " + mState);
+    }
+
+    @NonNull
+    @Override
+    public String getDumpableName() {
+        return TAG;
+    }
+
     /**
      * The angle between two halves of the foldable device in degrees. The angle is '0' when
      * the device is fully closed and '180' when the device is fully open and flat.
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
index ba72977..021a667 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -39,6 +39,7 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.Trace;
+import android.util.Dumpable;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -52,6 +53,7 @@
 import com.android.server.policy.feature.flags.FeatureFlags;
 import com.android.server.policy.feature.flags.FeatureFlagsImpl;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
@@ -87,6 +89,8 @@
     // the conditions needed for availability.
     private final SparseArray<BooleanSupplier> mStateAvailabilityConditions = new SparseArray<>();
 
+    private final DeviceStateConfiguration[] mConfigurations;
+
     @GuardedBy("mLock")
     private final SparseBooleanArray mExternalDisplaysConnected = new SparseBooleanArray();
 
@@ -142,6 +146,7 @@
         mHingeAngleSensor = hingeAngleSensor;
         mHallSensor = hallSensor;
         mDisplayManager = displayManager;
+        mConfigurations = deviceStateConfigurations;
         mIsDualDisplayBlockingEnabled = featureFlags.enableDualDisplayBlocking();
 
         sensorManager.registerListener(this, mHingeAngleSensor, SENSOR_DELAY_FASTEST);
@@ -350,16 +355,20 @@
     @GuardedBy("mLock")
     private void dumpSensorValues() {
         Slog.i(TAG, "Sensor values:");
-        dumpSensorValues("Hall Sensor", mHallSensor, mLastHallSensorEvent);
-        dumpSensorValues("Hinge Angle Sensor", mHingeAngleSensor, mLastHingeAngleSensorEvent);
+        dumpSensorValues(mHallSensor, mLastHallSensorEvent);
+        dumpSensorValues(mHingeAngleSensor, mLastHingeAngleSensorEvent);
         Slog.i(TAG, "isScreenOn: " + isScreenOn());
     }
 
     @GuardedBy("mLock")
-    private void dumpSensorValues(String sensorType, Sensor sensor, @Nullable SensorEvent event) {
+    private void dumpSensorValues(Sensor sensor, @Nullable SensorEvent event) {
+        Slog.i(TAG, toSensorValueString(sensor, event));
+    }
+
+    private String toSensorValueString(Sensor sensor, @Nullable SensorEvent event) {
         String sensorString = sensor == null ? "null" : sensor.getName();
         String eventValues = event == null ? "null" : Arrays.toString(event.values);
-        Slog.i(TAG, sensorType + " : " + sensorString + " : " + eventValues);
+        return sensorString + " : " + eventValues;
     }
 
     @Override
@@ -414,6 +423,34 @@
         }
     }
 
+    @Override
+    public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        writer.println("FoldableDeviceStateProvider");
+
+        synchronized (mLock) {
+            writer.println("  mLastReportedState = " + mLastReportedState);
+            writer.println("  mPowerSaveModeEnabled = " + mPowerSaveModeEnabled);
+            writer.println("  mThermalStatus = " + mThermalStatus);
+            writer.println("  mLastHingeAngleSensorEvent = " +
+                    toSensorValueString(mHingeAngleSensor, mLastHingeAngleSensorEvent));
+            writer.println("  mLastHallSensorEvent = " +
+                    toSensorValueString(mHallSensor, mLastHallSensorEvent));
+        }
+
+        writer.println();
+        writer.println("  Predicates:");
+
+        for (int i = 0; i < mConfigurations.length; i++) {
+            final DeviceStateConfiguration configuration = mConfigurations[i];
+            final Predicate<FoldableDeviceStateProvider> predicate =
+                    configuration.mActiveStatePredicate;
+
+            if (predicate instanceof Dumpable dumpable) {
+                dumpable.dump(writer, /* args= */ null);
+            }
+        }
+    }
+
     /**
      * Configuration for a single device state, contains information about the state like
      * identifier, name, flags and a predicate that should return true if the state should
diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java
index fd21a32..2359422c9 100644
--- a/services/java/com/android/server/SystemConfigService.java
+++ b/services/java/com/android/server/SystemConfigService.java
@@ -22,6 +22,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.SignedPackage;
+import android.content.pm.SignedPackageParcel;
 import android.os.Binder;
 import android.os.ISystemConfig;
 import android.util.ArrayMap;
@@ -119,6 +121,26 @@
                             pmi.canQueryPackage(Binder.getCallingUid(), preventUserDisablePackage))
                     .collect(toList());
         }
+
+        @Override
+        public List<SignedPackageParcel> getEnhancedConfirmationTrustedPackages() {
+            getContext().enforceCallingOrSelfPermission(
+                    Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES,
+                    "Caller must hold " + Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES);
+
+            return SystemConfig.getInstance().getEnhancedConfirmationTrustedPackages().stream()
+                    .map(SignedPackage::getData).toList();
+        }
+
+        @Override
+        public List<SignedPackageParcel> getEnhancedConfirmationTrustedInstallers() {
+            getContext().enforceCallingOrSelfPermission(
+                    Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES,
+                    "Caller must hold " + Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES);
+
+            return SystemConfig.getInstance().getEnhancedConfirmationTrustedInstallers().stream()
+                    .map(SignedPackage::getData).toList();
+        }
     };
 
     public SystemConfigService(Context context) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2b8bcc7..b79d20a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -269,6 +269,8 @@
             "com.android.server.backup.BackupManagerService$Lifecycle";
     private static final String APPWIDGET_SERVICE_CLASS =
             "com.android.server.appwidget.AppWidgetService";
+    private static final String ARC_NETWORK_SERVICE_CLASS =
+            "com.android.server.arc.net.ArcNetworkService";
     private static final String ARC_PERSISTENT_DATA_BLOCK_SERVICE_CLASS =
             "com.android.server.arc.persistent_data_block.ArcPersistentDataBlockService";
     private static final String ARC_SYSTEM_HEALTH_SERVICE =
@@ -2069,13 +2071,24 @@
             if (context.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_WIFI)) {
                 // Wifi Service must be started first for wifi-related services.
-                t.traceBegin("StartWifi");
-                mSystemServiceManager.startServiceFromJar(
-                        WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
-                t.traceEnd();
-                t.traceBegin("StartWifiScanning");
-                mSystemServiceManager.startServiceFromJar(
-                        WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
+                if (!isArc) {
+                    t.traceBegin("StartWifi");
+                    mSystemServiceManager.startServiceFromJar(
+                            WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
+                    t.traceEnd();
+                    t.traceBegin("StartWifiScanning");
+                    mSystemServiceManager.startServiceFromJar(
+                            WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
+                    t.traceEnd();
+                }
+            }
+
+            // ARC - ArcNetworkService registers the ARC network stack and replaces the
+            // stock WiFi service in both ARC++ container and ARCVM. Always starts the ARC network
+            // stack regardless of whether FEATURE_WIFI is enabled/disabled (b/254755875).
+            if (isArc) {
+                t.traceBegin("StartArcNetworking");
+                mSystemServiceManager.startService(ARC_NETWORK_SERVICE_CLASS);
                 t.traceEnd();
             }
 
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index 6fff012..8ad557c 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -58,10 +58,10 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -1043,6 +1043,27 @@
                 UserHandle.SYSTEM).getArchiveState()).isEqualTo(archiveState);
     }
 
+    @RequiresFlagsEnabled(Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+    @Test
+    public void testWriteReadAppMetadataSource() {
+        Settings settings = makeSettings();
+        PackageSetting packageSetting = createPackageSetting(PACKAGE_NAME_1);
+        packageSetting.setAppId(Process.FIRST_APPLICATION_UID);
+        packageSetting.setPkg(PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed()
+                .setUid(packageSetting.getAppId())
+                .hideAsFinal());
+
+        packageSetting.setAppMetadataSource(PackageManager.APP_METADATA_SOURCE_INSTALLER);
+        settings.mPackages.put(PACKAGE_NAME_1, packageSetting);
+
+        settings.writeLPr(computer, /*sync=*/true);
+        settings.mPackages.clear();
+
+        assertThat(settings.readLPw(computer, createFakeUsers()), is(true));
+        assertThat(settings.getPackageLPr(PACKAGE_NAME_1).getAppMetadataSource(),
+                is(PackageManager.APP_METADATA_SOURCE_INSTALLER));
+    }
+
     @Test
     public void testPackageRestrictionsDistractionFlagsDefault() {
         final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java b/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java
index 9a143d5..1e81951 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java
@@ -189,7 +189,7 @@
         assertThat(response.geoidHeightMeters).isWithin(2).of(-5.0622);
         assertThat(response.geoidHeightErrorMeters).isGreaterThan(0f);
         assertThat(response.geoidHeightErrorMeters).isLessThan(1f);
-        assertThat(response.expirationDistanceMeters).isWithin(1).of(-6.33);
+        assertThat(response.expirationDistanceMeters).isWithin(1).of(120490);
         assertThat(response.additionalGeoidHeightErrorMeters).isGreaterThan(0f);
         assertThat(response.additionalGeoidHeightErrorMeters).isLessThan(1f);
         assertThat(response.success).isTrue();
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index bf00b75..4535ece 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -270,7 +270,7 @@
         assertThat(value.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)).isEqualTo(PACKAGE);
         assertThat(value.getIntExtra(PackageInstaller.EXTRA_STATUS, 0)).isEqualTo(
                 PackageInstaller.STATUS_FAILURE);
-        assertThat(value.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)).isEqualTo(
+        assertThat(value.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)).contains(
                 String.format("Package %s not found.", PACKAGE));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 7dcfc88..fa39364 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -30,6 +30,7 @@
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertThrows;
 
+import android.annotation.NonNull;
 import android.hardware.devicestate.DeviceStateInfo;
 import android.hardware.devicestate.DeviceStateRequest;
 import android.hardware.devicestate.IDeviceStateManagerCallback;
@@ -52,6 +53,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Optional;
@@ -959,6 +961,10 @@
             }
             onComplete.run();
         }
+
+        @Override
+        public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        }
     }
 
     private static final class TestDeviceStateProvider implements DeviceStateProvider {
@@ -1001,6 +1007,10 @@
         public void setState(int identifier) {
             mListener.onStateChanged(identifier);
         }
+
+        @Override
+        public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
+        }
     }
 
     private static final class TestDeviceStateManagerCallback extends
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 5081198..7053597 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -39,7 +39,6 @@
 import static org.mockito.Mockito.when;
 
 import android.app.PropertyInvalidatedCache;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
@@ -49,8 +48,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.widget.ILockSettingsStateListener;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockSettingsStateListener;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.VerifyCredentialResponse;
 
@@ -412,7 +411,7 @@
         mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS);
         final LockscreenCredential password = newPassword("password");
         setCredential(PRIMARY_USER_ID, password);
-        final ILockSettingsStateListener listener = mockLockSettingsStateListener();
+        final LockSettingsStateListener listener = mock(LockSettingsStateListener.class);
         mLocalService.registerLockSettingsStateListener(listener);
 
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
@@ -429,7 +428,7 @@
         final LockscreenCredential password = newPassword("password");
         setCredential(PRIMARY_USER_ID, password);
         final LockscreenCredential badPassword = newPassword("badPassword");
-        final ILockSettingsStateListener listener = mockLockSettingsStateListener();
+        final LockSettingsStateListener listener = mock(LockSettingsStateListener.class);
         mLocalService.registerLockSettingsStateListener(listener);
 
         assertEquals(VerifyCredentialResponse.RESPONSE_ERROR,
@@ -445,7 +444,7 @@
         final LockscreenCredential password = newPassword("password");
         setCredential(PRIMARY_USER_ID, password);
         final LockscreenCredential badPassword = newPassword("badPassword");
-        final ILockSettingsStateListener listener = mockLockSettingsStateListener();
+        final LockSettingsStateListener listener = mock(LockSettingsStateListener.class);
 
         mLocalService.registerLockSettingsStateListener(listener);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
@@ -599,12 +598,4 @@
             assertNotEquals(0, mGateKeeperService.getSecureUserId(userId));
         }
     }
-
-    private ILockSettingsStateListener mockLockSettingsStateListener() {
-        ILockSettingsStateListener listener = mock(ILockSettingsStateListener.Stub.class);
-        IBinder binder = mock(IBinder.class);
-        when(binder.isBinderAlive()).thenReturn(true);
-        when(listener.asBinder()).thenReturn(binder);
-        return listener;
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/FileHashCacheTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/FileHashCacheTests.java
new file mode 100644
index 0000000..5df7a5e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/FileHashCacheTests.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 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.net.watchlist;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.annotation.NonNull;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.system.Os;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.HexDump;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * atest frameworks-services -c com.android.server.net.watchlist.FileHashCacheTests
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class FileHashCacheTests {
+
+    private static final String APK_A = "A.apk";
+    private static final String APK_B = "B.apk";
+    private static final String APK_A_CONTENT = "AAA";
+    private static final String APK_A_ALT_CONTENT = "AAA_ALT";
+    private static final String APK_B_CONTENT = "BBB";
+
+    private static final String PERSIST_FILE_NAME_FOR_TEST = "file_hash_cache";
+
+    // Sha256 of "AAA"
+    private static final String APK_A_CONTENT_HASH =
+            "CB1AD2119D8FAFB69566510EE712661F9F14B83385006EF92AEC47F523A38358";
+    // Sha256 of "AAA_ALT"
+    private static final String APK_A_ALT_CONTENT_HASH =
+            "2AB726E3C5B316F4C7507BFCCC3861F0473523D572E0C62BA21601C20693AEF0";
+    // Sha256 of "BBB"
+    private static final String APK_B_CONTENT_HASH =
+            "DCDB704109A454784B81229D2B05F368692E758BFA33CB61D04C1B93791B0273";
+
+    @Before
+    public void setUp() throws Exception {
+        final File persistFile = getFile(PERSIST_FILE_NAME_FOR_TEST);
+        persistFile.delete();
+        FileHashCache.sPersistFileName = persistFile.getAbsolutePath();
+        getFile(APK_A).delete();
+        getFile(APK_B).delete();
+        FileHashCache.sSaveDeferredDelayMillis = 0;
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void testFileHashCache_generic() throws Exception {
+        final File apkA = getFile(APK_A);
+        final File apkB = getFile(APK_B);
+
+        Looper.prepare();
+        FileHashCache fileHashCache = new FileHashCache(new InlineHandler());
+
+        assertFalse(getFile(PERSIST_FILE_NAME_FOR_TEST).exists());
+
+        // No hash for non-existing files.
+        assertNull("Found existing entry in the cache",
+                fileHashCache.getSha256HashFromCache(apkA));
+        assertNull("Found existing entry in the cache",
+                fileHashCache.getSha256HashFromCache(apkB));
+        try {
+            fileHashCache.getSha256Hash(apkA);
+            fail("Not reached");
+        } catch (IOException e) { }
+        try {
+            fileHashCache.getSha256Hash(apkB);
+            fail("Not reached");
+        } catch (IOException e) { }
+
+        assertFalse(getFile(PERSIST_FILE_NAME_FOR_TEST).exists());
+        FileUtils.stringToFile(apkA, APK_A_CONTENT);
+        FileUtils.stringToFile(apkB, APK_B_CONTENT);
+
+        assertEquals(APK_A_CONTENT_HASH, HexDump.toHexString(fileHashCache.getSha256Hash(apkA)));
+        assertTrue(getFile(PERSIST_FILE_NAME_FOR_TEST).exists());
+        assertEquals(APK_B_CONTENT_HASH, HexDump.toHexString(fileHashCache.getSha256Hash(apkB)));
+        assertEquals(APK_A_CONTENT_HASH,
+                HexDump.toHexString(fileHashCache.getSha256HashFromCache(apkA)));
+        assertEquals(APK_B_CONTENT_HASH,
+                HexDump.toHexString(fileHashCache.getSha256HashFromCache(apkB)));
+
+        // Recreate handler. It should read persistent state.
+        fileHashCache = new FileHashCache(new InlineHandler());
+        assertEquals(APK_A_CONTENT_HASH,
+                HexDump.toHexString(fileHashCache.getSha256HashFromCache(apkA)));
+        assertEquals(APK_B_CONTENT_HASH,
+                HexDump.toHexString(fileHashCache.getSha256HashFromCache(apkB)));
+
+        // Modify one APK. Cache entry should be invalidated. Make sure that FS timestamp resolution
+        // allows us to detect update.
+        final long before = Os.stat(apkA.getAbsolutePath()).st_ctime;
+        do {
+            FileUtils.stringToFile(apkA, APK_A_ALT_CONTENT);
+        } while (android.system.Os.stat(apkA.getAbsolutePath()).st_ctime == before);
+
+        assertNull("Found stale entry in the cache", fileHashCache.getSha256HashFromCache(apkA));
+        assertEquals(APK_A_ALT_CONTENT_HASH,
+                HexDump.toHexString(fileHashCache.getSha256Hash(apkA)));
+    }
+
+    // Helper handler that executes tasks inline in context of current thread if time is good for
+    // this.
+    private static class InlineHandler extends Handler {
+        @Override
+        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+            if (SystemClock.uptimeMillis() >= uptimeMillis && getLooper().isCurrentThread()) {
+                dispatchMessage(msg);
+                return true;
+            }
+            return super.sendMessageAtTime(msg, uptimeMillis);
+        }
+    }
+
+    private File getFile(@NonNull String name) {
+        return new File(InstrumentationRegistry.getContext().getFilesDir(), name);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index aca96ad..03cdbbd 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -21,8 +21,13 @@
 import static org.junit.Assert.assertEquals;
 import static org.testng.Assert.expectThrows;
 
+import android.content.pm.Signature;
+import android.content.pm.SignedPackage;
 import android.os.Build;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -68,6 +73,8 @@
 
     @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
 
+    @Rule public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Before
     public void setUp() throws Exception {
         mSysConfig = new SystemConfigTestClass();
@@ -696,6 +703,43 @@
         assertThat(mSysConfig.getSystemAppUpdateOwnerPackageName("com.foo")).isNull();
     }
 
+    /**
+     * Tests that SystemConfig::getEnhancedConfirmationTrustedInstallers correctly parses a list of
+     * SignedPackage objects.
+     */
+    @Test
+    @RequiresFlagsEnabled(
+            android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+    public void getEnhancedConfirmationTrustedInstallers_returnsTrustedInstallers()
+            throws IOException {
+        String pkgName = "com.example.app";
+        String certificateDigestStr = "E9:7A:BC:2C:D1:CA:8D:58:6A:57:0B:8C:F8:60:AA:D2:"
+                + "8D:13:30:2A:FB:C9:00:2C:5D:53:B2:6C:09:A4:85:A0";
+
+        byte[] certificateDigest = new Signature(certificateDigestStr).toByteArray();
+        String contents = "<config>"
+                + "<" + "enhanced-confirmation-trusted-installer" + " "
+                + "package=\"" + pkgName + "\""
+                + " sha256-cert-digest=\"" + certificateDigestStr + "\""
+                + "/>"
+                + "</config>";
+
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "enhanced-confirmation.xml", contents);
+        readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        ArraySet<SignedPackage> actualTrustedInstallers =
+                mSysConfig.getEnhancedConfirmationTrustedInstallers();
+
+        assertThat(actualTrustedInstallers.size()).isEqualTo(1);
+        SignedPackage actual = actualTrustedInstallers.stream().findFirst().orElseThrow();
+        SignedPackage expected = new SignedPackage(pkgName, certificateDigest);
+
+        assertThat(actual.getCertificateDigest()).isEqualTo(expected.getCertificateDigest());
+        assertThat(actual.getPkgName()).isEqualTo(expected.getPkgName());
+        assertThat(actual).isEqualTo(expected);
+    }
+
     private void parseSharedLibraries(String contents) throws IOException {
         File folder = createTempSubfolder("permissions_folder");
         createTempFile(folder, "permissions.xml", contents);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ef0ac33..8261dee 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1254,6 +1254,11 @@
         verify(mAlarmManager).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture());
         assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME,
                 captor.getValue().getIntent().getPackage());
+
+        mService.cancelScheduledTimeoutLocked(r);
+        verify(mAlarmManager).cancel(captor.capture());
+        assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME,
+                captor.getValue().getIntent().getPackage());
     }
 
     @Test
@@ -11681,7 +11686,7 @@
 
         // style + self managed call - bypasses block
         when(mTelecomManager.isInSelfManagedCall(
-                r.getSbn().getPackageName(), r.getUser())).thenReturn(true);
+                r.getSbn().getPackageName(), r.getUser(), true)).thenReturn(true);
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
                 r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
 
@@ -11764,7 +11769,7 @@
         // style + self managed call - bypasses block
         mService.clearNotifications();
         reset(mUsageStats);
-        when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), r.getUser()))
+        when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), r.getUser(), true))
                 .thenReturn(true);
 
         mService.addEnqueuedNotification(r);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 670d097..130a8ca 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -66,6 +66,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
@@ -114,11 +115,13 @@
                     NotificationManager.IMPORTANCE_UNSPECIFIED);
     private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
 
-    private static final long[] CUSTOM_VIBRATION = new long[] {
+    private static final long[] CUSTOM_NOTIFICATION_VIBRATION = new long[] {
             300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
             300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
             300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400 };
-    private static final long[] CUSTOM_CHANNEL_VIBRATION = new long[] {300, 400, 300, 400 };
+    private static final long[] CUSTOM_CHANNEL_VIBRATION_PATTERN = new long[] {300, 400, 300, 400 };
+    private static final VibrationEffect CUSTOM_CHANNEL_VIBRATION_EFFECT =
+            VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
     private static final Uri CUSTOM_SOUND = Settings.System.DEFAULT_ALARM_ALERT_URI;
     private static final AudioAttributes CUSTOM_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
@@ -135,6 +138,7 @@
         MockitoAnnotations.initMocks(this);
 
         when(mMockContext.getSystemService(eq(Vibrator.class))).thenReturn(mVibrator);
+        when(mVibrator.areVibrationFeaturesSupported(any())).thenReturn(true);
         final Resources res = mContext.getResources();
         when(mMockContext.getResources()).thenReturn(res);
         when(mMockContext.getPackageManager()).thenReturn(mPm);
@@ -168,8 +172,8 @@
             if (defaultVibration) {
                 defaults |= Notification.DEFAULT_VIBRATE;
             } else {
-                builder.setVibrate(CUSTOM_VIBRATION);
-                channel.setVibrationPattern(CUSTOM_CHANNEL_VIBRATION);
+                builder.setVibrate(CUSTOM_NOTIFICATION_VIBRATION);
+                channel.setVibrationPattern(CUSTOM_CHANNEL_VIBRATION_PATTERN);
             }
         }
         if (lights) {
@@ -217,26 +221,39 @@
         return new StatusBarNotification(mPkg, mPkg, id1, tag1, uid, uid, n, mUser, null, uid);
     }
 
+    private StatusBarNotification getNotification(
+            long[] channelVibrationPattern,
+            VibrationEffect channelVibrationEffect,
+            boolean insistent) {
+        if (channelVibrationPattern != null) {
+            channel.setVibrationPattern(channelVibrationPattern);
+        } else if (channelVibrationEffect != null) {
+            channel.setVibrationEffect(channelVibrationEffect);
+        }
 
-    private StatusBarNotification getInsistentNotification(boolean defaultVibration) {
         final Builder builder = new Builder(mMockContext)
                 .setContentTitle("foo")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .setPriority(Notification.PRIORITY_HIGH);
-        int defaults = 0;
-        if (defaultVibration) {
-            defaults |= Notification.DEFAULT_VIBRATE;
-        } else {
-            builder.setVibrate(CUSTOM_VIBRATION);
-            channel.setVibrationPattern(CUSTOM_CHANNEL_VIBRATION);
-        }
-        builder.setDefaults(defaults);
-        builder.setFlag(Notification.FLAG_INSISTENT, true);
+                .setPriority(Notification.PRIORITY_HIGH)
+                .setVibrate(CUSTOM_NOTIFICATION_VIBRATION)
+                .setFlag(Notification.FLAG_INSISTENT, insistent);
 
         Notification n = builder.build();
         return new StatusBarNotification(mPkg, mPkg, id1, tag1, uid, uid, n, mUser, null, uid);
     }
 
+    private StatusBarNotification getNotification(
+            VibrationEffect channelVibrationEffect, boolean insistent) {
+        return getNotification(
+                /* channelVibrationPattern= */ null, channelVibrationEffect, insistent);
+    }
+
+    private StatusBarNotification getNotification(
+            long[] channelVibrationPattern, boolean insistent) {
+        return getNotification(
+                channelVibrationPattern, /* channelVibrationEffect= */ null, insistent);
+    }
+
     private StatusBarNotification getMessagingStyleNotification() {
         return getMessagingStyleNotification(mPkg);
     }
@@ -344,7 +361,7 @@
 
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertEquals(VibratorHelper.createWaveformVibration(
-                CUSTOM_VIBRATION, /* insistent= */ false), record.getVibration());
+                CUSTOM_NOTIFICATION_VIBRATION, /* insistent= */ false), record.getVibration());
     }
 
     @Test
@@ -358,30 +375,137 @@
 
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
         assertNotEquals(VibratorHelper.createWaveformVibration(
-                CUSTOM_VIBRATION, /* insistent= */ false), record.getVibration());
+                CUSTOM_NOTIFICATION_VIBRATION, /* insistent= */ false), record.getVibration());
     }
 
     @Test
-    public void testVibration_custom_upgradeUsesChannel() {
+    public void testVibration_customPattern_nonInsistent_usesCustomPattern() {
         channel.enableVibration(true);
-        // post upgrade, custom vibration.
-        StatusBarNotification sbn = getNotification(PKG_O, false /* noisy */,
-                false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */,
-                false /* lights */, false /* defaultLights */, null /* group */);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ false);
 
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
         assertEquals(VibratorHelper.createWaveformVibration(
-                CUSTOM_CHANNEL_VIBRATION, /* insistent= */ false), record.getVibration());
+                CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ false), record.getVibration());
     }
 
     @Test
-    public void testVibration_insistent_createsInsistentVibrationEffect() {
+    public void testVibration_customPattern_insistent_createsInsistentEffect() {
         channel.enableVibration(true);
-        StatusBarNotification sbn = getInsistentNotification(false /* defaultBuzz */);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ true);
 
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
         assertEquals(VibratorHelper.createWaveformVibration(
-                CUSTOM_CHANNEL_VIBRATION, /* insistent= */ true), record.getVibration());
+                CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ true), record.getVibration());
+    }
+
+    @Test
+    public void testVibration_customEffect_flagNotEnabled_usesDefaultEffect() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        channel.enableVibration(true);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_EFFECT, /* insistent= */ false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        VibrationEffect effect = record.getVibration();
+        assertNotEquals(effect, CUSTOM_CHANNEL_VIBRATION_EFFECT);
+        assertNotNull(effect);
+    }
+
+    @Test
+    public void testVibration_customEffect_effectNotSupported_usesDefaultEffect() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        when(mVibrator.areVibrationFeaturesSupported(any())).thenReturn(false);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_EFFECT, /* insistent= */ false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        VibrationEffect effect = record.getVibration();
+        assertNotEquals(effect, CUSTOM_CHANNEL_VIBRATION_EFFECT);
+        assertNotNull(effect);
+    }
+
+    @Test
+    public void testVibration_customNonRepeatingEffect_nonInsistent_usesCustomEffect() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_EFFECT, /* insistent= */ false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        assertEquals(CUSTOM_CHANNEL_VIBRATION_EFFECT, record.getVibration());
+    }
+
+    @Test
+    public void testVibration_customNonRepeatingEffect_insistent_createsInsistentEffect() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_EFFECT, /* insistent= */ true);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        VibrationEffect repeatingEffect =
+                CUSTOM_CHANNEL_VIBRATION_EFFECT
+                        .applyRepeatingIndefinitely(true, /* loopDelayMs= */ 0);
+        assertEquals(repeatingEffect, record.getVibration());
+    }
+
+    @Test
+    public void testVibration_customRepeatingEffect_nonInsistent_createsNonRepeatingCustomEffect() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        VibrationEffect repeatingEffect =
+                CUSTOM_CHANNEL_VIBRATION_EFFECT
+                        .applyRepeatingIndefinitely(true, /* loopDelayMs= */ 0);
+        StatusBarNotification sbn = getNotification(repeatingEffect, /* insistent= */ false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        assertEquals(CUSTOM_CHANNEL_VIBRATION_EFFECT, record.getVibration());
+    }
+
+    @Test
+    public void testVibration_customRepeatingEffect_insistent_usesCustomEffect() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        VibrationEffect repeatingEffect =
+                CUSTOM_CHANNEL_VIBRATION_EFFECT
+                        .applyRepeatingIndefinitely(true, /* loopDelayMs= */ 0);
+        StatusBarNotification sbn = getNotification(repeatingEffect, /* insistent= */ true);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        assertEquals(repeatingEffect, record.getVibration());
+    }
+
+    @Test
+    public void testVibration_noCustomVibration_vibrationEnabled_usesDefaultVibration() {
+        channel.enableVibration(true);
+        StatusBarNotification sbn = getNotification(
+                /* channelVibrationPattern= */ null,
+                /* channelVibrationEffect= */ null,
+                /* insistent= */ false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        assertNotNull(record.getVibration());
+    }
+
+    @Test
+    public void testVibration_noCustomVibration_vibrationNotEnabled_usesNoVibration() {
+        channel.enableVibration(false);
+        StatusBarNotification sbn = getNotification(
+                /* channelVibrationPattern= */ null,
+                /* channelVibrationEffect= */ null,
+                /* insistent= */ false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        assertNull(record.getVibration());
+    }
+
+    @Test
+    public void testVibration_customVibration_vibrationNotEnabled_usesNoVibration() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+        StatusBarNotification sbn = getNotification(
+                CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ false);
+        channel.enableVibration(false);
+
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+        assertNull(record.getVibration());
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 5ddac03..8b55778 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -391,6 +391,7 @@
         assertEquals(expected.getSound(), actual.getSound());
         assertEquals(expected.canBypassDnd(), actual.canBypassDnd());
         assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern()));
+        assertEquals(expected.getVibrationEffect(), actual.getVibrationEffect());
         assertEquals(expected.getGroup(), actual.getGroup());
         assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes());
         assertEquals(expected.getLightColor(), actual.getLightColor());
@@ -410,6 +411,7 @@
         assertEquals(parent.getSound(), actual.getSound());
         assertEquals(parent.canBypassDnd(), actual.canBypassDnd());
         assertTrue(Arrays.equals(parent.getVibrationPattern(), actual.getVibrationPattern()));
+        assertEquals(parent.getVibrationEffect(), actual.getVibrationEffect());
         assertEquals(parent.getGroup(), actual.getGroup());
         assertEquals(parent.getAudioAttributes(), actual.getAudioAttributes());
         assertEquals(parent.getLightColor(), actual.getLightColor());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 95850ac..f63ff6e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1003,7 +1003,13 @@
         dc.computeScreenConfiguration(config, ROTATION_0);
         dc.onRequestedOverrideConfigurationChanged(config);
         assertEquals(Configuration.ORIENTATION_LANDSCAPE, config.orientation);
-        assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getNaturalOrientation());
+        assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getNaturalConfigurationOrientation());
+        window.setOverrideOrientation(SCREEN_ORIENTATION_NOSENSOR);
+        assertEquals(Configuration.ORIENTATION_LANDSCAPE,
+                window.getRequestedConfigurationOrientation());
+        // Note that getNaturalOrientation is based on logical display size. So it is portrait if
+        // the display width equals to height.
+        assertEquals(Configuration.ORIENTATION_PORTRAIT, dc.getNaturalOrientation());
     }
 
     @Test
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 74aabe1..aa9c0c8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -94,7 +94,6 @@
 import com.android.server.wm.TaskOrganizerController.PendingTaskEvent;
 import com.android.window.flags.Flags;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -153,13 +152,6 @@
         return createTask(mDisplayContent);
     }
 
-    @Before
-    public void setUp() {
-        // We defer callbacks since we need to adjust task surface visibility, but for these tests,
-        // just run the callbacks synchronously
-        mWm.mAtmService.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer((r) -> r.run());
-    }
-
     @Test
     public void testAppearVanish() throws RemoteException {
         final ITaskOrganizer organizer = registerMockOrganizer();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 0ee78e3..28e03bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1791,7 +1791,6 @@
         TestStartingWindowOrganizer(ActivityTaskManagerService service) {
             mAtm = service;
             mWMService = mAtm.mWindowManager;
-            mAtm.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer(Runnable::run);
             mAtm.mTaskOrganizerController.registerTaskOrganizer(this);
         }
 
diff --git a/telecomm/OWNERS b/telecomm/OWNERS
index b57b7c7..bb2ac51 100644
--- a/telecomm/OWNERS
+++ b/telecomm/OWNERS
@@ -6,4 +6,5 @@
 rgreenwalt@google.com
 grantmenke@google.com
 pmadapurmath@google.com
-tjstuart@google.com
\ No newline at end of file
+tjstuart@google.com
+huiwang@google.com
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 9ee48d8..1df6cf7 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -1017,9 +1017,11 @@
     /**
      * Connection event used to communicate a {@link android.telephony.CallQuality} report from
      * telephony to Telecom for relaying to
-     * {@link DiagnosticCall#onCallQualityReceived(CallQuality)}.
+     * {@link CallDiagnostics#onCallQualityReceived(CallQuality)}.
      * @hide
      */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
     public static final String EVENT_CALL_QUALITY_REPORT =
             "android.telecom.event.CALL_QUALITY_REPORT";
 
@@ -1028,6 +1030,8 @@
      * {@link android.telephony.CallQuality} data.
      * @hide
      */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
     public static final String EXTRA_CALL_QUALITY_REPORT =
             "android.telecom.extra.CALL_QUALITY_REPORT";
 
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 2c6e1e4..e62bd90 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1359,8 +1359,9 @@
 
     /**
      * Returns a list of {@link PhoneAccountHandle}s which can be used to make and receive phone
-     * calls. The returned list includes those accounts which have been explicitly enabled by
-     * the user or enabled by other users but visible to the user.
+     * calls. The returned list includes those accounts which have been explicitly enabled.
+     * In contrast to {@link #getCallCapablePhoneAccounts}, this also includes accounts from
+     * the calling user's {@link android.os.UserManager#getUserProfiles} profile group.
      *
      * @see #EXTRA_PHONE_ACCOUNT_HANDLE
      * @return A list of {@code PhoneAccountHandle} objects.
@@ -2752,17 +2753,23 @@
      *
      * @param packageName the package name of the app to check calls for.
      * @param userHandle the user handle on which to check for calls.
+     * @param hasCrossUserAccess indicates if calls should be detected across all users.
      * @return {@code true} if there are ongoing calls, {@code false} otherwise.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            Manifest.permission.INTERACT_ACROSS_USERS
+    })
     public boolean isInSelfManagedCall(@NonNull String packageName,
-            @NonNull UserHandle userHandle) {
+            @NonNull UserHandle userHandle, boolean hasCrossUserAccess) {
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
                 return service.isInSelfManagedCall(packageName, userHandle,
-                        mContext.getOpPackageName());
+                        mContext.getOpPackageName(), hasCrossUserAccess);
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException isInSelfManagedCall: " + e);
                 e.rethrowFromSystemServer();
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index f1bfd22..412e827 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -395,7 +395,7 @@
      * @see TelecomServiceImpl#isInSelfManagedCall
      */
     boolean isInSelfManagedCall(String packageName, in UserHandle userHandle,
-        String callingPackage);
+        String callingPackage, boolean hasCrossUserAccess);
 
     /**
      * @see TelecomServiceImpl#addCall
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 287aa65..7607c64 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -15,4 +15,4 @@
 per-file CarrierConfigManager.java=amruthr@google.com,tgunn@google.com,rgreenwalt@google.com,satk@google.com
 
 #Domain Selection is jointly owned, add additional owners for domain selection specific files
-per-file TransportSelectorCallback.java,WwanSelectorCallback.java,DomainSelectionService.java,DomainSelectionService.aidl,DomainSelector.java,EmergencyRegResult.java,EmergencyRegResult.aidl,IDomainSelectionServiceController.aidl,IDomainSelector.aidl,ITransportSelectorCallback.aidl,ITransportSelectorResultCallback.aidl,IWwanSelectorCallback.aidl,IWwanSelectorResultCallback.aidl=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com
+per-file TransportSelectorCallback.java,WwanSelectorCallback.java,DomainSelectionService.java,DomainSelectionService.aidl,DomainSelector.java,EmergencyRegResult.java,EmergencyRegResult.aidl,IDomainSelectionServiceController.aidl,IDomainSelector.aidl,ITransportSelectorCallback.aidl,ITransportSelectorResultCallback.aidl,IWwanSelectorCallback.aidl,IWwanSelectorResultCallback.aidl=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com,jdyou@google.com
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e5a94c3..26c3025 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9692,6 +9692,19 @@
             "remove_satellite_plmn_in_manual_network_scan_bool";
 
     /**
+     * Determine whether to override roaming Wi-Fi Calling preference when device is connected to
+     * non-terrestrial network.
+     * {@code true}  - roaming preference cannot be changed by user independently.
+     *                 Roaming preference is overridden to
+     *                 {@link com.android.ims.ImsConfig.WfcModeFeatureValueConstants#WIFI_PREFERRED}
+     * {@code false} - roaming preference can be changed by user independently and is not
+     *                 overridden when device is connected to non-terrestrial network.
+     * @hide
+     */
+    public static final String KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL =
+            "override_wfc_roaming_mode_while_using_ntn_bool";
+
+    /**
      * An integer key holds the time interval for refreshing or re-querying the satellite
      * entitlement status from the entitlement server to ensure it is the latest.
      *
@@ -10817,6 +10830,7 @@
         sDefaults.putInt(KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT,
                 CellSignalStrengthLte.USE_RSRP);
         sDefaults.putBoolean(KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true);
+        sDefaults.putBoolean(KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL, true);
         sDefaults.putInt(KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT, 30);
         sDefaults.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index eb7e67d..1749545 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1927,34 +1927,25 @@
      * Then for SDK 35+, if the caller identity is personal profile, then this will return
      * subscription 1 only and vice versa.
      *
-     * <p> The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
-     * {@link SubscriptionInfo#getSubscriptionId}.
+     * <p> Returned records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
+     * {@link SubscriptionInfo#getSubscriptionId}. Beginning with Android SDK 35, this method will
+     * never return null.
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see
      * {@link TelephonyManager#hasCarrierPrivileges}).
      *
-     * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
-     * <ul>
-     * <li>
-     * If null is returned the current state is unknown but if a {@link OnSubscriptionsChangedListener}
-     * has been registered {@link OnSubscriptionsChangedListener#onSubscriptionsChanged} will be
-     * invoked in the future.
-     * </li>
-     * <li>
-     * If the list is empty then there are no {@link SubscriptionInfo} records currently available.
-     * </li>
-     * <li>
-     * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
-     * then by {@link SubscriptionInfo#getSubscriptionId}.
-     * </li>
-     * </ul>
+     * @return a list of the active {@link SubscriptionInfo} that is visible to the caller. If
+     *         an empty list or null is returned, then there are no active subscriptions that
+     *         are visible to the caller. If the number of active subscriptions available to
+     *         any caller changes, then this change will be indicated by
+     *         {@link OnSubscriptionsChangedListener#onSubscriptionsChanged}.
      *
      * @throws UnsupportedOperationException If the device does not have
-     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *         {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
+    public @Nullable List<SubscriptionInfo> getActiveSubscriptionInfoList() {
         List<SubscriptionInfo> activeList = null;
 
         try {
@@ -1970,6 +1961,8 @@
         if (activeList != null) {
             activeList = activeList.stream().filter(subInfo -> isSubscriptionVisible(subInfo))
                     .collect(Collectors.toList());
+        } else {
+            activeList = Collections.emptyList();
         }
         return activeList;
     }
@@ -1998,12 +1991,7 @@
      *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     public @NonNull List<SubscriptionInfo> getCompleteActiveSubscriptionInfoList() {
-        List<SubscriptionInfo> completeList = getActiveSubscriptionInfoList(
-                /* userVisibleonly */false);
-        if (completeList == null) {
-            completeList = new ArrayList<>();
-        }
-        return completeList;
+        return getActiveSubscriptionInfoList(/* userVisibleonly */ false);
     }
 
     /**
@@ -2032,7 +2020,7 @@
     *
     * @hide
     */
-    public @Nullable List<SubscriptionInfo> getActiveSubscriptionInfoList(boolean userVisibleOnly) {
+    public @NonNull List<SubscriptionInfo> getActiveSubscriptionInfoList(boolean userVisibleOnly) {
         List<SubscriptionInfo> activeList = null;
 
         try {
@@ -2045,11 +2033,13 @@
             // ignore it
         }
 
-        if (!userVisibleOnly || activeList == null) {
-            return activeList;
-        } else {
+        if (activeList == null || activeList.isEmpty()) {
+            return Collections.emptyList();
+        } else if (userVisibleOnly) {
             return activeList.stream().filter(subInfo -> isSubscriptionVisible(subInfo))
                     .collect(Collectors.toList());
+        } else {
+            return activeList;
         }
     }
 
@@ -2086,7 +2076,7 @@
      * @hide
      */
     @SystemApi
-    public List<SubscriptionInfo> getAvailableSubscriptionInfoList() {
+    public @Nullable List<SubscriptionInfo> getAvailableSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
 
         try {
@@ -2098,7 +2088,7 @@
         } catch (RemoteException ex) {
             // ignore it
         }
-        return result;
+        return (result == null) ? Collections.emptyList() : result;
     }
 
     /**
@@ -2128,7 +2118,7 @@
      * @throws UnsupportedOperationException If the device does not have
      *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
-    public List<SubscriptionInfo> getAccessibleSubscriptionInfoList() {
+    public @Nullable List<SubscriptionInfo> getAccessibleSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
 
         try {
@@ -2139,7 +2129,7 @@
         } catch (RemoteException ex) {
             // ignore it
         }
-        return result;
+        return (result == null) ? Collections.emptyList() : result;
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 89661a4..f82463b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -18709,9 +18709,9 @@
      */
     @SystemApi
     @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
-    public static final class EmergencyCallDiagnosticParams {
+    public static final class EmergencyCallDiagnosticData {
         public static final class Builder {
-            private boolean mCollectTelecomDumpSys;
+            private boolean mCollectTelecomDumpsys;
             private boolean mCollectTelephonyDumpsys;
 
             // If this is set to a value other than -1L, then the logcat collection is enabled.
@@ -18724,9 +18724,9 @@
              * @param collectTelecomDumpsys Determines whether telecom dumpsys should be collected.
              * @return Builder instance corresponding to the configured call diagnostic params.
              */
-            public @NonNull Builder setTelecomDumpSysCollectionEnabled(
+            public @NonNull Builder setTelecomDumpsysCollectionEnabled(
                     boolean collectTelecomDumpsys) {
-                mCollectTelecomDumpSys = collectTelecomDumpsys;
+                mCollectTelecomDumpsys = collectTelecomDumpsys;
                 return this;
             }
 
@@ -18735,7 +18735,7 @@
              * @param collectTelephonyDumpsys Determines if telephony dumpsys should be collected.
              * @return Builder instance corresponding to the configured call diagnostic params.
              */
-            public @NonNull Builder setTelephonyDumpSysCollectionEnabled(
+            public @NonNull Builder setTelephonyDumpsysCollectionEnabled(
                     boolean collectTelephonyDumpsys) {
                 mCollectTelephonyDumpsys = collectTelephonyDumpsys;
                 return this;
@@ -18753,35 +18753,35 @@
             }
 
             /**
-             * Build the EmergencyCallDiagnosticParams from the provided Builder config.
-             * @return {@link EmergencyCallDiagnosticParams} instance from provided builder.
+             * Build the EmergencyCallDiagnosticData from the provided Builder config.
+             * @return {@link EmergencyCallDiagnosticData} instance from provided builder.
              */
-            public @NonNull EmergencyCallDiagnosticParams build() {
-                return new EmergencyCallDiagnosticParams(mCollectTelecomDumpSys,
+            public @NonNull EmergencyCallDiagnosticData build() {
+                return new EmergencyCallDiagnosticData(mCollectTelecomDumpsys,
                         mCollectTelephonyDumpsys, mLogcatStartTimeMillis);
             }
         }
 
-        private boolean mCollectTelecomDumpSys;
+        private boolean mCollectTelecomDumpsys;
         private boolean mCollectTelephonyDumpsys;
         private boolean mCollectLogcat;
         private long mLogcatStartTimeMillis;
 
         private static long sUnsetLogcatStartTime = -1L;
 
-        private EmergencyCallDiagnosticParams(boolean collectTelecomDumpSys,
+        private EmergencyCallDiagnosticData(boolean collectTelecomDumpsys,
                 boolean collectTelephonyDumpsys, long logcatStartTimeMillis) {
-            mCollectTelecomDumpSys = collectTelecomDumpSys;
+            mCollectTelecomDumpsys = collectTelecomDumpsys;
             mCollectTelephonyDumpsys = collectTelephonyDumpsys;
             mLogcatStartTimeMillis = logcatStartTimeMillis;
             mCollectLogcat = logcatStartTimeMillis != sUnsetLogcatStartTime;
         }
 
-        public boolean isTelecomDumpSysCollectionEnabled() {
-            return mCollectTelecomDumpSys;
+        public boolean isTelecomDumpsysCollectionEnabled() {
+            return mCollectTelecomDumpsys;
         }
 
-        public boolean isTelephonyDumpSysCollectionEnabled() {
+        public boolean isTelephonyDumpsysCollectionEnabled() {
             return mCollectTelephonyDumpsys;
         }
 
@@ -18796,12 +18796,12 @@
 
         @Override
         public String toString() {
-            return "EmergencyCallDiagnosticParams{" +
-                    "mCollectTelecomDumpSys=" + mCollectTelecomDumpSys +
-                    ", mCollectTelephonyDumpsys=" + mCollectTelephonyDumpsys +
-                    ", mCollectLogcat=" + mCollectLogcat +
-                    ", mLogcatStartTimeMillis=" + mLogcatStartTimeMillis +
-                    '}';
+            return "EmergencyCallDiagnosticData{"
+                    + "mCollectTelecomDumpsys=" + mCollectTelecomDumpsys
+                    + ", mCollectTelephonyDumpsys=" + mCollectTelephonyDumpsys
+                    + ", mCollectLogcat=" + mCollectLogcat
+                    + ", mLogcatStartTimeMillis=" + mLogcatStartTimeMillis
+                    + '}';
         }
     }
 
@@ -18809,7 +18809,7 @@
      * Request telephony to persist state for debugging emergency call failures.
      *
      * @param dropboxTag Tag to use when persisting data to dropbox service.
-     * @param params Parameters controlling what is collected.
+     * @param data Parameters controlling what is collected in the diagnostics.
      *
      * @hide
      */
@@ -18817,7 +18817,7 @@
     @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
     @RequiresPermission(android.Manifest.permission.DUMP)
     public void persistEmergencyCallDiagnosticData(@NonNull String dropboxTag,
-            @NonNull EmergencyCallDiagnosticParams params) {
+            @NonNull EmergencyCallDiagnosticData data) {
         try {
             ITelephony telephony = ITelephony.Stub.asInterface(
                     TelephonyFrameworkInitializer
@@ -18826,10 +18826,10 @@
                             .get());
             if (telephony != null) {
                 telephony.persistEmergencyCallDiagnosticData(dropboxTag,
-                        params.isLogcatCollectionEnabled(),
-                        params.getLogcatCollectionStartTimeMillis(),
-                        params.isTelecomDumpSysCollectionEnabled(),
-                        params.isTelephonyDumpSysCollectionEnabled());
+                        data.isLogcatCollectionEnabled(),
+                        data.getLogcatCollectionStartTimeMillis(),
+                        data.isTelecomDumpsysCollectionEnabled(),
+                        data.isTelephonyDumpsysCollectionEnabled());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error while persistEmergencyCallDiagnosticData: " + e);
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
index 5434c82..5f1bc87 100644
--- a/tests/Input/src/com/android/test/input/InputDeviceTest.java
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -67,8 +67,14 @@
         assertEquals("keyCharacterMap not equal", keyCharacterMap, outKeyCharacterMap);
 
         for (int j = 0; j < device.getMotionRanges().size(); j++) {
-            assertMotionRangeEquals(device.getMotionRanges().get(j),
-                    outDevice.getMotionRanges().get(j));
+            InputDevice.MotionRange motionRange = device.getMotionRanges().get(j);
+            assertMotionRangeEquals(motionRange, outDevice.getMotionRanges().get(j));
+
+            int axis = motionRange.getAxis();
+            int source = motionRange.getSource();
+            assertEquals(
+                    device.getViewBehavior().shouldSmoothScroll(axis, source),
+                    outDevice.getViewBehavior().shouldSmoothScroll(axis, source));
         }
     }
 
@@ -93,7 +99,8 @@
                 .setHasBattery(true)
                 .setKeyboardLanguageTag("en-US")
                 .setKeyboardLayoutType("qwerty")
-                .setUsiVersion(new HostUsiVersion(2, 0));
+                .setUsiVersion(new HostUsiVersion(2, 0))
+                .setShouldSmoothScroll(true);
 
         for (int i = 0; i < 30; i++) {
             deviceBuilder.addMotionRange(
diff --git a/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h
index 5d3abc6..9276402 100644
--- a/tools/aapt/ApkBuilder.h
+++ b/tools/aapt/ApkBuilder.h
@@ -44,7 +44,7 @@
     android::status_t createSplitForConfigs(const std::set<ConfigDescription>& configs);
 
     /**
-     * Adds a file to be written to the final APK. It's name must not collide
+     * Adds a file to be written to the final APK. Its name must not collide
      * with that of any files previously added. When a Split APK is being
      * generated, duplicates can exist as long as they are in different splits
      * (resources.arsc, AndroidManifest.xml).
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 2f2ef92..66a0510 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -187,7 +187,7 @@
         "       be loaded alongside the base APK at runtime.\n"
         "   --feature-of\n"
         "       Builds a split APK that is a feature of the apk specified here. Resources\n"
-        "       in the base APK can be referenced from the the feature APK.\n"
+        "       in the base APK can be referenced from the feature APK.\n"
         "   --feature-after\n"
         "       An app can have multiple Feature Split APKs which must be totally ordered.\n"
         "       If --feature-of is specified, this flag specifies which Feature Split APK\n"