Merge "Don't lock VibrationStepConductor state only executed by VibrationThread." into tm-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index b843dca..cae6cdc 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -25,6 +25,7 @@
 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_APP_UPDATE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_USER_FLAG_INTERACTION;
 import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
 import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
@@ -1581,7 +1582,11 @@
                     // Only user force can bypass the delay restriction. If the user forced the
                     // app into the RESTRICTED bucket, then a toast confirming the action
                     // shouldn't be surprising.
-                    if (Build.IS_DEBUGGABLE) {
+                    // Exclude REASON_SUB_FORCED_USER_FLAG_INTERACTION since the RESTRICTED bucket
+                    // isn't directly visible in that flow.
+                    if (Build.IS_DEBUGGABLE
+                            && (reason & REASON_SUB_MASK)
+                            != REASON_SUB_FORCED_USER_FLAG_INTERACTION) {
                         Toast.makeText(mContext,
                                 // Since AppStandbyController sits low in the lock hierarchy,
                                 // make sure not to call out with the lock held.
diff --git a/api/Android.bp b/api/Android.bp
index bbe26b7..40472b7 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -93,7 +93,6 @@
 // Silence reflection warnings. See b/168689341
 metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
 metalava_cmd += " --quiet --no-banner --format=v2 "
-metalava_cmd += " --hide ChangedThrows "
 
 genrule {
     name: "current-api-xml",
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 9c6402a..738b9cf 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -37,7 +37,6 @@
 #include "idmap2/Idmap.h"
 #include "idmap2/LogInfo.h"
 
-using android::Res_value;
 using ::testing::NotNull;
 
 using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index 5a1d808..32b3d13 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -29,8 +29,6 @@
 #include "idmap2/LogInfo.h"
 #include "idmap2/ResourceMapping.h"
 
-using android::Res_value;
-
 using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
 
 namespace android::idmap2 {
diff --git a/core/api/current.txt b/core/api/current.txt
index 3a6fc59..77efa4d3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -189,6 +189,7 @@
     field public static final String START_VIEW_APP_FEATURES = "android.permission.START_VIEW_APP_FEATURES";
     field public static final String START_VIEW_PERMISSION_USAGE = "android.permission.START_VIEW_PERMISSION_USAGE";
     field public static final String STATUS_BAR = "android.permission.STATUS_BAR";
+    field public static final String SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE = "android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE";
     field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW";
     field public static final String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
     field public static final String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT";
@@ -5559,11 +5560,11 @@
 
   public final class GameState implements android.os.Parcelable {
     ctor public GameState(boolean, int);
-    ctor public GameState(boolean, int, @Nullable String, @NonNull android.os.Bundle);
+    ctor public GameState(boolean, int, int, int);
     method public int describeContents();
-    method @Nullable public String getDescription();
-    method @NonNull public android.os.Bundle getMetadata();
+    method public int getLabel();
     method public int getMode();
+    method public int getQuality();
     method public boolean isLoading();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.GameState> CREATOR;
@@ -5677,6 +5678,7 @@
   }
 
   public class KeyguardManager {
+    method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void addKeyguardLockedStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.KeyguardLockedStateListener);
     method @Deprecated public android.content.Intent createConfirmDeviceCredentialIntent(CharSequence, CharSequence);
     method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult);
     method @Deprecated public boolean inKeyguardRestrictedInputMode();
@@ -5685,6 +5687,7 @@
     method public boolean isKeyguardLocked();
     method public boolean isKeyguardSecure();
     method @Deprecated public android.app.KeyguardManager.KeyguardLock newKeyguardLock(String);
+    method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void removeKeyguardLockedStateListener(@NonNull android.app.KeyguardManager.KeyguardLockedStateListener);
     method public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable android.app.KeyguardManager.KeyguardDismissCallback);
   }
 
@@ -5700,6 +5703,10 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void reenableKeyguard();
   }
 
+  @java.lang.FunctionalInterface public static interface KeyguardManager.KeyguardLockedStateListener {
+    method public void onKeyguardLockedStateChanged(boolean);
+  }
+
   @Deprecated public static interface KeyguardManager.OnKeyguardExitResult {
     method @Deprecated public void onKeyguardExitResult(boolean);
   }
@@ -31791,7 +31798,7 @@
     method public android.os.PowerManager.WakeLock newWakeLock(int, String);
     method @RequiresPermission(android.Manifest.permission.REBOOT) public void reboot(@Nullable String);
     method public void removeThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener);
-    field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
+    field @Deprecated public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
     field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
     field public static final String ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED = "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED";
     field public static final String ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED = "android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED";
@@ -53112,7 +53119,7 @@
     field public static final int RESULT_SHOWN = 2; // 0x2
     field public static final int RESULT_UNCHANGED_HIDDEN = 1; // 0x1
     field public static final int RESULT_UNCHANGED_SHOWN = 0; // 0x0
-    field public static final int SHOW_FORCED = 2; // 0x2
+    field @Deprecated public static final int SHOW_FORCED = 2; // 0x2
     field public static final int SHOW_IMPLICIT = 1; // 0x1
   }
 
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 594f46b..7ec239d 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -413,6 +413,7 @@
 
   public class StorageManager {
     method public long computeStorageCacheBytes(@NonNull java.io.File);
+    method @Nullable public String getCloudMediaProvider();
     method public void notifyAppIoBlocked(@NonNull java.util.UUID, int, int, int);
     method public void notifyAppIoResumed(@NonNull java.util.UUID, int, int, int);
     method public void setCloudMediaProvider(@Nullable String);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 9757b2f..e64392b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2347,6 +2347,8 @@
 
   public abstract class DreamOverlayService extends android.app.Service {
     ctor public DreamOverlayService();
+    method @Nullable public final CharSequence getDreamLabel();
+    method public final boolean isPreviewMode();
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams);
     method public final void requestExit();
@@ -3082,6 +3084,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
     method public boolean hasActiveInputConnection(@Nullable android.view.View);
     method public boolean isInputMethodPickerShown();
+    field public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // 0xcc1a029L
   }
 
 }
diff --git a/core/java/android/app/GameState.java b/core/java/android/app/GameState.java
index 979dd34..fe6e554 100644
--- a/core/java/android/app/GameState.java
+++ b/core/java/android/app/GameState.java
@@ -18,8 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -88,12 +86,11 @@
     // One of the states listed above.
     private final @GameStateMode int mMode;
 
-    // This is a game specific description. For example can be level or scene name.
-    private final @Nullable String mDescription;
+    // A developer-supplied enum, e.g. to indicate level or scene.
+    private final int mLabel;
 
-    // This contains any other game specific parameters not covered by the fields above. It can be
-    // quality parameter data, settings, or game modes.
-    private final @NonNull Bundle mMetaData;
+    // The developer-supplied enum, e.g. to indicate the current quality level.
+    private final int mQuality;
 
     /**
      * Create a GameState with the specified loading status.
@@ -101,29 +98,28 @@
      * @param mode The game state mode of type @GameStateMode.
      */
     public GameState(boolean isLoading, @GameStateMode int mode) {
-        this(isLoading, mode, null, new Bundle());
+        this(isLoading, mode, -1, -1);
     }
 
     /**
      * Create a GameState with the given state variables.
      * @param isLoading Whether the game is in the loading state.
-     * @param mode The game state mode of type @GameStateMode.
-     * @param description An optional description of the state.
-     * @param metaData Optional metadata.
+     * @param mode The game state mode.
+     * @param label An optional developer-supplied enum e.g. for the current level.
+     * @param quality An optional developer-supplied enum, e.g. for the current quality level.
      */
-    public GameState(boolean isLoading, @GameStateMode int mode, @Nullable String description,
-            @NonNull Bundle metaData) {
+    public GameState(boolean isLoading, @GameStateMode int mode, int label, int quality) {
         mIsLoading = isLoading;
         mMode = mode;
-        mDescription = description;
-        mMetaData = metaData;
+        mLabel = label;
+        mQuality = quality;
     }
 
     private GameState(Parcel in) {
         mIsLoading = in.readBoolean();
         mMode = in.readInt();
-        mDescription = in.readString();
-        mMetaData = in.readBundle();
+        mLabel = in.readInt();
+        mQuality = in.readInt();
     }
 
     /**
@@ -141,17 +137,19 @@
     }
 
     /**
-     * @return The state description, or null if one is not set.
+     * @return The developer-supplied enum, e.g. to indicate level or scene. The default value (if
+     * not supplied) is -1.
      */
-    public @Nullable String getDescription() {
-        return mDescription;
+    public int getLabel() {
+        return mLabel;
     }
 
     /**
-     * @return metadata associated with the state.
+     * @return The developer-supplied enum, e.g. to indicate the current quality level. The default
+     * value (if not suplied) is -1.
      */
-    public @NonNull Bundle getMetadata() {
-        return mMetaData;
+    public int getQuality() {
+        return mQuality;
     }
 
     @Override
@@ -163,8 +161,8 @@
     public void writeToParcel(@NonNull Parcel parcel, int flags) {
         parcel.writeBoolean(mIsLoading);
         parcel.writeInt(mMode);
-        parcel.writeString(mDescription);
-        parcel.writeBundle(mMetaData);
+        parcel.writeInt(mLabel);
+        parcel.writeInt(mQuality);
     }
 
     /**
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 7c48a57..fe0edfe 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -510,7 +510,6 @@
     void noteAlarmFinish(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String tag);
     @UnsupportedAppUsage
     int getPackageProcessState(in String packageName, in String callingPackage);
-    void updateDeviceOwner(in String packageName);
 
     // Start of N transactions
     // Start Binder transaction tracking for all applications.
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 9910000..87ba197 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -52,6 +52,7 @@
 import android.view.WindowManagerGlobal;
 
 import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.IKeyguardLockedStateListener;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
 import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
@@ -183,6 +184,10 @@
     })
     @interface LockTypes {}
 
+    // TODO(b/220379118): register only one binder listener and keep a map of listener to executor.
+    private final ArrayMap<KeyguardLockedStateListener, IKeyguardLockedStateListener>
+            mKeyguardLockedStateListeners = new ArrayMap<>();
+
     /**
      * Get an intent to prompt the user to confirm credentials (pin, pattern, password or biometrics
      * if enrolled) for the current user of the device. The caller is expected to launch this
@@ -534,7 +539,7 @@
     /**
      * Return whether the keyguard is currently locked.
      *
-     * @return true if keyguard is locked.
+     * @return {@code true} if the keyguard is locked.
      */
     public boolean isKeyguardLocked() {
         try {
@@ -550,7 +555,7 @@
      *
      * <p>See also {@link #isDeviceSecure()} which ignores SIM locked states.
      *
-     * @return true if a PIN, pattern or password is set or a SIM card is locked.
+     * @return {@code true} if a PIN, pattern or password is set or a SIM card is locked.
      */
     public boolean isKeyguardSecure() {
         try {
@@ -565,7 +570,7 @@
      * keyguard password emergency screen). When in such mode, certain keys,
      * such as the Home key and the right soft keys, don't work.
      *
-     * @return true if in keyguard restricted input mode.
+     * @return {@code true} if in keyguard restricted input mode.
      * @deprecated Use {@link #isKeyguardLocked()} instead.
      */
     public boolean inKeyguardRestrictedInputMode() {
@@ -576,7 +581,7 @@
      * Returns whether the device is currently locked and requires a PIN, pattern or
      * password to unlock.
      *
-     * @return true if unlocking the device currently requires a PIN, pattern or
+     * @return {@code true} if unlocking the device currently requires a PIN, pattern or
      * password.
      */
     public boolean isDeviceLocked() {
@@ -603,7 +608,7 @@
      *
      * <p>See also {@link #isKeyguardSecure} which treats SIM locked states as secure.
      *
-     * @return true if a PIN, pattern or password was set.
+     * @return {@code true} if a PIN, pattern or password was set.
      */
     public boolean isDeviceSecure() {
         return isDeviceSecure(mContext.getUserId());
@@ -762,7 +767,7 @@
     *        as the output of String#getBytes
     * @param complexity - complexity level imposed by the requester
     *        as defined in {@code DevicePolicyManager.PasswordComplexity}
-    * @return true if the password is valid, false otherwise
+    * @return {@code true} if the password is valid, false otherwise
     * @hide
     */
     @RequiresPermission(Manifest.permission.SET_INITIAL_LOCK)
@@ -821,7 +826,7 @@
     *        as the output of String#getBytes
     * @param complexity - complexity level imposed by the requester
     *        as defined in {@code DevicePolicyManager.PasswordComplexity}
-    * @return true if the lock is successfully set, false otherwise
+    * @return {@code true} if the lock is successfully set, false otherwise
     * @hide
     */
     @RequiresPermission(Manifest.permission.SET_INITIAL_LOCK)
@@ -903,8 +908,8 @@
     /**
      * Remove a weak escrow token.
      *
-     * @return true if the given handle refers to a valid weak token previously returned from
-     * {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise.
+     * @return {@code true} if the given handle refers to a valid weak token previously returned
+     * from {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise.
      * @hide
      */
     @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
@@ -944,7 +949,7 @@
     /**
      * Register the given WeakEscrowTokenRemovedListener.
      *
-     * @return true if the listener is registered successfully, return false otherwise.
+     * @return {@code true} if the listener is registered successfully, return false otherwise.
      * @hide
      */
     @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
@@ -982,7 +987,7 @@
     /**
      * Unregister the given WeakEscrowTokenRemovedListener.
      *
-     * @return true if the listener is unregistered successfully, return false otherwise.
+     * @return {@code true} if the listener is unregistered successfully, return false otherwise.
      * @hide
      */
     @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
@@ -1076,4 +1081,61 @@
                 throw new IllegalArgumentException("Unknown lock type " + lockType);
         }
     }
+
+    /**
+     * Listener for keyguard locked state changes.
+     */
+    @FunctionalInterface
+    public interface KeyguardLockedStateListener {
+        /**
+         * Callback function that executes when the keyguard locked state changes.
+         */
+        void onKeyguardLockedStateChanged(boolean isKeyguardLocked);
+    }
+
+    /**
+     * Registers a listener to execute when the keyguard visibility changes.
+     *
+     * @param listener The listener to add to receive keyguard visibility changes.
+     */
+    @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+    public void addKeyguardLockedStateListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull KeyguardLockedStateListener listener) {
+        synchronized (mKeyguardLockedStateListeners) {
+            try {
+                final IKeyguardLockedStateListener innerListener =
+                        new IKeyguardLockedStateListener.Stub() {
+                    @Override
+                    public void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
+                        executor.execute(
+                                () -> listener.onKeyguardLockedStateChanged(isKeyguardLocked));
+                    }
+                };
+                mWM.addKeyguardLockedStateListener(innerListener);
+                mKeyguardLockedStateListeners.put(listener, innerListener);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Unregisters a listener that executes when the keyguard visibility changes.
+     */
+    @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+    public void removeKeyguardLockedStateListener(@NonNull KeyguardLockedStateListener listener) {
+        synchronized (mKeyguardLockedStateListeners) {
+            IKeyguardLockedStateListener innerListener = mKeyguardLockedStateListeners.get(
+                    listener);
+            if (innerListener == null) {
+                return;
+            }
+            try {
+                mWM.removeKeyguardLockedStateListener(innerListener);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mKeyguardLockedStateListeners.remove(listener);
+        }
+    }
 }
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index bf4b514..eed92c1 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -37,6 +37,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.io.IOException;
+
 /**
  * The SoundTriggerModule provides APIs to control sound models and sound detection
  * on a given sound trigger hardware module.
@@ -137,13 +139,39 @@
             if (model instanceof SoundTrigger.GenericSoundModel) {
                 SoundModel aidlModel = ConversionUtil.api2aidlGenericSoundModel(
                         (SoundTrigger.GenericSoundModel) model);
-                soundModelHandle[0] = mService.loadModel(aidlModel);
+                try {
+                    soundModelHandle[0] = mService.loadModel(aidlModel);
+                } finally {
+                    // TODO(b/219825762): We should be able to use the entire object in a
+                    //  try-with-resources
+                    //   clause, instead of having to explicitly close internal fields.
+                    if (aidlModel.data != null) {
+                        try {
+                            aidlModel.data.close();
+                        } catch (IOException e) {
+                            Log.e(TAG, "Failed to close file", e);
+                        }
+                    }
+                }
                 return SoundTrigger.STATUS_OK;
             }
             if (model instanceof SoundTrigger.KeyphraseSoundModel) {
                 PhraseSoundModel aidlModel = ConversionUtil.api2aidlPhraseSoundModel(
                         (SoundTrigger.KeyphraseSoundModel) model);
-                soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
+                try {
+                    soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
+                } finally {
+                    // TODO(b/219825762): We should be able to use the entire object in a
+                    //  try-with-resources
+                    //   clause, instead of having to explicitly close internal fields.
+                    if (aidlModel.common.data != null) {
+                        try {
+                            aidlModel.common.data.close();
+                        } catch (IOException e) {
+                            Log.e(TAG, "Failed to close file", e);
+                        }
+                    }
+                }
                 return SoundTrigger.STATUS_OK;
             }
             return SoundTrigger.STATUS_BAD_VALUE;
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 315eef7..e3be4d3 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -183,25 +183,29 @@
     /**
      * Wake lock flag: Turn the screen on when the wake lock is acquired.
      * <p>
-     * Normally wake locks don't actually wake the device, they just cause
-     * the screen to remain on once it's already on.  Think of the video player
-     * application as the normal behavior.  Notifications that pop up and want
-     * the device to be on are the exception; use this flag to be like them.
+     * Normally wake locks don't actually wake the device, they just cause the screen to remain on
+     * once it's already on. This flag will cause the device to wake up when the wake lock is
+     * acquired.
      * </p><p>
      * Android TV playback devices attempt to turn on the HDMI-connected TV via HDMI-CEC on any
      * wake-up, including wake-ups triggered by wake locks.
      * </p><p>
      * Cannot be used with {@link #PARTIAL_WAKE_LOCK}.
      * </p>
+     *
+     * @deprecated Most applications should use {@link android.R.attr#turnScreenOn} or
+     * {@link android.app.Activity#setTurnScreenOn(boolean)} instead, as this prevents the previous
+     * foreground app from being resumed first when the screen turns on. Note that this flag may
+     * require a permission in the future.
      */
+    @Deprecated
     public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000;
 
     /**
      * Wake lock flag: When this wake lock is released, poke the user activity timer
      * so the screen stays on for a little longer.
      * <p>
-     * Will not turn the screen on if it is not already on.
-     * See {@link #ACQUIRE_CAUSES_WAKEUP} if you want that.
+     * This will not turn the screen on if it is not already on.
      * </p><p>
      * Cannot be used with {@link #PARTIAL_WAKE_LOCK}.
      * </p>
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index cc98339..48e2827 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -3022,9 +3022,17 @@
         }
     }
 
-    /** @hide */
-    @TestApi
+    /**
+     * Returns the authority of the current cloud media provider that was set by the
+     * {@link android.service.storage.ExternalStorageService} holding the
+     * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission via
+     * {@link #setCloudMediaProvider(String)}.
+     *
+     * @hide
+     */
     @Nullable
+    @TestApi
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public String getCloudMediaProvider() {
         try {
             return mStorageManager.getCloudMediaProvider();
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index 163d6ed..bfc3b8b 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -36,6 +36,9 @@
     private static final String TAG = "DreamOverlayService";
     private static final boolean DEBUG = false;
     private boolean mShowComplications;
+    private boolean mIsPreviewMode;
+    @Nullable
+    private CharSequence mDreamLabel;
 
     private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
         @Override
@@ -56,6 +59,8 @@
     public final IBinder onBind(@NonNull Intent intent) {
         mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS,
                 DreamService.DEFAULT_SHOW_COMPLICATIONS);
+        mIsPreviewMode = intent.getBooleanExtra(DreamService.EXTRA_IS_PREVIEW, false);
+        mDreamLabel = intent.getCharSequenceExtra(DreamService.EXTRA_DREAM_LABEL);
         return mDreamOverlay.asBinder();
     }
 
@@ -84,4 +89,19 @@
     public final boolean shouldShowComplications() {
         return mShowComplications;
     }
+
+    /**
+     * Returns whether the dream is running in preview mode.
+     */
+    public final boolean isPreviewMode() {
+        return mIsPreviewMode;
+    }
+
+    /**
+     * Returns the user-facing label of the currently running dream.
+     */
+    @Nullable
+    public final CharSequence getDreamLabel() {
+        return mDreamLabel;
+    }
 }
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 3459172..db622d3 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -216,6 +216,18 @@
             "android.service.dreams.SHOW_COMPLICATIONS";
 
     /**
+     * Extra containing a boolean for whether we are showing this dream in preview mode.
+     * @hide
+     */
+    public static final String EXTRA_IS_PREVIEW = "android.service.dreams.IS_PREVIEW";
+
+    /**
+     * The user-facing label of the current dream service.
+     * @hide
+     */
+    public static final String EXTRA_DREAM_LABEL = "android.service.dreams.DREAM_LABEL";
+
+    /**
      * The default value for whether to show complications on the overlay.
      * @hide
      */
@@ -258,15 +270,19 @@
         }
 
         public void bind(Context context, @Nullable ComponentName overlayService,
-                ComponentName dreamService) {
+                ComponentName dreamService, boolean isPreviewMode) {
             if (overlayService == null) {
                 return;
             }
 
+            final ServiceInfo serviceInfo = fetchServiceInfo(context, dreamService);
+
             final Intent overlayIntent = new Intent();
             overlayIntent.setComponent(overlayService);
             overlayIntent.putExtra(EXTRA_SHOW_COMPLICATIONS,
-                    fetchShouldShowComplications(context, dreamService));
+                    fetchShouldShowComplications(context, serviceInfo));
+            overlayIntent.putExtra(EXTRA_DREAM_LABEL, fetchDreamLabel(context, serviceInfo));
+            overlayIntent.putExtra(EXTRA_IS_PREVIEW, isPreviewMode);
 
             context.bindService(overlayIntent,
                     this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE);
@@ -988,8 +1004,11 @@
 
         // Connect to the overlay service if present.
         if (!mWindowless) {
-            mOverlayConnection.bind(this, intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT),
-                    new ComponentName(this, getClass()));
+            mOverlayConnection.bind(
+                    /* context= */ this,
+                    intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT),
+                    new ComponentName(this, getClass()),
+                    intent.getBooleanExtra(EXTRA_IS_PREVIEW, /* defaultValue= */ false));
         }
 
         return mDreamServiceWrapper;
@@ -1111,7 +1130,10 @@
      * @hide
      */
     @Nullable
-    public static DreamMetadata getDreamMetadata(Context context, ServiceInfo serviceInfo) {
+    public static DreamMetadata getDreamMetadata(Context context,
+            @Nullable ServiceInfo serviceInfo) {
+        if (serviceInfo == null) return null;
+
         final PackageManager pm = context.getPackageManager();
 
         final TypedArray rawMetadata = readMetadata(pm, serviceInfo);
@@ -1350,22 +1372,33 @@
      * {@link DreamService#DEFAULT_SHOW_COMPLICATIONS}.
      */
     private static boolean fetchShouldShowComplications(Context context,
-            ComponentName componentName) {
+            @Nullable ServiceInfo serviceInfo) {
+        final DreamMetadata metadata = getDreamMetadata(context, serviceInfo);
+        if (metadata != null) {
+            return metadata.showComplications;
+        }
+        return DEFAULT_SHOW_COMPLICATIONS;
+    }
+
+    @Nullable
+    private static CharSequence fetchDreamLabel(Context context,
+            @Nullable ServiceInfo serviceInfo) {
+        if (serviceInfo == null) return null;
+        final PackageManager pm = context.getPackageManager();
+        return serviceInfo.loadLabel(pm);
+    }
+
+    @Nullable
+    private static ServiceInfo fetchServiceInfo(Context context, ComponentName componentName) {
         final PackageManager pm = context.getPackageManager();
 
         try {
-            final ServiceInfo si = pm.getServiceInfo(componentName,
+            return pm.getServiceInfo(componentName,
                     PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
-            final DreamMetadata metadata = getDreamMetadata(context, si);
-
-            if (metadata != null) {
-                return metadata.showComplications;
-            }
         } catch (PackageManager.NameNotFoundException e) {
             if (DEBUG) Log.w(TAG, "cannot find component " + componentName.flattenToShortString());
         }
-
-        return DEFAULT_SHOW_COMPLICATIONS;
+        return null;
     }
 
     @Override
diff --git a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
index 8fe6f71..adc9251 100644
--- a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
+++ b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
@@ -40,7 +40,7 @@
 
     private final IBinder mTargetInputToken;
     private final SelectionToolbarRenderService.TransferTouchListener mTransferTouchListener;
-    private Rect mContentRect;
+    private final Rect mContentRect = new Rect();
 
     private int mLastDownX = -1;
     private int mLastDownY = -1;
@@ -57,7 +57,7 @@
      * Sets the Rect that shows the selection toolbar content.
      */
     public void setContentRect(Rect contentRect) {
-        mContentRect = contentRect;
+        mContentRect.set(contentRect);
     }
 
     @Override
diff --git a/core/java/android/util/DumpableContainer.java b/core/java/android/util/DumpableContainer.java
index e7b2acd..fef5acd 100644
--- a/core/java/android/util/DumpableContainer.java
+++ b/core/java/android/util/DumpableContainer.java
@@ -18,8 +18,7 @@
 import android.annotation.NonNull;
 
 /**
- * Objects that contains a list of {@link Dumpable}, which will be dumped when the object itself
- * is dumped.
+ * Represents a container that manages {@link Dumpable dumpables}.
  */
 public interface DumpableContainer {
 
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index ce21086..53b842a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -18,6 +18,7 @@
 
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.IKeyguardLockedStateListener;
 import com.android.internal.policy.IShortcutService;
 
 import android.app.IAssistDataReceiver;
@@ -199,6 +200,9 @@
     boolean isKeyguardSecure(int userId);
     void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message);
 
+    void addKeyguardLockedStateListener(in IKeyguardLockedStateListener listener);
+    void removeKeyguardLockedStateListener(in IKeyguardLockedStateListener listener);
+
     // Requires INTERACT_ACROSS_USERS_FULL permission
     void setSwitchingUser(boolean switching);
 
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 2edfda5..a13579d 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -328,7 +328,8 @@
      */
     public @Nullable SurfacePackage getSurfacePackage() {
         if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
-            return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection,
+            return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"),
+                mAccessibilityEmbeddedConnection,
                 mWm.getFocusGrantToken(), mRemoteInterface);
         } else {
             return null;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 2717463..017b8aa 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -46,6 +46,8 @@
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
 import android.app.ActivityThread;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -356,6 +358,21 @@
     /** @hide */
     public static final int SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES = 2;
 
+    /**
+     * Clear {@link #SHOW_FORCED} flag when the next IME focused application changed.
+     *
+     * <p>
+     * Note that when this flag enabled in server side, {@link #SHOW_FORCED} will no longer
+     * affect the next focused application to keep showing IME, in case of unexpected IME visible
+     * when the next focused app isn't be the IME requester. </p>
+     *
+     * @hide
+     */
+    @TestApi
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // This is a bug id.
+
     @UnsupportedAppUsage
     final IInputMethodManager mService;
     final Looper mMainLooper;
@@ -1646,7 +1663,14 @@
      * Flag for {@link #showSoftInput} to indicate that the user has forced
      * the input method open (such as by long-pressing menu) so it should
      * not be closed until they explicitly do so.
+     *
+     * @deprecated Use {@link #showSoftInput} without this flag instead. Using this flag can lead
+     * to the soft input remaining visible even when the calling application is closed. The
+     * use of this flag can make the soft input remains visible globally. Starting in
+     * {@link Build.VERSION_CODES#TIRAMISU Android T}, this flag only has an effect while the
+     * caller is currently focused.
      */
+    @Deprecated
     public static final int SHOW_FORCED = 0x0002;
 
     /**
diff --git a/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl b/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl
new file mode 100644
index 0000000..ee50219
--- /dev/null
+++ b/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+oneway interface IKeyguardLockedStateListener {
+    void onKeyguardLockedStateChanged(boolean isKeyguardLocked);
+}
\ No newline at end of file
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c33b7c9..e8f7b93 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -382,6 +382,7 @@
     optional bool in_size_compat_mode = 32;
     optional float min_aspect_ratio = 33;
     optional bool provides_max_bounds = 34;
+    optional bool enable_recents_screenshot = 35;
 }
 
 /* represents WindowToken */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d4c03e4..58a3bb4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4119,6 +4119,13 @@
     <permission android:name="android.permission.MANAGE_HOTWORD_DETECTION"
                 android:protectionLevel="internal|preinstalled" />
 
+    <!-- Allows an application to subscribe to keyguard locked (i.e., showing) state.
+         <p>Protection level: internal|role
+         <p>Intended for use by ROLE_ASSISTANT only.
+    -->
+    <permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
+                android:protectionLevel="internal|role"/>
+
     <!-- Must be required by a {@link android.service.autofill.AutofillService},
          to ensure that only the system can bind to it.
          <p>Protection level: signature
@@ -6912,6 +6919,10 @@
                  android:permission="android.permission.BIND_JOB_SERVICE">
         </service>
 
+        <service android:name="com.android.server.appsearch.contactsindexer.ContactsIndexerMaintenanceService"
+                 android:permission="android.permission.BIND_JOB_SERVICE">
+        </service>
+
         <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"
             android:exported="false">
             <intent-filter>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 744c3dab..adf8f8e 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -606,6 +606,9 @@
     <!-- The padding ratio of the Accessibility icon foreground drawable -->
     <item name="accessibility_icon_foreground_padding_ratio" type="dimen">21.88%</item>
 
+    <!-- The minimum window size of the accessibility window magnifier -->
+    <dimen name="accessibility_window_magnifier_min_size">122dp</dimen>
+
     <!-- Margin around the various security views -->
     <dimen name="keyguard_muliuser_selector_margin">8dp</dimen>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d4513d0..558e3c3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4399,6 +4399,7 @@
   <java-symbol type="color" name="accessibility_focus_highlight_color" />
   <!-- Width of the outline stroke used by the accessibility focus rectangle -->
   <java-symbol type="dimen" name="accessibility_focus_highlight_stroke_width" />
+  <java-symbol type="dimen" name="accessibility_window_magnifier_min_size" />
 
   <java-symbol type="bool" name="config_attachNavBarToAppDuringTransition" />
 
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 8a8231d..bb8a43d 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -18,8 +18,8 @@
     <dimen name="dismiss_circle_size">96dp</dimen>
     <dimen name="dismiss_circle_small">60dp</dimen>
 
-    <!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
-    <dimen name="floating_dismiss_gradient_height">250dp</dimen>
+    <!-- The height of the gradient indicating the dismiss edge when moving a PIP or bubble. -->
+    <dimen name="floating_dismiss_gradient_height">548dp</dimen>
 
     <!-- The padding around a PiP actions. -->
     <dimen name="pip_action_padding">16dp</dimen>
@@ -129,6 +129,9 @@
     <dimen name="bubble_dismiss_encircle_size">52dp</dimen>
     <!-- Padding around the view displayed when the bubble is expanded -->
     <dimen name="bubble_expanded_view_padding">16dp</dimen>
+    <!-- Padding for the edge of the expanded view that is closest to the edge of the screen used
+         when displaying in landscape on a large screen. -->
+    <dimen name="bubble_expanded_view_largescreen_landscape_padding">128dp</dimen>
     <!-- This should be at least the size of bubble_expanded_view_padding; it is used to include
          a slight touch slop around the expanded view. -->
     <dimen name="bubble_expanded_view_slop">8dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 75b19fb..bc0db36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -67,7 +67,11 @@
     /** The max percent of screen width to use for the flyout on phone. */
     public static final float FLYOUT_MAX_WIDTH_PERCENT = 0.6f;
     /** The percent of screen width that should be used for the expanded view on a large screen. **/
-    public static final float EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT = 0.72f;
+    private static final float EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT = 0.48f;
+    /** The percent of screen width that should be used for the expanded view on a large screen. **/
+    private static final float EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT = 0.70f;
+    /** The percent of screen width that should be used for the expanded view on a small tablet. **/
+    private static final float EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT = 0.72f;
 
     private Context mContext;
     private WindowManager mWindowManager;
@@ -77,6 +81,7 @@
     private boolean mImeVisible;
     private int mImeHeight;
     private boolean mIsLargeScreen;
+    private boolean mIsSmallTablet;
 
     private Rect mPositionRect;
     private int mDefaultMaxBubbles;
@@ -86,7 +91,8 @@
 
     private int mExpandedViewMinHeight;
     private int mExpandedViewLargeScreenWidth;
-    private int mExpandedViewLargeScreenInset;
+    private int mExpandedViewLargeScreenInsetClosestEdge;
+    private int mExpandedViewLargeScreenInsetFurthestEdge;
 
     private int mOverflowWidth;
     private int mExpandedViewPadding;
@@ -127,17 +133,26 @@
                 | WindowInsets.Type.statusBars()
                 | WindowInsets.Type.displayCutout());
 
-        mIsLargeScreen = mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
+        final Rect bounds = windowMetrics.getBounds();
+        Configuration config = mContext.getResources().getConfiguration();
+        mIsLargeScreen = config.smallestScreenWidthDp >= 600;
+        if (mIsLargeScreen) {
+            float largestEdgeDp = Math.max(config.screenWidthDp, config.screenHeightDp);
+            mIsSmallTablet = largestEdgeDp < 960;
+        } else {
+            mIsSmallTablet = false;
+        }
 
         if (BubbleDebugConfig.DEBUG_POSITIONER) {
             Log.w(TAG, "update positioner:"
                     + " rotation: " + mRotation
                     + " insets: " + insets
                     + " isLargeScreen: " + mIsLargeScreen
-                    + " bounds: " + windowMetrics.getBounds()
+                    + " isSmallTablet: " + mIsSmallTablet
+                    + " bounds: " + bounds
                     + " showingInTaskbar: " + mShowingInTaskbar);
         }
-        updateInternal(mRotation, insets, windowMetrics.getBounds());
+        updateInternal(mRotation, insets, bounds);
     }
 
     /**
@@ -172,11 +187,31 @@
         mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing);
         mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
         mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
-        mExpandedViewLargeScreenWidth = (int) (bounds.width()
-                * EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT);
-        mExpandedViewLargeScreenInset = mIsLargeScreen
-                ? (bounds.width() - mExpandedViewLargeScreenWidth) / 2
-                : mExpandedViewPadding;
+        if (mIsSmallTablet) {
+            mExpandedViewLargeScreenWidth = (int) (bounds.width()
+                    * EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT);
+        } else {
+            mExpandedViewLargeScreenWidth = isLandscape()
+                    ? (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT)
+                    : (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT);
+        }
+        if (mIsLargeScreen) {
+            if (isLandscape() && !mIsSmallTablet) {
+                mExpandedViewLargeScreenInsetClosestEdge = res.getDimensionPixelSize(
+                        R.dimen.bubble_expanded_view_largescreen_landscape_padding);
+                mExpandedViewLargeScreenInsetFurthestEdge = bounds.width()
+                        - mExpandedViewLargeScreenInsetClosestEdge
+                        - mExpandedViewLargeScreenWidth;
+            } else {
+                final int centeredInset = (bounds.width() - mExpandedViewLargeScreenWidth) / 2;
+                mExpandedViewLargeScreenInsetClosestEdge = centeredInset;
+                mExpandedViewLargeScreenInsetFurthestEdge = centeredInset;
+            }
+        } else {
+            mExpandedViewLargeScreenInsetClosestEdge = mExpandedViewPadding;
+            mExpandedViewLargeScreenInsetFurthestEdge = mExpandedViewPadding;
+        }
+
         mOverflowWidth = mIsLargeScreen
                 ? mExpandedViewLargeScreenWidth
                 : res.getDimensionPixelSize(
@@ -328,15 +363,18 @@
     public int[] getExpandedViewContainerPadding(boolean onLeft, boolean isOverflow) {
         final int pointerTotalHeight = mPointerHeight - mPointerOverlap;
         if (mIsLargeScreen) {
+            // Note:
+            // If we're in portrait OR if we're a small tablet, then the two insets values will
+            // be equal. If we're landscape and a large tablet, the two values will be different.
             // [left, top, right, bottom]
             mPaddings[0] = onLeft
-                    ? mExpandedViewLargeScreenInset - pointerTotalHeight
-                    : mExpandedViewLargeScreenInset;
+                    ? mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight
+                    : mExpandedViewLargeScreenInsetFurthestEdge;
             mPaddings[1] = 0;
             mPaddings[2] = onLeft
-                    ? mExpandedViewLargeScreenInset
-                    : mExpandedViewLargeScreenInset - pointerTotalHeight;
-            // Overflow doesn't show manage button / get padding from it so add padding here for it
+                    ? mExpandedViewLargeScreenInsetFurthestEdge
+                    : mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight;
+            // Overflow doesn't show manage button / get padding from it so add padding here
             mPaddings[3] = isOverflow ? mExpandedViewPadding : 0;
             return mPaddings;
         } else {
@@ -494,12 +532,13 @@
         float x;
         float y;
         if (showBubblesVertically()) {
+            int inset = mExpandedViewLargeScreenInsetClosestEdge;
             y = rowStart + positionInRow;
             int left = mIsLargeScreen
-                    ? mExpandedViewLargeScreenInset - mExpandedViewPadding - mBubbleSize
+                    ? inset - mExpandedViewPadding - mBubbleSize
                     : mPositionRect.left;
             int right = mIsLargeScreen
-                    ? mPositionRect.right - mExpandedViewLargeScreenInset + mExpandedViewPadding
+                    ? mPositionRect.right - inset + mExpandedViewPadding
                     : mPositionRect.right - mBubbleSize;
             x = state.onLeft
                     ? left
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
index 74672a3..063dac3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
@@ -16,11 +16,16 @@
 
 package com.android.wm.shell.bubbles
 
+import android.animation.ObjectAnimator
 import android.content.Context
-import android.graphics.drawable.TransitionDrawable
+import android.graphics.Color
+import android.graphics.drawable.GradientDrawable
+import android.util.IntProperty
 import android.view.Gravity
 import android.view.View
 import android.view.ViewGroup
+import android.view.WindowManager
+import android.view.WindowInsets
 import android.widget.FrameLayout
 import androidx.dynamicanimation.animation.DynamicAnimation
 import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
@@ -28,8 +33,6 @@
 import com.android.wm.shell.R
 import com.android.wm.shell.animation.PhysicsAnimator
 import com.android.wm.shell.common.DismissCircleView
-import android.view.WindowInsets
-import android.view.WindowManager
 
 /*
  * View that handles interactions between DismissCircleView and BubbleStackView.
@@ -41,9 +44,21 @@
 
     private val animator = PhysicsAnimator.getInstance(circle)
     private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY)
-    private val DISMISS_SCRIM_FADE_MS = 200
+    private val DISMISS_SCRIM_FADE_MS = 200L
     private var wm: WindowManager =
             context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+    private var gradientDrawable = createGradient()
+
+    private val GRADIENT_ALPHA: IntProperty<GradientDrawable> =
+            object : IntProperty<GradientDrawable>("alpha") {
+        override fun setValue(d: GradientDrawable, percent: Int) {
+            d.alpha = percent
+        }
+        override fun get(d: GradientDrawable): Int {
+            return d.alpha
+        }
+    }
+
     init {
         setLayoutParams(LayoutParams(
             ViewGroup.LayoutParams.MATCH_PARENT,
@@ -53,8 +68,7 @@
         setClipToPadding(false)
         setClipChildren(false)
         setVisibility(View.INVISIBLE)
-        setBackgroundResource(
-            R.drawable.floating_dismiss_gradient_transition)
+        setBackgroundDrawable(gradientDrawable)
 
         val targetSize: Int = resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
         addView(circle, LayoutParams(targetSize, targetSize,
@@ -71,7 +85,11 @@
         if (isShowing) return
         isShowing = true
         setVisibility(View.VISIBLE)
-        (getBackground() as TransitionDrawable).startTransition(DISMISS_SCRIM_FADE_MS)
+        val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
+                gradientDrawable.alpha, 255)
+        alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
+        alphaAnim.start()
+
         animator.cancel()
         animator
             .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring)
@@ -85,7 +103,10 @@
     fun hide() {
         if (!isShowing) return
         isShowing = false
-        (getBackground() as TransitionDrawable).reverseTransition(DISMISS_SCRIM_FADE_MS)
+        val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
+                gradientDrawable.alpha, 0)
+        alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
+        alphaAnim.start()
         animator
             .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(),
                 spring)
@@ -93,6 +114,13 @@
             .start()
     }
 
+    /**
+     * Cancels the animator for the dismiss target.
+     */
+    fun cancelAnimators() {
+        animator.cancel()
+    }
+
     fun updateResources() {
         updatePadding()
         layoutParams.height = resources.getDimensionPixelSize(
@@ -104,6 +132,20 @@
         circle.requestLayout()
     }
 
+    private fun createGradient(): GradientDrawable {
+        val gradientColor = context.resources.getColor(android.R.color.system_neutral1_900)
+        val alpha = 0.7f * 255
+        val gradientColorWithAlpha = Color.argb(alpha.toInt(),
+                Color.red(gradientColor),
+                Color.green(gradientColor),
+                Color.blue(gradientColor))
+        val gd = GradientDrawable(
+                GradientDrawable.Orientation.BOTTOM_TOP,
+                intArrayOf(gradientColorWithAlpha, Color.TRANSPARENT))
+        gd.setAlpha(0)
+        return gd
+    }
+
     private fun updatePadding() {
         val insets: WindowInsets = wm.getCurrentWindowMetrics().getWindowInsets()
         val navInset = insets.getInsetsIgnoringVisibility(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index f7c92fe..8a482fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -144,21 +144,42 @@
         return Math.max(dividerInset, radius);
     }
 
-    /** Gets bounds of the primary split. */
+    /** Gets bounds of the primary split with screen based coordinate. */
     public Rect getBounds1() {
         return new Rect(mBounds1);
     }
 
-    /** Gets bounds of the secondary split. */
+    /** Gets bounds of the primary split with parent based coordinate. */
+    public Rect getRefBounds1() {
+        Rect outBounds = getBounds1();
+        outBounds.offset(-mRootBounds.left, -mRootBounds.top);
+        return outBounds;
+    }
+
+    /** Gets bounds of the secondary split with screen based coordinate. */
     public Rect getBounds2() {
         return new Rect(mBounds2);
     }
 
-    /** Gets bounds of divider window. */
+    /** Gets bounds of the secondary split with parent based coordinate. */
+    public Rect getRefBounds2() {
+        final Rect outBounds = getBounds2();
+        outBounds.offset(-mRootBounds.left, -mRootBounds.top);
+        return outBounds;
+    }
+
+    /** Gets bounds of divider window with screen based coordinate. */
     public Rect getDividerBounds() {
         return new Rect(mDividerBounds);
     }
 
+    /** Gets bounds of divider window with parent based coordinate. */
+    public Rect getRefDividerBounds() {
+        final Rect outBounds = getDividerBounds();
+        outBounds.offset(-mRootBounds.left, -mRootBounds.top);
+        return outBounds;
+    }
+
     /** Returns leash of the current divider bar. */
     @Nullable
     public SurfaceControl getDividerLeash() {
@@ -452,14 +473,17 @@
             SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
         final SurfaceControl dividerLeash = getDividerLeash();
         if (dividerLeash != null) {
-            t.setPosition(dividerLeash, mDividerBounds.left, mDividerBounds.top);
+            mTempRect.set(getRefDividerBounds());
+            t.setPosition(dividerLeash, mTempRect.left, mTempRect.top);
             // Resets layer of divider bar to make sure it is always on top.
             t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER);
         }
-        t.setPosition(leash1, mBounds1.left, mBounds1.top)
-                .setWindowCrop(leash1, mBounds1.width(), mBounds1.height());
-        t.setPosition(leash2, mBounds2.left, mBounds2.top)
-                .setWindowCrop(leash2, mBounds2.width(), mBounds2.height());
+        mTempRect.set(getRefBounds1());
+        t.setPosition(leash1, mTempRect.left, mTempRect.top)
+                .setWindowCrop(leash1, mTempRect.width(), mTempRect.height());
+        mTempRect.set(getRefBounds2());
+        t.setPosition(leash2, mTempRect.left, mTempRect.top)
+                .setWindowCrop(leash2, mTempRect.width(), mTempRect.height());
 
         if (mImePositionProcessor.adjustSurfaceLayoutForIme(
                 t, dividerLeash, leash1, leash2, dimLayer1, dimLayer2)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index 180e3fb..d7322ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -138,8 +138,8 @@
         // destination are different.
         final float scale = srcW <= srcH ? (float) destW / srcW : (float) destH / srcH;
         final Rect crop = mTmpDestinationRect;
-        crop.set(0, 0, Transitions.ENABLE_SHELL_TRANSITIONS ? destH
-                : destW, Transitions.ENABLE_SHELL_TRANSITIONS ? destW : destH);
+        crop.set(0, 0, Transitions.SHELL_TRANSITIONS_ROTATION ? destH
+                : destW, Transitions.SHELL_TRANSITIONS_ROTATION ? destW : destH);
         // Inverse scale for crop to fit in screen coordinates.
         crop.scale(1 / scale);
         crop.offset(insets.left, insets.top);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 60aac68..91615fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -97,7 +97,7 @@
      * meaningful if {@link #mInFixedRotation} is true.
      */
     @Surface.Rotation
-    private int mFixedRotation;
+    private int mEndFixedRotation;
     /** Whether the PIP window has fade out for fixed rotation. */
     private boolean mHasFadeOut;
 
@@ -153,7 +153,7 @@
         final TransitionInfo.Change currentPipChange = findCurrentPipChange(info);
         final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
         mInFixedRotation = fixedRotationChange != null;
-        mFixedRotation = mInFixedRotation
+        mEndFixedRotation = mInFixedRotation
                 ? fixedRotationChange.getEndFixedRotation()
                 : ROTATION_UNDEFINED;
 
@@ -257,7 +257,7 @@
         final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo();
         if (taskInfo != null) {
             startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(),
-                    new Rect(mExitDestinationBounds));
+                    new Rect(mExitDestinationBounds), Surface.ROTATION_0);
         }
         mExitDestinationBounds.setEmpty();
         mCurrentPipTaskToken = null;
@@ -332,30 +332,31 @@
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback,
             @NonNull TransitionInfo.Change pipChange) {
-        TransitionInfo.Change displayRotationChange = null;
-        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
-            final TransitionInfo.Change change = info.getChanges().get(i);
-            if (change.getMode() == TRANSIT_CHANGE
-                    && (change.getFlags() & FLAG_IS_DISPLAY) != 0
-                    && change.getStartRotation() != change.getEndRotation()) {
-                displayRotationChange = change;
-                break;
-            }
-        }
-
-        if (displayRotationChange != null) {
-            // Exiting PIP to fullscreen with orientation change.
-            startExpandAndRotationAnimation(info, startTransaction, finishTransaction,
-                    finishCallback, displayRotationChange, pipChange);
-            return;
-        }
-
-        // When there is no rotation, we can simply expand the PIP window.
         mFinishCallback = (wct, wctCB) -> {
             mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
             finishCallback.onTransitionFinished(wct, wctCB);
         };
 
+        // Check if it is Shell rotation.
+        if (Transitions.SHELL_TRANSITIONS_ROTATION) {
+            TransitionInfo.Change displayRotationChange = null;
+            for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+                final TransitionInfo.Change change = info.getChanges().get(i);
+                if (change.getMode() == TRANSIT_CHANGE
+                        && (change.getFlags() & FLAG_IS_DISPLAY) != 0
+                        && change.getStartRotation() != change.getEndRotation()) {
+                    displayRotationChange = change;
+                    break;
+                }
+            }
+            if (displayRotationChange != null) {
+                // Exiting PIP to fullscreen with orientation change.
+                startExpandAndRotationAnimation(info, startTransaction, finishTransaction,
+                        displayRotationChange, pipChange);
+                return;
+            }
+        }
+
         // Set the initial frame as scaling the end to the start.
         final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds());
         final Point offset = pipChange.getEndRelOffset();
@@ -364,13 +365,41 @@
         mSurfaceTransactionHelper.scale(startTransaction, pipChange.getLeash(),
                 destinationBounds, mPipBoundsState.getBounds());
         startTransaction.apply();
-        startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds);
+
+        // Check if it is fixed rotation.
+        final int rotationDelta;
+        if (mInFixedRotation) {
+            final int startRotation = pipChange.getStartRotation();
+            final int endRotation = mEndFixedRotation;
+            rotationDelta = deltaRotation(startRotation, endRotation);
+            final Rect endBounds = new Rect(destinationBounds);
+
+            // Set the end frame since the display won't rotate until fixed rotation is finished
+            // in the next display change transition.
+            rotateBounds(endBounds, destinationBounds, rotationDelta);
+            final int degree, x, y;
+            if (rotationDelta == ROTATION_90) {
+                degree = 90;
+                x = destinationBounds.right;
+                y = destinationBounds.top;
+            } else {
+                degree = -90;
+                x = destinationBounds.left;
+                y = destinationBounds.bottom;
+            }
+            mSurfaceTransactionHelper.rotateAndScaleWithCrop(finishTransaction,
+                    pipChange.getLeash(), endBounds, endBounds, new Rect(), degree, x, y,
+                    true /* isExpanding */, rotationDelta == ROTATION_270 /* clockwise */);
+        } else {
+            rotationDelta = Surface.ROTATION_0;
+        }
+        startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds,
+                rotationDelta);
     }
 
     private void startExpandAndRotationAnimation(@NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
             @NonNull SurfaceControl.Transaction finishTransaction,
-            @NonNull Transitions.TransitionFinishCallback finishCallback,
             @NonNull TransitionInfo.Change displayRotationChange,
             @NonNull TransitionInfo.Change pipChange) {
         final int rotateDelta = deltaRotation(displayRotationChange.getStartRotation(),
@@ -380,11 +409,6 @@
         final CounterRotatorHelper rotator = new CounterRotatorHelper();
         rotator.handleClosingChanges(info, startTransaction, displayRotationChange);
 
-        mFinishCallback = (wct, wctCB) -> {
-            mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
-            finishCallback.onTransitionFinished(wct, wctCB);
-        };
-
         // Get the start bounds in new orientation.
         final Rect startBounds = new Rect(pipChange.getStartAbsBounds());
         rotateBounds(startBounds, displayRotationChange.getStartAbsBounds(), rotateDelta);
@@ -425,12 +449,11 @@
     }
 
     private void startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
-            final Rect destinationBounds) {
-        PipAnimationController.PipTransitionAnimator animator =
+            final Rect destinationBounds, final int rotationDelta) {
+        final PipAnimationController.PipTransitionAnimator animator =
                 mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(),
                         mPipBoundsState.getBounds(), destinationBounds, null,
-                        TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
-
+                        TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, rotationDelta);
         animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP)
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(mEnterExitAnimationDuration)
@@ -526,7 +549,7 @@
 
         mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
         mFinishCallback = finishCallback;
-        final int endRotation = mInFixedRotation ? mFixedRotation : enterPip.getEndRotation();
+        final int endRotation = mInFixedRotation ? mEndFixedRotation : enterPip.getEndRotation();
         return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
                 startTransaction, finishTransaction, enterPip.getStartRotation(),
                 endRotation);
@@ -545,8 +568,8 @@
                 taskInfo.pictureInPictureParams, currentBounds);
         if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
             // Need to get the bounds of new rotation in old rotation for fixed rotation,
-            sourceHintRect = computeRotatedBounds(rotationDelta, startRotation, endRotation,
-                    taskInfo, TRANSITION_DIRECTION_TO_PIP, destinationBounds, sourceHintRect);
+            computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo,
+                    destinationBounds, sourceHintRect);
         }
         PipAnimationController.PipTransitionAnimator animator;
         // Set corner radius for entering pip.
@@ -583,8 +606,6 @@
             startTransaction.setMatrix(leash, tmpTransform, new float[9]);
         }
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
-            // Reverse the rotation for Shell transition animation.
-            rotationDelta = deltaRotation(rotationDelta, 0);
             animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
                     currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
                     0 /* startingAngle */, rotationDelta);
@@ -617,33 +638,22 @@
         return true;
     }
 
-    /** Computes destination bounds in old rotation and returns source hint rect if available. */
-    @Nullable
-    private Rect computeRotatedBounds(int rotationDelta, int startRotation, int endRotation,
-            TaskInfo taskInfo, int direction, Rect outDestinationBounds,
-            @Nullable Rect sourceHintRect) {
-        if (direction == TRANSITION_DIRECTION_TO_PIP) {
-            mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation);
-            final Rect displayBounds = mPipBoundsState.getDisplayBounds();
-            outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
-            // Transform the destination bounds to current display coordinates.
-            rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation);
-            // When entering PiP (from button navigation mode), adjust the source rect hint by
-            // display cutout if applicable.
-            if (sourceHintRect != null && taskInfo.displayCutoutInsets != null) {
-                if (rotationDelta == Surface.ROTATION_270) {
-                    sourceHintRect.offset(taskInfo.displayCutoutInsets.left,
-                            taskInfo.displayCutoutInsets.top);
-                }
+    /** Computes destination bounds in old rotation and updates source hint rect if available. */
+    private void computeEnterPipRotatedBounds(int rotationDelta, int startRotation, int endRotation,
+            TaskInfo taskInfo, Rect outDestinationBounds, @Nullable Rect outSourceHintRect) {
+        mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation);
+        final Rect displayBounds = mPipBoundsState.getDisplayBounds();
+        outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds());
+        // Transform the destination bounds to current display coordinates.
+        rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation);
+        // When entering PiP (from button navigation mode), adjust the source rect hint by
+        // display cutout if applicable.
+        if (outSourceHintRect != null && taskInfo.displayCutoutInsets != null) {
+            if (rotationDelta == Surface.ROTATION_270) {
+                outSourceHintRect.offset(taskInfo.displayCutoutInsets.left,
+                        taskInfo.displayCutoutInsets.top);
             }
-        } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) {
-            final Rect rotatedDestinationBounds = new Rect(outDestinationBounds);
-            rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(),
-                    rotationDelta);
-            return PipBoundsAlgorithm.getValidSourceHintRect(taskInfo.pictureInPictureParams,
-                    rotatedDestinationBounds);
         }
-        return sourceHintRect;
     }
 
     private void startExitToSplitAnimation(TransitionInfo info,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 915c593..3115f8a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -20,27 +20,20 @@
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.drawable.TransitionDrawable;
-import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.view.WindowManager;
-import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.wm.shell.R;
-import com.android.wm.shell.animation.PhysicsAnimator;
+import com.android.wm.shell.bubbles.DismissView;
 import com.android.wm.shell.common.DismissCircleView;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
@@ -56,9 +49,6 @@
     /* The multiplier to apply scale the target size by when applying the magnetic field radius */
     private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f;
 
-    /** Duration of the dismiss scrim fading in/out. */
-    private static final int DISMISS_TRANSITION_DURATION_MS = 200;
-
     /**
      * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
      * PIP.
@@ -69,7 +59,7 @@
      * Container for the dismiss circle, so that it can be animated within the container via
      * translation rather than within the WindowManager via slow layout animations.
      */
-    private ViewGroup mTargetViewContainer;
+    private DismissView mTargetViewContainer;
 
     /** Circle view used to render the dismiss target. */
     private DismissCircleView mTargetView;
@@ -79,16 +69,6 @@
      */
     private MagnetizedObject.MagneticTarget mMagneticTarget;
 
-    /**
-     * PhysicsAnimator instance for animating the dismiss target in/out.
-     */
-    private PhysicsAnimator<View> mMagneticTargetAnimator;
-
-    /** Default configuration to use for springing the dismiss target in/out. */
-    private final PhysicsAnimator.SpringConfig mTargetSpringConfig =
-            new PhysicsAnimator.SpringConfig(
-                    SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
-
     // Allow dragging the PIP to a location to close it
     private boolean mEnableDismissDragToEdge;
 
@@ -125,12 +105,8 @@
             cleanUpDismissTarget();
         }
 
-        mTargetView = new DismissCircleView(mContext);
-        mTargetViewContainer = new FrameLayout(mContext);
-        mTargetViewContainer.setBackgroundDrawable(
-                mContext.getDrawable(R.drawable.floating_dismiss_gradient_transition));
-        mTargetViewContainer.setClipChildren(false);
-        mTargetViewContainer.addView(mTargetView);
+        mTargetViewContainer = new DismissView(mContext);
+        mTargetView = mTargetViewContainer.getCircle();
         mTargetViewContainer.setOnApplyWindowInsetsListener((view, windowInsets) -> {
             if (!windowInsets.equals(mWindowInsets)) {
                 mWindowInsets = windowInsets;
@@ -187,7 +163,6 @@
             }
         });
 
-        mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
     }
 
     @Override
@@ -213,19 +188,13 @@
         if (mTargetView == null) {
             return;
         }
+        if (mTargetViewContainer != null) {
+            mTargetViewContainer.updateResources();
+        }
 
         final Resources res = mContext.getResources();
         mTargetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
         mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
-        final WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
-        final Insets navInset = insets.getInsetsIgnoringVisibility(
-                WindowInsets.Type.navigationBars());
-        final FrameLayout.LayoutParams newParams =
-                new FrameLayout.LayoutParams(mTargetSize, mTargetSize);
-        newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-        newParams.bottomMargin = navInset.bottom + mContext.getResources().getDimensionPixelSize(
-                R.dimen.floating_dismiss_bottom_margin);
-        mTargetView.setLayoutParams(newParams);
 
         // Set the magnetic field radius equal to the target size from the center of the target
         setMagneticFieldRadiusPercent(mMagneticFieldRadiusPercent);
@@ -261,7 +230,7 @@
     /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
     public void createOrUpdateDismissTarget() {
         if (!mTargetViewContainer.isAttachedToWindow()) {
-            mMagneticTargetAnimator.cancel();
+            mTargetViewContainer.cancelAnimators();
 
             mTargetViewContainer.setVisibility(View.INVISIBLE);
             mTargetViewContainer.getViewTreeObserver().removeOnPreDrawListener(this);
@@ -312,18 +281,8 @@
         createOrUpdateDismissTarget();
 
         if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
-            mTargetView.setTranslationY(mTargetViewContainer.getHeight());
-            mTargetViewContainer.setVisibility(View.VISIBLE);
             mTargetViewContainer.getViewTreeObserver().addOnPreDrawListener(this);
-
-            // Cancel in case we were in the middle of animating it out.
-            mMagneticTargetAnimator.cancel();
-            mMagneticTargetAnimator
-                    .spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig)
-                    .start();
-
-            ((TransitionDrawable) mTargetViewContainer.getBackground()).startTransition(
-                    DISMISS_TRANSITION_DURATION_MS);
+            mTargetViewContainer.show();
         }
     }
 
@@ -332,16 +291,7 @@
         if (!mEnableDismissDragToEdge) {
             return;
         }
-
-        mMagneticTargetAnimator
-                .spring(DynamicAnimation.TRANSLATION_Y,
-                        mTargetViewContainer.getHeight(),
-                        mTargetSpringConfig)
-                .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE))
-                .start();
-
-        ((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition(
-                DISMISS_TRANSITION_DURATION_MS);
+        mTargetViewContainer.hide();
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index 69d6c9e..32ebe2d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -33,6 +33,7 @@
 import android.util.Log;
 import android.view.SurfaceControl;
 import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.WindowManagerGlobal;
 
 import androidx.annotation.Nullable;
 
@@ -143,7 +144,6 @@
         mSystemWindows.addView(mPipMenuView,
                 getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
                 0, SHELL_ROOT_LAYER_PIP);
-        mPipMenuView.setFocusGrantToken(mSystemWindows.getFocusGrantToken(mPipMenuView));
     }
 
     @Override
@@ -164,6 +164,7 @@
                 t.setPosition(menuSurfaceControl, menuBounds.left, menuBounds.top);
                 t.apply();
             }
+            grantPipMenuFocus(true);
             mPipMenuView.show(mInMoveMode, mDelegate.getPipGravity());
         }
     }
@@ -194,8 +195,9 @@
             if (DEBUG) Log.d(TAG, "hideMenu()");
         }
 
-        mPipMenuView.hide(mInMoveMode);
+        mPipMenuView.hide();
         if (!mInMoveMode) {
+            grantPipMenuFocus(false);
             mDelegate.closeMenu();
         }
     }
@@ -453,4 +455,15 @@
 
         void closePip();
     }
+
+    private void grantPipMenuFocus(boolean grantFocus) {
+        if (DEBUG) Log.d(TAG, "grantWindowFocus(" + grantFocus + ")");
+
+        try {
+            WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+                    mSystemWindows.getFocusGrantToken(mPipMenuView), grantFocus);
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to update focus", e);
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 773e9bf..3090139 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -30,7 +30,6 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Handler;
-import android.os.IBinder;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
@@ -39,7 +38,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewRootImpl;
-import android.view.WindowManagerGlobal;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -72,7 +70,6 @@
     private final ImageView mArrowRight;
     private final ImageView mArrowDown;
     private final ImageView mArrowLeft;
-    private IBinder mFocusGrantToken = null;
 
     private final ViewGroup mScrollView;
     private final ViewGroup mHorizontalScrollView;
@@ -152,10 +149,6 @@
         mListener = listener;
     }
 
-    void setFocusGrantToken(IBinder token) {
-        mFocusGrantToken = token;
-    }
-
     void setExpandedModeEnabled(boolean enabled) {
         mExpandButton.setVisibility(enabled ? VISIBLE : GONE);
     }
@@ -170,8 +163,6 @@
 
     void show(boolean inMoveMode, int gravity) {
         if (DEBUG) Log.d(TAG, "show(), inMoveMode: " + inMoveMode);
-        grantWindowFocus(true);
-
         if (inMoveMode) {
             showMovementHints(gravity);
         } else {
@@ -180,15 +171,11 @@
         animateAlphaTo(1, mMenuFrameView);
     }
 
-    void hide(boolean isInMoveMode) {
+    void hide() {
         if (DEBUG) Log.d(TAG, "hide()");
         animateAlphaTo(0, mActionButtonsContainer);
         animateAlphaTo(0, mMenuFrameView);
         hideMovementHints();
-
-        if (!isInMoveMode) {
-            grantWindowFocus(false);
-        }
     }
 
     private void animateAlphaTo(float alpha, View view) {
@@ -217,17 +204,6 @@
                 || mArrowLeft.getAlpha() != 0f;
     }
 
-    private void grantWindowFocus(boolean grantFocus) {
-        if (DEBUG) Log.d(TAG, "grantWindowFocus(" + grantFocus + ")");
-
-        try {
-            WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
-                    mFocusGrantToken, grantFocus);
-        } catch (Exception e) {
-            Log.e(TAG, "Unable to update focus", e);
-        }
-    }
-
     void setAdditionalActions(List<RemoteAction> actions, Handler mainHandler) {
         if (DEBUG) Log.d(TAG, "setAdditionalActions()");
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 81dacdb..76641f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -979,7 +979,8 @@
             t.setAlpha(dividerLeash, 1);
             t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER);
             t.setPosition(dividerLeash,
-                    mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top);
+                    mSplitLayout.getRefDividerBounds().left,
+                    mSplitLayout.getRefDividerBounds().top);
         } else {
             t.hide(dividerLeash);
         }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index 65eb9aa..57bcbc0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.apppairs
 
 import android.platform.test.annotations.Presubmit
+import android.view.Display
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -24,6 +25,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
 import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -60,7 +62,18 @@
                 // TODO pair apps through normal UX flow
                 executeShellCommand(
                         composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
-                nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) }
+                val waitConditions = mutableListOf(
+                    WindowManagerConditionsFactory.isWindowVisible(primaryApp.component),
+                    WindowManagerConditionsFactory.isLayerVisible(primaryApp.component),
+                    WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
+
+                nonResizeableApp?.let {
+                    waitConditions.add(
+                        WindowManagerConditionsFactory.isWindowVisible(nonResizeableApp.component))
+                    waitConditions.add(
+                        WindowManagerConditionsFactory.isLayerVisible(nonResizeableApp.component))
+                }
+                wmHelper.waitFor(*waitConditions.toTypedArray())
             }
         }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
index 0f00ede..cc5b9f9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -62,7 +62,7 @@
         if (wmHelper == null) {
             device.waitForIdle()
         } else {
-            require(wmHelper.waitImeShown()) { "IME did not appear" }
+            wmHelper.waitImeShown()
         }
     }
 
@@ -79,7 +79,7 @@
             if (wmHelper == null) {
                 uiDevice.waitForIdle()
             } else {
-                require(wmHelper.waitImeGone()) { "IME did did not close" }
+                wmHelper.waitImeGone()
             }
         } else {
             // While pressing the back button should close the IME on TV as well, it may also lead
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 7e232ea..e9d438a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -58,17 +58,27 @@
         }
     }
 
-    /** {@inheritDoc}  */
-    override fun launchViaIntent(
+    /**
+     * Launches the app through an intent instead of interacting with the launcher and waits
+     * until the app window is in PIP mode
+     */
+    @JvmOverloads
+    fun launchViaIntentAndWaitForPip(
         wmHelper: WindowManagerStateHelper,
-        expectedWindowName: String,
-        action: String?,
+        expectedWindowName: String = "",
+        action: String? = null,
         stringExtras: Map<String, String>
     ) {
-        super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
-        wmHelper.waitPipShown()
+        launchViaIntentAndWaitShown(wmHelper, expectedWindowName, action, stringExtras,
+            waitConditions = arrayOf(WindowManagerStateHelper.pipShownCondition))
     }
 
+    /**
+     * Expand the PIP window back to full screen via intent and wait until the app is visible
+     */
+    fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) =
+        launchViaIntentAndWaitShown(wmHelper)
+
     private fun focusOnObject(selector: BySelector): Boolean {
         // We expect all the focusable UI elements to be arranged in a way so that it is possible
         // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index f2c5093..274d34b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -64,7 +64,18 @@
      * Defines the transition used to run the test
      */
     override val transition: FlickerBuilder.() -> Unit
-        get() = buildTransition(eachRun = true, stringExtras = emptyMap()) {
+        get() = {
+            setupAndTeardown(this)
+            setup {
+                eachRun {
+                    pipApp.launchViaIntent(wmHelper)
+                }
+            }
+            teardown {
+                eachRun {
+                    pipApp.exit(wmHelper)
+                }
+            }
             transitions {
                 pipApp.clickEnterPipButton(wmHelper)
             }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index 4f98b70..e2d0834 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -74,7 +74,7 @@
                 // This will bring PipApp to fullscreen
                 pipApp.expandPipWindowToApp(wmHelper)
                 // Wait until the other app is no longer visible
-                wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName())
+                wmHelper.waitForSurfaceAppeared(testApp.component)
             }
         }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index e00d749..3fe6f02 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -72,9 +72,9 @@
             }
             transitions {
                 // This will bring PipApp to fullscreen
-                pipApp.launchViaIntent(wmHelper)
+                pipApp.exitPipToFullScreenViaIntent(wmHelper)
                 // Wait until the other app is no longer visible
-                wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName())
+                wmHelper.waitForWindowSurfaceDisappeared(testApp.component)
             }
         }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index bb66f7b..654fa4e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -122,15 +122,14 @@
 
             setup {
                 test {
-                    removeAllTasksButHome()
                     if (!eachRun) {
-                        pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+                        pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
                         wmHelper.waitPipShown()
                     }
                 }
                 eachRun {
                     if (eachRun) {
-                        pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+                        pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
                         wmHelper.waitPipShown()
                     }
                 }
@@ -145,7 +144,6 @@
                     if (!eachRun) {
                         pipApp.exit(wmHelper)
                     }
-                    removeAllTasksButHome()
                 }
             }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index dda1a82..85527c8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -18,6 +18,7 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
@@ -51,6 +52,7 @@
         final SurfaceControl leash = createMockSurface();
         SplitLayout out = mock(SplitLayout.class);
         doReturn(dividerBounds).when(out).getDividerBounds();
+        doReturn(dividerBounds).when(out).getRefDividerBounds();
         doReturn(leash).when(out).getDividerLeash();
         return out;
     }
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index e0f04a1..9f52bf1 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -108,7 +108,6 @@
     private long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN;
     private @HardwareBuffer.Format int mHardwareBufferFormat;
     private @NamedDataSpace int mDataSpace;
-    private boolean mUseLegacyImageFormat;
 
     // Field below is used by native code, do not access or modify.
     private int mWriterFormat;
@@ -257,7 +256,6 @@
                 + ", maxImages: " + maxImages);
         }
 
-        mUseLegacyImageFormat = useLegacyImageFormat;
         // Note that the underlying BufferQueue is working in synchronous mode
         // to avoid dropping any buffers.
         mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, width, height,
@@ -334,12 +332,21 @@
             int hardwareBufferFormat, int dataSpace, int width, int height, long usage) {
         mMaxImages = maxImages;
         mUsage = usage;
-        mHardwareBufferFormat = hardwareBufferFormat;
-        mDataSpace = dataSpace;
-        int publicFormat = PublicFormatUtils.getPublicFormat(hardwareBufferFormat, dataSpace);
+        int imageFormat;
+        // if useSurfaceImageFormatInfo is true, imageFormat will be set to UNKNOWN
+        // and retrieve corresponding hardwareBufferFormat and dataSpace here.
+        if (useSurfaceImageFormatInfo) {
+            imageFormat = ImageFormat.UNKNOWN;
+            mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat);
+            mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat);
+        } else {
+            imageFormat = PublicFormatUtils.getPublicFormat(hardwareBufferFormat, dataSpace);
+            mHardwareBufferFormat = hardwareBufferFormat;
+            mDataSpace = dataSpace;
+        }
 
         initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, false,
-                publicFormat, hardwareBufferFormat, dataSpace, width, height, usage);
+                imageFormat, hardwareBufferFormat, dataSpace, width, height, usage);
     }
 
     /**
@@ -884,27 +891,17 @@
         private @HardwareBuffer.Format int mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
         private @NamedDataSpace int mDataSpace = DataSpace.DATASPACE_UNKNOWN;
         private boolean mUseSurfaceImageFormatInfo = true;
-        // set this as true temporarily now as a workaround to get correct format
-        // when using surface format by default without overriding the image format
-        // in the builder pattern
-        private boolean mUseLegacyImageFormat = true;
+        private boolean mUseLegacyImageFormat = false;
 
         /**
          * Constructs a new builder for {@link ImageWriter}.
          *
-         * <p>Uses {@code surface} input parameter to retrieve image format, hal format
-         * and hal dataspace value for default. </p>
-         *
          * @param surface The destination Surface this writer produces Image data into.
          *
          * @throws IllegalArgumentException if the surface is already abandoned.
          */
         public Builder(@NonNull Surface surface) {
             mSurface = surface;
-            // retrieve format from surface
-            mImageFormat = SurfaceUtils.getSurfaceFormat(surface);
-            mDataSpace = SurfaceUtils.getSurfaceDataspace(surface);
-            mHardwareBufferFormat = PublicFormatUtils.getHalFormat(mImageFormat);
         }
 
         /**
@@ -1058,11 +1055,6 @@
             mWidth = writer.mWidth;
             mHeight = writer.mHeight;
             mDataSpace = writer.mDataSpace;
-
-            if (!mOwner.mUseLegacyImageFormat) {
-                mFormat = PublicFormatUtils.getPublicFormat(
-                    mOwner.mHardwareBufferFormat, mDataSpace);
-            }
         }
 
         @Override
@@ -1083,7 +1075,7 @@
         public int getFormat() {
             throwISEIfImageIsInvalid();
 
-            if (mOwner.mUseLegacyImageFormat && mFormat == -1) {
+            if (mFormat == -1) {
                 mFormat = nativeGetFormat(mDataSpace);
             }
             return mFormat;
diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
index 961fab3..4ed7e19 100644
--- a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
@@ -22,6 +22,7 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.os.Handler;
 import android.os.Looper;
@@ -34,7 +35,10 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -48,13 +52,14 @@
 
     private static DeviceStateRotationLockSettingsManager sSingleton;
 
-    private final String[] mDeviceStateRotationLockDefaults;
     private final Handler mMainHandler = new Handler(Looper.getMainLooper());
     private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
     private final SecureSettings mSecureSettings;
+    private String[] mDeviceStateRotationLockDefaults;
     private SparseIntArray mDeviceStateRotationLockSettings;
     private SparseIntArray mDeviceStateRotationLockFallbackSettings;
     private String mLastSettingValue;
+    private List<SettableDeviceState> mSettableDeviceStates;
 
     @VisibleForTesting
     DeviceStateRotationLockSettingsManager(Context context, SecureSettings secureSettings) {
@@ -79,6 +84,12 @@
         return sSingleton;
     }
 
+    /** Resets the singleton instance of this class. Only used for testing. */
+    @VisibleForTesting
+    public static synchronized void resetInstance() {
+        sSingleton = null;
+    }
+
     /** Returns true if device-state based rotation lock settings are enabled. */
     public static boolean isDeviceStateRotationLockEnabled(Context context) {
         return context.getResources()
@@ -186,6 +197,12 @@
         return true;
     }
 
+    /** Returns a list of device states and their respective auto-rotation setting availability. */
+    public List<SettableDeviceState> getSettableDeviceStates() {
+        // Returning a copy to make sure that nothing outside can mutate our internal list.
+        return new ArrayList<>(mSettableDeviceStates);
+    }
+
     private void initializeInMemoryMap() {
         String serializedSetting =
                 mSecureSettings.getStringForUser(
@@ -220,6 +237,17 @@
         }
     }
 
+    /**
+     * Resets the state of the class and saved settings back to the default values provided by the
+     * resources config.
+     */
+    @VisibleForTesting
+    public void resetStateForTesting(Resources resources) {
+        mDeviceStateRotationLockDefaults =
+                resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults);
+        fallbackOnDefaults();
+    }
+
     private void fallbackOnDefaults() {
         loadDefaults();
         persistSettings();
@@ -259,6 +287,7 @@
     }
 
     private void loadDefaults() {
+        mSettableDeviceStates = new ArrayList<>(mDeviceStateRotationLockDefaults.length);
         mDeviceStateRotationLockSettings = new SparseIntArray(
                 mDeviceStateRotationLockDefaults.length);
         mDeviceStateRotationLockFallbackSettings = new SparseIntArray(1);
@@ -279,6 +308,8 @@
                                         + values.length);
                     }
                 }
+                boolean isSettable = rotationLockSetting != DEVICE_STATE_ROTATION_LOCK_IGNORED;
+                mSettableDeviceStates.add(new SettableDeviceState(deviceState, isSettable));
                 mDeviceStateRotationLockSettings.put(deviceState, rotationLockSetting);
             } catch (NumberFormatException e) {
                 Log.wtf(TAG, "Error parsing settings entry. Entry was: " + entry, e);
@@ -308,4 +339,38 @@
         /** Called whenever the settings have changed. */
         void onSettingsChanged();
     }
+
+    /** Represents a device state and whether it has an auto-rotation setting. */
+    public static class SettableDeviceState {
+        private final int mDeviceState;
+        private final boolean mIsSettable;
+
+        SettableDeviceState(int deviceState, boolean isSettable) {
+            mDeviceState = deviceState;
+            mIsSettable = isSettable;
+        }
+
+        /** Returns the device state associated with this object. */
+        public int getDeviceState() {
+            return mDeviceState;
+        }
+
+        /** Returns whether there is an auto-rotation setting for this device state. */
+        public boolean isSettable() {
+            return mIsSettable;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof SettableDeviceState)) return false;
+            SettableDeviceState that = (SettableDeviceState) o;
+            return mDeviceState == that.mDeviceState && mIsSettable == that.mIsSettable;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mDeviceState, mIsSettable);
+        }
+    }
 }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
index 1a45384..81006dd 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
@@ -30,12 +30,17 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.R;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DeviceStateRotationLockSettingsManagerTest {
@@ -94,4 +99,22 @@
 
         assertThat(mNumSettingsChanges).isEqualTo(3);
     }
+
+    @Test
+    public void getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() {
+        when(mMockResources.getStringArray(
+                R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
+                new String[]{"2:2", "4:0", "1:1", "0:0"});
+
+        List<SettableDeviceState> settableDeviceStates =
+                DeviceStateRotationLockSettingsManager.getInstance(
+                        mMockContext).getSettableDeviceStates();
+
+        assertThat(settableDeviceStates).containsExactly(
+                new SettableDeviceState(/* deviceState= */ 2, /* isSettable= */ true),
+                new SettableDeviceState(/* deviceState= */ 4, /* isSettable= */ false),
+                new SettableDeviceState(/* deviceState= */ 1, /* isSettable= */ true),
+                new SettableDeviceState(/* deviceState= */ 0, /* isSettable= */ false)
+        ).inOrder();
+    }
 }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index f0b180e..121f9e5 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -588,6 +588,9 @@
     <uses-permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" />
     <uses-permission android:name="android.permission.BIND_HOTWORD_DETECTION_SERVICE" />
 
+    <!-- Permission required for CTS test - KeyguardLockedStateApiTest -->
+    <uses-permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
+
     <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION"/>
 
     <!-- Permission required for CTS test - ResourceObserverNativeTest -->
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index dad4c19..b98f413 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -123,6 +123,7 @@
     <dimen name="bouncer_user_switcher_icon_size">190dp</dimen>
     <dimen name="bouncer_user_switcher_icon_size_plus_margin">222dp</dimen>
 
+    <dimen name="user_switcher_fullscreen_horizontal_gap">64dp</dimen>
     <dimen name="user_switcher_icon_selected_width">8dp</dimen>
     <dimen name="user_switcher_fullscreen_button_text_size">14sp</dimen>
     <dimen name="user_switcher_fullscreen_button_padding">12dp</dimen>
diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
index 2d883bc..6bb6c2d 100644
--- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
+++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
@@ -21,9 +21,8 @@
     android:id="@+id/user_switcher_root"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layout_marginBottom="40dp"
-    android:layout_marginEnd="60dp"
-    android:layout_marginStart="60dp">
+    android:layout_marginVertical="40dp"
+    android:layout_marginHorizontal="60dp">
 
   <androidx.constraintlayout.helper.widget.Flow
       android:id="@+id/flow"
@@ -36,7 +35,7 @@
       app:flow_horizontalBias="0.5"
       app:flow_verticalAlign="center"
       app:flow_wrapMode="chain"
-      app:flow_horizontalGap="64dp"
+      app:flow_horizontalGap="@dimen/user_switcher_fullscreen_horizontal_gap"
       app:flow_verticalGap="44dp"
       app:flow_horizontalStyle="packed"/>
 
diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml
index 3319442..a3d9a69 100644
--- a/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml
+++ b/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml
@@ -21,8 +21,8 @@
   <ImageView
       android:id="@+id/user_switcher_icon"
       android:layout_gravity="center"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content" />
+      android:layout_width="@dimen/bouncer_user_switcher_icon_size_plus_margin"
+      android:layout_height="@dimen/bouncer_user_switcher_icon_size_plus_margin" />
   <TextView
       style="@style/Bouncer.UserSwitcher.Spinner.Item"
       android:id="@+id/user_switcher_text"
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index c37c804..8a4516a 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -42,4 +42,8 @@
          the shade (in alpha) -->
     <dimen name="lockscreen_shade_scrim_transition_distance">200dp</dimen>
 
+    <!-- Distance that the full shade transition takes in order for media to fully transition to
+     the shade -->
+    <dimen name="lockscreen_shade_media_transition_distance">200dp</dimen>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 0d20403..de03993 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -43,6 +43,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Range;
+import android.util.Size;
 import android.view.Choreographer;
 import android.view.Display;
 import android.view.Gravity;
@@ -62,6 +63,8 @@
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.accessibility.IRemoteMagnificationAnimationCallback;
 
+import androidx.core.math.MathUtils;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.systemui.R;
@@ -166,6 +169,7 @@
     private final Rect mMagnificationFrameBoundary = new Rect();
     // The top Y of the system gesture rect at the bottom. Set to -1 if it is invalid.
     private int mSystemGestureTop = -1;
+    private int mMinWindowSize;
 
     private final WindowMagnificationAnimationController mAnimationController;
     private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@@ -208,8 +212,10 @@
         mBounceEffectDuration = mResources.getInteger(
                 com.android.internal.R.integer.config_shortAnimTime);
         updateDimensions();
-        setMagnificationFrameWith(mWindowBounds, mWindowBounds.width() / 2,
-                mWindowBounds.height() / 2);
+
+        final Size windowSize = getDefaultWindowSizeWithWindowBounds(mWindowBounds);
+        setMagnificationFrame(windowSize.getWidth(), windowSize.getHeight(),
+                mWindowBounds.width() / 2, mWindowBounds.height() / 2);
         computeBounceAnimationScale();
 
         mMirrorWindowControl = mirrorWindowControl;
@@ -281,6 +287,8 @@
                 R.dimen.magnification_drag_view_size);
         mOuterBorderSize = mResources.getDimensionPixelSize(
                 R.dimen.magnification_outer_border_margin);
+        mMinWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
     }
 
     private void computeBounceAnimationScale() {
@@ -414,9 +422,12 @@
             return false;
         }
         mWindowBounds.set(currentWindowBounds);
+        final Size windowSize = getDefaultWindowSizeWithWindowBounds(mWindowBounds);
         final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width();
         final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height();
-        setMagnificationFrameWith(mWindowBounds, (int) newCenterX, (int) newCenterY);
+
+        setMagnificationFrame(windowSize.getWidth(), windowSize.getHeight(), (int) newCenterX,
+                (int) newCenterY);
         calculateMagnificationFrameBoundary();
         return true;
     }
@@ -454,11 +465,6 @@
 
         mWindowBounds.set(currentWindowBounds);
 
-        calculateMagnificationFrameBoundary();
-
-        if (!isWindowVisible()) {
-            return;
-        }
         // Keep MirrorWindow position on the screen unchanged when device rotates 90°
         // clockwise or anti-clockwise.
 
@@ -469,14 +475,13 @@
         } else if (rotationDegree == 270) {
             matrix.postTranslate(0, mWindowBounds.height());
         }
-        // The rect of MirrorView is going to be transformed.
-        LayoutParams params =
-                (LayoutParams) mMirrorView.getLayoutParams();
-        mTmpRect.set(params.x, params.y, params.x + params.width, params.y + params.height);
-        final RectF transformedRect = new RectF(mTmpRect);
+
+        final RectF transformedRect = new RectF(mMagnificationFrame);
+        // The window frame is going to be transformed by the rotation matrix.
+        transformedRect.inset(-mMirrorSurfaceMargin, -mMirrorSurfaceMargin);
         matrix.mapRect(transformedRect);
-        moveWindowMagnifier(transformedRect.left - mTmpRect.left,
-                transformedRect.top - mTmpRect.top);
+        setWindowSizeAndCenter((int) transformedRect.width(), (int) transformedRect.height(),
+                (int) transformedRect.centerX(), (int) transformedRect.centerY());
     }
 
     /** Returns the rotation degree change of two {@link Surface.Rotation} */
@@ -573,16 +578,52 @@
         }
     }
 
-    private void setMagnificationFrameWith(Rect windowBounds, int centerX, int centerY) {
+    /**
+     * Sets the window size with given width and height in pixels without changing the
+     * window center. The width or the height will be clamped in the range
+     * [{@link #mMinWindowSize}, screen width or height].
+     *
+     * @param width the window width in pixels
+     * @param height the window height in pixels.
+     */
+    public void setWindowSize(int width, int height) {
+        setWindowSizeAndCenter(width, height, Float.NaN, Float.NaN);
+    }
+
+    void setWindowSizeAndCenter(int width, int height, float centerX, float centerY) {
+        width = MathUtils.clamp(width, mMinWindowSize, mWindowBounds.width());
+        height = MathUtils.clamp(height, mMinWindowSize, mWindowBounds.height());
+
+        if (Float.isNaN(centerX)) {
+            centerX = mMagnificationFrame.centerX();
+        }
+        if (Float.isNaN(centerX)) {
+            centerY = mMagnificationFrame.centerY();
+        }
+
+        final int frameWidth = width - 2 * mMirrorSurfaceMargin;
+        final int frameHeight = height - 2 * mMirrorSurfaceMargin;
+        setMagnificationFrame(frameWidth, frameHeight, (int) centerX, (int) centerY);
+        calculateMagnificationFrameBoundary();
+        // Correct the frame position to ensure it is inside the boundary.
+        updateMagnificationFramePosition(0, 0);
+        modifyWindowMagnification(true);
+    }
+
+    private void setMagnificationFrame(int width, int height, int centerX, int centerY) {
         // Sets the initial frame area for the mirror and place it to the given center on the
         // display.
+        final int initX = centerX - width / 2;
+        final int initY = centerY - height / 2;
+        mMagnificationFrame.set(initX, initY, initX + width, initY + height);
+    }
+
+    private Size getDefaultWindowSizeWithWindowBounds(Rect windowBounds) {
         int initSize = Math.min(windowBounds.width(), windowBounds.height()) / 2;
         initSize = Math.min(mResources.getDimensionPixelSize(R.dimen.magnification_max_frame_size),
                 initSize);
         initSize += 2 * mMirrorSurfaceMargin;
-        final int initX = centerX - initSize / 2;
-        final int initY = centerY - initSize / 2;
-        mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize);
+        return new Size(initSize, initSize);
     }
 
     /**
@@ -596,8 +637,7 @@
         }
         mTransaction.show(mMirrorSurface)
                 .reparent(mMirrorSurface, mMirrorSurfaceView.getSurfaceControl());
-
-        modifyWindowMagnification(mTransaction);
+        modifyWindowMagnification(false);
     }
 
     private void addDragTouchListeners() {
@@ -615,18 +655,25 @@
     }
 
     /**
-     * Modifies the placement of the mirrored content when the position of mMirrorView is updated.
+     * Modifies the placement of the mirrored content when the position or size of mMirrorView is
+     * updated.
+     *
+     * @param computeWindowSize set to {@code true} to compute window size with
+     * {@link #mMagnificationFrame}.
      */
-    private void modifyWindowMagnification(SurfaceControl.Transaction t) {
+    private void modifyWindowMagnification(boolean computeWindowSize) {
         mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback);
-        updateMirrorViewLayout();
+        updateMirrorViewLayout(computeWindowSize);
     }
 
     /**
-     * Updates the layout params of MirrorView and translates MirrorView position when the view is
-     * moved close to the screen edges.
+     * Updates the layout params of MirrorView based on the size of {@link #mMagnificationFrame}
+     * and translates MirrorView position when the view is moved close to the screen edges;
+     *
+     * @param computeWindowSize set to {@code true} to compute window size with
+     * {@link #mMagnificationFrame}.
      */
-    private void updateMirrorViewLayout() {
+    private void updateMirrorViewLayout(boolean computeWindowSize) {
         if (!isWindowVisible()) {
             return;
         }
@@ -637,6 +684,10 @@
                 (LayoutParams) mMirrorView.getLayoutParams();
         params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
         params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
+        if (computeWindowSize) {
+            params.width = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin;
+            params.height = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin;
+        }
 
         // Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView
         // able to move close to the screen edges.
@@ -899,7 +950,7 @@
             createMirrorWindow();
             showControls();
         } else {
-            modifyWindowMagnification(mTransaction);
+            modifyWindowMagnification(false);
         }
     }
 
@@ -930,7 +981,7 @@
             return;
         }
         if (updateMagnificationFramePosition((int) offsetX, (int) offsetY)) {
-            modifyWindowMagnification(mTransaction);
+            modifyWindowMagnification(false);
         }
     }
 
@@ -1014,9 +1065,15 @@
         pw.println("      mOverlapWithGestureInsets:" + mOverlapWithGestureInsets);
         pw.println("      mScale:" + mScale);
         pw.println("      mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty"));
+        pw.println("      mMagnificationFrameBoundary:"
+                + (isWindowVisible() ? mMagnificationFrameBoundary : "empty"));
+        pw.println("      mMagnificationFrame:"
+                + (isWindowVisible() ? mMagnificationFrame : "empty"));
         pw.println("      mSourceBounds:"
                  + (isWindowVisible() ? mSourceBounds : "empty"));
         pw.println("      mSystemGestureTop:" + mSystemGestureTop);
+        pw.println("      mMagnificationFrameOffsetX:" + mMagnificationFrameOffsetX);
+        pw.println("      mMagnificationFrameOffsetY:" + mMagnificationFrameOffsetY);
     }
 
     private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index be76e8f..7450103 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -18,12 +18,9 @@
 
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
 
-import android.graphics.Rect;
-import android.graphics.Region;
 import android.os.Handler;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
@@ -62,43 +59,6 @@
     // Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
     private final Handler mHandler;
 
-    // A hook into the internal inset calculation where we declare the overlays as the only
-    // touchable regions.
-    private final ViewTreeObserver.OnComputeInternalInsetsListener
-            mOnComputeInternalInsetsListener =
-            new ViewTreeObserver.OnComputeInternalInsetsListener() {
-                @Override
-                public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
-                    inoutInfo.setTouchableInsets(
-                            ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-                    final Region region = new Region();
-                    final Rect rect = new Rect();
-                    final int childCount = mDreamOverlayContentView.getChildCount();
-                    for (int i = 0; i < childCount; i++) {
-                        View child = mDreamOverlayContentView.getChildAt(i);
-
-                        if (mComplicationHostViewController.getView() == child) {
-                            region.op(mComplicationHostViewController.getTouchRegions(),
-                                    Region.Op.UNION);
-                            continue;
-                        }
-
-                        if (child.getGlobalVisibleRect(rect)) {
-                            region.op(rect, Region.Op.UNION);
-                        }
-                    }
-
-                    // Add the notifications drag area to the tap region (otherwise the
-                    // notifications shade can't be dragged down).
-                    if (mDreamOverlayContentView.getGlobalVisibleRect(rect)) {
-                        rect.bottom = rect.top + mDreamOverlayNotificationsDragAreaHeight;
-                        region.op(rect, Region.Op.UNION);
-                    }
-
-                    inoutInfo.touchableRegion.set(region);
-                }
-            };
-
     @Inject
     public DreamOverlayContainerViewController(
             DreamOverlayContainerView containerView,
@@ -136,16 +96,12 @@
 
     @Override
     protected void onViewAttached() {
-        mView.getViewTreeObserver()
-                .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
         mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
     }
 
     @Override
     protected void onViewDetached() {
         mHandler.removeCallbacks(this::updateBurnInOffsets);
-        mView.getViewTreeObserver()
-                .removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
     }
 
     View getContainerView() {
@@ -162,6 +118,7 @@
         // so no translation occurs when the values don't change.
         mView.setTranslationX(getBurnInOffset(mMaxBurnInOffset * 2, true)
                 - mMaxBurnInOffset);
+
         mView.setTranslationY(getBurnInOffset(mMaxBurnInOffset * 2, false)
                 - mMaxBurnInOffset);
 
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index ed82ab0..32b2309 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -24,6 +24,7 @@
 import android.net.NetworkRequest;
 
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.touch.TouchInsetManager;
 import com.android.systemui.util.ViewController;
 
 import java.lang.annotation.Retention;
@@ -48,6 +49,7 @@
     private static final int WIFI_STATUS_AVAILABLE = 2;
 
     private final ConnectivityManager mConnectivityManager;
+    private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
 
     private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
             .clearCapabilities()
@@ -77,9 +79,11 @@
     @Inject
     public DreamOverlayStatusBarViewController(
             DreamOverlayStatusBarView view,
-            ConnectivityManager connectivityManager) {
+            ConnectivityManager connectivityManager,
+            TouchInsetManager.TouchInsetSession touchInsetSession) {
         super(view);
         mConnectivityManager = connectivityManager;
+        mTouchInsetSession = touchInsetSession;
     }
 
     @Override
@@ -92,11 +96,13 @@
         onWifiAvailabilityChanged(
                 capabilities != null
                         && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
+        mTouchInsetSession.addViewToTracking(mView);
     }
 
     @Override
     protected void onViewDetached() {
         mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+        mTouchInsetSession.clear();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
index 0b80d8a..437e799 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -27,6 +27,7 @@
 import androidx.constraintlayout.widget.Constraints;
 
 import com.android.systemui.R;
+import com.android.systemui.touch.TouchInsetManager;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -52,6 +53,7 @@
     private static class ViewEntry implements Comparable<ViewEntry> {
         private final View mView;
         private final ComplicationLayoutParams mLayoutParams;
+        private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
         private final Parent mParent;
         @Complication.Category
         private final int mCategory;
@@ -61,7 +63,8 @@
          * Default constructor. {@link Parent} allows for the {@link ViewEntry}'s surrounding
          * view hierarchy to be accessed without traversing the entire view tree.
          */
-        ViewEntry(View view, ComplicationLayoutParams layoutParams, int category, Parent parent,
+        ViewEntry(View view, ComplicationLayoutParams layoutParams,
+                TouchInsetManager.TouchInsetSession touchSession, int category, Parent parent,
                 int margin) {
             mView = view;
             // Views that are generated programmatically do not have a unique id assigned to them
@@ -70,9 +73,12 @@
             // {@link Complication.ViewHolder} should not reference the root container by id.
             mView.setId(View.generateViewId());
             mLayoutParams = layoutParams;
+            mTouchInsetSession = touchSession;
             mCategory = category;
             mParent = parent;
             mMargin = margin;
+
+            touchSession.addViewToTracking(mView);
         }
 
         /**
@@ -217,6 +223,7 @@
             mParent.removeEntry(this);
 
             ((ViewGroup) mView.getParent()).removeView(mView);
+            mTouchInsetSession.removeViewFromTracking(mView);
         }
 
         @Override
@@ -242,15 +249,18 @@
          */
         private static class Builder {
             private final View mView;
+            private final TouchInsetManager.TouchInsetSession mTouchSession;
             private final ComplicationLayoutParams mLayoutParams;
             private final int mCategory;
             private Parent mParent;
             private int mMargin;
 
-            Builder(View view, ComplicationLayoutParams lp, @Complication.Category int category) {
+            Builder(View view, TouchInsetManager.TouchInsetSession touchSession,
+                    ComplicationLayoutParams lp, @Complication.Category int category) {
                 mView = view;
                 mLayoutParams = lp;
                 mCategory = category;
+                mTouchSession = touchSession;
             }
 
             /**
@@ -291,7 +301,8 @@
              * Builds and returns the resulting {@link ViewEntry}.
              */
             ViewEntry build() {
-                return new ViewEntry(mView, mLayoutParams, mCategory, mParent, mMargin);
+                return new ViewEntry(mView, mLayoutParams, mTouchSession, mCategory, mParent,
+                        mMargin);
             }
         }
 
@@ -442,13 +453,16 @@
     private final int mMargin;
     private final HashMap<ComplicationId, ViewEntry> mEntries = new HashMap<>();
     private final HashMap<Integer, PositionGroup> mPositions = new HashMap<>();
+    private final TouchInsetManager.TouchInsetSession mSession;
 
     /** */
     @Inject
     public ComplicationLayoutEngine(@Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout layout,
-            @Named(COMPLICATION_MARGIN) int margin) {
+            @Named(COMPLICATION_MARGIN) int margin,
+            TouchInsetManager.TouchInsetSession session) {
         mLayout = layout;
         mMargin = margin;
+        mSession = session;
     }
 
     /**
@@ -468,7 +482,7 @@
             removeComplication(id);
         }
 
-        final ViewEntry.Builder entryBuilder = new ViewEntry.Builder(view, lp, category)
+        final ViewEntry.Builder entryBuilder = new ViewEntry.Builder(view, mSession, lp, category)
                 .setMargin(mMargin);
 
         // Add position group if doesn't already exist
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 839a05e..63676d6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -29,6 +29,9 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.DreamOverlayContainerView;
 import com.android.systemui.dreams.DreamOverlayStatusBarView;
+import com.android.systemui.touch.TouchInsetManager;
+
+import java.util.concurrent.Executor;
 
 import javax.inject.Named;
 
@@ -65,6 +68,21 @@
 
     /** */
     @Provides
+    public static TouchInsetManager.TouchInsetSession providesTouchInsetSession(
+            TouchInsetManager manager) {
+        return manager.createSession();
+    }
+
+    /** */
+    @Provides
+    @DreamOverlayComponent.DreamOverlayScope
+    public static TouchInsetManager providesTouchInsetManager(@Main Executor executor,
+            DreamOverlayContainerView view) {
+        return new TouchInsetManager(executor, view);
+    }
+
+    /** */
+    @Provides
     @DreamOverlayComponent.DreamOverlayScope
     public static DreamOverlayStatusBarView providesDreamOverlayStatusBarView(
             DreamOverlayContainerView view) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index eee3955..f4b6fbd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -795,6 +795,9 @@
     @TransformationType
     fun calculateTransformationType(): Int {
         if (isTransitioningToFullShade) {
+            if (inSplitShade) {
+                return TRANSFORMATION_TYPE_TRANSITION
+            }
             return TRANSFORMATION_TYPE_FADE
         }
         if (previousLocation == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QS ||
@@ -961,6 +964,7 @@
             (qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
             qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
             !hasActiveMedia -> LOCATION_QS
+            onLockscreen && isSplitShadeExpanding() -> LOCATION_QS
             onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
             onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN
             else -> LOCATION_QQS
@@ -986,6 +990,10 @@
         return location
     }
 
+    private fun isSplitShadeExpanding(): Boolean {
+        return inSplitShade && isTransitioningToFullShade
+    }
+
     /**
      * Are we currently transforming to the full shade and already in QQS
      */
@@ -993,6 +1001,10 @@
         if (!isTransitioningToFullShade) {
             return false
         }
+        if (inSplitShade) {
+            // Split shade doesn't use QQS.
+            return false
+        }
         return fullShadeTransitionProgress > 0.5f
     }
 
@@ -1000,6 +1012,10 @@
      * Is the current transformationType fading
      */
     private fun isCurrentlyFading(): Boolean {
+        if (isSplitShadeExpanding()) {
+            // Split shade always uses transition instead of fade.
+            return false
+        }
         if (isTransitioningToFullShade) {
             return true
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index a262b8a..217210a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -165,6 +165,7 @@
             powerMenuLite.visibility = View.GONE
         }
         settingsButton.setOnClickListener(onClickListener)
+        multiUserSetting.isListening = true
         if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
             val securityFooter = securityFooterController.view as DualHeightHorizontalLinearLayout
             securityFootersContainer?.addView(securityFooter)
@@ -215,6 +216,7 @@
 
     override fun onViewDetached() {
         setListening(false)
+        multiUserSetting.isListening = false
     }
 
     fun setListening(listening: Boolean) {
@@ -222,7 +224,6 @@
             return
         }
         this.listening = listening
-        multiUserSetting.isListening = listening
         if (this.listening) {
             userInfoController.addCallback(onUserInfoChangedListener)
             updateView()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 3c7933f..3ef7220 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -513,7 +513,8 @@
         mContainer.setExpansion(expansion);
         final float translationScaleY = (mInSplitShade
                 ? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1);
-        boolean onKeyguardAndExpanded = isKeyguardState() && !mShowCollapsedOnKeyguard;
+        boolean onKeyguard = isKeyguardState();
+        boolean onKeyguardAndExpanded = onKeyguard && !mShowCollapsedOnKeyguard;
         if (!mHeaderAnimating && !headerWillBeAnimating()) {
             getView().setTranslationY(
                     onKeyguardAndExpanded
@@ -547,6 +548,7 @@
                 mHeader.updateResources();
             }
         }
+        mQSPanelController.setIsOnKeyguard(onKeyguard);
         mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
         mQSFooterActionController.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
         mQSPanelController.setRevealExpansion(expansion);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 5126fcb..b04d752 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -110,6 +110,8 @@
     private final ArrayMap<View, Integer> mChildrenLayoutTop = new ArrayMap<>();
     private final Rect mClippingRect = new Rect();
     private boolean mUseNewFooter = false;
+    private ViewGroup mMediaHostView;
+    private boolean mShouldMoveMediaOnExpansion = true;
 
     public QSPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -289,9 +291,15 @@
         for (int i = 0; i < getChildCount(); i++) {
             View child = getChildAt(i);
             if (move) {
+                int topOffset;
+                if (child == mMediaHostView && !mShouldMoveMediaOnExpansion) {
+                    topOffset = 0;
+                } else {
+                    topOffset = tileHeightOffset;
+                }
                 int top = Objects.requireNonNull(mChildrenLayoutTop.get(child));
-                child.setLeftTopRightBottom(child.getLeft(), top + tileHeightOffset,
-                        child.getRight(), top + tileHeightOffset + child.getHeight());
+                child.setLeftTopRightBottom(child.getLeft(), top + topOffset,
+                        child.getRight(), top + topOffset + child.getHeight());
             }
             if (child == mTileLayout) {
                 move = true;
@@ -463,6 +471,7 @@
         if (!mUsingMediaPlayer) {
             return;
         }
+        mMediaHostView = hostView;
         ViewGroup newParent = horizontal ? mHorizontalLinearLayout : this;
         ViewGroup currentParent = (ViewGroup) hostView.getParent();
         if (currentParent != newParent) {
@@ -656,6 +665,19 @@
         updatePadding();
     }
 
+    /**
+     * Sets whether the media container should move during the expansion of the QS Panel.
+     *
+     * As the QS Panel expands and the QS unsquish, the views below the QS tiles move to adapt to
+     * the new height of the QS tiles.
+     *
+     * In some cases this might not be wanted for media. One example is when there is a transition
+     * animation of the media container happening on split shade lock screen.
+     */
+    public void setShouldMoveMediaOnExpansion(boolean shouldMoveMediaOnExpansion) {
+        mShouldMoveMediaOnExpansion = shouldMoveMediaOnExpansion;
+    }
+
     private class H extends Handler {
         private static final int ANNOUNCE_FOR_ACCESSIBILITY = 1;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 3172aa9..6572daa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -419,6 +419,16 @@
         return mView.getBrightnessView();
     }
 
+    /** Sets whether we are currently on lock screen. */
+    public void setIsOnKeyguard(boolean isOnKeyguard) {
+        boolean isOnSplitShadeLockscreen = mShouldUseSplitNotificationShade && isOnKeyguard;
+        // When the split shade is expanding on lockscreen, the media container transitions from the
+        // lockscreen to QS.
+        // We have to prevent the media container position from moving during the transition to have
+        // a smooth translation animation without stuttering.
+        mView.setShouldMoveMediaOnExpansion(!isOnSplitShadeLockscreen);
+    }
+
     /** */
     public static final class TileRecord {
         public TileRecord(QSTile tile, com.android.systemui.plugins.qs.QSTileView tileView) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 8366bdd..8a9d6dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -342,9 +342,7 @@
                     qS.setTransitionToFullShadeAmount(field, qSDragProgress)
                     notificationPanelController.setTransitionToFullShadeAmount(field,
                             false /* animate */, 0 /* delay */)
-                    // TODO: appear media also in split shade
-                    val mediaAmount = if (useSplitShade) 0f else field
-                    mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount)
+                    mediaHierarchyManager.setTransitionToFullShadeAmount(field)
                     transitionToShadeAmountCommon(field)
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 9f9e7d9..5caf4f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -26,6 +26,7 @@
 import android.app.IActivityManager;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.graphics.PixelFormat;
 import android.graphics.Region;
 import android.os.Binder;
@@ -117,6 +118,7 @@
      * @see #batchApplyWindowLayoutParams(Runnable)
      */
     private int mDeferWindowLayoutParams;
+    private boolean mLastKeyguardRotationAllowed;
 
     @Inject
     public NotificationShadeWindowControllerImpl(Context context, WindowManager windowManager,
@@ -143,7 +145,7 @@
         mScreenOffAnimationController = screenOffAnimationController;
         dumpManager.registerDumpable(getClass().getName(), this);
         mAuthController = authController;
-
+        mLastKeyguardRotationAllowed = mKeyguardStateController.isKeyguardScreenRotationAllowed();
         mLockScreenDisplayTimeout = context.getResources()
                 .getInteger(R.integer.config_lockScreenDisplayTimeout);
         ((SysuiStatusBarStateController) statusBarStateController)
@@ -779,6 +781,17 @@
         setKeyguardDark(useDarkText);
     }
 
+    @Override
+    public void onConfigChanged(Configuration newConfig) {
+        final boolean newScreenRotationAllowed = mKeyguardStateController
+                .isKeyguardScreenRotationAllowed();
+
+        if (mLastKeyguardRotationAllowed != newScreenRotationAllowed) {
+            apply(mCurrentState);
+            mLastKeyguardRotationAllowed = newScreenRotationAllowed;
+        }
+    }
+
     /**
      * When keyguard will be dismissed but didn't start animation yet.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/touch/TouchInsetManager.java b/packages/SystemUI/src/com/android/systemui/touch/TouchInsetManager.java
new file mode 100644
index 0000000..de4e1e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touch/TouchInsetManager.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touch;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.View;
+import android.view.ViewRootImpl;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.concurrent.Executor;
+
+/**
+ * {@link TouchInsetManager} handles setting the touchable inset regions for a given View. This
+ * is useful for passing through touch events for all but select areas.
+ */
+public class TouchInsetManager {
+    /**
+     * {@link TouchInsetSession} provides an individualized session with the
+     * {@link TouchInsetManager}, linking any action to the client.
+     */
+    public static class TouchInsetSession {
+        private final TouchInsetManager mManager;
+
+        private final HashSet<View> mTrackedViews;
+        private final Executor mExecutor;
+
+        private final View.OnLayoutChangeListener mOnLayoutChangeListener =
+                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
+                        -> updateTouchRegion();
+
+        /**
+         * Default constructor
+         * @param manager The parent {@link TouchInsetManager} which will be affected by actions on
+         *                this session.
+         * @param rootView The parent of views that will be tracked.
+         * @param executor An executor for marshalling operations.
+         */
+        TouchInsetSession(TouchInsetManager manager, Executor executor) {
+            mManager = manager;
+            mTrackedViews = new HashSet<>();
+            mExecutor = executor;
+        }
+
+        /**
+         * Adds a descendant of the root view to be tracked.
+         * @param view {@link View} to be tracked.
+         */
+        public void addViewToTracking(View view) {
+            mExecutor.execute(() -> {
+                mTrackedViews.add(view);
+                view.addOnLayoutChangeListener(mOnLayoutChangeListener);
+                updateTouchRegion();
+            });
+        }
+
+        /**
+         * Removes a view from further tracking
+         * @param view {@link View} to be removed.
+         */
+        public void removeViewFromTracking(View view) {
+            mExecutor.execute(() -> {
+                mTrackedViews.remove(view);
+                view.removeOnLayoutChangeListener(mOnLayoutChangeListener);
+                updateTouchRegion();
+            });
+        }
+
+        private void updateTouchRegion() {
+            final Region cumulativeRegion = Region.obtain();
+
+            mTrackedViews.stream().forEach(view -> {
+                final Rect boundaries = new Rect();
+                view.getBoundsOnScreen(boundaries);
+                cumulativeRegion.op(boundaries, Region.Op.UNION);
+            });
+
+            mManager.setTouchRegion(this, cumulativeRegion);
+
+            cumulativeRegion.recycle();
+        }
+
+        /**
+         * Removes all tracked views and updates insets accordingly.
+         */
+        public void clear() {
+            mExecutor.execute(() -> {
+                mManager.clearRegion(this);
+                mTrackedViews.clear();
+            });
+        }
+    }
+
+    private final HashMap<TouchInsetSession, Region> mDefinedRegions = new HashMap<>();
+    private final Executor mExecutor;
+    private final View mRootView;
+
+    private final View.OnAttachStateChangeListener mAttachListener =
+            new View.OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View v) {
+                    updateTouchInset();
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                }
+            };
+
+    /**
+     * Default constructor.
+     * @param executor An {@link Executor} to marshal all operations on.
+     * @param rootView The root {@link View} for all views in sessions.
+     */
+    public TouchInsetManager(Executor executor, View rootView) {
+        mExecutor = executor;
+        mRootView = rootView;
+        mRootView.addOnAttachStateChangeListener(mAttachListener);
+
+    }
+
+    /**
+     * Creates a new associated session.
+     */
+    public TouchInsetSession createSession() {
+        return new TouchInsetSession(this, mExecutor);
+    }
+
+    private void updateTouchInset() {
+        final ViewRootImpl viewRootImpl = mRootView.getViewRootImpl();
+
+        if (viewRootImpl == null) {
+            return;
+        }
+
+        final Region aggregateRegion = Region.obtain();
+
+        for (Region region : mDefinedRegions.values()) {
+            aggregateRegion.op(region, Region.Op.UNION);
+        }
+
+        viewRootImpl.setTouchableRegion(aggregateRegion);
+
+        aggregateRegion.recycle();
+    }
+
+    protected void setTouchRegion(TouchInsetSession session, Region region) {
+        final Region introducedRegion = Region.obtain(region);
+        mExecutor.execute(() -> {
+            mDefinedRegions.put(session, introducedRegion);
+            updateTouchInset();
+        });
+    }
+
+    private void clearRegion(TouchInsetSession session) {
+        mExecutor.execute(() -> {
+            final Region storedRegion = mDefinedRegions.remove(session);
+
+            if (storedRegion != null) {
+                storedRegion.recycle();
+            }
+
+            updateTouchInset();
+        });
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index 14585fb..c0d7925 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -35,9 +35,8 @@
 import android.widget.ArrayAdapter
 import android.widget.ImageView
 import android.widget.TextView
-
 import androidx.constraintlayout.helper.widget.Flow
-
+import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.util.UserIcons
 import com.android.settingslib.Utils
 import com.android.systemui.R
@@ -47,12 +46,12 @@
 import com.android.systemui.statusbar.phone.ShadeController
 import com.android.systemui.statusbar.policy.UserSwitcherController
 import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter
-import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord
 import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA
 import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA
+import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord
 import com.android.systemui.util.LifecycleActivity
-
 import javax.inject.Inject
+import kotlin.math.ceil
 
 private const val USER_VIEW = "user_view"
 
@@ -137,6 +136,18 @@
             return UserIcons.getDefaultUserIcon(resources, item.info.id, false)
         }
 
+        fun getTotalUserViews(): Int {
+            return users.count { item ->
+                !doNotRenderUserView(item)
+            }
+        }
+
+        fun doNotRenderUserView(item: UserRecord): Boolean {
+            return item.isAddUser ||
+                    item.isAddSupervisedUser ||
+                    item.isGuest && item.info == null
+        }
+
         private fun getDrawable(item: UserRecord): Drawable {
             var drawable = if (item.isCurrent && item.isGuest) {
                 getDrawable(R.drawable.ic_avatar_guest_user)
@@ -211,7 +222,8 @@
 
         userSwitcherController.init(parent)
         initBroadcastReceiver()
-        buildUserViews()
+
+        parent.post { buildUserViews() }
     }
 
     private fun showPopupMenu() {
@@ -272,16 +284,32 @@
         }
         parent.removeViews(start, count)
         addUserRecords.clear()
-
         val flow = requireViewById<Flow>(R.id.flow)
+        val totalWidth = parent.width
+        val userViewCount = adapter.getTotalUserViews()
+        val maxColumns = getMaxColumns(userViewCount)
+        val horizontalGap = resources
+            .getDimensionPixelSize(R.dimen.user_switcher_fullscreen_horizontal_gap)
+        val totalWidthOfHorizontalGap = (maxColumns - 1) * horizontalGap
+        val maxWidgetDiameter = (totalWidth - totalWidthOfHorizontalGap) / maxColumns
+
+        flow.setMaxElementsWrap(maxColumns)
+
         for (i in 0 until adapter.getCount()) {
             val item = adapter.getItem(i)
-            if (item.isAddUser ||
-                item.isAddSupervisedUser ||
-                item.isGuest && item.info == null) {
+            if (adapter.doNotRenderUserView(item)) {
                 addUserRecords.add(item)
             } else {
                 val userView = adapter.getView(i, null, parent)
+                userView.requireViewById<ImageView>(R.id.user_switcher_icon).apply {
+                    val lp = layoutParams
+                    if (maxWidgetDiameter < lp.width) {
+                        lp.width = maxWidgetDiameter
+                        lp.height = maxWidgetDiameter
+                        layoutParams = lp
+                    }
+                }
+
                 userView.setId(View.generateViewId())
                 parent.addView(userView)
 
@@ -333,6 +361,11 @@
         broadcastDispatcher.registerReceiver(broadcastReceiver, filter)
     }
 
+    @VisibleForTesting
+    fun getMaxColumns(userCount: Int): Int {
+        return if (userCount < 5) 4 else ceil(userCount / 2.0).toInt()
+    }
+
     private class ItemAdapter(
         val parentContext: Context,
         val resource: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 1dd5e22..6e5926d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -88,6 +88,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 
 @LargeTest
 @TestableLooper.RunWithLooper
@@ -345,15 +346,17 @@
 
     @Test
     public void onOrientationChanged_disabled_updateDisplayRotation() {
-        final Display display = Mockito.spy(mContext.getDisplay());
-        when(display.getRotation()).thenReturn(Surface.ROTATION_90);
-        when(mContext.getDisplay()).thenReturn(display);
+        final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
+        // Rotate the window clockwise 90 degree.
+        windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
+                windowBounds.right);
+        mWindowManager.setWindowBounds(windowBounds);
+        final int newRotation = simulateRotateTheDevice();
 
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
-        });
+        mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
+                ActivityInfo.CONFIG_ORIENTATION));
 
-        assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
+        assertEquals(newRotation, mWindowMagnificationController.mRotation);
     }
 
     @Test
@@ -603,6 +606,113 @@
         ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag());
     }
 
+    @Test
+    public void setMinimumWindowSize_enabled_expectedWindowSize() {
+        final int minimumWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        final int  expectedWindowHeight = minimumWindowSize;
+        final int  expectedWindowWidth = minimumWindowSize;
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
+            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+
+        });
+
+        assertEquals(expectedWindowHeight, actualWindowHeight.get());
+        assertEquals(expectedWindowWidth, actualWindowWidth.get());
+    }
+
+    @Test
+    public void setMinimumWindowSizeThenEnable_expectedWindowSize() {
+        final int minimumWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        final int  expectedWindowHeight = minimumWindowSize;
+        final int  expectedWindowWidth = minimumWindowSize;
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
+            mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+                    Float.NaN, Float.NaN);
+            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+        });
+
+        assertEquals(expectedWindowHeight, actualWindowHeight.get());
+        assertEquals(expectedWindowWidth, actualWindowWidth.get());
+    }
+
+    @Test
+    public void setWindowSizeLessThanMin_enabled_minimumWindowSize() {
+        final int minimumWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSize(minimumWindowSize - 10,
+                    minimumWindowSize - 10);
+            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+        });
+
+        assertEquals(minimumWindowSize, actualWindowHeight.get());
+        assertEquals(minimumWindowSize, actualWindowWidth.get());
+    }
+
+    @Test
+    public void setWindowSizeLargerThanScreenSize_enabled_windowSizeIsScreenSize() {
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSize(bounds.width() + 10, bounds.height() + 10);
+            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+        });
+
+        assertEquals(bounds.height(), actualWindowHeight.get());
+        assertEquals(bounds.width(), actualWindowWidth.get());
+    }
+
+    @Test
+    public void setWindowCenterOutOfScreen_enabled_magnificationCenterIsInsideTheScreen() {
+
+        final int minimumWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        final AtomicInteger magnificationCenterX = new AtomicInteger();
+        final AtomicInteger magnificationCenterY = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSizeAndCenter(minimumWindowSize,
+                    minimumWindowSize, bounds.right, bounds.bottom);
+            magnificationCenterX.set((int) mWindowMagnificationController.getCenterX());
+            magnificationCenterY.set((int) mWindowMagnificationController.getCenterY());
+        });
+
+        assertTrue(magnificationCenterX.get() < bounds.right);
+        assertTrue(magnificationCenterY.get() < bounds.bottom);
+    }
+
     private CharSequence getAccessibilityWindowTitle() {
         final View mirrorView = mWindowManager.getAttachedView();
         if (mirrorView == null) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 7af039b..8ce10b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -17,7 +17,6 @@
 package com.android.systemui.dreams;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.eq;
@@ -118,31 +117,6 @@
     }
 
     @Test
-    public void testOnViewAttachedRegistersComputeInsetsListener() {
-        mController.onViewAttached();
-        verify(mViewTreeObserver).addOnComputeInternalInsetsListener(any());
-    }
-
-    @Test
-    public void testOnViewDetachedUnregistersComputeInsetsListener() {
-        mController.onViewDetached();
-        verify(mViewTreeObserver).removeOnComputeInternalInsetsListener(any());
-    }
-
-    @Test
-    public void testComputeInsetsListenerReturnsRegion() {
-        final ArgumentCaptor<ViewTreeObserver.OnComputeInternalInsetsListener>
-                computeInsetsListenerCapture =
-                ArgumentCaptor.forClass(ViewTreeObserver.OnComputeInternalInsetsListener.class);
-        mController.onViewAttached();
-        verify(mViewTreeObserver).addOnComputeInternalInsetsListener(
-                computeInsetsListenerCapture.capture());
-        final ViewTreeObserver.InternalInsetsInfo info = new ViewTreeObserver.InternalInsetsInfo();
-        computeInsetsListenerCapture.getValue().onComputeInternalInsets(info);
-        assertNotNull(info.touchableRegion);
-    }
-
-    @Test
     public void testBurnInProtectionStartsWhenContentViewAttached() {
         mController.onViewAttached();
         verify(mHandler).postDelayed(any(Runnable.class), eq(BURN_IN_PROTECTION_UPDATE_INTERVAL));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 58ffbfa..15aaf5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -163,6 +163,31 @@
     }
 
     @Test
+    public void testPreviewModeFalseByDefault() {
+        mService.onBind(new Intent());
+
+        assertThat(mService.isPreviewMode()).isFalse();
+    }
+
+    @Test
+    public void testPreviewModeSetByIntentExtra() {
+        final Intent intent = new Intent();
+        intent.putExtra(DreamService.EXTRA_IS_PREVIEW, true);
+        mService.onBind(intent);
+
+        assertThat(mService.isPreviewMode()).isTrue();
+    }
+
+    @Test
+    public void testDreamLabel() {
+        final Intent intent = new Intent();
+        intent.putExtra(DreamService.EXTRA_DREAM_LABEL, "TestDream");
+        mService.onBind(intent);
+
+        assertThat(mService.getDreamLabel()).isEqualTo("TestDream");
+    }
+
+    @Test
     public void testDestroy() {
         mService.onDestroy();
         mMainExecutor.runAllReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 6587029..ad8d44d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -29,6 +29,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.touch.TouchInsetManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -49,13 +50,16 @@
     NetworkCapabilities mNetworkCapabilities;
     @Mock
     Network mNetwork;
+    @Mock
+    TouchInsetManager.TouchInsetSession mTouchSession;
 
     DreamOverlayStatusBarViewController mController;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mController = new DreamOverlayStatusBarViewController(mView, mConnectivityManager);
+        mController = new DreamOverlayStatusBarViewController(mView, mConnectivityManager,
+                mTouchSession);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
index 64b267d..51dcf2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
@@ -29,6 +29,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.touch.TouchInsetManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -46,6 +47,9 @@
     @Mock
     ConstraintLayout mLayout;
 
+    @Mock
+    TouchInsetManager.TouchInsetSession mTouchSession;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
@@ -112,7 +116,8 @@
                 Complication.CATEGORY_STANDARD,
                 mLayout);
 
-        final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
+        final ComplicationLayoutEngine engine =
+                new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
         addComplication(engine, firstViewInfo);
 
         // Ensure the view is added to the top end corner
@@ -139,7 +144,8 @@
                 Complication.CATEGORY_STANDARD,
                 mLayout);
 
-        final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
+        final ComplicationLayoutEngine engine =
+                new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
         addComplication(engine, firstViewInfo);
 
         // Ensure the view is added to the top end corner
@@ -155,7 +161,8 @@
      */
     @Test
     public void testDirectionLayout() {
-        final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
+        final ComplicationLayoutEngine engine =
+                new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
 
         final ViewInfo firstViewInfo = new ViewInfo(
                 new ComplicationLayoutParams(
@@ -203,7 +210,8 @@
      */
     @Test
     public void testPositionLayout() {
-        final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
+        final ComplicationLayoutEngine engine =
+                new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
 
         final ViewInfo firstViewInfo = new ViewInfo(
                 new ComplicationLayoutParams(
@@ -290,7 +298,8 @@
     @Test
     public void testMargin() {
         final int margin = 5;
-        final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, margin);
+        final ComplicationLayoutEngine engine =
+                new ComplicationLayoutEngine(mLayout, margin, mTouchSession);
 
         final ViewInfo firstViewInfo = new ViewInfo(
                 new ComplicationLayoutParams(
@@ -364,7 +373,8 @@
      */
     @Test
     public void testRemoval() {
-        final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
+        final ComplicationLayoutEngine engine =
+                new ComplicationLayoutEngine(mLayout, 0, mTouchSession);
 
         final ViewInfo firstViewInfo = new ViewInfo(
                 new ComplicationLayoutParams(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index e606be1..b359ae5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -33,10 +33,11 @@
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.FakeConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.animation.UniqueObjectHostView
-import junit.framework.Assert
+import com.android.systemui.util.mockito.any
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertNotNull
 import org.junit.Before
 import org.junit.Rule
@@ -44,16 +45,16 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
-import org.mockito.Mockito.any
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.anyLong
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -83,8 +84,6 @@
     @Mock
     private lateinit var keyguardViewController: KeyguardViewController
     @Mock
-    private lateinit var configurationController: ConfigurationController
-    @Mock
     private lateinit var uniqueObjectHostView: UniqueObjectHostView
     @Mock
     private lateinit var dreamOverlayStateController: DreamOverlayStateController
@@ -97,6 +96,7 @@
     val mockito = MockitoJUnit.rule()
     private lateinit var mediaHiearchyManager: MediaHierarchyManager
     private lateinit var mediaFrame: ViewGroup
+    private val configurationController = FakeConfigurationController()
 
     @Before
     fun setup() {
@@ -176,12 +176,7 @@
 
     @Test
     fun testGoingToFullShade() {
-        // Let's set it onto Lock screen
-        `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-        `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
-            true)
-        statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
-        clearInvocations(mediaCarouselController)
+        goToLockscreen()
 
         // Let's transition all the way to full shade
         mediaHiearchyManager.setTransitionToFullShadeAmount(100000f)
@@ -204,41 +199,48 @@
 
         // Let's make sure alpha is set
         mediaHiearchyManager.setTransitionToFullShadeAmount(2.0f)
-        Assert.assertTrue("alpha should not be 1.0f when cross fading", mediaFrame.alpha != 1.0f)
+        assertThat(mediaFrame.alpha).isNotEqualTo(1.0f)
     }
 
     @Test
     fun testTransformationOnLockScreenIsFading() {
-        // Let's set it onto Lock screen
-        `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-        `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
-            true)
-        statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
-        clearInvocations(mediaCarouselController)
+        goToLockscreen()
+        expandQS()
 
-        // Let's transition from lockscreen to qs
-        mediaHiearchyManager.qsExpansion = 1.0f
         val transformType = mediaHiearchyManager.calculateTransformationType()
-        Assert.assertTrue("media isn't transforming to qs with a fade",
-            transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
+        assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
+    }
+
+    @Test
+    fun calculateTransformationType_onLockShade_inSplitShade_goingToFullShade_returnsTransition() {
+        enableSplitShade()
+        goToLockscreen()
+        expandQS()
+        mediaHiearchyManager.setTransitionToFullShadeAmount(10000f)
+
+        val transformType = mediaHiearchyManager.calculateTransformationType()
+        assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION)
+    }
+
+    @Test
+    fun calculateTransformationType_onLockShade_inSplitShade_notExpanding_returnsFade() {
+        enableSplitShade()
+        goToLockscreen()
+        goToLockedShade()
+        expandQS()
+        mediaHiearchyManager.setTransitionToFullShadeAmount(0f)
+
+        val transformType = mediaHiearchyManager.calculateTransformationType()
+        assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
     }
 
     @Test
     fun testTransformationOnLockScreenToQQSisFading() {
-        // Let's set it onto Lock screen
-        `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-        `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
-            true)
-        statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
-        clearInvocations(mediaCarouselController)
+        goToLockscreen()
+        goToLockedShade()
 
-        // Let's transition from lockscreen to qs
-        `when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
-        statusBarCallback.value.onStatePreChange(StatusBarState.KEYGUARD,
-            StatusBarState.SHADE_LOCKED)
         val transformType = mediaHiearchyManager.calculateTransformationType()
-        Assert.assertTrue("media isn't transforming to qqswith a fade",
-            transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
+        assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
     }
 
     @Test
@@ -254,4 +256,32 @@
 
         verify(mediaCarouselController).closeGuts()
     }
-}
\ No newline at end of file
+
+    private fun enableSplitShade() {
+        context.getOrCreateTestableResources().addOverride(
+            R.bool.config_use_split_notification_shade, true
+        )
+        configurationController.notifyConfigurationChanged()
+    }
+
+    private fun goToLockscreen() {
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+        whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
+            true
+        )
+        statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
+        clearInvocations(mediaCarouselController)
+    }
+
+    private fun goToLockedShade() {
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+        statusBarCallback.value.onStatePreChange(
+            StatusBarState.KEYGUARD,
+            StatusBarState.SHADE_LOCKED
+        )
+    }
+
+    private fun expandQS() {
+        mediaHiearchyManager.qsExpansion = 1.0f
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
index 8ce50a6..f736f26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -100,7 +100,9 @@
 
     @After
     fun tearDown() {
-        ViewUtils.detachView(view)
+        if (view.isAttachedToWindow) {
+            ViewUtils.detachView(view)
+        }
     }
 
     @Test
@@ -139,8 +141,7 @@
 
     @Test
     fun testMultiUserSwitchUpdatedWhenSettingChanged() {
-        // When expanded, listening is true
-        controller.setListening(true)
+        // Always listening to setting while View is attached
         testableLooper.processAllMessages()
 
         val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch)
@@ -156,4 +157,24 @@
 
         assertThat(multiUserSwitch.visibility).isEqualTo(View.VISIBLE)
     }
+
+    @Test
+    fun testMultiUserSettingNotListenedAfterDetach() {
+        testableLooper.processAllMessages()
+
+        val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch)
+        assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE)
+
+        ViewUtils.detachView(view)
+
+        // The setting is only used as an indicator for whether the view should refresh. The actual
+        // value of the setting is ignored; isMultiUserEnabled is the source of truth
+        whenever(multiUserSwitchController.isMultiUserEnabled).thenReturn(true)
+
+        // Changing the value of USER_SWITCHER_ENABLED should cause the view to update
+        fakeSettings.putIntForUser(Settings.Global.USER_SWITCHER_ENABLED, 1, userTracker.userId)
+        testableLooper.processAllMessages()
+
+        assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 9076e16..75ccd8f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.statusbar.phone.NotificationPanelViewController
 import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.phone.StatusBar
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.FakeConfigurationController
 import org.junit.After
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNotNull
@@ -66,7 +66,6 @@
     @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
     @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
     @Mock lateinit var scrimController: ScrimController
-    @Mock lateinit var configurationController: ConfigurationController
     @Mock lateinit var falsingManager: FalsingManager
     @Mock lateinit var notificationPanelController: NotificationPanelViewController
     @Mock lateinit var nsslController: NotificationStackScrollLayoutController
@@ -77,6 +76,8 @@
     @Mock lateinit var qS: QS
     @JvmField @Rule val mockito = MockitoJUnit.rule()
 
+    private val configurationController = FakeConfigurationController()
+
     @Before
     fun setup() {
         val helper = NotificationTestHelper(
@@ -244,4 +245,27 @@
         verify(qS).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
         verify(depthController).transitionToFullShadeProgress = anyFloat()
     }
+
+    @Test
+    fun setDragDownAmount_setsValueOnMediaHierarchyManager() {
+        transitionController.dragDownAmount = 10f
+
+        verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
+    }
+
+    @Test
+    fun setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManager() {
+        enableSplitShade()
+
+        transitionController.dragDownAmount = 10f
+
+        verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
+    }
+
+    private fun enableSplitShade() {
+        context.getOrCreateTestableResources().addOverride(
+            R.bool.config_use_split_notification_shade, true
+        )
+        configurationController.notifyConfigurationChanged()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
index 671ab59..c797bc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
@@ -32,6 +32,8 @@
 import static org.mockito.Mockito.when;
 
 import android.app.IActivityManager;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
@@ -228,6 +230,36 @@
     }
 
     @Test
+    public void rotationBecameAllowed_layoutParamsUpdated() {
+        mNotificationShadeWindowController.setKeyguardShowing(true);
+        when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false);
+        mNotificationShadeWindowController.onConfigChanged(new Configuration());
+        clearInvocations(mWindowManager);
+
+        when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true);
+        mNotificationShadeWindowController.onConfigChanged(new Configuration());
+
+        verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
+        assertThat(mLayoutParameters.getValue().screenOrientation)
+                .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_USER);
+    }
+
+    @Test
+    public void rotationBecameNotAllowed_layoutParamsUpdated() {
+        mNotificationShadeWindowController.setKeyguardShowing(true);
+        when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true);
+        mNotificationShadeWindowController.onConfigChanged(new Configuration());
+        clearInvocations(mWindowManager);
+
+        when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false);
+        mNotificationShadeWindowController.onConfigChanged(new Configuration());
+
+        verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
+        assertThat(mLayoutParameters.getValue().screenOrientation)
+                .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
+    }
+
+    @Test
     public void batchApplyWindowLayoutParams_doesNotDispatchEvents() {
         mNotificationShadeWindowController.setForceDozeBrightness(true);
         verify(mWindowManager).updateViewLayout(any(), any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
new file mode 100644
index 0000000..3a5d9ee
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
@@ -0,0 +1,31 @@
+package com.android.systemui.statusbar.policy
+
+import android.content.res.Configuration
+
+/** Fake implementation of [ConfigurationController] for tests. */
+class FakeConfigurationController : ConfigurationController {
+
+    private var listener: ConfigurationController.ConfigurationListener? = null
+
+    override fun addCallback(listener: ConfigurationController.ConfigurationListener) {
+        this.listener = listener
+    }
+
+    override fun removeCallback(listener: ConfigurationController.ConfigurationListener) {
+        this.listener = null
+    }
+
+    override fun onConfigurationChanged(newConfiguration: Configuration?) {
+        listener?.onConfigChanged(newConfiguration)
+    }
+
+    override fun notifyThemeChanged() {
+        listener?.onThemeChanged()
+    }
+
+    fun notifyConfigurationChanged() {
+        onConfigurationChanged(newConfiguration = null)
+    }
+
+    override fun isLayoutRtl(): Boolean = false
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java
new file mode 100644
index 0000000..14b9bfb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touch;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.view.ViewRootImpl;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class TouchInsetManagerTest extends SysuiTestCase {
+    @Mock
+    private View mRootView;
+
+    @Mock
+    private ViewRootImpl mRootViewImpl;
+
+    private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mRootView.getViewRootImpl()).thenReturn(mRootViewImpl);
+    }
+
+    @Test
+    public void testRootViewOnAttachedHandling() {
+        // Create inset manager
+        final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor,
+                mRootView);
+
+        final ArgumentCaptor<View.OnAttachStateChangeListener> listener =
+                ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
+
+        // Ensure manager has registered to listen to attached state of root view.
+        verify(mRootView).addOnAttachStateChangeListener(listener.capture());
+
+        // Trigger attachment and verify touchable region is set.
+        listener.getValue().onViewAttachedToWindow(mRootView);
+        verify(mRootViewImpl).setTouchableRegion(any());
+    }
+
+    @Test
+    public void testInsetRegionPropagation() {
+        // Create inset manager
+        final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor,
+                mRootView);
+
+        // Create session
+        final TouchInsetManager.TouchInsetSession session = insetManager.createSession();
+
+        // Add a view to the session.
+        final Rect rect = new Rect(0, 0, 2, 2);
+
+        session.addViewToTracking(createView(rect));
+        mFakeExecutor.runAllReady();
+
+        // Check to see if view was properly accounted for.
+        final Region expectedRegion = Region.obtain();
+        expectedRegion.op(rect, Region.Op.UNION);
+        verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
+    }
+
+    @Test
+    public void testMultipleRegions() {
+        // Create inset manager
+        final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor,
+                mRootView);
+
+        // Create session
+        final TouchInsetManager.TouchInsetSession session = insetManager.createSession();
+
+        // Add a view to the session.
+        final Rect firstBounds = new Rect(0, 0, 2, 2);
+        session.addViewToTracking(createView(firstBounds));
+
+        mFakeExecutor.runAllReady();
+        clearInvocations(mRootViewImpl);
+
+        // Create second session
+        final TouchInsetManager.TouchInsetSession secondSession = insetManager.createSession();
+
+        // Add a view to the second session.
+        final Rect secondBounds = new Rect(4, 4, 8, 10);
+        secondSession.addViewToTracking(createView(secondBounds));
+
+        mFakeExecutor.runAllReady();
+
+        // Check to see if all views and sessions was properly accounted for.
+        {
+            final Region expectedRegion = Region.obtain();
+            expectedRegion.op(firstBounds, Region.Op.UNION);
+            expectedRegion.op(secondBounds, Region.Op.UNION);
+            verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
+        }
+
+
+        clearInvocations(mRootViewImpl);
+
+        // clear first session, ensure second session is still reflected.
+        session.clear();
+        mFakeExecutor.runAllReady();
+        {
+            final Region expectedRegion = Region.obtain();
+            expectedRegion.op(firstBounds, Region.Op.UNION);
+            verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
+        }
+    }
+
+    @Test
+    public void testMultipleViews() {
+        // Create inset manager
+        final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor,
+                mRootView);
+
+        // Create session
+        final TouchInsetManager.TouchInsetSession session = insetManager.createSession();
+
+        // Add a view to the session.
+        final Rect firstViewBounds = new Rect(0, 0, 2, 2);
+        session.addViewToTracking(createView(firstViewBounds));
+
+        // only capture second invocation.
+        mFakeExecutor.runAllReady();
+        clearInvocations(mRootViewImpl);
+
+        // Add a second view to the session
+        final Rect secondViewBounds = new Rect(4, 4, 9, 10);
+        final View secondView = createView(secondViewBounds);
+        session.addViewToTracking(secondView);
+
+        mFakeExecutor.runAllReady();
+
+        // Check to see if all views and sessions was properly accounted for.
+        {
+            final Region expectedRegion = Region.obtain();
+            expectedRegion.op(firstViewBounds, Region.Op.UNION);
+            expectedRegion.op(secondViewBounds, Region.Op.UNION);
+            verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
+        }
+
+        // Remove second view.
+        session.removeViewFromTracking(secondView);
+
+        clearInvocations(mRootViewImpl);
+        mFakeExecutor.runAllReady();
+
+        // Ensure first view still reflected in touch region.
+        {
+            final Region expectedRegion = Region.obtain();
+            expectedRegion.op(firstViewBounds, Region.Op.UNION);
+            verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion));
+        }
+    }
+
+    private View createView(Rect bounds) {
+        final Rect rect = new Rect(bounds);
+        final View view = Mockito.mock(View.class);
+        doAnswer(invocation -> {
+            ((Rect) invocation.getArgument(0)).set(rect);
+            return null;
+        }).when(view).getBoundsOnScreen(any());
+
+        return view;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
new file mode 100644
index 0000000..d4be881
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.user
+
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.LayoutInflater
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.phone.ShadeController
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+class UserSwitcherActivityTest : SysuiTestCase() {
+    @Mock
+    private lateinit var activity: UserSwitcherActivity
+    @Mock
+    private lateinit var userSwitcherController: UserSwitcherController
+    @Mock
+    private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock
+    private lateinit var layoutInflater: LayoutInflater
+    @Mock
+    private lateinit var falsingManager: FalsingManager
+    @Mock
+    private lateinit var userManager: UserManager
+    @Mock
+    private lateinit var shadeController: ShadeController
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        activity = UserSwitcherActivity(
+            userSwitcherController,
+            broadcastDispatcher,
+            layoutInflater,
+            falsingManager,
+            userManager,
+            shadeController
+        )
+    }
+
+    @Test
+    fun testMaxColumns() {
+        assertThat(activity.getMaxColumns(3)).isEqualTo(4)
+        assertThat(activity.getMaxColumns(4)).isEqualTo(4)
+        assertThat(activity.getMaxColumns(5)).isEqualTo(3)
+        assertThat(activity.getMaxColumns(6)).isEqualTo(3)
+        assertThat(activity.getMaxColumns(7)).isEqualTo(4)
+        assertThat(activity.getMaxColumns(9)).isEqualTo(5)
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6813f3f8..bdb3e80 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -694,12 +694,6 @@
         return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
     }
 
-    /**
-     * The package name of the DeviceOwner. This package is not permitted to have its data cleared.
-     * <p>Not actually used</p>
-     */
-    private volatile String mDeviceOwnerName;
-
     private volatile int mDeviceOwnerUid = INVALID_UID;
 
     /**
@@ -6238,17 +6232,6 @@
     }
 
     @Override
-    public void updateDeviceOwner(String packageName) {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != 0 && callingUid != SYSTEM_UID) {
-            throw new SecurityException("updateDeviceOwner called from non-system process");
-        }
-        synchronized (this) {
-            mDeviceOwnerName = packageName;
-        }
-    }
-
-    @Override
     public void updateLockTaskPackages(int userId, String[] packages) {
         mActivityTaskManager.updateLockTaskPackages(userId, packages);
     }
@@ -15623,24 +15606,20 @@
         }
         for (int i = 0, size = processes.size(); i < size; i++) {
             ProcessRecord app = processes.get(i);
-            pw.println(String.format("------ DUMP RESOURCES %s (%s)  ------",
+            pw.println(String.format("Resources History for %s (%s)",
                     app.processName,
                     app.info.packageName));
             pw.flush();
             try {
-                TransferPipe tp = new TransferPipe();
+                TransferPipe tp = new TransferPipe("  ");
                 try {
                     IApplicationThread thread = app.getThread();
                     if (thread != null) {
                         app.getThread().dumpResources(tp.getWriteFd(), null);
                         tp.go(fd.getFileDescriptor(), 2000);
-                        pw.println(String.format("------ END DUMP RESOURCES %s (%s)  ------",
-                                app.processName,
-                                app.info.packageName));
-                        pw.flush();
                     } else {
                         pw.println(String.format(
-                                "------ DUMP RESOURCES %s (%s) failed, no thread ------",
+                                "  Resources history for %s (%s) failed, no thread",
                                 app.processName,
                                 app.info.packageName));
                     }
@@ -15648,11 +15627,7 @@
                     tp.kill();
                 }
             } catch (IOException e) {
-                pw.println(String.format(
-                        "------ EXCEPTION DUMPING RESOURCES for %s (%s): %s ------",
-                        app.processName,
-                        app.info.packageName,
-                        e.getMessage()));
+                pw.println("  " + e.getMessage());
                 pw.flush();
             }
 
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index b813bc4..0edbea0 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -90,6 +90,7 @@
 import com.android.internal.compat.CompatibilityOverrideConfig;
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
@@ -268,16 +269,34 @@
                     break;
                 }
                 case SET_GAME_STATE: {
-                    if (mPowerManagerInternal == null) {
-                        final Bundle data = msg.getData();
-                        Slog.d(TAG, "Error setting loading mode for package "
-                                + data.getString(PACKAGE_NAME_MSG_KEY)
-                                + " and userId " + data.getInt(USER_ID_MSG_KEY));
-                        break;
-                    }
                     final GameState gameState = (GameState) msg.obj;
                     final boolean isLoading = gameState.isLoading();
-                    mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading);
+                    final Bundle data = msg.getData();
+                    final String packageName = data.getString(PACKAGE_NAME_MSG_KEY);
+                    final int userId = data.getInt(USER_ID_MSG_KEY);
+
+                    // Restrict to games only. Requires performance mode to be enabled.
+                    final boolean boostEnabled =
+                            getGameMode(packageName, userId) == GameManager.GAME_MODE_PERFORMANCE;
+                    int uid;
+                    try {
+                        uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+                    } catch (NameNotFoundException e) {
+                        Slog.v(TAG, "Failed to get package metadata");
+                        uid = -1;
+                    }
+                    FrameworkStatsLog.write(FrameworkStatsLog.GAME_STATE_CHANGED, packageName, uid,
+                            boostEnabled, gameStateModeToStatsdGameState(gameState.getMode()),
+                            isLoading, gameState.getLabel(), gameState.getQuality());
+
+                    if (boostEnabled) {
+                        if (mPowerManagerInternal == null) {
+                            Slog.d(TAG, "Error setting loading mode for package " + packageName
+                                    + " and userId " + userId);
+                            break;
+                        }
+                        mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading);
+                    }
                     break;
                 }
             }
@@ -387,12 +406,6 @@
             // Restrict to games only.
             return;
         }
-
-        if (getGameMode(packageName, userId) != GameManager.GAME_MODE_PERFORMANCE) {
-            // Requires performance mode to be enabled.
-            return;
-        }
-
         final Message msg = mHandler.obtainMessage(SET_GAME_STATE);
         final Bundle data = new Bundle();
         data.putString(PACKAGE_NAME_MSG_KEY, packageName);
@@ -1543,6 +1556,22 @@
         return out.toString();
     }
 
+    private static int gameStateModeToStatsdGameState(int mode) {
+        switch (mode) {
+            case GameState.MODE_NONE:
+                return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_NONE;
+            case GameState.MODE_GAMEPLAY_INTERRUPTIBLE:
+                return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_GAMEPLAY_INTERRUPTIBLE;
+            case GameState.MODE_GAMEPLAY_UNINTERRUPTIBLE:
+                return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_GAMEPLAY_UNINTERRUPTIBLE;
+            case GameState.MODE_CONTENT:
+                return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_CONTENT;
+            case GameState.MODE_UNKNOWN:
+            default:
+                return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_UNKNOWN;
+        }
+    }
+
     private static ServiceThread createServiceThread() {
         ServiceThread handlerThread = new ServiceThread(TAG,
                 Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 76754d3..4a1a950 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -104,7 +104,7 @@
             pw.println("  mCurrentDream:");
             pw.println("    mToken=" + mCurrentDream.mToken);
             pw.println("    mName=" + mCurrentDream.mName);
-            pw.println("    mIsTest=" + mCurrentDream.mIsTest);
+            pw.println("    mIsPreviewMode=" + mCurrentDream.mIsPreviewMode);
             pw.println("    mCanDoze=" + mCurrentDream.mCanDoze);
             pw.println("    mUserId=" + mCurrentDream.mUserId);
             pw.println("    mBound=" + mCurrentDream.mBound);
@@ -117,7 +117,7 @@
     }
 
     public void startDream(Binder token, ComponentName name,
-            boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock,
+            boolean isPreviewMode, boolean canDoze, int userId, PowerManager.WakeLock wakeLock,
             ComponentName overlayComponentName) {
         stopDream(true /*immediate*/, "starting new dream");
 
@@ -127,10 +127,10 @@
             mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);
 
             Slog.i(TAG, "Starting dream: name=" + name
-                    + ", isTest=" + isTest + ", canDoze=" + canDoze
+                    + ", isPreviewMode=" + isPreviewMode + ", canDoze=" + canDoze
                     + ", userId=" + userId);
 
-            mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId, wakeLock);
+            mCurrentDream = new DreamRecord(token, name, isPreviewMode, canDoze, userId, wakeLock);
 
             mDreamStartTime = SystemClock.elapsedRealtime();
             MetricsLogger.visible(mContext,
@@ -140,6 +140,7 @@
             intent.setComponent(name);
             intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
             intent.putExtra(DreamService.EXTRA_DREAM_OVERLAY_COMPONENT, overlayComponentName);
+            intent.putExtra(DreamService.EXTRA_IS_PREVIEW, isPreviewMode);
             try {
                 if (!mContext.bindServiceAsUser(intent, mCurrentDream,
                         Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
@@ -190,7 +191,8 @@
             final DreamRecord oldDream = mCurrentDream;
             mCurrentDream = null;
             Slog.i(TAG, "Stopping dream: name=" + oldDream.mName
-                    + ", isTest=" + oldDream.mIsTest + ", canDoze=" + oldDream.mCanDoze
+                    + ", isPreviewMode=" + oldDream.mIsPreviewMode
+                    + ", canDoze=" + oldDream.mCanDoze
                     + ", userId=" + oldDream.mUserId
                     + ", reason='" + reason + "'"
                     + (mSavedStopReason == null ? "" : "(from '" + mSavedStopReason + "')"));
@@ -247,7 +249,7 @@
 
         mCurrentDream.mService = service;
 
-        if (!mCurrentDream.mIsTest) {
+        if (!mCurrentDream.mIsPreviewMode) {
             mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL);
             mCurrentDream.mSentStartBroadcast = true;
         }
@@ -263,7 +265,7 @@
     private final class DreamRecord implements DeathRecipient, ServiceConnection {
         public final Binder mToken;
         public final ComponentName mName;
-        public final boolean mIsTest;
+        public final boolean mIsPreviewMode;
         public final boolean mCanDoze;
         public final int mUserId;
 
@@ -275,11 +277,11 @@
 
         public boolean mWakingGently;
 
-        public DreamRecord(Binder token, ComponentName name,
-                boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
+        DreamRecord(Binder token, ComponentName name, boolean isPreviewMode,
+                boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
             mToken = token;
             mName = name;
-            mIsTest = isTest;
+            mIsPreviewMode = isPreviewMode;
             mCanDoze = canDoze;
             mUserId  = userId;
             mWakeLock = wakeLock;
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index f0a6af3..22d32a6 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -87,7 +87,7 @@
     private Binder mCurrentDreamToken;
     private ComponentName mCurrentDreamName;
     private int mCurrentDreamUserId;
-    private boolean mCurrentDreamIsTest;
+    private boolean mCurrentDreamIsPreview;
     private boolean mCurrentDreamCanDoze;
     private boolean mCurrentDreamIsDozing;
     private boolean mCurrentDreamIsWaking;
@@ -169,7 +169,7 @@
         pw.println("mCurrentDreamToken=" + mCurrentDreamToken);
         pw.println("mCurrentDreamName=" + mCurrentDreamName);
         pw.println("mCurrentDreamUserId=" + mCurrentDreamUserId);
-        pw.println("mCurrentDreamIsTest=" + mCurrentDreamIsTest);
+        pw.println("mCurrentDreamIsPreview=" + mCurrentDreamIsPreview);
         pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze);
         pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing);
         pw.println("mCurrentDreamIsWaking=" + mCurrentDreamIsWaking);
@@ -190,7 +190,7 @@
 
     private boolean isDreamingInternal() {
         synchronized (mLock) {
-            return mCurrentDreamToken != null && !mCurrentDreamIsTest
+            return mCurrentDreamToken != null && !mCurrentDreamIsPreview
                     && !mCurrentDreamIsWaking;
         }
     }
@@ -235,7 +235,7 @@
 
     private void testDreamInternal(ComponentName dream, int userId) {
         synchronized (mLock) {
-            startDreamLocked(dream, true /*isTest*/, false /*canDoze*/, userId);
+            startDreamLocked(dream, true /*isPreviewMode*/, false /*canDoze*/, userId);
         }
     }
 
@@ -244,7 +244,7 @@
         final ComponentName dream = chooseDreamForUser(doze, userId);
         if (dream != null) {
             synchronized (mLock) {
-                startDreamLocked(dream, false /*isTest*/, doze, userId);
+                startDreamLocked(dream, false /*isPreviewMode*/, doze, userId);
             }
         }
     }
@@ -395,10 +395,10 @@
     }
 
     private void startDreamLocked(final ComponentName name,
-            final boolean isTest, final boolean canDoze, final int userId) {
+            final boolean isPreviewMode, final boolean canDoze, final int userId) {
         if (!mCurrentDreamIsWaking
                 && Objects.equals(mCurrentDreamName, name)
-                && mCurrentDreamIsTest == isTest
+                && mCurrentDreamIsPreview == isPreviewMode
                 && mCurrentDreamCanDoze == canDoze
                 && mCurrentDreamUserId == userId) {
             Slog.i(TAG, "Already in target dream.");
@@ -412,7 +412,7 @@
         final Binder newToken = new Binder();
         mCurrentDreamToken = newToken;
         mCurrentDreamName = name;
-        mCurrentDreamIsTest = isTest;
+        mCurrentDreamIsPreview = isPreviewMode;
         mCurrentDreamCanDoze = canDoze;
         mCurrentDreamUserId = userId;
 
@@ -424,7 +424,7 @@
                 .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream");
         mHandler.post(wakeLock.wrap(() -> {
             mAtmInternal.notifyDreamStateChanged(true);
-            mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock,
+            mController.startDream(newToken, name, isPreviewMode, canDoze, userId, wakeLock,
                     mDreamOverlayServiceName);
         }));
     }
@@ -457,7 +457,7 @@
         }
         mCurrentDreamToken = null;
         mCurrentDreamName = null;
-        mCurrentDreamIsTest = false;
+        mCurrentDreamIsPreview = false;
         mCurrentDreamCanDoze = false;
         mCurrentDreamUserId = 0;
         mCurrentDreamIsWaking = false;
diff --git a/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java b/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java
new file mode 100644
index 0000000..83ca16d
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static android.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION;
+import static android.view.inputmethod.InputMethodManager.CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.compat.IPlatformCompat;
+
+/**
+ * A utility class used by {@link InputMethodManagerService} to manage the platform
+ * compatibility changes on IMF (Input Method Framework) side.
+ */
+final class ImePlatformCompatUtils {
+    private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface(
+            ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+
+    /**
+     * Whether to finish the {@link android.view.inputmethod.InputConnection} when the device
+     * becomes {@link android.os.PowerManager#isInteractive non-interactive}.
+     *
+     * @param imeUid The uid of the IME application
+     */
+    public boolean shouldFinishInputWithReportToIme(int imeUid) {
+        return isChangeEnabledByUid(FINISH_INPUT_NO_FALLBACK_CONNECTION, imeUid);
+    }
+
+    /**
+     *  Whether to clear {@link android.view.inputmethod.InputMethodManager#SHOW_FORCED} flag
+     *  when the next IME focused application changed.
+     *
+     * @param clientUid The uid of the app
+     */
+    public boolean shouldClearShowForcedFlag(int clientUid) {
+        return isChangeEnabledByUid(CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING, clientUid);
+    }
+
+    private boolean isChangeEnabledByUid(long changeFlag, int uid) {
+        boolean result = false;
+        try {
+            result = mPlatformCompat.isChangeEnabledByUid(changeFlag, uid);
+        } catch (RemoteException e) {
+        }
+        return result;
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 0b7e391..eb1de2a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -15,7 +15,6 @@
 
 package com.android.server.inputmethod;
 
-import static android.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 import static android.os.IServiceManager.DUMP_FLAG_PROTO;
@@ -148,7 +147,6 @@
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.inputmethod.DirectBootAwareness;
@@ -279,6 +277,7 @@
     final WindowManagerInternal mWindowManagerInternal;
     final PackageManagerInternal mPackageManagerInternal;
     final InputManagerInternal mInputManagerInternal;
+    final ImePlatformCompatUtils mImePlatformCompatUtils;
     final boolean mHasFeature;
     private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap =
             new ArrayMap<>();
@@ -691,8 +690,6 @@
      */
     boolean mIsInteractive = true;
 
-    private final IPlatformCompat mPlatformCompat;
-
     int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
 
     /**
@@ -1627,6 +1624,7 @@
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
+        mImePlatformCompatUtils = new ImePlatformCompatUtils();
         mImeDisplayValidator = mWindowManagerInternal::getDisplayImePolicy;
         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
         mUserManager = mContext.getSystemService(UserManager.class);
@@ -1634,8 +1632,7 @@
         mAccessibilityManager = AccessibilityManager.getInstance(context);
         mHasFeature = context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_INPUT_METHODS);
-        mPlatformCompat = IPlatformCompat.Stub.asInterface(
-                ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+
         mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
 
         Bundle extras = new Bundle();
@@ -3620,6 +3617,14 @@
             return InputBindResult.INVALID_USER;
         }
 
+        final boolean shouldClearFlag = mImePlatformCompatUtils.shouldClearShowForcedFlag(cs.uid);
+        // In case mShowForced flag affects the next client to keep IME visible, when the current
+        // client is leaving due to the next focused client, we clear mShowForced flag when the
+        // next client's targetSdkVersion is T or higher.
+        if (mCurFocusedWindow != windowToken && mShowForced && shouldClearFlag) {
+            mShowForced = false;
+        }
+
         // cross-profile access is always allowed here to allow profile-switching.
         if (!mSettings.isCurrentProfile(userId)) {
             Slog.w(TAG, "A background user is requesting window. Hiding IME.");
@@ -4728,14 +4733,9 @@
 
             // Inform the current client of the change in active status
             if (mCurClient != null && mCurClient.client != null) {
-                boolean reportToImeController = false;
-                try {
-                    reportToImeController = mPlatformCompat.isChangeEnabledByUid(
-                            FINISH_INPUT_NO_FALLBACK_CONNECTION, getCurMethodUidLocked());
-                } catch (RemoteException e) {
-                }
                 scheduleSetActiveToClient(mCurClient, mIsInteractive, mInFullscreenMode,
-                        reportToImeController);
+                        mImePlatformCompatUtils.shouldFinishInputWithReportToIme(
+                                getCurMethodUidLocked()));
             }
         }
     }
diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
index 01aee7b..db81393 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
@@ -34,7 +34,6 @@
 import android.os.Binder;
 import android.os.HandlerThread;
 import android.os.LocaleList;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -45,7 +44,6 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -89,32 +87,24 @@
     // SparseArray because it is more memory-efficient than a HashMap.
     private final SparseArray<StagedData> mStagedData;
 
-    private final PackageMonitor mPackageMonitor;
     private final BroadcastReceiver mUserMonitor;
 
     LocaleManagerBackupHelper(LocaleManagerService localeManagerService,
-            PackageManagerInternal pmInternal) {
+            PackageManagerInternal pmInternal, HandlerThread broadcastHandlerThread) {
         this(localeManagerService.mContext, localeManagerService, pmInternal, Clock.systemUTC(),
-                new SparseArray<>());
+                new SparseArray<>(), broadcastHandlerThread);
     }
 
     @VisibleForTesting LocaleManagerBackupHelper(Context context,
             LocaleManagerService localeManagerService,
-            PackageManagerInternal pmInternal, Clock clock, SparseArray<StagedData> stagedData) {
+            PackageManagerInternal pmInternal, Clock clock, SparseArray<StagedData> stagedData,
+            HandlerThread broadcastHandlerThread) {
         mContext = context;
         mLocaleManagerService = localeManagerService;
         mPackageManagerInternal = pmInternal;
         mClock = clock;
         mStagedData = stagedData;
 
-        HandlerThread broadcastHandlerThread = new HandlerThread(TAG,
-                Process.THREAD_PRIORITY_BACKGROUND);
-        broadcastHandlerThread.start();
-
-        mPackageMonitor = new PackageMonitorImpl();
-        mPackageMonitor.register(context, broadcastHandlerThread.getLooper(),
-                UserHandle.ALL,
-                true);
         mUserMonitor = new UserMonitor();
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -127,11 +117,6 @@
         return mUserMonitor;
     }
 
-    @VisibleForTesting
-    PackageMonitor getPackageMonitor() {
-        return mPackageMonitor;
-    }
-
     /**
      * @see LocaleManagerInternal#getBackupPayload(int userId)
      */
@@ -267,6 +252,53 @@
         BackupManager.dataChanged(SYSTEM_BACKUP_PACKAGE_KEY);
     }
 
+    /**
+     * <p><b>Note:</b> This is invoked by service's common monitor
+     * {@link LocaleManagerServicePackageMonitor#onPackageAdded} when a new package is
+     * added on device.
+     */
+    void onPackageAdded(String packageName, int uid) {
+        try {
+            synchronized (mStagedDataLock) {
+                cleanStagedDataForOldEntriesLocked();
+
+                int userId = UserHandle.getUserId(uid);
+                if (mStagedData.contains(userId)) {
+                    // Perform lazy restore only if the staged data exists.
+                    doLazyRestoreLocked(packageName, userId);
+                }
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception in onPackageAdded.", e);
+        }
+    }
+
+    /**
+     * <p><b>Note:</b> This is invoked by service's common monitor
+     * {@link LocaleManagerServicePackageMonitor#onPackageDataCleared} when a package's data
+     * is cleared.
+     */
+    void onPackageDataCleared() {
+        try {
+            notifyBackupManager();
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception in onPackageDataCleared.", e);
+        }
+    }
+
+    /**
+     * <p><b>Note:</b> This is invoked by service's common monitor
+     * {@link LocaleManagerServicePackageMonitor#onPackageRemoved} when a package is removed
+     * from device.
+     */
+    void onPackageRemoved() {
+        try {
+            notifyBackupManager();
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception in onPackageRemoved.", e);
+        }
+    }
+
     private boolean isPackageInstalledForUser(String packageName, int userId) {
         PackageInfo pkgInfo = null;
         try {
@@ -395,48 +427,6 @@
     }
 
     /**
-     * Helper to monitor package states.
-     *
-     * <p>We're interested in package added, package data cleared and package removed events.
-     */
-    private final class PackageMonitorImpl extends PackageMonitor {
-        @Override
-        public void onPackageAdded(String packageName, int uid) {
-            try {
-                synchronized (mStagedDataLock) {
-                    cleanStagedDataForOldEntriesLocked();
-
-                    int userId = UserHandle.getUserId(uid);
-                    if (mStagedData.contains(userId)) {
-                        // Perform lazy restore only if the staged data exists.
-                        doLazyRestoreLocked(packageName, userId);
-                    }
-                }
-            } catch (Exception e) {
-                Slog.e(TAG, "Exception in onPackageAdded.", e);
-            }
-        }
-
-        @Override
-        public void onPackageDataCleared(String packageName, int uid) {
-            try {
-                notifyBackupManager();
-            } catch (Exception e) {
-                Slog.e(TAG, "Exception in onPackageDataCleared.", e);
-            }
-        }
-
-        @Override
-        public void onPackageRemoved(String packageName, int uid) {
-            try {
-                notifyBackupManager();
-            } catch (Exception e) {
-                Slog.e(TAG, "Exception in onPackageRemoved.", e);
-            }
-        }
-    }
-
-    /**
      * Performs lazy restore from the staged data.
      *
      * <p>This is invoked by the package monitor on the package added callback.
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index d459f8d..c427705 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -29,6 +29,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
+import android.os.HandlerThread;
 import android.os.LocaleList;
 import android.os.Process;
 import android.os.RemoteException;
@@ -38,14 +39,13 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.io.FileDescriptor;
-import java.io.PrintWriter;
 
 /**
  * The implementation of ILocaleManager.aidl.
@@ -62,6 +62,8 @@
 
     private LocaleManagerBackupHelper mBackupHelper;
 
+    private final PackageMonitor mPackageMonitor;
+
     public static final boolean DEBUG = false;
 
     public LocaleManagerService(Context context) {
@@ -71,15 +73,26 @@
         mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+
+        HandlerThread broadcastHandlerThread = new HandlerThread(TAG,
+                Process.THREAD_PRIORITY_BACKGROUND);
+        broadcastHandlerThread.start();
+
         mBackupHelper = new LocaleManagerBackupHelper(this,
-                mPackageManagerInternal);
+                mPackageManagerInternal, broadcastHandlerThread);
+
+        mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper);
+        mPackageMonitor.register(context, broadcastHandlerThread.getLooper(),
+                UserHandle.ALL,
+                true);
     }
 
     @VisibleForTesting
     LocaleManagerService(Context context, ActivityTaskManagerInternal activityTaskManagerInternal,
             ActivityManagerInternal activityManagerInternal,
             PackageManagerInternal packageManagerInternal,
-            LocaleManagerBackupHelper localeManagerBackupHelper) {
+            LocaleManagerBackupHelper localeManagerBackupHelper,
+            PackageMonitor packageMonitor) {
         super(context);
         mContext = context;
         mBinderService = new LocaleManagerBinderService();
@@ -87,6 +100,7 @@
         mActivityManagerInternal = activityManagerInternal;
         mPackageManagerInternal = packageManagerInternal;
         mBackupHelper = localeManagerBackupHelper;
+        mPackageMonitor = packageMonitor;
     }
 
     @Override
@@ -130,11 +144,6 @@
         }
 
         @Override
-        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            LocaleManagerService.this.dump(fd, pw, args);
-        }
-
-        @Override
         public void onShellCommand(FileDescriptor in, FileDescriptor out,
                 FileDescriptor err, String[] args, ShellCallback callback,
                 ResultReceiver resultReceiver) {
@@ -407,14 +416,6 @@
         return null;
     }
 
-    /**
-     * Dumps useful info related to service.
-     */
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-        // TODO(b/201766221): Implement when there is state.
-    }
-
     private void logMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) {
         FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_LOCALES_CHANGED,
                 atomRecordForMetrics.mCallingUid,
diff --git a/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java
new file mode 100644
index 0000000..b459be7
--- /dev/null
+++ b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import com.android.internal.content.PackageMonitor;
+
+/**
+ * Helper to monitor package states inside {@link LocaleManagerService}.
+ *
+ * <p> These listeners forward the call to different aspects of locale service that
+ * handle the business logic.
+ * <p> We're interested in package added, package data cleared and package removed events.
+ */
+final class LocaleManagerServicePackageMonitor extends PackageMonitor {
+    private LocaleManagerBackupHelper mBackupHelper;
+
+    LocaleManagerServicePackageMonitor(LocaleManagerBackupHelper localeManagerBackupHelper) {
+        mBackupHelper = localeManagerBackupHelper;
+    }
+
+    @Override
+    public void onPackageAdded(String packageName, int uid) {
+        mBackupHelper.onPackageAdded(packageName, uid);
+    }
+
+    @Override
+    public void onPackageDataCleared(String packageName, int uid) {
+        mBackupHelper.onPackageDataCleared();
+    }
+
+    @Override
+    public void onPackageRemoved(String packageName, int uid) {
+        mBackupHelper.onPackageRemoved();
+    }
+}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index d42e2c6..0b8f94c 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1607,6 +1607,8 @@
 
     public @Nullable Location getLastLocation(LastLocationRequest request,
             CallerIdentity identity, @PermissionLevel int permissionLevel) {
+        request = calculateLastLocationRequest(request);
+
         if (!isActive(request.isBypass(), identity)) {
             return null;
         }
@@ -1634,6 +1636,38 @@
         return location;
     }
 
+    private LastLocationRequest calculateLastLocationRequest(LastLocationRequest baseRequest) {
+        LastLocationRequest.Builder builder = new LastLocationRequest.Builder(baseRequest);
+
+        boolean locationSettingsIgnored = baseRequest.isLocationSettingsIgnored();
+        if (locationSettingsIgnored) {
+            // if we are not currently allowed use location settings ignored, disable it
+            if (!mSettingsHelper.getIgnoreSettingsAllowlist().contains(
+                    getIdentity().getPackageName(), getIdentity().getAttributionTag())
+                    && !mLocationManagerInternal.isProvider(null, getIdentity())) {
+                locationSettingsIgnored = false;
+            }
+
+            builder.setLocationSettingsIgnored(locationSettingsIgnored);
+        }
+
+        boolean adasGnssBypass = baseRequest.isAdasGnssBypass();
+        if (adasGnssBypass) {
+            // if we are not currently allowed use adas gnss bypass, disable it
+            if (!GPS_PROVIDER.equals(mName)) {
+                Log.e(TAG, "adas gnss bypass request received in non-gps provider");
+                adasGnssBypass = false;
+            } else if (!mLocationSettings.getUserSettings(
+                    getIdentity().getUserId()).isAdasGnssLocationEnabled()) {
+                adasGnssBypass = false;
+            }
+
+            builder.setAdasGnssBypass(adasGnssBypass);
+        }
+
+        return builder.build();
+    }
+
     /**
      * This function does not perform any permissions or safety checks, by calling it you are
      * committing to performing all applicable checks yourself. This always returns a "fine"
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index c638201..9bbae4b 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -29,7 +29,9 @@
 import android.os.IHwBinder;
 import android.os.RemoteException;
 import android.system.OsConstants;
+import android.util.Log;
 
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -54,6 +56,8 @@
  * </ul>
  */
 final class SoundTriggerHw2Compat implements ISoundTriggerHal {
+    private static final String TAG = "SoundTriggerHw2Compat";
+
     private final @NonNull Runnable mRebootRunnable;
     private final @NonNull IHwBinder mBinder;
     private @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
@@ -226,6 +230,16 @@
             return handle.get();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
+        } finally {
+            // TODO(b/219825762): We should be able to use the entire object in a try-with-resources
+            //   clause, instead of having to explicitly close internal fields.
+            if (hidlModel.data != null) {
+                try {
+                    hidlModel.data.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "Failed to close file", e);
+                }
+            }
         }
     }
 
@@ -252,6 +266,16 @@
             return handle.get();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
+        } finally {
+            // TODO(b/219825762): We should be able to use the entire object in a try-with-resources
+            //   clause, instead of having to explicitly close internal fields.
+            if (hidlModel.common.data != null) {
+                try {
+                    hidlModel.common.data.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "Failed to close file", e);
+                }
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b0efa5b..d772586 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -148,6 +148,7 @@
 import static com.android.server.wm.ActivityRecordProto.APP_STOPPED;
 import static com.android.server.wm.ActivityRecordProto.CLIENT_VISIBLE;
 import static com.android.server.wm.ActivityRecordProto.DEFER_HIDING_CLIENT;
+import static com.android.server.wm.ActivityRecordProto.ENABLE_RECENTS_SCREENSHOT;
 import static com.android.server.wm.ActivityRecordProto.FILLS_PARENT;
 import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK;
 import static com.android.server.wm.ActivityRecordProto.IN_SIZE_COMPAT_MODE;
@@ -771,7 +772,7 @@
     // Last visibility state we reported to the app token.
     boolean reportedVisible;
 
-    boolean mEnablePreviewScreenshots = true;
+    boolean mEnableRecentsScreenshot = true;
 
     // Information about an application starting window if displayed.
     // Note: these are de-referenced before the starting window animates away.
@@ -5151,7 +5152,7 @@
      * See {@link Activity#setRecentsScreenshotEnabled}.
      */
     void setRecentsScreenshotEnabled(boolean enabled) {
-        mEnablePreviewScreenshots = enabled;
+        mEnableRecentsScreenshot = enabled;
     }
 
     /**
@@ -5163,7 +5164,7 @@
      *         screenshot.
      */
     boolean shouldUseAppThemeSnapshot() {
-        return !mEnablePreviewScreenshots || forAllWindows(WindowState::isSecureLocked,
+        return !mEnableRecentsScreenshot || forAllWindows(WindowState::isSecureLocked,
                 true /* topToBottom */);
     }
 
@@ -9184,6 +9185,7 @@
         // Only record if max bounds sandboxing is applied, if the caller has the necessary
         // permission to access the device configs.
         proto.write(PROVIDES_MAX_BOUNDS, providesMaxBounds());
+        proto.write(ENABLE_RECENTS_SCREENSHOT, mEnableRecentsScreenshot);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4c5c705..fd24565 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5499,7 +5499,7 @@
      * display, which set unrestricted keep-clear areas.
      *
      * For context on restricted vs unrestricted keep-clear areas, see
-     * {@link android.Manifest.permission.USE_UNRESTRICTED_KEEP_CLEAR_AREAS}.
+     * {@link android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS}.
      */
     void getKeepClearAreas(List<Rect> outRestricted, List<Rect> outUnrestricted) {
         final Matrix tmpMatrix = new Matrix();
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index c267cba..c13ae95 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -30,6 +30,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.IApplicationThread;
+import android.app.WindowConfiguration;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
 import android.os.RemoteException;
@@ -296,6 +297,17 @@
         return ci.mVisible;
     }
 
+    @WindowConfiguration.WindowingMode
+    int getWindowingModeAtStart(@NonNull WindowContainer wc) {
+        if (mCollectingTransition == null) return wc.getWindowingMode();
+        final Transition.ChangeInfo ci = mCollectingTransition.mChanges.get(wc);
+        if (ci == null) {
+            // not part of transition, so use current state.
+            return wc.getWindowingMode();
+        }
+        return ci.mWindowingMode;
+    }
+
     @WindowManager.TransitionType
     int getCollectingTransitionType() {
         return mCollectingTransition != null ? mCollectingTransition.mType : TRANSIT_NONE;
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 24493e2..14737d4 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -196,8 +196,11 @@
                         "Win " + w + ": token animating, looking behind.");
             }
             mFindResults.setIsWallpaperTargetForLetterbox(w.hasWallpaperForLetterboxBackground());
-            // Found a target! End search.
-            return true;
+            // While the keyguard is going away, both notification shade and a normal activity such
+            // as a launcher can satisfy criteria for a wallpaper target. In this case, we should
+            // chose the normal activity, otherwise wallpaper becomes invisible when a new animation
+            // starts before the keyguard going away animation finishes.
+            return w.mActivityRecord != null;
         }
         return false;
     };
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5b1021e..709f885 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -200,6 +200,7 @@
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
 import android.os.RemoteCallback;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -288,6 +289,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.IKeyguardLockedStateListener;
 import com.android.internal.policy.IShortcutService;
 import com.android.internal.policy.KeyInterceptionInfo;
 import com.android.internal.protolog.ProtoLogImpl;
@@ -460,6 +462,10 @@
 
     final private KeyguardDisableHandler mKeyguardDisableHandler;
 
+    private final RemoteCallbackList<IKeyguardLockedStateListener> mKeyguardLockedStateListeners =
+            new RemoteCallbackList<>();
+    private boolean mDispatchedKeyguardLockedState = false;
+
     // VR Vr2d Display Id.
     int mVr2dDisplayId = INVALID_DISPLAY;
     boolean mVrModeEnabled = false;
@@ -3029,6 +3035,7 @@
     @Override
     public void onKeyguardShowingAndNotOccludedChanged() {
         mH.sendEmptyMessage(H.RECOMPUTE_FOCUS);
+        dispatchKeyguardLockedStateState();
     }
 
     @Override
@@ -3218,6 +3225,50 @@
         }
     }
 
+    @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+    @Override
+    public void addKeyguardLockedStateListener(IKeyguardLockedStateListener listener) {
+        enforceSubscribeToKeyguardLockedStatePermission();
+        boolean registered = mKeyguardLockedStateListeners.register(listener);
+        if (!registered) {
+            Slog.w(TAG, "Failed to register listener: " + listener);
+        }
+    }
+
+    @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+    @Override
+    public void removeKeyguardLockedStateListener(IKeyguardLockedStateListener listener) {
+        enforceSubscribeToKeyguardLockedStatePermission();
+        mKeyguardLockedStateListeners.unregister(listener);
+    }
+
+    private void enforceSubscribeToKeyguardLockedStatePermission() {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE,
+                Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE
+                        + " permission required to read keyguard visibility");
+    }
+
+    private void dispatchKeyguardLockedStateState() {
+        mH.post(() -> {
+            final boolean isKeyguardLocked = mPolicy.isKeyguardShowing();
+            if (mDispatchedKeyguardLockedState == isKeyguardLocked) {
+                return;
+            }
+            final int n = mKeyguardLockedStateListeners.beginBroadcast();
+            for (int i = 0; i < n; i++) {
+                try {
+                    mKeyguardLockedStateListeners.getBroadcastItem(i).onKeyguardLockedStateChanged(
+                            isKeyguardLocked);
+                } catch (RemoteException e) {
+                    // Handled by the RemoteCallbackList.
+                }
+            }
+            mKeyguardLockedStateListeners.finishBroadcast();
+            mDispatchedKeyguardLockedState = isKeyguardLocked;
+        });
+    }
+
     @Override
     public void setSwitchingUser(boolean switching) {
         if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index ccaa03a..a2e8813 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 
@@ -645,8 +646,10 @@
         final ActivityRecord r = asActivityRecord();
         if (r != null) {
             final Task rootTask = r.getRootTask();
-            // Don't transform the activity in PiP because the PiP task organizer will handle it.
-            if (rootTask != null && rootTask.inPinnedWindowingMode()) {
+            // Don't transform the activity exiting PiP because the PiP task organizer will handle
+            // it.
+            if (rootTask != null && mTransitionController.getWindowingModeAtStart(rootTask)
+                    == WINDOWING_MODE_PINNED) {
                 return;
             }
         }
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index a9c6b8d..11714dc 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -50,6 +50,7 @@
 #include "android_runtime/Log.h"
 #include "gnss/AGnss.h"
 #include "gnss/AGnssRil.h"
+#include "gnss/Gnss.h"
 #include "gnss/GnssAntennaInfo.h"
 #include "gnss/GnssAntennaInfoCallback.h"
 #include "gnss/GnssBatching.h"
@@ -68,18 +69,7 @@
 
 static jclass class_gnssPowerStats;
 
-static jmethodID method_reportLocation;
-static jmethodID method_reportStatus;
-static jmethodID method_reportSvStatus;
-static jmethodID method_reportNmea;
-static jmethodID method_setTopHalCapabilities;
-static jmethodID method_setGnssYearOfHardware;
-static jmethodID method_setGnssHardwareModelName;
-static jmethodID method_psdsDownloadRequest;
 static jmethodID method_reportNiNotification;
-static jmethodID method_requestLocation;
-static jmethodID method_requestUtcTime;
-static jmethodID method_reportGnssServiceDied;
 static jmethodID method_reportGnssPowerStats;
 static jmethodID method_reportNfwNotification;
 static jmethodID method_isInEmergencySession;
@@ -92,7 +82,6 @@
 using android::String16;
 using android::wp;
 using android::binder::Status;
-using android::gnss::GnssConfigurationInterface;
 
 using android::hardware::Return;
 using android::hardware::Void;
@@ -128,12 +117,8 @@
 using android::hardware::gnss::GnssPowerStats;
 using android::hardware::gnss::IGnssPowerIndication;
 using android::hardware::gnss::IGnssPowerIndicationCallback;
-using android::hardware::gnss::PsdsType;
 
-using IAGnssAidl = android::hardware::gnss::IAGnss;
-using IAGnssRilAidl = android::hardware::gnss::IAGnssRil;
 using IGnssAidl = android::hardware::gnss::IGnss;
-using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
 using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching;
 using IGnssDebugAidl = android::hardware::gnss::IGnssDebug;
 using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
@@ -142,575 +127,30 @@
 using GnssLocationAidl = android::hardware::gnss::GnssLocation;
 using IGnssAntennaInfoAidl = android::hardware::gnss::IGnssAntennaInfo;
 
-struct GnssDeathRecipient : virtual public hidl_death_recipient
-{
-    // hidl_death_recipient interface
-    virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override {
-        ALOGE("IGNSS hidl service failed, trying to recover...");
-
-        JNIEnv* env = android::AndroidRuntime::getJNIEnv();
-        env->CallVoidMethod(android::mCallbacksObj, method_reportGnssServiceDied);
-    }
-};
-
-// Must match the value from GnssMeasurement.java
-static const uint32_t SVID_FLAGS_HAS_BASEBAND_CN0 = (1<<4);
-
-sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
-sp<IGnss_V1_0> gnssHal = nullptr;
-sp<IGnss_V1_1> gnssHal_V1_1 = nullptr;
-sp<IGnss_V2_0> gnssHal_V2_0 = nullptr;
-sp<IGnss_V2_1> gnssHal_V2_1 = nullptr;
-sp<IGnssAidl> gnssHalAidl = nullptr;
-sp<IGnssBatchingAidl> gnssBatchingAidlIface = nullptr;
-sp<IGnssPsdsAidl> gnssPsdsAidlIface = nullptr;
-sp<IGnssXtra> gnssXtraIface = nullptr;
 sp<IGnssNi> gnssNiIface = nullptr;
 sp<IGnssPowerIndication> gnssPowerIndicationIface = nullptr;
 
-std::unique_ptr<GnssConfigurationInterface> gnssConfigurationIface = nullptr;
+std::unique_ptr<android::gnss::GnssHal> gnssHal = nullptr;
+std::unique_ptr<android::gnss::AGnssInterface> agnssIface = nullptr;
+std::unique_ptr<android::gnss::AGnssRilInterface> agnssRilIface = nullptr;
+std::unique_ptr<android::gnss::GnssAntennaInfoInterface> gnssAntennaInfoIface = nullptr;
+std::unique_ptr<android::gnss::GnssConfigurationInterface> gnssConfigurationIface = nullptr;
 std::unique_ptr<android::gnss::GnssMeasurementInterface> gnssMeasurementIface = nullptr;
 std::unique_ptr<android::gnss::GnssNavigationMessageInterface> gnssNavigationMessageIface = nullptr;
 std::unique_ptr<android::gnss::GnssBatchingInterface> gnssBatchingIface = nullptr;
-std::unique_ptr<android::gnss::GnssGeofenceInterface> gnssGeofencingIface = nullptr;
-std::unique_ptr<android::gnss::AGnssInterface> agnssIface = nullptr;
 std::unique_ptr<android::gnss::GnssDebugInterface> gnssDebugIface = nullptr;
-std::unique_ptr<android::gnss::AGnssRilInterface> agnssRilIface = nullptr;
+std::unique_ptr<android::gnss::GnssGeofenceInterface> gnssGeofencingIface = nullptr;
+std::unique_ptr<android::gnss::GnssPsdsInterface> gnssPsdsIface = nullptr;
 std::unique_ptr<android::gnss::GnssVisibilityControlInterface> gnssVisibilityControlIface = nullptr;
-std::unique_ptr<android::gnss::GnssAntennaInfoInterface> gnssAntennaInfoIface = nullptr;
 std::unique_ptr<android::gnss::MeasurementCorrectionsInterface> gnssMeasurementCorrectionsIface =
         nullptr;
 
-#define WAKE_LOCK_NAME  "GPS"
-
 namespace android {
 
 namespace {
 
-// Returns true if location has lat/long information.
-bool hasLatLong(const GnssLocationAidl& location) {
-    return (location.gnssLocationFlags & GnssLocationAidl::HAS_LAT_LONG) != 0;
-}
-
-// Returns true if location has lat/long information.
-bool hasLatLong(const GnssLocation_V1_0& location) {
-    return (static_cast<uint32_t>(location.gnssLocationFlags) &
-            GnssLocationFlags::HAS_LAT_LONG) != 0;
-}
-
-// Returns true if location has lat/long information.
-bool hasLatLong(const GnssLocation_V2_0& location) {
-    return hasLatLong(location.v1_0);
-}
-
-bool isSvStatusRegistered = false;
-bool isNmeaRegistered = false;
-
 }  // namespace
 
-static inline jboolean boolToJbool(bool value) {
-    return value ? JNI_TRUE : JNI_FALSE;
-}
-
-static GnssLocationAidl createGnssLocation(jint gnssLocationFlags, jdouble latitudeDegrees,
-                                           jdouble longitudeDegrees, jdouble altitudeMeters,
-                                           jfloat speedMetersPerSec, jfloat bearingDegrees,
-                                           jfloat horizontalAccuracyMeters,
-                                           jfloat verticalAccuracyMeters,
-                                           jfloat speedAccuracyMetersPerSecond,
-                                           jfloat bearingAccuracyDegrees, jlong timestamp,
-                                           jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
-                                           jdouble elapsedRealtimeUncertaintyNanos) {
-    GnssLocationAidl location;
-    location.gnssLocationFlags = static_cast<int>(gnssLocationFlags);
-    location.latitudeDegrees = static_cast<double>(latitudeDegrees);
-    location.longitudeDegrees = static_cast<double>(longitudeDegrees);
-    location.altitudeMeters = static_cast<double>(altitudeMeters);
-    location.speedMetersPerSec = static_cast<double>(speedMetersPerSec);
-    location.bearingDegrees = static_cast<double>(bearingDegrees);
-    location.horizontalAccuracyMeters = static_cast<double>(horizontalAccuracyMeters);
-    location.verticalAccuracyMeters = static_cast<double>(verticalAccuracyMeters);
-    location.speedAccuracyMetersPerSecond = static_cast<double>(speedAccuracyMetersPerSecond);
-    location.bearingAccuracyDegrees = static_cast<double>(bearingAccuracyDegrees);
-    location.timestampMillis = static_cast<uint64_t>(timestamp);
-
-    location.elapsedRealtime.flags = static_cast<int>(elapsedRealtimeFlags);
-    location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
-    location.elapsedRealtime.timeUncertaintyNs =
-            static_cast<double>(elapsedRealtimeUncertaintyNanos);
-
-    return location;
-}
-
-static GnssLocation_V1_0 createGnssLocation_V1_0(
-        jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees,
-        jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
-        jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
-        jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
-        jlong timestamp) {
-    GnssLocation_V1_0 location;
-    location.gnssLocationFlags = static_cast<uint16_t>(gnssLocationFlags);
-    location.latitudeDegrees = static_cast<double>(latitudeDegrees);
-    location.longitudeDegrees = static_cast<double>(longitudeDegrees);
-    location.altitudeMeters = static_cast<double>(altitudeMeters);
-    location.speedMetersPerSec = static_cast<float>(speedMetersPerSec);
-    location.bearingDegrees = static_cast<float>(bearingDegrees);
-    location.horizontalAccuracyMeters = static_cast<float>(horizontalAccuracyMeters);
-    location.verticalAccuracyMeters = static_cast<float>(verticalAccuracyMeters);
-    location.speedAccuracyMetersPerSecond = static_cast<float>(speedAccuracyMetersPerSecond);
-    location.bearingAccuracyDegrees = static_cast<float>(bearingAccuracyDegrees);
-    location.timestamp = static_cast<uint64_t>(timestamp);
-
-    return location;
-}
-
-static GnssLocation_V2_0 createGnssLocation_V2_0(
-        jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees,
-        jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
-        jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
-        jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
-        jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
-        jdouble elapsedRealtimeUncertaintyNanos) {
-    GnssLocation_V2_0 location;
-    location.v1_0 = createGnssLocation_V1_0(
-            gnssLocationFlags, latitudeDegrees, longitudeDegrees, altitudeMeters,
-            speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters,
-            verticalAccuracyMeters, speedAccuracyMetersPerSecond,
-            bearingAccuracyDegrees, timestamp);
-
-    location.elapsedRealtime.flags = static_cast<uint16_t>(elapsedRealtimeFlags);
-    location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
-    location.elapsedRealtime.timeUncertaintyNs = static_cast<uint64_t>(elapsedRealtimeUncertaintyNanos);
-
-    return location;
-}
-
-/*
- * GnssCallback class implements the callback methods for IGnss interface.
- */
-struct GnssCallback : public IGnssCallback_V2_1 {
-    Return<void> gnssLocationCb(const GnssLocation_V1_0& location) override;
-    Return<void> gnssStatusCb(const IGnssCallback_V1_0::GnssStatusValue status) override;
-    Return<void> gnssSvStatusCb(const IGnssCallback_V1_0::GnssSvStatus& svStatus) override {
-        return gnssSvStatusCbImpl<IGnssCallback_V1_0::GnssSvStatus, IGnssCallback_V1_0::GnssSvInfo>(
-                svStatus);
-    }
-    Return<void> gnssNmeaCb(int64_t timestamp, const android::hardware::hidl_string& nmea) override;
-    Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override;
-    Return<void> gnssAcquireWakelockCb() override;
-    Return<void> gnssReleaseWakelockCb() override;
-    Return<void> gnssRequestTimeCb() override;
-    Return<void> gnssRequestLocationCb(const bool independentFromGnss) override;
-
-    Return<void> gnssSetSystemInfoCb(const IGnssCallback_V1_0::GnssSystemInfo& info) override;
-
-    // New in 1.1
-    Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
-
-    // New in 2.0
-    Return<void> gnssRequestLocationCb_2_0(const bool independentFromGnss, const bool isUserEmergency)
-            override;
-    Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
-    Return<void> gnssLocationCb_2_0(const GnssLocation_V2_0& location) override;
-    Return<void> gnssSvStatusCb_2_0(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList) override {
-        return gnssSvStatusCbImpl<hidl_vec<IGnssCallback_V2_0::GnssSvInfo>,
-                                  IGnssCallback_V1_0::GnssSvInfo>(svInfoList);
-    }
-
-    // New in 2.1
-    Return<void> gnssSvStatusCb_2_1(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList) override {
-        return gnssSvStatusCbImpl<hidl_vec<IGnssCallback_V2_1::GnssSvInfo>,
-                                  IGnssCallback_V1_0::GnssSvInfo>(svInfoList);
-    }
-    Return<void> gnssSetCapabilitiesCb_2_1(uint32_t capabilities) override;
-
-    // TODO: Reconsider allocation cost vs threadsafety on these statics
-    static const char* sNmeaString;
-    static size_t sNmeaStringLength;
-
-    template <class T>
-    static Return<void> gnssLocationCbImpl(const T& location);
-
-    template <class T_list, class T_sv_info>
-    static Return<void> gnssSvStatusCbImpl(const T_list& svStatus);
-
-private:
-    template <class T>
-    static uint32_t getHasBasebandCn0DbHzFlag(const T& svStatus) {
-        return 0;
-    }
-
-    template <class T>
-    static double getBasebandCn0DbHz(const T& svStatus, size_t i) {
-        return 0.0;
-    }
-
-    template <class T>
-    static uint32_t getGnssSvInfoListSize(const T& svInfoList) {
-        return svInfoList.size();
-    }
-
-    static const IGnssCallbackAidl::GnssSvInfo& getGnssSvInfoOfIndex(
-            const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) {
-        return svInfoList[i];
-    }
-
-    static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
-            const IGnssCallback_V1_0::GnssSvStatus& svStatus, size_t i) {
-        return svStatus.gnssSvList.data()[i];
-    }
-
-    static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
-            const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList, size_t i) {
-        return svInfoList[i].v1_0;
-    }
-
-    static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex(
-            const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
-        return svInfoList[i].v2_0.v1_0;
-    }
-
-    template <class T>
-    static uint32_t getConstellationType(const T& svInfoList, size_t i) {
-        return static_cast<uint32_t>(svInfoList[i].constellation);
-    }
-};
-
-Return<void> GnssCallback::gnssNameCb(const android::hardware::hidl_string& name) {
-    ALOGD("%s: name=%s\n", __func__, name.c_str());
-
-    JNIEnv* env = getJniEnv();
-    jstring jstringName = env->NewStringUTF(name.c_str());
-    env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName);
-    if (jstringName) {
-        env->DeleteLocalRef(jstringName);
-    }
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-
-    return Void();
-}
-
-const char* GnssCallback::sNmeaString = nullptr;
-size_t GnssCallback::sNmeaStringLength = 0;
-
-template<class T>
-Return<void> GnssCallback::gnssLocationCbImpl(const T& location) {
-    JNIEnv* env = getJniEnv();
-
-    jobject jLocation = translateGnssLocation(env, location);
-
-    env->CallVoidMethod(mCallbacksObj,
-                        method_reportLocation,
-                        boolToJbool(hasLatLong(location)),
-                        jLocation);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    env->DeleteLocalRef(jLocation);
-    return Void();
-}
-
-Return<void> GnssCallback::gnssLocationCb(const GnssLocation_V1_0& location) {
-    return gnssLocationCbImpl<GnssLocation_V1_0>(location);
-}
-
-Return<void>
-GnssCallback::gnssLocationCb_2_0(const GnssLocation_V2_0& location) {
-    return gnssLocationCbImpl<GnssLocation_V2_0>(location);
-}
-
-Return<void> GnssCallback::gnssStatusCb(const IGnssCallback_V2_0::GnssStatusValue status) {
-    JNIEnv* env = getJniEnv();
-    env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Void();
-}
-
-template<>
-uint32_t GnssCallback::getHasBasebandCn0DbHzFlag(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>&
-        svStatus) {
-    return SVID_FLAGS_HAS_BASEBAND_CN0;
-}
-
-template <>
-uint32_t GnssCallback::getHasBasebandCn0DbHzFlag(
-        const std::vector<IGnssCallbackAidl::GnssSvInfo>& svStatus) {
-    return SVID_FLAGS_HAS_BASEBAND_CN0;
-}
-
-template <>
-double GnssCallback::getBasebandCn0DbHz(
-        const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) {
-    return svInfoList[i].basebandCN0DbHz;
-}
-
-template<>
-double GnssCallback::getBasebandCn0DbHz(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList,
-        size_t i) {
-    return svInfoList[i].basebandCN0DbHz;
-}
-
-template <>
-uint32_t GnssCallback::getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) {
-    return svStatus.numSvs;
-}
-
-template <>
-uint32_t GnssCallback::getConstellationType(const IGnssCallback_V1_0::GnssSvStatus& svStatus,
-                                            size_t i) {
-    return static_cast<uint32_t>(svStatus.gnssSvList.data()[i].constellation);
-}
-
-template <>
-uint32_t GnssCallback::getConstellationType(
-        const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
-    return static_cast<uint32_t>(svInfoList[i].v2_0.constellation);
-}
-
-template <class T_list, class T_sv_info>
-Return<void> GnssCallback::gnssSvStatusCbImpl(const T_list& svStatus) {
-    // In HIDL or AIDL v1, if no listener is registered, do not report svInfoList to the framework.
-    if (gnssHalAidl == nullptr || gnssHalAidl->getInterfaceVersion() <= 1) {
-        if (!isSvStatusRegistered) {
-            return Void();
-        }
-    }
-
-    JNIEnv* env = getJniEnv();
-
-    uint32_t listSize = getGnssSvInfoListSize(svStatus);
-
-    jintArray svidWithFlagArray = env->NewIntArray(listSize);
-    jfloatArray cn0Array = env->NewFloatArray(listSize);
-    jfloatArray elevArray = env->NewFloatArray(listSize);
-    jfloatArray azimArray = env->NewFloatArray(listSize);
-    jfloatArray carrierFreqArray = env->NewFloatArray(listSize);
-    jfloatArray basebandCn0Array = env->NewFloatArray(listSize);
-
-    jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0);
-    jfloat* cn0s = env->GetFloatArrayElements(cn0Array, 0);
-    jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
-    jfloat* azim = env->GetFloatArrayElements(azimArray, 0);
-    jfloat* carrierFreq = env->GetFloatArrayElements(carrierFreqArray, 0);
-    jfloat* basebandCn0s = env->GetFloatArrayElements(basebandCn0Array, 0);
-
-    /*
-     * Read GNSS SV info.
-     */
-    for (size_t i = 0; i < listSize; ++i) {
-        enum ShiftWidth: uint8_t {
-            SVID_SHIFT_WIDTH = 12,
-            CONSTELLATION_TYPE_SHIFT_WIDTH = 8
-        };
-
-        const T_sv_info& info = getGnssSvInfoOfIndex(svStatus, i);
-        svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) |
-            (getConstellationType(svStatus, i) << CONSTELLATION_TYPE_SHIFT_WIDTH) |
-            static_cast<uint32_t>(info.svFlag);
-        cn0s[i] = info.cN0Dbhz;
-        elev[i] = info.elevationDegrees;
-        azim[i] = info.azimuthDegrees;
-        carrierFreq[i] = info.carrierFrequencyHz;
-        svidWithFlags[i] |= getHasBasebandCn0DbHzFlag(svStatus);
-        basebandCn0s[i] = getBasebandCn0DbHz(svStatus, i);
-    }
-
-    env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
-    env->ReleaseFloatArrayElements(cn0Array, cn0s, 0);
-    env->ReleaseFloatArrayElements(elevArray, elev, 0);
-    env->ReleaseFloatArrayElements(azimArray, azim, 0);
-    env->ReleaseFloatArrayElements(carrierFreqArray, carrierFreq, 0);
-    env->ReleaseFloatArrayElements(basebandCn0Array, basebandCn0s, 0);
-
-    env->CallVoidMethod(mCallbacksObj, method_reportSvStatus,
-            static_cast<jint>(listSize), svidWithFlagArray, cn0Array, elevArray, azimArray,
-            carrierFreqArray, basebandCn0Array);
-
-    env->DeleteLocalRef(svidWithFlagArray);
-    env->DeleteLocalRef(cn0Array);
-    env->DeleteLocalRef(elevArray);
-    env->DeleteLocalRef(azimArray);
-    env->DeleteLocalRef(carrierFreqArray);
-    env->DeleteLocalRef(basebandCn0Array);
-
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Void();
-}
-
-Return<void> GnssCallback::gnssNmeaCb(int64_t timestamp,
-                                      const ::android::hardware::hidl_string& nmea) {
-    // In HIDL, if no listener is registered, do not report nmea to the framework.
-    if (!isNmeaRegistered) {
-        return Void();
-    }
-    JNIEnv* env = getJniEnv();
-    /*
-     * The Java code will call back to read these values.
-     * We do this to avoid creating unnecessary String objects.
-     */
-    sNmeaString = nmea.c_str();
-    sNmeaStringLength = nmea.size();
-
-    env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Void();
-}
-
-Return<void> GnssCallback::gnssSetCapabilitesCb(uint32_t capabilities) {
-    ALOGD("%s: %du\n", __func__, capabilities);
-
-    JNIEnv* env = getJniEnv();
-    env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Void();
-}
-
-Return<void> GnssCallback::gnssSetCapabilitiesCb_2_0(uint32_t capabilities) {
-    return GnssCallback::gnssSetCapabilitesCb(capabilities);
-}
-
-Return<void> GnssCallback::gnssSetCapabilitiesCb_2_1(uint32_t capabilities) {
-    return GnssCallback::gnssSetCapabilitesCb(capabilities);
-}
-
-Return<void> GnssCallback::gnssAcquireWakelockCb() {
-    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
-    return Void();
-}
-
-Return<void> GnssCallback::gnssReleaseWakelockCb() {
-    release_wake_lock(WAKE_LOCK_NAME);
-    return Void();
-}
-
-Return<void> GnssCallback::gnssRequestTimeCb() {
-    JNIEnv* env = getJniEnv();
-    env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Void();
-}
-
-Return<void> GnssCallback::gnssRequestLocationCb(const bool independentFromGnss) {
-    return GnssCallback::gnssRequestLocationCb_2_0(independentFromGnss, /* isUserEmergency= */
-            false);
-}
-
-Return<void> GnssCallback::gnssRequestLocationCb_2_0(const bool independentFromGnss, const bool
-        isUserEmergency) {
-    JNIEnv* env = getJniEnv();
-    env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss),
-            boolToJbool(isUserEmergency));
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Void();
-}
-
-Return<void> GnssCallback::gnssSetSystemInfoCb(const IGnssCallback_V2_0::GnssSystemInfo& info) {
-    ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw);
-
-    JNIEnv* env = getJniEnv();
-    env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware,
-                        info.yearOfHw);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Void();
-}
-
-class GnssCallbackAidl : public android::hardware::gnss::BnGnssCallback {
-public:
-    Status gnssSetCapabilitiesCb(const int capabilities) override;
-    Status gnssStatusCb(const GnssStatusValue status) override;
-    Status gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) override;
-    Status gnssLocationCb(const GnssLocationAidl& location) override;
-    Status gnssNmeaCb(const int64_t timestamp, const std::string& nmea) override;
-    Status gnssAcquireWakelockCb() override;
-    Status gnssReleaseWakelockCb() override;
-    Status gnssSetSystemInfoCb(const GnssSystemInfo& info) override;
-    Status gnssRequestTimeCb() override;
-    Status gnssRequestLocationCb(const bool independentFromGnss,
-                                 const bool isUserEmergency) override;
-};
-
-Status GnssCallbackAidl::gnssSetCapabilitiesCb(const int capabilities) {
-    ALOGD("GnssCallbackAidl::%s: %du\n", __func__, capabilities);
-    JNIEnv* env = getJniEnv();
-    env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssStatusCb(const GnssStatusValue status) {
-    JNIEnv* env = getJniEnv();
-    env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) {
-    GnssCallback::gnssSvStatusCbImpl<std::vector<GnssSvInfo>, GnssSvInfo>(svInfoList);
-    return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssLocationCb(const GnssLocationAidl& location) {
-    GnssCallback::gnssLocationCbImpl<GnssLocationAidl>(location);
-    return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssNmeaCb(const int64_t timestamp, const std::string& nmea) {
-    // In AIDL v1, if no listener is registered, do not report nmea to the framework.
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() <= 1) {
-        if (!isNmeaRegistered) {
-            return Status::ok();
-        }
-    }
-    JNIEnv* env = getJniEnv();
-    /*
-     * The Java code will call back to read these values.
-     * We do this to avoid creating unnecessary String objects.
-     */
-    GnssCallback::sNmeaString = nmea.c_str();
-    GnssCallback::sNmeaStringLength = nmea.size();
-
-    env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssAcquireWakelockCb() {
-    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
-    return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssReleaseWakelockCb() {
-    release_wake_lock(WAKE_LOCK_NAME);
-    return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssSetSystemInfoCb(const GnssSystemInfo& info) {
-    ALOGD("%s: yearOfHw=%d, name=%s\n", __func__, info.yearOfHw, info.name.c_str());
-    JNIEnv* env = getJniEnv();
-    env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw);
-    jstring jstringName = env->NewStringUTF(info.name.c_str());
-    env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName);
-    if (jstringName) {
-        env->DeleteLocalRef(jstringName);
-    }
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssRequestTimeCb() {
-    JNIEnv* env = getJniEnv();
-    env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Status::ok();
-}
-
-Status GnssCallbackAidl::gnssRequestLocationCb(const bool independentFromGnss,
-                                               const bool isUserEmergency) {
-    JNIEnv* env = getJniEnv();
-    env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss),
-                        boolToJbool(isUserEmergency));
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Status::ok();
-}
-
 /*
  * GnssPowerIndicationCallback class implements the callback methods for the IGnssPowerIndication
  * interface.
@@ -756,35 +196,6 @@
 }
 
 /*
- * GnssPsdsCallback class implements the callback methods for the IGnssPsds
- * interface.
- */
-struct GnssPsdsCallbackAidl : public android::hardware::gnss::BnGnssPsdsCallback {
-    Status downloadRequestCb(PsdsType psdsType) override {
-        ALOGD("%s. psdsType: %d", __func__, static_cast<int32_t>(psdsType));
-        JNIEnv* env = getJniEnv();
-        env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, psdsType);
-        checkAndClearExceptionFromCallback(env, __FUNCTION__);
-        return Status::ok();
-    }
-};
-
-/**
- * GnssXtraCallback class implements the callback methods for the IGnssXtra
- * interface.
- */
-class GnssXtraCallback : public IGnssXtraCallback {
-    Return<void> downloadRequestCb() override;
-};
-
-Return<void> GnssXtraCallback::downloadRequestCb() {
-    JNIEnv* env = getJniEnv();
-    env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, /* psdsType= */ 1);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-    return Void();
-}
-
-/*
  * GnssNiCallback implements callback methods required by the IGnssNi interface.
  */
 struct GnssNiCallback : public IGnssNiCallback {
@@ -822,41 +233,7 @@
 
 /* Initializes the GNSS service handle. */
 static void android_location_gnss_hal_GnssNative_set_gps_service_handle() {
-    gnssHalAidl = waitForVintfService<IGnssAidl>();
-    if (gnssHalAidl != nullptr) {
-        ALOGD("Successfully got GNSS AIDL handle. Version=%d.", gnssHalAidl->getInterfaceVersion());
-        if (gnssHalAidl->getInterfaceVersion() >= 2) {
-            return;
-        }
-    }
-
-    ALOGD("Trying IGnss_V2_1::getService()");
-    gnssHal_V2_1 = IGnss_V2_1::getService();
-    if (gnssHal_V2_1 != nullptr) {
-        gnssHal = gnssHal_V2_1;
-        gnssHal_V2_0 = gnssHal_V2_1;
-        gnssHal_V1_1 = gnssHal_V2_1;
-        gnssHal = gnssHal_V2_1;
-        return;
-    }
-
-    ALOGD("gnssHal 2.1 was null, trying 2.0");
-    gnssHal_V2_0 = IGnss_V2_0::getService();
-    if (gnssHal_V2_0 != nullptr) {
-        gnssHal = gnssHal_V2_0;
-        gnssHal_V1_1 = gnssHal_V2_0;
-        return;
-    }
-
-    ALOGD("gnssHal 2.0 was null, trying 1.1");
-    gnssHal_V1_1 = IGnss_V1_1::getService();
-    if (gnssHal_V1_1 != nullptr) {
-        gnssHal = gnssHal_V1_1;
-        return;
-    }
-
-    ALOGD("gnssHal 1.1 was null, trying 1.0");
-    gnssHal = IGnss_V1_0::getService();
+    gnssHal = std::make_unique<gnss::GnssHal>();
 }
 
 /* One time initialization at system boot */
@@ -865,21 +242,10 @@
     android_location_gnss_hal_GnssNative_set_gps_service_handle();
 
     // Cache methodIDs and class IDs.
-    method_reportLocation = env->GetMethodID(clazz, "reportLocation",
-            "(ZLandroid/location/Location;)V");
-    method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
-    method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "(I[I[F[F[F[F[F)V");
-    method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
-    method_setTopHalCapabilities = env->GetMethodID(clazz, "setTopHalCapabilities", "(I)V");
-    method_setGnssYearOfHardware = env->GetMethodID(clazz, "setGnssYearOfHardware", "(I)V");
-    method_setGnssHardwareModelName = env->GetMethodID(clazz, "setGnssHardwareModelName",
-            "(Ljava/lang/String;)V");
-    method_psdsDownloadRequest = env->GetMethodID(clazz, "psdsDownloadRequest", "(I)V");
+
     method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification",
             "(IIIIILjava/lang/String;Ljava/lang/String;II)V");
-    method_requestLocation = env->GetMethodID(clazz, "requestLocation", "(ZZ)V");
-    method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V");
-    method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V");
+
     method_reportNfwNotification = env->GetMethodID(clazz, "reportNfwNotification",
             "(Ljava/lang/String;BLjava/lang/String;BLjava/lang/String;BZZ)V");
     method_reportGnssPowerStats =
@@ -894,17 +260,19 @@
     class_gnssPowerStats = (jclass)env->NewGlobalRef(gnssPowerStatsClass);
     method_gnssPowerStatsCtor = env->GetMethodID(class_gnssPowerStats, "<init>", "(IJDDDDDD[D)V");
 
+    gnss::AGnss_class_init_once(env, clazz);
+    gnss::AGnssRil_class_init_once(env, clazz);
+    gnss::Gnss_class_init_once(env, clazz);
     gnss::GnssAntennaInfo_class_init_once(env, clazz);
     gnss::GnssBatching_class_init_once(env, clazz);
     gnss::GnssConfiguration_class_init_once(env);
     gnss::GnssGeofence_class_init_once(env, clazz);
     gnss::GnssMeasurement_class_init_once(env, clazz);
     gnss::GnssNavigationMessage_class_init_once(env, clazz);
+    gnss::GnssPsds_class_init_once(env, clazz);
     gnss::GnssVisibilityControl_class_init_once(env, clazz);
     gnss::MeasurementCorrections_class_init_once(env, clazz);
     gnss::MeasurementCorrectionsCallback_class_init_once(env, clazz);
-    gnss::AGnss_class_init_once(env, clazz);
-    gnss::AGnssRil_class_init_once(env, clazz);
     gnss::Utils_class_init_once(env);
 }
 
@@ -923,313 +291,26 @@
         android_location_gnss_hal_GnssNative_set_gps_service_handle();
     }
 
-    if (gnssHal == nullptr && gnssHalAidl == nullptr) {
+    if (gnssHal == nullptr || !gnssHal->isSupported()) {
         ALOGE("Unable to get GPS service\n");
         return;
     }
 
-    // TODO: linkToDeath for AIDL HAL
-
-    if (gnssHal != nullptr) {
-        gnssHalDeathRecipient = new GnssDeathRecipient();
-        hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0);
-        if (!linked.isOk()) {
-            ALOGE("Transaction error in linking to GnssHAL death: %s",
-                  linked.description().c_str());
-        } else if (!linked) {
-            ALOGW("Unable to link to GnssHal death notifications");
-        } else {
-            ALOGD("Link to death notification successful");
-        }
-    }
-
-    if (gnssHalAidl != nullptr) {
-        sp<IGnssPsdsAidl> gnssPsdsAidl;
-        auto status = gnssHalAidl->getExtensionPsds(&gnssPsdsAidl);
-        if (status.isOk()) {
-            gnssPsdsAidlIface = gnssPsdsAidl;
-        } else {
-            ALOGD("Unable to get a handle to PSDS AIDL interface.");
-        }
-    } else if (gnssHal != nullptr) {
-        auto gnssXtra = gnssHal->getExtensionXtra();
-        if (!gnssXtra.isOk()) {
-            ALOGD("Unable to get a handle to Xtra");
-        } else {
-            gnssXtraIface = gnssXtra;
-        }
-    }
-
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        sp<IAGnssRilAidl> agnssRilAidl;
-        auto status = gnssHalAidl->getExtensionAGnssRil(&agnssRilAidl);
-        if (checkAidlStatus(status, "Unable to get a handle to AGnssRil interface.")) {
-            agnssRilIface = std::make_unique<gnss::AGnssRil>(agnssRilAidl);
-        }
-    } else if (gnssHal_V2_0 != nullptr) {
-        auto agnssRil_V2_0 = gnssHal_V2_0->getExtensionAGnssRil_2_0();
-        if (checkHidlReturn(agnssRil_V2_0, "Unable to get a handle to AGnssRil_V2_0")) {
-            agnssRilIface = std::make_unique<gnss::AGnssRil_V2_0>(agnssRil_V2_0);
-        }
-    } else if (gnssHal != nullptr) {
-        auto agnssRil_V1_0 = gnssHal->getExtensionAGnssRil();
-        if (checkHidlReturn(agnssRil_V1_0, "Unable to get a handle to AGnssRil_V1_0")) {
-            agnssRilIface = std::make_unique<gnss::AGnssRil_V1_0>(agnssRil_V1_0);
-        }
-    }
-
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        sp<IAGnssAidl> agnssAidl;
-        auto status = gnssHalAidl->getExtensionAGnss(&agnssAidl);
-        if (checkAidlStatus(status, "Unable to get a handle to AGnss interface.")) {
-            agnssIface = std::make_unique<gnss::AGnss>(agnssAidl);
-        }
-    } else if (gnssHal_V2_0 != nullptr) {
-        auto agnss_V2_0 = gnssHal_V2_0->getExtensionAGnss_2_0();
-        if (checkHidlReturn(agnss_V2_0, "Unable to get a handle to AGnss_V2_0")) {
-            agnssIface = std::make_unique<gnss::AGnss_V2_0>(agnss_V2_0);
-        }
-    } else if (gnssHal != nullptr) {
-        auto agnss_V1_0 = gnssHal->getExtensionAGnss();
-        if (checkHidlReturn(agnss_V1_0, "Unable to get a handle to AGnss_V1_0")) {
-            agnssIface = std::make_unique<gnss::AGnss_V1_0>(agnss_V1_0);
-        }
-    }
-
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        sp<hardware::gnss::IGnssNavigationMessageInterface> gnssNavigationMessage;
-        auto status = gnssHalAidl->getExtensionGnssNavigationMessage(&gnssNavigationMessage);
-        if (checkAidlStatus(status,
-                            "Unable to get a handle to GnssNavigationMessage AIDL interface.")) {
-            gnssNavigationMessageIface =
-                    std::make_unique<gnss::GnssNavigationMessageAidl>(gnssNavigationMessage);
-        }
-    } else if (gnssHal != nullptr) {
-        auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
-        if (checkHidlReturn(gnssNavigationMessage,
-                            "Unable to get a handle to GnssNavigationMessage interface.")) {
-            gnssNavigationMessageIface =
-                    std::make_unique<gnss::GnssNavigationMessageHidl>(gnssNavigationMessage);
-        }
-    }
-
-    // Allow all causal combinations between IGnss.hal and IGnssMeasurement.hal. That means,
-    // 2.1@IGnss can be paired with {1.0, 1,1, 2.0, 2.1}@IGnssMeasurement
-    // 2.0@IGnss can be paired with {1.0, 1,1, 2.0}@IGnssMeasurement
-    // 1.1@IGnss can be paired {1.0, 1.1}@IGnssMeasurement
-    // 1.0@IGnss is paired with 1.0@IGnssMeasurement
-    gnssMeasurementIface = nullptr;
-    if (gnssHalAidl != nullptr) {
-        sp<hardware::gnss::IGnssMeasurementInterface> gnssMeasurement;
-        auto status = gnssHalAidl->getExtensionGnssMeasurement(&gnssMeasurement);
-        if (checkAidlStatus(status, "Unable to get a handle to GnssMeasurement AIDL interface.")) {
-            gnssMeasurementIface =
-                    std::make_unique<android::gnss::GnssMeasurement>(gnssMeasurement);
-        }
-    }
-    if (gnssHal_V2_1 != nullptr && gnssMeasurementIface == nullptr) {
-        auto gnssMeasurement = gnssHal_V2_1->getExtensionGnssMeasurement_2_1();
-        if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_1")) {
-            gnssMeasurementIface =
-                    std::make_unique<android::gnss::GnssMeasurement_V2_1>(gnssMeasurement);
-        }
-    }
-    if (gnssHal_V2_0 != nullptr && gnssMeasurementIface == nullptr) {
-        auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0();
-        if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_0")) {
-            gnssMeasurementIface =
-                    std::make_unique<android::gnss::GnssMeasurement_V2_0>(gnssMeasurement);
-        }
-    }
-    if (gnssHal_V1_1 != nullptr && gnssMeasurementIface == nullptr) {
-        auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1();
-        if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_1")) {
-            gnssMeasurementIface =
-                    std::make_unique<android::gnss::GnssMeasurement_V1_1>(gnssMeasurement);
-        }
-    }
-    if (gnssHal != nullptr && gnssMeasurementIface == nullptr) {
-        auto gnssMeasurement = gnssHal->getExtensionGnssMeasurement();
-        if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_0")) {
-            gnssMeasurementIface =
-                    std::make_unique<android::gnss::GnssMeasurement_V1_0>(gnssMeasurement);
-        }
-    }
-
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        sp<IGnssAntennaInfoAidl> gnssAntennaInfoAidl;
-        auto status = gnssHalAidl->getExtensionGnssAntennaInfo(&gnssAntennaInfoAidl);
-        if (checkAidlStatus(status, "Unable to get a handle to GnssAntennaInfo interface.")) {
-            gnssAntennaInfoIface = std::make_unique<gnss::GnssAntennaInfoAidl>(gnssAntennaInfoAidl);
-        }
-    } else if (gnssHal_V2_1 != nullptr) {
-        auto gnssAntennaInfo_V2_1 = gnssHal_V2_1->getExtensionGnssAntennaInfo();
-        if (checkHidlReturn(gnssAntennaInfo_V2_1,
-                            "Unable to get a handle to GnssAntennaInfo_V2_1")) {
-            gnssAntennaInfoIface =
-                    std::make_unique<gnss::GnssAntennaInfo_V2_1>(gnssAntennaInfo_V2_1);
-        }
-    }
-
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        sp<android::hardware::gnss::measurement_corrections::IMeasurementCorrectionsInterface>
-                gnssMeasurementCorrectionsAidl;
-        auto status =
-                gnssHalAidl->getExtensionMeasurementCorrections(&gnssMeasurementCorrectionsAidl);
-        if (checkAidlStatus(status,
-                            "Unable to get a handle to GnssVisibilityControl AIDL interface.")) {
-            gnssMeasurementCorrectionsIface =
-                    std::make_unique<gnss::MeasurementCorrectionsIface_Aidl>(
-                            gnssMeasurementCorrectionsAidl);
-        }
-    }
-    if (gnssHal_V2_1 != nullptr && gnssMeasurementCorrectionsIface == nullptr) {
-        auto gnssCorrections = gnssHal_V2_1->getExtensionMeasurementCorrections_1_1();
-        if (checkHidlReturn(gnssCorrections,
-                            "Unable to get a handle to GnssMeasurementCorrections HIDL "
-                            "interface")) {
-            gnssMeasurementCorrectionsIface =
-                    std::make_unique<gnss::MeasurementCorrectionsIface_V1_1>(gnssCorrections);
-        }
-    }
-    if (gnssHal_V2_0 != nullptr && gnssMeasurementCorrectionsIface == nullptr) {
-        auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections();
-        if (checkHidlReturn(gnssCorrections,
-                            "Unable to get a handle to GnssMeasurementCorrections HIDL "
-                            "interface")) {
-            gnssMeasurementCorrectionsIface =
-                    std::make_unique<gnss::MeasurementCorrectionsIface_V1_0>(gnssCorrections);
-        }
-    }
-
-    // Allow all causal combinations between IGnss.hal and IGnssDebug.hal. That means,
-    // 2.0@IGnss can be paired with {1.0, 2.0}@IGnssDebug
-    // 1.0@IGnss is paired with 1.0@IGnssDebug
-
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        sp<IGnssDebugAidl> gnssDebugAidl;
-        auto status = gnssHalAidl->getExtensionGnssDebug(&gnssDebugAidl);
-        if (checkAidlStatus(status, "Unable to get a handle to GnssDebug interface.")) {
-            gnssDebugIface = std::make_unique<gnss::GnssDebug>(gnssDebugAidl);
-        }
-    }
-    if (gnssHal_V2_0 != nullptr && gnssDebugIface == nullptr) {
-        auto gnssDebug_V2_0 = gnssHal_V2_0->getExtensionGnssDebug_2_0();
-        if (checkHidlReturn(gnssDebug_V2_0, "Unable to get a handle to GnssDebug_V2_0.")) {
-            gnssDebugIface = std::make_unique<gnss::GnssDebug_V2_0>(gnssDebug_V2_0);
-        }
-    }
-    if (gnssHal != nullptr && gnssDebugIface == nullptr) {
-        auto gnssDebug_V1_0 = gnssHal->getExtensionGnssDebug();
-        if (checkHidlReturn(gnssDebug_V1_0, "Unable to get a handle to GnssDebug_V1_0.")) {
-            gnssDebugIface = std::make_unique<gnss::GnssDebug_V1_0>(gnssDebug_V1_0);
-        }
-    }
-
-    if (gnssHal != nullptr) {
-        auto gnssNi = gnssHal->getExtensionGnssNi();
-        if (!gnssNi.isOk()) {
-            ALOGD("Unable to get a handle to GnssNi");
-        } else {
-            gnssNiIface = gnssNi;
-        }
-    }
-
-    if (gnssHalAidl != nullptr) {
-        sp<IGnssConfigurationAidl> gnssConfigurationAidl;
-        auto status = gnssHalAidl->getExtensionGnssConfiguration(&gnssConfigurationAidl);
-        if (checkAidlStatus(status,
-                            "Unable to get a handle to GnssConfiguration AIDL interface.")) {
-            gnssConfigurationIface =
-                    std::make_unique<android::gnss::GnssConfiguration>(gnssConfigurationAidl);
-        }
-    } else if (gnssHal_V2_1 != nullptr) {
-        auto gnssConfiguration = gnssHal_V2_1->getExtensionGnssConfiguration_2_1();
-        if (checkHidlReturn(gnssConfiguration,
-                            "Unable to get a handle to GnssConfiguration_V2_1")) {
-            gnssConfigurationIface =
-                    std::make_unique<android::gnss::GnssConfiguration_V2_1>(gnssConfiguration);
-        }
-    } else if (gnssHal_V2_0 != nullptr) {
-        auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0();
-        if (checkHidlReturn(gnssConfiguration,
-                            "Unable to get a handle to GnssConfiguration_V2_0")) {
-            gnssConfigurationIface =
-                    std::make_unique<android::gnss::GnssConfiguration_V2_0>(gnssConfiguration);
-        }
-    } else if (gnssHal_V1_1 != nullptr) {
-        auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
-        if (checkHidlReturn(gnssConfiguration,
-                            "Unable to get a handle to GnssConfiguration_V1_1")) {
-            gnssConfigurationIface =
-                    std::make_unique<gnss::GnssConfiguration_V1_1>(gnssConfiguration);
-        }
-    } else if (gnssHal != nullptr) {
-        auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration();
-        if (checkHidlReturn(gnssConfiguration,
-                            "Unable to get a handle to GnssConfiguration_V1_0")) {
-            gnssConfigurationIface =
-                    std::make_unique<gnss::GnssConfiguration_V1_0>(gnssConfiguration);
-        }
-    }
-
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        sp<hardware::gnss::IGnssGeofence> gnssGeofence;
-        auto status = gnssHalAidl->getExtensionGnssGeofence(&gnssGeofence);
-        if (checkAidlStatus(status, "Unable to get a handle to GnssGeofence AIDL interface.")) {
-            gnssGeofencingIface = std::make_unique<gnss::GnssGeofenceAidl>(gnssGeofence);
-        }
-    } else if (gnssHal != nullptr) {
-        auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
-        if (checkHidlReturn(gnssGeofencing, "Unable to get a handle to GnssGeofencing")) {
-            gnssGeofencingIface = std::make_unique<gnss::GnssGeofenceHidl>(gnssGeofencing);
-        }
-    }
-
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        sp<android::hardware::gnss::IGnssBatching> gnssBatchingAidl;
-        auto status = gnssHalAidl->getExtensionGnssBatching(&gnssBatchingAidl);
-        if (checkAidlStatus(status, "Unable to get a handle to GnssBatching interface.")) {
-            gnssBatchingIface = std::make_unique<gnss::GnssBatching>(gnssBatchingAidl);
-        }
-    } else if (gnssHal_V2_0 != nullptr) {
-        auto gnssBatching_V2_0 = gnssHal_V2_0->getExtensionGnssBatching_2_0();
-        if (checkHidlReturn(gnssBatching_V2_0, "Unable to get a handle to GnssBatching_V2_0")) {
-            gnssBatchingIface = std::make_unique<gnss::GnssBatching_V2_0>(gnssBatching_V2_0);
-        }
-    }
-    if (gnssHal != nullptr && gnssBatchingIface == nullptr) {
-        auto gnssBatching_V1_0 = gnssHal->getExtensionGnssBatching();
-        if (checkHidlReturn(gnssBatching_V1_0, "Unable to get a handle to GnssBatching")) {
-            gnssBatchingIface = std::make_unique<gnss::GnssBatching_V1_0>(gnssBatching_V1_0);
-        }
-    }
-
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        sp<android::hardware::gnss::visibility_control::IGnssVisibilityControl>
-                gnssVisibilityControlAidl;
-        auto status = gnssHalAidl->getExtensionGnssVisibilityControl(&gnssVisibilityControlAidl);
-        if (checkAidlStatus(status,
-                            "Unable to get a handle to GnssVisibilityControl AIDL interface.")) {
-            gnssVisibilityControlIface =
-                    std::make_unique<gnss::GnssVisibilityControlAidl>(gnssVisibilityControlAidl);
-        }
-    } else if (gnssHal_V2_0 != nullptr) {
-        auto gnssVisibilityControlHidl = gnssHal_V2_0->getExtensionVisibilityControl();
-        if (checkHidlReturn(gnssVisibilityControlHidl,
-                            "Unable to get a handle to GnssVisibilityControl HIDL interface")) {
-            gnssVisibilityControlIface =
-                    std::make_unique<gnss::GnssVisibilityControlHidl>(gnssVisibilityControlHidl);
-        }
-    }
-
-    if (gnssHalAidl != nullptr) {
-        sp<IGnssPowerIndication> gnssPowerIndication;
-        auto status = gnssHalAidl->getExtensionGnssPowerIndication(&gnssPowerIndication);
-        if (checkAidlStatus(status, "Unable to get a handle to GnssPowerIndication interface.")) {
-            gnssPowerIndicationIface = gnssPowerIndication;
-        }
-    }
+    gnssHal->linkToDeath();
+    gnssPsdsIface = gnssHal->getGnssPsdsInterface();
+    agnssRilIface = gnssHal->getAGnssRilInterface();
+    agnssIface = gnssHal->getAGnssInterface();
+    gnssNavigationMessageIface = gnssHal->getGnssNavigationMessageInterface();
+    gnssMeasurementIface = gnssHal->getGnssMeasurementInterface();
+    gnssAntennaInfoIface = gnssHal->getGnssAntennaInfoInterface();
+    gnssMeasurementCorrectionsIface = gnssHal->getMeasurementCorrectionsInterface();
+    gnssDebugIface = gnssHal->getGnssDebugInterface();
+    gnssNiIface = gnssHal->getGnssNiInterface();
+    gnssConfigurationIface = gnssHal->getGnssConfigurationInterface();
+    gnssGeofencingIface = gnssHal->getGnssGeofenceInterface();
+    gnssBatchingIface = gnssHal->getGnssBatchingInterface();
+    gnssVisibilityControlIface = gnssHal->getGnssVisibilityControlInterface();
+    gnssPowerIndicationIface = gnssHal->getGnssPowerIndicationInterface();
 
     if (mCallbacksObj) {
         ALOGE("Callbacks already initialized");
@@ -1239,7 +320,7 @@
 }
 
 static jboolean android_location_gnss_hal_GnssNative_is_supported(JNIEnv* /* env */, jclass) {
-    return (gnssHalAidl != nullptr || gnssHal != nullptr) ? JNI_TRUE : JNI_FALSE;
+    return (gnssHal != nullptr && gnssHal->isSupported()) ? JNI_TRUE : JNI_FALSE;
 }
 
 static jboolean android_location_GnssNetworkConnectivityHandler_is_agps_ril_supported(
@@ -1268,52 +349,18 @@
     /*
      * Fail if the main interface fails to initialize
      */
-    if (gnssHal == nullptr && gnssHalAidl == nullptr) {
+    if (!gnssHal->isSupported()) {
         ALOGE("Unable to initialize GNSS HAL.");
         return JNI_FALSE;
     }
 
-    // Set top level IGnss.hal callback.
-    if (gnssHal != nullptr) {
-        Return<bool> result = false;
-        sp<IGnssCallback_V2_1> gnssCbIface = new GnssCallback();
-        if (gnssHal_V2_1 != nullptr) {
-            result = gnssHal_V2_1->setCallback_2_1(gnssCbIface);
-        } else if (gnssHal_V2_0 != nullptr) {
-            result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
-        } else if (gnssHal_V1_1 != nullptr) {
-            result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
-        } else {
-            result = gnssHal->setCallback(gnssCbIface);
-        }
-        if (!checkHidlReturn(result, "IGnss setCallback() failed.")) {
-            return JNI_FALSE;
-        }
-    }
+    // Set top level IGnss HAL callback.
+    gnssHal->setCallback();
 
-    if (gnssHalAidl != nullptr) {
-        sp<IGnssCallbackAidl> gnssCbIfaceAidl = new GnssCallbackAidl();
-        auto status = gnssHalAidl->setCallback(gnssCbIfaceAidl);
-        if (!checkAidlStatus(status, "IGnssAidl setCallback() failed.")) {
-            return JNI_FALSE;
-        }
-    }
-
-    // Set IGnssPsds or IGnssXtra callback.
-    if (gnssPsdsAidlIface != nullptr) {
-        sp<IGnssPsdsCallbackAidl> gnssPsdsCallbackAidl = new GnssPsdsCallbackAidl();
-        auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl);
-        if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.")) {
-            gnssPsdsAidlIface = nullptr;
-        }
-    } else if (gnssXtraIface != nullptr) {
-        sp<IGnssXtraCallback> gnssXtraCbIface = new GnssXtraCallback();
-        auto result = gnssXtraIface->setCallback(gnssXtraCbIface);
-        if (!checkHidlReturn(result, "IGnssXtra setCallback() failed.")) {
-            gnssXtraIface = nullptr;
-        } else {
-            ALOGI("Unable to initialize IGnssXtra interface.");
-        }
+    // Set IGnssPsds callback.
+    if (gnssPsdsIface == nullptr ||
+        !gnssPsdsIface->setCallback(std::make_unique<gnss::GnssPsdsCallback>())) {
+        ALOGI("Unable to initialize IGnssPsds interface.");
     }
 
     // Set IAGnss callback.
@@ -1373,145 +420,47 @@
 }
 
 static void android_location_gnss_hal_GnssNative_cleanup(JNIEnv* /* env */, jclass) {
-    if (gnssHalAidl != nullptr) {
-        auto status = gnssHalAidl->close();
-        checkAidlStatus(status, "IGnssAidl close() failed.");
-    }
-
-    if (gnssHal != nullptr) {
-        auto result = gnssHal->cleanup();
-        checkHidlReturn(result, "IGnss cleanup() failed.");
-    }
+    gnssHal->close();
 }
 
 static jboolean android_location_gnss_hal_GnssNative_set_position_mode(
         JNIEnv* /* env */, jclass, jint mode, jint recurrence, jint min_interval,
         jint preferred_accuracy, jint preferred_time, jboolean low_power_mode) {
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        IGnssAidl::PositionModeOptions options;
-        options.mode = static_cast<IGnssAidl::GnssPositionMode>(mode);
-        options.recurrence = static_cast<IGnssAidl::GnssPositionRecurrence>(recurrence);
-        options.minIntervalMs = min_interval;
-        options.preferredAccuracyMeters = preferred_accuracy;
-        options.preferredTimeMs = preferred_time;
-        options.lowPowerMode = low_power_mode;
-        auto status = gnssHalAidl->setPositionMode(options);
-        return checkAidlStatus(status, "IGnssAidl setPositionMode() failed.");
-    }
-
-    Return<bool> result = false;
-    if (gnssHal_V1_1 != nullptr) {
-         result = gnssHal_V1_1->setPositionMode_1_1(static_cast<IGnss_V1_0::GnssPositionMode>(mode),
-                 static_cast<IGnss_V1_0::GnssPositionRecurrence>(recurrence),
-                 min_interval,
-                 preferred_accuracy,
-                 preferred_time,
-                 low_power_mode);
-     } else if (gnssHal != nullptr) {
-         result = gnssHal->setPositionMode(static_cast<IGnss_V1_0::GnssPositionMode>(mode),
-                 static_cast<IGnss_V1_0::GnssPositionRecurrence>(recurrence),
-                 min_interval,
-                 preferred_accuracy,
-                 preferred_time);
-    }
-
-    return checkHidlReturn(result, "IGnss setPositionMode() failed.");
+    return gnssHal->setPositionMode(mode, recurrence, min_interval, preferred_accuracy,
+                                    preferred_time, low_power_mode);
 }
 
 static jboolean android_location_gnss_hal_GnssNative_start(JNIEnv* /* env */, jclass) {
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        auto status = gnssHalAidl->start();
-        return checkAidlStatus(status, "IGnssAidl start() failed.");
-    }
-
-    if (gnssHal == nullptr) {
-        return JNI_FALSE;
-    }
-
-    auto result = gnssHal->start();
-    return checkHidlReturn(result, "IGnss start() failed.");
+    return gnssHal->start();
 }
 
 static jboolean android_location_gnss_hal_GnssNative_stop(JNIEnv* /* env */, jclass) {
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        auto status = gnssHalAidl->stop();
-        return checkAidlStatus(status, "IGnssAidl stop() failed.");
-    }
-
-    if (gnssHal == nullptr) {
-        return JNI_FALSE;
-    }
-
-    auto result = gnssHal->stop();
-    return checkHidlReturn(result, "IGnss stop() failed.");
+    return gnssHal->stop();
 }
 
 static jboolean android_location_gnss_hal_GnssNative_start_sv_status_collection(JNIEnv* /* env */,
                                                                                 jclass) {
-    isSvStatusRegistered = true;
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        auto status = gnssHalAidl->startSvStatus();
-        return checkAidlStatus(status, "IGnssAidl startSvStatus() failed.");
-    }
-    if (gnssHal == nullptr) {
-        return JNI_FALSE;
-    }
-    return JNI_TRUE;
+    return gnssHal->startSvStatus();
 }
 
 static jboolean android_location_gnss_hal_GnssNative_stop_sv_status_collection(JNIEnv* /* env */,
                                                                                jclass) {
-    isSvStatusRegistered = false;
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        auto status = gnssHalAidl->stopSvStatus();
-        return checkAidlStatus(status, "IGnssAidl stopSvStatus() failed.");
-    }
-    if (gnssHal == nullptr) {
-        return JNI_FALSE;
-    }
-    return JNI_TRUE;
+    return gnssHal->stopSvStatus();
 }
 
 static jboolean android_location_gnss_hal_GnssNative_start_nmea_message_collection(
         JNIEnv* /* env */, jclass) {
-    isNmeaRegistered = true;
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        auto status = gnssHalAidl->startNmea();
-        return checkAidlStatus(status, "IGnssAidl startNmea() failed.");
-    }
-    if (gnssHal == nullptr) {
-        return JNI_FALSE;
-    }
-    return JNI_TRUE;
+    return gnssHal->startNmea();
 }
 
 static jboolean android_location_gnss_hal_GnssNative_stop_nmea_message_collection(JNIEnv* /* env */,
                                                                                   jclass) {
-    isNmeaRegistered = false;
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        auto status = gnssHalAidl->stopNmea();
-        return checkAidlStatus(status, "IGnssAidl stopNmea() failed.");
-    }
-    if (gnssHal == nullptr) {
-        return JNI_FALSE;
-    }
-    return JNI_TRUE;
+    return gnssHal->stopNmea();
 }
 
 static void android_location_gnss_hal_GnssNative_delete_aiding_data(JNIEnv* /* env */, jclass,
                                                                     jint flags) {
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        auto status = gnssHalAidl->deleteAidingData(static_cast<IGnssAidl::GnssAidingData>(flags));
-        checkAidlStatus(status, "IGnssAidl deleteAidingData() failed.");
-        return;
-    }
-
-    if (gnssHal == nullptr) {
-        return;
-    }
-
-    auto result = gnssHal->deleteAidingData(static_cast<IGnss_V1_0::GnssAidingData>(flags));
-    checkHidlReturn(result, "IGnss deleteAidingData() failed.");
+    gnssHal->deleteAidingData(flags);
 }
 
 static void android_location_gnss_hal_GnssNative_agps_set_reference_location_cellid(
@@ -1535,30 +484,13 @@
 
 static jint android_location_gnss_hal_GnssNative_read_nmea(JNIEnv* env, jclass,
                                                            jbyteArray nmeaArray, jint buffer_size) {
-    // this should only be called from within a call to reportNmea
-    jbyte* nmea = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(nmeaArray, 0));
-    int length = GnssCallback::sNmeaStringLength;
-    if (length > buffer_size)
-        length = buffer_size;
-    memcpy(nmea, GnssCallback::sNmeaString, length);
-    env->ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT);
-    return (jint) length;
+    return gnssHal->readNmea(nmeaArray, buffer_size);
 }
 
 static void android_location_gnss_hal_GnssNative_inject_time(JNIEnv* /* env */, jclass, jlong time,
                                                              jlong timeReference,
                                                              jint uncertainty) {
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        auto status = gnssHalAidl->injectTime(time, timeReference, uncertainty);
-        checkAidlStatus(status, "IGnssAidl injectTime() failed.");
-        return;
-    }
-
-    if (gnssHal == nullptr) {
-        return;
-    }
-    auto result = gnssHal->injectTime(time, timeReference, uncertainty);
-    checkHidlReturn(result, "IGnss injectTime() failed.");
+    gnssHal->injectTime(time, timeReference, uncertainty);
 }
 
 static void android_location_gnss_hal_GnssNative_inject_best_location(
@@ -1568,58 +500,12 @@
         jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, jlong timestamp,
         jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
         jdouble elapsedRealtimeUncertaintyNanos) {
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        GnssLocationAidl location =
-                createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
-                                   altitudeMeters, speedMetersPerSec, bearingDegrees,
-                                   horizontalAccuracyMeters, verticalAccuracyMeters,
-                                   speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
-                                   elapsedRealtimeFlags, elapsedRealtimeNanos,
-                                   elapsedRealtimeUncertaintyNanos);
-        auto status = gnssHalAidl->injectBestLocation(location);
-        checkAidlStatus(status, "IGnssAidl injectBestLocation() failed.");
-        return;
-    }
-
-    if (gnssHal_V2_0 != nullptr) {
-        GnssLocation_V2_0 location = createGnssLocation_V2_0(
-                gnssLocationFlags,
-                latitudeDegrees,
-                longitudeDegrees,
-                altitudeMeters,
-                speedMetersPerSec,
-                bearingDegrees,
-                horizontalAccuracyMeters,
-                verticalAccuracyMeters,
-                speedAccuracyMetersPerSecond,
-                bearingAccuracyDegrees,
-                timestamp,
-                elapsedRealtimeFlags,
-                elapsedRealtimeNanos,
-                elapsedRealtimeUncertaintyNanos);
-        auto result = gnssHal_V2_0->injectBestLocation_2_0(location);
-        checkHidlReturn(result, "IGnss injectBestLocation_2_0() failed.");
-        return;
-    }
-
-    if (gnssHal_V1_1 != nullptr) {
-        GnssLocation_V1_0 location = createGnssLocation_V1_0(
-                gnssLocationFlags,
-                latitudeDegrees,
-                longitudeDegrees,
-                altitudeMeters,
-                speedMetersPerSec,
-                bearingDegrees,
-                horizontalAccuracyMeters,
-                verticalAccuracyMeters,
-                speedAccuracyMetersPerSecond,
-                bearingAccuracyDegrees,
-                timestamp);
-        auto result = gnssHal_V1_1->injectBestLocation(location);
-        checkHidlReturn(result, "IGnss injectBestLocation() failed.");
-    }
-
-    ALOGE("IGnss injectBestLocation() is called but gnssHal_V1_1 is not available.");
+    gnssHal->injectBestLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+                                altitudeMeters, speedMetersPerSec, bearingDegrees,
+                                horizontalAccuracyMeters, verticalAccuracyMeters,
+                                speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
+                                elapsedRealtimeFlags, elapsedRealtimeNanos,
+                                elapsedRealtimeUncertaintyNanos);
 }
 
 static void android_location_gnss_hal_GnssNative_inject_location(
@@ -1629,51 +515,25 @@
         jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, jlong timestamp,
         jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
         jdouble elapsedRealtimeUncertaintyNanos) {
-    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        GnssLocationAidl location =
-                createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
-                                   altitudeMeters, speedMetersPerSec, bearingDegrees,
-                                   horizontalAccuracyMeters, verticalAccuracyMeters,
-                                   speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
-                                   elapsedRealtimeFlags, elapsedRealtimeNanos,
-                                   elapsedRealtimeUncertaintyNanos);
-        auto status = gnssHalAidl->injectLocation(location);
-        checkAidlStatus(status, "IGnssAidl injectLocation() failed.");
-        return;
-    }
-
-    if (gnssHal == nullptr) {
-        return;
-    }
-    auto result =
-            gnssHal->injectLocation(latitudeDegrees, longitudeDegrees, horizontalAccuracyMeters);
-    checkHidlReturn(result, "IGnss injectLocation() failed.");
+    gnssHal->injectLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees, altitudeMeters,
+                            speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters,
+                            verticalAccuracyMeters, speedAccuracyMetersPerSecond,
+                            bearingAccuracyDegrees, timestamp, elapsedRealtimeFlags,
+                            elapsedRealtimeNanos, elapsedRealtimeUncertaintyNanos);
 }
 
 static jboolean android_location_gnss_hal_GnssNative_supports_psds(JNIEnv* /* env */, jclass) {
-    return (gnssPsdsAidlIface != nullptr || gnssXtraIface != nullptr) ? JNI_TRUE : JNI_FALSE;
+    return (gnssPsdsIface != nullptr) ? JNI_TRUE : JNI_FALSE;
 }
 
 static void android_location_gnss_hal_GnssNative_inject_psds_data(JNIEnv* env, jclass,
                                                                   jbyteArray data, jint length,
                                                                   jint psdsType) {
-    if (gnssPsdsAidlIface == nullptr && gnssXtraIface == nullptr) {
-        ALOGE("%s: IGnssPsdsAidl or IGnssXtra interface not available.", __func__);
+    if (gnssPsdsIface == nullptr) {
+        ALOGE("%s: IGnssPsds or IGnssXtra interface not available.", __func__);
         return;
     }
-
-    jbyte* bytes = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(data, 0));
-    if (gnssPsdsAidlIface != nullptr) {
-        auto status = gnssPsdsAidlIface->injectPsdsData(static_cast<PsdsType>(psdsType),
-                                                        std::vector<uint8_t>((const uint8_t*)bytes,
-                                                                             (const uint8_t*)bytes +
-                                                                                     length));
-        checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.");
-    } else if (gnssXtraIface != nullptr) {
-        auto result = gnssXtraIface->injectXtraData(std::string((const char*)bytes, length));
-        checkHidlReturn(result, "IGnssXtra injectXtraData() failed.");
-    }
-    env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
+    gnssPsdsIface->injectPsdsData(data, length, psdsType);
 }
 
 static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_open(
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index e52df15..0531ae2 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -28,6 +28,8 @@
         "AGnssRil.cpp",
         "AGnssRilCallback.cpp",
         "GnssAntennaInfo.cpp",
+        "Gnss.cpp",
+        "GnssCallback.cpp",
         "GnssAntennaInfoCallback.cpp",
         "GnssBatching.cpp",
         "GnssBatchingCallback.cpp",
@@ -39,6 +41,8 @@
         "GnssMeasurementCallback.cpp",
         "GnssNavigationMessage.cpp",
         "GnssNavigationMessageCallback.cpp",
+        "GnssPsds.cpp",
+        "GnssPsdsCallback.cpp",
         "GnssVisibilityControl.cpp",
         "GnssVisibilityControlCallback.cpp",
         "MeasurementCorrections.cpp",
@@ -55,6 +59,7 @@
         "libhidlbase",
         "liblog",
         "libnativehelper",
+        "libhardware_legacy",
         "libutils",
         "android.hardware.gnss-V2-cpp",
         "android.hardware.gnss@1.0",
diff --git a/services/core/jni/gnss/Gnss.cpp b/services/core/jni/gnss/Gnss.cpp
new file mode 100644
index 0000000..f6459ea
--- /dev/null
+++ b/services/core/jni/gnss/Gnss.cpp
@@ -0,0 +1,759 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Define LOG_TAG before <log/log.h> to overwrite the default value.
+#define LOG_TAG "GnssJni"
+
+#include "Gnss.h"
+
+#include <binder/IServiceManager.h>
+
+#include "Utils.h"
+
+namespace android::gnss {
+
+using hardware::Return;
+
+using GnssLocationAidl = hardware::gnss::GnssLocation;
+using GnssLocation_V1_0 = hardware::gnss::V1_0::GnssLocation;
+using GnssLocation_V2_0 = hardware::gnss::V2_0::GnssLocation;
+using IAGnssAidl = hardware::gnss::IAGnss;
+using IAGnssRilAidl = hardware::gnss::IAGnssRil;
+using IGnssAidl = hardware::gnss::IGnss;
+using IGnss_V1_0 = hardware::gnss::V1_0::IGnss;
+using IGnss_V1_1 = hardware::gnss::V1_1::IGnss;
+using IGnss_V2_0 = hardware::gnss::V2_0::IGnss;
+using IGnss_V2_1 = hardware::gnss::V2_1::IGnss;
+using IGnssAntennaInfoAidl = hardware::gnss::IGnssAntennaInfo;
+using IGnssCallbackAidl = hardware::gnss::IGnssCallback;
+using IGnssCallback_V1_0 = hardware::gnss::V1_0::IGnssCallback;
+using IGnssCallback_V2_0 = hardware::gnss::V2_0::IGnssCallback;
+using IGnssCallback_V2_1 = hardware::gnss::V2_1::IGnssCallback;
+using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration;
+using IGnssDebugAidl = hardware::gnss::IGnssDebug;
+using android::hardware::gnss::IGnssPsds;
+
+namespace {
+
+GnssLocationAidl createGnssLocation(jint gnssLocationFlags, jdouble latitudeDegrees,
+                                    jdouble longitudeDegrees, jdouble altitudeMeters,
+                                    jfloat speedMetersPerSec, jfloat bearingDegrees,
+                                    jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+                                    jfloat speedAccuracyMetersPerSecond,
+                                    jfloat bearingAccuracyDegrees, jlong timestamp,
+                                    jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+                                    jdouble elapsedRealtimeUncertaintyNanos) {
+    GnssLocationAidl location;
+    location.gnssLocationFlags = static_cast<int>(gnssLocationFlags);
+    location.latitudeDegrees = static_cast<double>(latitudeDegrees);
+    location.longitudeDegrees = static_cast<double>(longitudeDegrees);
+    location.altitudeMeters = static_cast<double>(altitudeMeters);
+    location.speedMetersPerSec = static_cast<double>(speedMetersPerSec);
+    location.bearingDegrees = static_cast<double>(bearingDegrees);
+    location.horizontalAccuracyMeters = static_cast<double>(horizontalAccuracyMeters);
+    location.verticalAccuracyMeters = static_cast<double>(verticalAccuracyMeters);
+    location.speedAccuracyMetersPerSecond = static_cast<double>(speedAccuracyMetersPerSecond);
+    location.bearingAccuracyDegrees = static_cast<double>(bearingAccuracyDegrees);
+    location.timestampMillis = static_cast<uint64_t>(timestamp);
+
+    location.elapsedRealtime.flags = static_cast<int>(elapsedRealtimeFlags);
+    location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
+    location.elapsedRealtime.timeUncertaintyNs =
+            static_cast<double>(elapsedRealtimeUncertaintyNanos);
+
+    return location;
+}
+
+GnssLocation_V1_0 createGnssLocation_V1_0(jint gnssLocationFlags, jdouble latitudeDegrees,
+                                          jdouble longitudeDegrees, jdouble altitudeMeters,
+                                          jfloat speedMetersPerSec, jfloat bearingDegrees,
+                                          jfloat horizontalAccuracyMeters,
+                                          jfloat verticalAccuracyMeters,
+                                          jfloat speedAccuracyMetersPerSecond,
+                                          jfloat bearingAccuracyDegrees, jlong timestamp) {
+    GnssLocation_V1_0 location;
+    location.gnssLocationFlags = static_cast<uint16_t>(gnssLocationFlags);
+    location.latitudeDegrees = static_cast<double>(latitudeDegrees);
+    location.longitudeDegrees = static_cast<double>(longitudeDegrees);
+    location.altitudeMeters = static_cast<double>(altitudeMeters);
+    location.speedMetersPerSec = static_cast<float>(speedMetersPerSec);
+    location.bearingDegrees = static_cast<float>(bearingDegrees);
+    location.horizontalAccuracyMeters = static_cast<float>(horizontalAccuracyMeters);
+    location.verticalAccuracyMeters = static_cast<float>(verticalAccuracyMeters);
+    location.speedAccuracyMetersPerSecond = static_cast<float>(speedAccuracyMetersPerSecond);
+    location.bearingAccuracyDegrees = static_cast<float>(bearingAccuracyDegrees);
+    location.timestamp = static_cast<uint64_t>(timestamp);
+
+    return location;
+}
+
+GnssLocation_V2_0 createGnssLocation_V2_0(jint gnssLocationFlags, jdouble latitudeDegrees,
+                                          jdouble longitudeDegrees, jdouble altitudeMeters,
+                                          jfloat speedMetersPerSec, jfloat bearingDegrees,
+                                          jfloat horizontalAccuracyMeters,
+                                          jfloat verticalAccuracyMeters,
+                                          jfloat speedAccuracyMetersPerSecond,
+                                          jfloat bearingAccuracyDegrees, jlong timestamp,
+                                          jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+                                          jdouble elapsedRealtimeUncertaintyNanos) {
+    GnssLocation_V2_0 location;
+    location.v1_0 = createGnssLocation_V1_0(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+                                            altitudeMeters, speedMetersPerSec, bearingDegrees,
+                                            horizontalAccuracyMeters, verticalAccuracyMeters,
+                                            speedAccuracyMetersPerSecond, bearingAccuracyDegrees,
+                                            timestamp);
+
+    location.elapsedRealtime.flags = static_cast<uint16_t>(elapsedRealtimeFlags);
+    location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos);
+    location.elapsedRealtime.timeUncertaintyNs =
+            static_cast<uint64_t>(elapsedRealtimeUncertaintyNanos);
+
+    return location;
+}
+
+} // anonymous namespace
+
+// Implementation of GnssHal, which unifies all versions of GNSS HALs
+
+GnssHal::GnssHal() {
+    gnssHalAidl = waitForVintfService<IGnssAidl>();
+    if (gnssHalAidl != nullptr) {
+        ALOGD("Successfully got GNSS AIDL handle. Version=%d.", gnssHalAidl->getInterfaceVersion());
+        if (gnssHalAidl->getInterfaceVersion() >= 2) {
+            return;
+        }
+    }
+
+    ALOGD("Trying IGnss_V2_1::getService()");
+    gnssHal_V2_1 = IGnss_V2_1::getService();
+    if (gnssHal_V2_1 != nullptr) {
+        gnssHal_V2_0 = gnssHal_V2_1;
+        gnssHal_V1_1 = gnssHal_V2_1;
+        gnssHal = gnssHal_V2_1;
+        return;
+    }
+
+    ALOGD("gnssHal 2.1 was null, trying 2.0");
+    gnssHal_V2_0 = IGnss_V2_0::getService();
+    if (gnssHal_V2_0 != nullptr) {
+        gnssHal_V1_1 = gnssHal_V2_0;
+        gnssHal = gnssHal_V2_0;
+        return;
+    }
+
+    ALOGD("gnssHal 2.0 was null, trying 1.1");
+    gnssHal_V1_1 = IGnss_V1_1::getService();
+    if (gnssHal_V1_1 != nullptr) {
+        gnssHal = gnssHal_V1_1;
+        return;
+    }
+
+    ALOGD("gnssHal 1.1 was null, trying 1.0");
+    gnssHal = IGnss_V1_0::getService();
+}
+
+jboolean GnssHal::isSupported() {
+    return (gnssHalAidl != nullptr || gnssHal != nullptr) ? JNI_TRUE : JNI_FALSE;
+}
+
+void GnssHal::linkToDeath() {
+    // TODO: linkToDeath for AIDL HAL
+
+    if (gnssHal != nullptr) {
+        gnssHalDeathRecipient = new GnssDeathRecipient();
+        hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0);
+        if (!linked.isOk()) {
+            ALOGE("Transaction error in linking to GnssHAL death: %s",
+                  linked.description().c_str());
+        } else if (!linked) {
+            ALOGW("Unable to link to GnssHal death notifications");
+        } else {
+            ALOGD("Link to death notification successful");
+        }
+    }
+}
+
+jboolean GnssHal::setCallback() {
+    if (gnssHalAidl != nullptr) {
+        sp<IGnssCallbackAidl> gnssCbIfaceAidl = new GnssCallbackAidl();
+        auto status = gnssHalAidl->setCallback(gnssCbIfaceAidl);
+        if (!checkAidlStatus(status, "IGnssAidl setCallback() failed.")) {
+            return JNI_FALSE;
+        }
+    }
+    if (gnssHal != nullptr) {
+        Return<bool> result = false;
+        sp<IGnssCallback_V2_1> gnssCbIface = new GnssCallbackHidl();
+        if (gnssHal_V2_1 != nullptr) {
+            result = gnssHal_V2_1->setCallback_2_1(gnssCbIface);
+        } else if (gnssHal_V2_0 != nullptr) {
+            result = gnssHal_V2_0->setCallback_2_0(gnssCbIface);
+        } else if (gnssHal_V1_1 != nullptr) {
+            result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
+        } else {
+            result = gnssHal->setCallback(gnssCbIface);
+        }
+        if (!checkHidlReturn(result, "IGnss setCallback() failed.")) {
+            return JNI_FALSE;
+        }
+    }
+    return JNI_TRUE;
+}
+
+void GnssHal::close() {
+    if (gnssHalAidl != nullptr) {
+        auto status = gnssHalAidl->close();
+        checkAidlStatus(status, "IGnssAidl close() failed.");
+    }
+
+    if (gnssHal != nullptr) {
+        auto result = gnssHal->cleanup();
+        checkHidlReturn(result, "IGnss cleanup() failed.");
+    }
+}
+
+jboolean GnssHal::start() {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->start();
+        return checkAidlStatus(status, "IGnssAidl start() failed.");
+    }
+
+    if (gnssHal == nullptr) {
+        return JNI_FALSE;
+    }
+
+    auto result = gnssHal->start();
+    return checkHidlReturn(result, "IGnss start() failed.");
+}
+
+jboolean GnssHal::stop() {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->stop();
+        return checkAidlStatus(status, "IGnssAidl stop() failed.");
+    }
+
+    if (gnssHal == nullptr) {
+        return JNI_FALSE;
+    }
+
+    auto result = gnssHal->stop();
+    return checkHidlReturn(result, "IGnss stop() failed.");
+}
+
+jboolean GnssHal::startSvStatus() {
+    isSvStatusRegistered = true;
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->startSvStatus();
+        return checkAidlStatus(status, "IGnssAidl startSvStatus() failed.");
+    }
+    if (gnssHal == nullptr) {
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+jboolean GnssHal::stopSvStatus() {
+    isSvStatusRegistered = false;
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->stopSvStatus();
+        return checkAidlStatus(status, "IGnssAidl stopSvStatus() failed.");
+    }
+    if (gnssHal == nullptr) {
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+jboolean GnssHal::startNmea() {
+    isNmeaRegistered = true;
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->startNmea();
+        return checkAidlStatus(status, "IGnssAidl startNmea() failed.");
+    }
+    if (gnssHal == nullptr) {
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+jboolean GnssHal::stopNmea() {
+    isNmeaRegistered = false;
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->stopNmea();
+        return checkAidlStatus(status, "IGnssAidl stopNmea() failed.");
+    }
+    if (gnssHal == nullptr) {
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+jint GnssHal::readNmea(jbyteArray& nmeaArray, jint& buffer_size) {
+    // this should only be called from within a call to reportNmea
+    JNIEnv* env = getJniEnv();
+    jbyte* nmea = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(nmeaArray, 0));
+    int length = GnssCallbackHidl::sNmeaStringLength;
+    if (length > buffer_size) {
+        length = buffer_size;
+    }
+    memcpy(nmea, GnssCallbackHidl::sNmeaString, length);
+    env->ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT);
+    return (jint)length;
+}
+
+jboolean GnssHal::setPositionMode(jint mode, jint recurrence, jint min_interval,
+                                  jint preferred_accuracy, jint preferred_time,
+                                  jboolean low_power_mode) {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        IGnssAidl::PositionModeOptions options;
+        options.mode = static_cast<IGnssAidl::GnssPositionMode>(mode);
+        options.recurrence = static_cast<IGnssAidl::GnssPositionRecurrence>(recurrence);
+        options.minIntervalMs = min_interval;
+        options.preferredAccuracyMeters = preferred_accuracy;
+        options.preferredTimeMs = preferred_time;
+        options.lowPowerMode = low_power_mode;
+        auto status = gnssHalAidl->setPositionMode(options);
+        return checkAidlStatus(status, "IGnssAidl setPositionMode() failed.");
+    }
+
+    Return<bool> result = false;
+    if (gnssHal_V1_1 != nullptr) {
+        result = gnssHal_V1_1->setPositionMode_1_1(static_cast<IGnss_V1_0::GnssPositionMode>(mode),
+                                                   static_cast<IGnss_V1_0::GnssPositionRecurrence>(
+                                                           recurrence),
+                                                   min_interval, preferred_accuracy, preferred_time,
+                                                   low_power_mode);
+    } else if (gnssHal != nullptr) {
+        result = gnssHal->setPositionMode(static_cast<IGnss_V1_0::GnssPositionMode>(mode),
+                                          static_cast<IGnss_V1_0::GnssPositionRecurrence>(
+                                                  recurrence),
+                                          min_interval, preferred_accuracy, preferred_time);
+    }
+    return checkHidlReturn(result, "IGnss setPositionMode() failed.");
+}
+
+void GnssHal::deleteAidingData(jint flags) {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->deleteAidingData(static_cast<IGnssAidl::GnssAidingData>(flags));
+        checkAidlStatus(status, "IGnssAidl deleteAidingData() failed.");
+        return;
+    }
+
+    if (gnssHal == nullptr) {
+        return;
+    }
+
+    auto result = gnssHal->deleteAidingData(static_cast<IGnss_V1_0::GnssAidingData>(flags));
+    checkHidlReturn(result, "IGnss deleteAidingData() failed.");
+}
+
+void GnssHal::injectTime(jlong time, jlong timeReference, jint uncertainty) {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        auto status = gnssHalAidl->injectTime(time, timeReference, uncertainty);
+        checkAidlStatus(status, "IGnssAidl injectTime() failed.");
+        return;
+    }
+
+    if (gnssHal == nullptr) {
+        return;
+    }
+    auto result = gnssHal->injectTime(time, timeReference, uncertainty);
+    checkHidlReturn(result, "IGnss injectTime() failed.");
+}
+
+void GnssHal::injectLocation(jint gnssLocationFlags, jdouble latitudeDegrees,
+                             jdouble longitudeDegrees, jdouble altitudeMeters,
+                             jfloat speedMetersPerSec, jfloat bearingDegrees,
+                             jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+                             jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
+                             jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+                             jdouble elapsedRealtimeUncertaintyNanos) {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        GnssLocationAidl location =
+                createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+                                   altitudeMeters, speedMetersPerSec, bearingDegrees,
+                                   horizontalAccuracyMeters, verticalAccuracyMeters,
+                                   speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
+                                   elapsedRealtimeFlags, elapsedRealtimeNanos,
+                                   elapsedRealtimeUncertaintyNanos);
+        auto status = gnssHalAidl->injectLocation(location);
+        checkAidlStatus(status, "IGnssAidl injectLocation() failed.");
+        return;
+    }
+
+    if (gnssHal == nullptr) {
+        return;
+    }
+    auto result =
+            gnssHal->injectLocation(latitudeDegrees, longitudeDegrees, horizontalAccuracyMeters);
+    checkHidlReturn(result, "IGnss injectLocation() failed.");
+}
+
+void GnssHal::injectBestLocation(jint gnssLocationFlags, jdouble latitudeDegrees,
+                                 jdouble longitudeDegrees, jdouble altitudeMeters,
+                                 jfloat speedMetersPerSec, jfloat bearingDegrees,
+                                 jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+                                 jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
+                                 jlong timestamp, jint elapsedRealtimeFlags,
+                                 jlong elapsedRealtimeNanos,
+                                 jdouble elapsedRealtimeUncertaintyNanos) {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        GnssLocationAidl location =
+                createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+                                   altitudeMeters, speedMetersPerSec, bearingDegrees,
+                                   horizontalAccuracyMeters, verticalAccuracyMeters,
+                                   speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
+                                   elapsedRealtimeFlags, elapsedRealtimeNanos,
+                                   elapsedRealtimeUncertaintyNanos);
+        auto status = gnssHalAidl->injectBestLocation(location);
+        checkAidlStatus(status, "IGnssAidl injectBestLocation() failed.");
+        return;
+    }
+
+    if (gnssHal_V2_0 != nullptr) {
+        GnssLocation_V2_0 location =
+                createGnssLocation_V2_0(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+                                        altitudeMeters, speedMetersPerSec, bearingDegrees,
+                                        horizontalAccuracyMeters, verticalAccuracyMeters,
+                                        speedAccuracyMetersPerSecond, bearingAccuracyDegrees,
+                                        timestamp, elapsedRealtimeFlags, elapsedRealtimeNanos,
+                                        elapsedRealtimeUncertaintyNanos);
+        auto result = gnssHal_V2_0->injectBestLocation_2_0(location);
+        checkHidlReturn(result, "IGnss injectBestLocation_2_0() failed.");
+        return;
+    }
+
+    if (gnssHal_V1_1 != nullptr) {
+        GnssLocation_V1_0 location =
+                createGnssLocation_V1_0(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+                                        altitudeMeters, speedMetersPerSec, bearingDegrees,
+                                        horizontalAccuracyMeters, verticalAccuracyMeters,
+                                        speedAccuracyMetersPerSecond, bearingAccuracyDegrees,
+                                        timestamp);
+        auto result = gnssHal_V1_1->injectBestLocation(location);
+        checkHidlReturn(result, "IGnss injectBestLocation() failed.");
+        return;
+    }
+
+    ALOGE("IGnss injectBestLocation() is called but gnssHal_V1_1 is not available.");
+}
+
+std::unique_ptr<AGnssInterface> GnssHal::getAGnssInterface() {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        sp<IAGnssAidl> agnssAidl;
+        auto status = gnssHalAidl->getExtensionAGnss(&agnssAidl);
+        if (checkAidlStatus(status, "Unable to get a handle to AGnss interface.")) {
+            return std::make_unique<gnss::AGnss>(agnssAidl);
+        }
+    } else if (gnssHal_V2_0 != nullptr) {
+        auto agnss_V2_0 = gnssHal_V2_0->getExtensionAGnss_2_0();
+        if (checkHidlReturn(agnss_V2_0, "Unable to get a handle to AGnss_V2_0")) {
+            return std::make_unique<gnss::AGnss_V2_0>(agnss_V2_0);
+        }
+    } else if (gnssHal != nullptr) {
+        auto agnss_V1_0 = gnssHal->getExtensionAGnss();
+        if (checkHidlReturn(agnss_V1_0, "Unable to get a handle to AGnss_V1_0")) {
+            return std::make_unique<gnss::AGnss_V1_0>(agnss_V1_0);
+        }
+    }
+    return nullptr;
+}
+
+std::unique_ptr<AGnssRilInterface> GnssHal::getAGnssRilInterface() {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        sp<IAGnssRilAidl> agnssRilAidl;
+        auto status = gnssHalAidl->getExtensionAGnssRil(&agnssRilAidl);
+        if (checkAidlStatus(status, "Unable to get a handle to AGnssRil interface.")) {
+            return std::make_unique<gnss::AGnssRil>(agnssRilAidl);
+        }
+    } else if (gnssHal_V2_0 != nullptr) {
+        auto agnssRil_V2_0 = gnssHal_V2_0->getExtensionAGnssRil_2_0();
+        if (checkHidlReturn(agnssRil_V2_0, "Unable to get a handle to AGnssRil_V2_0")) {
+            return std::make_unique<gnss::AGnssRil_V2_0>(agnssRil_V2_0);
+        }
+    } else if (gnssHal != nullptr) {
+        auto agnssRil_V1_0 = gnssHal->getExtensionAGnssRil();
+        if (checkHidlReturn(agnssRil_V1_0, "Unable to get a handle to AGnssRil_V1_0")) {
+            return std::make_unique<gnss::AGnssRil_V1_0>(agnssRil_V1_0);
+        }
+    }
+    return nullptr;
+}
+
+std::unique_ptr<GnssNavigationMessageInterface> GnssHal::getGnssNavigationMessageInterface() {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        sp<hardware::gnss::IGnssNavigationMessageInterface> gnssNavigationMessage;
+        auto status = gnssHalAidl->getExtensionGnssNavigationMessage(&gnssNavigationMessage);
+        if (checkAidlStatus(status,
+                            "Unable to get a handle to GnssNavigationMessage AIDL interface.")) {
+            return std::make_unique<gnss::GnssNavigationMessageAidl>(gnssNavigationMessage);
+        }
+    } else if (gnssHal != nullptr) {
+        auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
+        if (checkHidlReturn(gnssNavigationMessage,
+                            "Unable to get a handle to GnssNavigationMessage interface.")) {
+            return std::make_unique<gnss::GnssNavigationMessageHidl>(gnssNavigationMessage);
+        }
+    }
+    return nullptr;
+}
+
+std::unique_ptr<GnssMeasurementInterface> GnssHal::getGnssMeasurementInterface() {
+    // Allow all causal combinations between IGnss.hal and IGnssMeasurement.hal. That means,
+    // 2.1@IGnss can be paired with {1.0, 1,1, 2.0, 2.1}@IGnssMeasurement
+    // 2.0@IGnss can be paired with {1.0, 1,1, 2.0}@IGnssMeasurement
+    // 1.1@IGnss can be paired {1.0, 1.1}@IGnssMeasurement
+    // 1.0@IGnss is paired with 1.0@IGnssMeasurement
+    if (gnssHalAidl != nullptr) {
+        sp<hardware::gnss::IGnssMeasurementInterface> gnssMeasurement;
+        auto status = gnssHalAidl->getExtensionGnssMeasurement(&gnssMeasurement);
+        if (checkAidlStatus(status, "Unable to get a handle to GnssMeasurement AIDL interface.")) {
+            return std::make_unique<android::gnss::GnssMeasurement>(gnssMeasurement);
+        }
+    }
+    if (gnssHal_V2_1 != nullptr) {
+        auto gnssMeasurement = gnssHal_V2_1->getExtensionGnssMeasurement_2_1();
+        if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_1")) {
+            return std::make_unique<android::gnss::GnssMeasurement_V2_1>(gnssMeasurement);
+        }
+    }
+    if (gnssHal_V2_0 != nullptr) {
+        auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0();
+        if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_0")) {
+            return std::make_unique<android::gnss::GnssMeasurement_V2_0>(gnssMeasurement);
+        }
+    }
+    if (gnssHal_V1_1 != nullptr) {
+        auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1();
+        if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_1")) {
+            return std::make_unique<android::gnss::GnssMeasurement_V1_1>(gnssMeasurement);
+        }
+    }
+    if (gnssHal != nullptr) {
+        auto gnssMeasurement = gnssHal->getExtensionGnssMeasurement();
+        if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_0")) {
+            return std::make_unique<android::gnss::GnssMeasurement_V1_0>(gnssMeasurement);
+        }
+    }
+    return nullptr;
+}
+
+std::unique_ptr<GnssDebugInterface> GnssHal::getGnssDebugInterface() {
+    // Allow all causal combinations between IGnss.hal and IGnssDebug.hal. That means,
+    // 2.0@IGnss can be paired with {1.0, 2.0}@IGnssDebug
+    // 1.0@IGnss is paired with 1.0@IGnssDebug
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        sp<IGnssDebugAidl> gnssDebugAidl;
+        auto status = gnssHalAidl->getExtensionGnssDebug(&gnssDebugAidl);
+        if (checkAidlStatus(status, "Unable to get a handle to GnssDebug interface.")) {
+            return std::make_unique<gnss::GnssDebug>(gnssDebugAidl);
+        }
+    }
+    if (gnssHal_V2_0 != nullptr) {
+        auto gnssDebug_V2_0 = gnssHal_V2_0->getExtensionGnssDebug_2_0();
+        if (checkHidlReturn(gnssDebug_V2_0, "Unable to get a handle to GnssDebug_V2_0.")) {
+            return std::make_unique<gnss::GnssDebug_V2_0>(gnssDebug_V2_0);
+        }
+    }
+    if (gnssHal != nullptr) {
+        auto gnssDebug_V1_0 = gnssHal->getExtensionGnssDebug();
+        if (checkHidlReturn(gnssDebug_V1_0, "Unable to get a handle to GnssDebug_V1_0.")) {
+            return std::make_unique<gnss::GnssDebug_V1_0>(gnssDebug_V1_0);
+        }
+    }
+    return nullptr;
+}
+
+std::unique_ptr<GnssConfigurationInterface> GnssHal::getGnssConfigurationInterface() {
+    if (gnssHalAidl != nullptr) {
+        sp<IGnssConfigurationAidl> gnssConfigurationAidl;
+        auto status = gnssHalAidl->getExtensionGnssConfiguration(&gnssConfigurationAidl);
+        if (checkAidlStatus(status,
+                            "Unable to get a handle to GnssConfiguration AIDL interface.")) {
+            return std::make_unique<android::gnss::GnssConfiguration>(gnssConfigurationAidl);
+        }
+    } else if (gnssHal_V2_1 != nullptr) {
+        auto gnssConfiguration = gnssHal_V2_1->getExtensionGnssConfiguration_2_1();
+        if (checkHidlReturn(gnssConfiguration,
+                            "Unable to get a handle to GnssConfiguration_V2_1")) {
+            return std::make_unique<android::gnss::GnssConfiguration_V2_1>(gnssConfiguration);
+        }
+    } else if (gnssHal_V2_0 != nullptr) {
+        auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0();
+        if (checkHidlReturn(gnssConfiguration,
+                            "Unable to get a handle to GnssConfiguration_V2_0")) {
+            return std::make_unique<android::gnss::GnssConfiguration_V2_0>(gnssConfiguration);
+        }
+    } else if (gnssHal_V1_1 != nullptr) {
+        auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
+        if (checkHidlReturn(gnssConfiguration,
+                            "Unable to get a handle to GnssConfiguration_V1_1")) {
+            return std::make_unique<gnss::GnssConfiguration_V1_1>(gnssConfiguration);
+        }
+    } else if (gnssHal != nullptr) {
+        auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration();
+        if (checkHidlReturn(gnssConfiguration,
+                            "Unable to get a handle to GnssConfiguration_V1_0")) {
+            return std::make_unique<gnss::GnssConfiguration_V1_0>(gnssConfiguration);
+        }
+    }
+    return nullptr;
+}
+
+std::unique_ptr<GnssGeofenceInterface> GnssHal::getGnssGeofenceInterface() {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        sp<hardware::gnss::IGnssGeofence> gnssGeofence;
+        auto status = gnssHalAidl->getExtensionGnssGeofence(&gnssGeofence);
+        if (checkAidlStatus(status, "Unable to get a handle to GnssGeofence AIDL interface.")) {
+            return std::make_unique<gnss::GnssGeofenceAidl>(gnssGeofence);
+        }
+    } else if (gnssHal != nullptr) {
+        auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
+        if (checkHidlReturn(gnssGeofencing, "Unable to get a handle to GnssGeofencing")) {
+            return std::make_unique<gnss::GnssGeofenceHidl>(gnssGeofencing);
+        }
+    }
+    return nullptr;
+}
+
+std::unique_ptr<GnssBatchingInterface> GnssHal::getGnssBatchingInterface() {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        sp<android::hardware::gnss::IGnssBatching> gnssBatchingAidl;
+        auto status = gnssHalAidl->getExtensionGnssBatching(&gnssBatchingAidl);
+        if (checkAidlStatus(status, "Unable to get a handle to GnssBatching interface.")) {
+            return std::make_unique<gnss::GnssBatching>(gnssBatchingAidl);
+        }
+    }
+    if (gnssHal_V2_0 != nullptr) {
+        auto gnssBatching_V2_0 = gnssHal_V2_0->getExtensionGnssBatching_2_0();
+        if (checkHidlReturn(gnssBatching_V2_0, "Unable to get a handle to GnssBatching_V2_0")) {
+            return std::make_unique<gnss::GnssBatching_V2_0>(gnssBatching_V2_0);
+        }
+    }
+    if (gnssHal != nullptr) {
+        auto gnssBatching_V1_0 = gnssHal->getExtensionGnssBatching();
+        if (checkHidlReturn(gnssBatching_V1_0, "Unable to get a handle to GnssBatching")) {
+            return std::make_unique<gnss::GnssBatching_V1_0>(gnssBatching_V1_0);
+        }
+    }
+    return nullptr;
+}
+
+std::unique_ptr<MeasurementCorrectionsInterface> GnssHal::getMeasurementCorrectionsInterface() {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        sp<android::hardware::gnss::measurement_corrections::IMeasurementCorrectionsInterface>
+                gnssMeasurementCorrectionsAidl;
+        auto status =
+                gnssHalAidl->getExtensionMeasurementCorrections(&gnssMeasurementCorrectionsAidl);
+        if (checkAidlStatus(status,
+                            "Unable to get a handle to GnssVisibilityControl AIDL interface.")) {
+            return std::make_unique<gnss::MeasurementCorrectionsIface_Aidl>(
+                    gnssMeasurementCorrectionsAidl);
+        }
+    }
+    if (gnssHal_V2_1 != nullptr) {
+        auto gnssCorrections = gnssHal_V2_1->getExtensionMeasurementCorrections_1_1();
+        if (checkHidlReturn(gnssCorrections,
+                            "Unable to get a handle to GnssMeasurementCorrections HIDL "
+                            "interface")) {
+            return std::make_unique<gnss::MeasurementCorrectionsIface_V1_1>(gnssCorrections);
+        }
+    }
+    if (gnssHal_V2_0 != nullptr) {
+        auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections();
+        if (checkHidlReturn(gnssCorrections,
+                            "Unable to get a handle to GnssMeasurementCorrections HIDL "
+                            "interface")) {
+            return std::make_unique<gnss::MeasurementCorrectionsIface_V1_0>(gnssCorrections);
+        }
+    }
+    return nullptr;
+}
+
+std::unique_ptr<GnssVisibilityControlInterface> GnssHal::getGnssVisibilityControlInterface() {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        sp<android::hardware::gnss::visibility_control::IGnssVisibilityControl>
+                gnssVisibilityControlAidl;
+        auto status = gnssHalAidl->getExtensionGnssVisibilityControl(&gnssVisibilityControlAidl);
+        if (checkAidlStatus(status,
+                            "Unable to get a handle to GnssVisibilityControl AIDL interface.")) {
+            return std::make_unique<gnss::GnssVisibilityControlAidl>(gnssVisibilityControlAidl);
+        }
+    } else if (gnssHal_V2_0 != nullptr) {
+        auto gnssVisibilityControlHidl = gnssHal_V2_0->getExtensionVisibilityControl();
+        if (checkHidlReturn(gnssVisibilityControlHidl,
+                            "Unable to get a handle to GnssVisibilityControl HIDL interface")) {
+            return std::make_unique<gnss::GnssVisibilityControlHidl>(gnssVisibilityControlHidl);
+        }
+    }
+    return nullptr;
+}
+
+std::unique_ptr<GnssAntennaInfoInterface> GnssHal::getGnssAntennaInfoInterface() {
+    if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+        sp<IGnssAntennaInfoAidl> gnssAntennaInfoAidl;
+        auto status = gnssHalAidl->getExtensionGnssAntennaInfo(&gnssAntennaInfoAidl);
+        if (checkAidlStatus(status, "Unable to get a handle to GnssAntennaInfo interface.")) {
+            return std::make_unique<gnss::GnssAntennaInfoAidl>(gnssAntennaInfoAidl);
+        }
+    } else if (gnssHal_V2_1 != nullptr) {
+        auto gnssAntennaInfo_V2_1 = gnssHal_V2_1->getExtensionGnssAntennaInfo();
+        if (checkHidlReturn(gnssAntennaInfo_V2_1,
+                            "Unable to get a handle to GnssAntennaInfo_V2_1")) {
+            return std::make_unique<gnss::GnssAntennaInfo_V2_1>(gnssAntennaInfo_V2_1);
+        }
+    }
+    return nullptr;
+}
+
+std::unique_ptr<GnssPsdsInterface> GnssHal::getGnssPsdsInterface() {
+    if (gnssHalAidl != nullptr) {
+        sp<IGnssPsds> gnssPsdsAidl;
+        auto status = gnssHalAidl->getExtensionPsds(&gnssPsdsAidl);
+        if (checkAidlStatus(status, "Unable to get a handle to PSDS interface.")) {
+            return std::make_unique<gnss::GnssPsdsAidl>(gnssPsdsAidl);
+        }
+    } else if (gnssHal != nullptr) {
+        auto gnssXtra = gnssHal->getExtensionXtra();
+        if (checkHidlReturn(gnssXtra, "Unable to get a handle to XTRA interface.")) {
+            return std::make_unique<gnss::GnssPsdsHidl>(gnssXtra);
+        }
+    }
+    return nullptr;
+}
+
+sp<hardware::gnss::IGnssPowerIndication> GnssHal::getGnssPowerIndicationInterface() {
+    if (gnssHalAidl != nullptr) {
+        sp<hardware::gnss::IGnssPowerIndication> gnssPowerIndication;
+        auto status = gnssHalAidl->getExtensionGnssPowerIndication(&gnssPowerIndication);
+        if (checkAidlStatus(status, "Unable to get a handle to GnssPowerIndication")) {
+            return gnssPowerIndication;
+        }
+    }
+    return nullptr;
+}
+
+sp<hardware::gnss::V1_0::IGnssNi> GnssHal::getGnssNiInterface() {
+    if (gnssHal != nullptr) {
+        auto gnssNi = gnssHal->getExtensionGnssNi();
+        if (checkHidlReturn(gnssNi, "Unable to get a handle to GnssNi")) {
+            return gnssNi;
+        }
+    }
+    return nullptr;
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/Gnss.h b/services/core/jni/gnss/Gnss.h
new file mode 100644
index 0000000..c6743d6
--- /dev/null
+++ b/services/core/jni/gnss/Gnss.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSS_H
+#define _ANDROID_SERVER_GNSS_GNSS_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnss.h>
+#include <android/hardware/gnss/1.1/IGnss.h>
+#include <android/hardware/gnss/2.0/IGnss.h>
+#include <android/hardware/gnss/2.1/IGnss.h>
+#include <android/hardware/gnss/BnGnss.h>
+#include <log/log.h>
+
+#include "AGnss.h"
+#include "AGnssRil.h"
+#include "GnssAntennaInfo.h"
+#include "GnssBatching.h"
+#include "GnssCallback.h"
+#include "GnssConfiguration.h"
+#include "GnssDebug.h"
+#include "GnssGeofence.h"
+#include "GnssMeasurement.h"
+#include "GnssNavigationMessage.h"
+#include "GnssPsds.h"
+#include "GnssVisibilityControl.h"
+#include "MeasurementCorrections.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+struct GnssDeathRecipient : virtual public hardware::hidl_death_recipient {
+    // hidl_death_recipient interface
+    virtual void serviceDied(uint64_t cookie, const wp<hidl::base::V1_0::IBase>& who) override {
+        ALOGE("IGNSS hidl service failed, trying to recover...");
+
+        JNIEnv* env = android::AndroidRuntime::getJNIEnv();
+        env->CallVoidMethod(android::mCallbacksObj, method_reportGnssServiceDied);
+    }
+};
+
+class GnssHal {
+public:
+    GnssHal();
+    ~GnssHal() {}
+
+    jboolean isSupported();
+    jboolean setCallback();
+    jboolean start();
+    jboolean stop();
+    jboolean setPositionMode(jint mode, jint recurrence, jint min_interval, jint preferred_accuracy,
+                             jint preferred_time, jboolean low_power_mode);
+    jboolean startSvStatus();
+    jboolean stopSvStatus();
+    jboolean startNmea();
+    jboolean stopNmea();
+    jint readNmea(jbyteArray& nmeaArray, jint& buffer_size);
+    void linkToDeath();
+    void close();
+    void deleteAidingData(jint flags);
+    void injectTime(jlong time, jlong timeReference, jint uncertainty);
+    void injectLocation(jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees,
+                        jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
+                        jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+                        jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
+                        jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+                        jdouble elapsedRealtimeUncertaintyNanos);
+    void injectBestLocation(jint gnssLocationFlags, jdouble latitudeDegrees,
+                            jdouble longitudeDegrees, jdouble altitudeMeters,
+                            jfloat speedMetersPerSec, jfloat bearingDegrees,
+                            jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+                            jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees,
+                            jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
+                            jdouble elapsedRealtimeUncertaintyNanos);
+
+    std::unique_ptr<AGnssInterface> getAGnssInterface();
+    std::unique_ptr<AGnssRilInterface> getAGnssRilInterface();
+    std::unique_ptr<GnssNavigationMessageInterface> getGnssNavigationMessageInterface();
+    std::unique_ptr<GnssMeasurementInterface> getGnssMeasurementInterface();
+    std::unique_ptr<GnssDebugInterface> getGnssDebugInterface();
+    std::unique_ptr<GnssConfigurationInterface> getGnssConfigurationInterface();
+    std::unique_ptr<GnssGeofenceInterface> getGnssGeofenceInterface();
+    std::unique_ptr<GnssBatchingInterface> getGnssBatchingInterface();
+    std::unique_ptr<MeasurementCorrectionsInterface> getMeasurementCorrectionsInterface();
+    std::unique_ptr<GnssVisibilityControlInterface> getGnssVisibilityControlInterface();
+    std::unique_ptr<GnssAntennaInfoInterface> getGnssAntennaInfoInterface();
+    std::unique_ptr<GnssPsdsInterface> getGnssPsdsInterface();
+
+    sp<hardware::gnss::IGnssPowerIndication> getGnssPowerIndicationInterface();
+    sp<hardware::gnss::V1_0::IGnssNi> getGnssNiInterface();
+
+private:
+    sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
+    sp<hardware::gnss::V1_0::IGnss> gnssHal = nullptr;
+    sp<hardware::gnss::V1_1::IGnss> gnssHal_V1_1 = nullptr;
+    sp<hardware::gnss::V2_0::IGnss> gnssHal_V2_0 = nullptr;
+    sp<hardware::gnss::V2_1::IGnss> gnssHal_V2_1 = nullptr;
+    sp<hardware::gnss::IGnss> gnssHalAidl = nullptr;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_Gnss_H
diff --git a/services/core/jni/gnss/GnssCallback.cpp b/services/core/jni/gnss/GnssCallback.cpp
new file mode 100644
index 0000000..b931e91
--- /dev/null
+++ b/services/core/jni/gnss/GnssCallback.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GnssCallbckJni"
+
+#include "GnssCallback.h"
+
+#include <hardware_legacy/power.h>
+
+#define WAKE_LOCK_NAME "GPS"
+
+namespace android::gnss {
+
+using android::hardware::gnss::V1_0::GnssLocationFlags;
+using binder::Status;
+using hardware::hidl_vec;
+using hardware::Return;
+using hardware::Void;
+
+using GnssLocationAidl = android::hardware::gnss::GnssLocation;
+using GnssLocation_V1_0 = android::hardware::gnss::V1_0::GnssLocation;
+using GnssLocation_V2_0 = android::hardware::gnss::V2_0::GnssLocation;
+using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
+using IGnssCallback_V1_0 = android::hardware::gnss::V1_0::IGnssCallback;
+using IGnssCallback_V2_0 = android::hardware::gnss::V2_0::IGnssCallback;
+using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback;
+
+jmethodID method_reportGnssServiceDied;
+
+namespace {
+
+jmethodID method_reportLocation;
+jmethodID method_reportStatus;
+jmethodID method_reportSvStatus;
+jmethodID method_reportNmea;
+jmethodID method_setTopHalCapabilities;
+jmethodID method_setGnssYearOfHardware;
+jmethodID method_setGnssHardwareModelName;
+jmethodID method_requestLocation;
+jmethodID method_requestUtcTime;
+
+// Returns true if location has lat/long information.
+inline bool hasLatLong(const GnssLocationAidl& location) {
+    return (location.gnssLocationFlags & hardware::gnss::GnssLocation::HAS_LAT_LONG) != 0;
+}
+
+// Returns true if location has lat/long information.
+inline bool hasLatLong(const GnssLocation_V1_0& location) {
+    return (static_cast<uint32_t>(location.gnssLocationFlags) & GnssLocationFlags::HAS_LAT_LONG) !=
+            0;
+}
+
+// Returns true if location has lat/long information.
+inline bool hasLatLong(const GnssLocation_V2_0& location) {
+    return hasLatLong(location.v1_0);
+}
+
+inline jboolean boolToJbool(bool value) {
+    return value ? JNI_TRUE : JNI_FALSE;
+}
+
+// Must match the value from GnssMeasurement.java
+const uint32_t SVID_FLAGS_HAS_BASEBAND_CN0 = (1 << 4);
+
+} // anonymous namespace
+
+bool isSvStatusRegistered = false;
+bool isNmeaRegistered = false;
+
+void Gnss_class_init_once(JNIEnv* env, jclass& clazz) {
+    method_reportLocation =
+            env->GetMethodID(clazz, "reportLocation", "(ZLandroid/location/Location;)V");
+    method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
+    method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "(I[I[F[F[F[F[F)V");
+    method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
+
+    method_setTopHalCapabilities = env->GetMethodID(clazz, "setTopHalCapabilities", "(I)V");
+    method_setGnssYearOfHardware = env->GetMethodID(clazz, "setGnssYearOfHardware", "(I)V");
+    method_setGnssHardwareModelName =
+            env->GetMethodID(clazz, "setGnssHardwareModelName", "(Ljava/lang/String;)V");
+
+    method_requestLocation = env->GetMethodID(clazz, "requestLocation", "(ZZ)V");
+    method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V");
+    method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V");
+}
+
+Status GnssCallbackAidl::gnssSetCapabilitiesCb(const int capabilities) {
+    ALOGD("GnssCallbackAidl::%s: %du\n", __func__, capabilities);
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssStatusCb(const GnssStatusValue status) {
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) {
+    GnssCallbackHidl::gnssSvStatusCbImpl<std::vector<GnssSvInfo>, GnssSvInfo>(svInfoList);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssLocationCb(const hardware::gnss::GnssLocation& location) {
+    GnssCallbackHidl::gnssLocationCbImpl<hardware::gnss::GnssLocation>(location);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssNmeaCb(const int64_t timestamp, const std::string& nmea) {
+    // In AIDL v1, if no listener is registered, do not report nmea to the framework.
+    if (getInterfaceVersion() <= 1) {
+        if (!isNmeaRegistered) {
+            return Status::ok();
+        }
+    }
+    JNIEnv* env = getJniEnv();
+    /*
+     * The Java code will call back to read these values.
+     * We do this to avoid creating unnecessary String objects.
+     */
+    GnssCallbackHidl::sNmeaString = nmea.c_str();
+    GnssCallbackHidl::sNmeaStringLength = nmea.size();
+
+    env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssAcquireWakelockCb() {
+    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssReleaseWakelockCb() {
+    release_wake_lock(WAKE_LOCK_NAME);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssSetSystemInfoCb(const GnssSystemInfo& info) {
+    ALOGD("%s: yearOfHw=%d, name=%s\n", __func__, info.yearOfHw, info.name.c_str());
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw);
+    jstring jstringName = env->NewStringUTF(info.name.c_str());
+    env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName);
+    if (jstringName) {
+        env->DeleteLocalRef(jstringName);
+    }
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssRequestTimeCb() {
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
+Status GnssCallbackAidl::gnssRequestLocationCb(const bool independentFromGnss,
+                                               const bool isUserEmergency) {
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss),
+                        boolToJbool(isUserEmergency));
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
+// Implementation of IGnssCallbackHidl
+
+Return<void> GnssCallbackHidl::gnssNameCb(const android::hardware::hidl_string& name) {
+    ALOGD("%s: name=%s\n", __func__, name.c_str());
+
+    JNIEnv* env = getJniEnv();
+    jstring jstringName = env->NewStringUTF(name.c_str());
+    env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName);
+    if (jstringName) {
+        env->DeleteLocalRef(jstringName);
+    }
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+
+    return Void();
+}
+
+const char* GnssCallbackHidl::sNmeaString = nullptr;
+size_t GnssCallbackHidl::sNmeaStringLength = 0;
+
+template <class T>
+Return<void> GnssCallbackHidl::gnssLocationCbImpl(const T& location) {
+    JNIEnv* env = getJniEnv();
+
+    jobject jLocation = translateGnssLocation(env, location);
+
+    env->CallVoidMethod(mCallbacksObj, method_reportLocation, boolToJbool(hasLatLong(location)),
+                        jLocation);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    env->DeleteLocalRef(jLocation);
+    return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssLocationCb(const GnssLocation_V1_0& location) {
+    return gnssLocationCbImpl<GnssLocation_V1_0>(location);
+}
+
+Return<void> GnssCallbackHidl::gnssLocationCb_2_0(const GnssLocation_V2_0& location) {
+    return gnssLocationCbImpl<GnssLocation_V2_0>(location);
+}
+
+Return<void> GnssCallbackHidl::gnssStatusCb(const IGnssCallback_V2_0::GnssStatusValue status) {
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Void();
+}
+
+template <>
+uint32_t GnssCallbackHidl::getHasBasebandCn0DbHzFlag(
+        const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svStatus) {
+    return SVID_FLAGS_HAS_BASEBAND_CN0;
+}
+
+template <>
+uint32_t GnssCallbackHidl::getHasBasebandCn0DbHzFlag(
+        const std::vector<IGnssCallbackAidl::GnssSvInfo>& svStatus) {
+    return SVID_FLAGS_HAS_BASEBAND_CN0;
+}
+
+template <>
+double GnssCallbackHidl::getBasebandCn0DbHz(
+        const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) {
+    return svInfoList[i].basebandCN0DbHz;
+}
+
+template <>
+double GnssCallbackHidl::getBasebandCn0DbHz(
+        const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
+    return svInfoList[i].basebandCN0DbHz;
+}
+
+template <>
+uint32_t GnssCallbackHidl::getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) {
+    return svStatus.numSvs;
+}
+
+template <>
+uint32_t GnssCallbackHidl::getConstellationType(const IGnssCallback_V1_0::GnssSvStatus& svStatus,
+                                                size_t i) {
+    return static_cast<uint32_t>(svStatus.gnssSvList.data()[i].constellation);
+}
+
+template <>
+uint32_t GnssCallbackHidl::getConstellationType(
+        const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) {
+    return static_cast<uint32_t>(svInfoList[i].v2_0.constellation);
+}
+
+template <class T_list, class T_sv_info>
+Return<void> GnssCallbackHidl::gnssSvStatusCbImpl(const T_list& svStatus) {
+    // In HIDL or AIDL v1, if no listener is registered, do not report svInfoList to the framework.
+    if (!isSvStatusRegistered) {
+        return Void();
+    }
+
+    JNIEnv* env = getJniEnv();
+
+    uint32_t listSize = getGnssSvInfoListSize(svStatus);
+
+    jintArray svidWithFlagArray = env->NewIntArray(listSize);
+    jfloatArray cn0Array = env->NewFloatArray(listSize);
+    jfloatArray elevArray = env->NewFloatArray(listSize);
+    jfloatArray azimArray = env->NewFloatArray(listSize);
+    jfloatArray carrierFreqArray = env->NewFloatArray(listSize);
+    jfloatArray basebandCn0Array = env->NewFloatArray(listSize);
+
+    jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0);
+    jfloat* cn0s = env->GetFloatArrayElements(cn0Array, 0);
+    jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
+    jfloat* azim = env->GetFloatArrayElements(azimArray, 0);
+    jfloat* carrierFreq = env->GetFloatArrayElements(carrierFreqArray, 0);
+    jfloat* basebandCn0s = env->GetFloatArrayElements(basebandCn0Array, 0);
+
+    /*
+     * Read GNSS SV info.
+     */
+    for (size_t i = 0; i < listSize; ++i) {
+        enum ShiftWidth : uint8_t { SVID_SHIFT_WIDTH = 12, CONSTELLATION_TYPE_SHIFT_WIDTH = 8 };
+
+        const T_sv_info& info = getGnssSvInfoOfIndex(svStatus, i);
+        svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) |
+                (getConstellationType(svStatus, i) << CONSTELLATION_TYPE_SHIFT_WIDTH) |
+                static_cast<uint32_t>(info.svFlag);
+        cn0s[i] = info.cN0Dbhz;
+        elev[i] = info.elevationDegrees;
+        azim[i] = info.azimuthDegrees;
+        carrierFreq[i] = info.carrierFrequencyHz;
+        svidWithFlags[i] |= getHasBasebandCn0DbHzFlag(svStatus);
+        basebandCn0s[i] = getBasebandCn0DbHz(svStatus, i);
+    }
+
+    env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
+    env->ReleaseFloatArrayElements(cn0Array, cn0s, 0);
+    env->ReleaseFloatArrayElements(elevArray, elev, 0);
+    env->ReleaseFloatArrayElements(azimArray, azim, 0);
+    env->ReleaseFloatArrayElements(carrierFreqArray, carrierFreq, 0);
+    env->ReleaseFloatArrayElements(basebandCn0Array, basebandCn0s, 0);
+
+    env->CallVoidMethod(mCallbacksObj, method_reportSvStatus, static_cast<jint>(listSize),
+                        svidWithFlagArray, cn0Array, elevArray, azimArray, carrierFreqArray,
+                        basebandCn0Array);
+
+    env->DeleteLocalRef(svidWithFlagArray);
+    env->DeleteLocalRef(cn0Array);
+    env->DeleteLocalRef(elevArray);
+    env->DeleteLocalRef(azimArray);
+    env->DeleteLocalRef(carrierFreqArray);
+    env->DeleteLocalRef(basebandCn0Array);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssNmeaCb(int64_t timestamp,
+                                          const ::android::hardware::hidl_string& nmea) {
+    // In HIDL, if no listener is registered, do not report nmea to the framework.
+    if (!isNmeaRegistered) {
+        return Void();
+    }
+    JNIEnv* env = getJniEnv();
+    /*
+     * The Java code will call back to read these values.
+     * We do this to avoid creating unnecessary String objects.
+     */
+    sNmeaString = nmea.c_str();
+    sNmeaStringLength = nmea.size();
+
+    env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssSetCapabilitesCb(uint32_t capabilities) {
+    ALOGD("%s: %du\n", __func__, capabilities);
+
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssSetCapabilitiesCb_2_0(uint32_t capabilities) {
+    return GnssCallbackHidl::gnssSetCapabilitesCb(capabilities);
+}
+
+Return<void> GnssCallbackHidl::gnssSetCapabilitiesCb_2_1(uint32_t capabilities) {
+    return GnssCallbackHidl::gnssSetCapabilitesCb(capabilities);
+}
+
+Return<void> GnssCallbackHidl::gnssAcquireWakelockCb() {
+    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
+    return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssReleaseWakelockCb() {
+    release_wake_lock(WAKE_LOCK_NAME);
+    return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssRequestTimeCb() {
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssRequestLocationCb(const bool independentFromGnss) {
+    return GnssCallbackHidl::gnssRequestLocationCb_2_0(independentFromGnss, /* isUserEmergency= */
+                                                       false);
+}
+
+Return<void> GnssCallbackHidl::gnssRequestLocationCb_2_0(const bool independentFromGnss,
+                                                         const bool isUserEmergency) {
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss),
+                        boolToJbool(isUserEmergency));
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Void();
+}
+
+Return<void> GnssCallbackHidl::gnssSetSystemInfoCb(const IGnssCallback_V2_0::GnssSystemInfo& info) {
+    ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw);
+
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Void();
+}
+
+} // namespace android::gnss
\ No newline at end of file
diff --git a/services/core/jni/gnss/GnssCallback.h b/services/core/jni/gnss/GnssCallback.h
new file mode 100644
index 0000000..a7f96fb
--- /dev/null
+++ b/services/core/jni/gnss/GnssCallback.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSCALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSCALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/2.1/IGnss.h>
+#include <android/hardware/gnss/BnGnssCallback.h>
+#include <log/log.h>
+
+#include <vector>
+
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+namespace {
+
+extern jmethodID method_reportLocation;
+extern jmethodID method_reportStatus;
+extern jmethodID method_reportSvStatus;
+extern jmethodID method_reportNmea;
+extern jmethodID method_setTopHalCapabilities;
+extern jmethodID method_setGnssYearOfHardware;
+extern jmethodID method_setGnssHardwareModelName;
+extern jmethodID method_requestLocation;
+extern jmethodID method_requestUtcTime;
+
+} // anonymous namespace
+
+extern bool isSvStatusRegistered;
+extern bool isNmeaRegistered;
+
+extern jmethodID method_reportGnssServiceDied;
+
+void Gnss_class_init_once(JNIEnv* env, jclass& clazz);
+
+/*
+ * GnssCallbackAidl class implements the callback methods for AIDL IGnssCallback interface.
+ */
+class GnssCallbackAidl : public hardware::gnss::BnGnssCallback {
+public:
+    binder::Status gnssSetCapabilitiesCb(const int capabilities) override;
+    binder::Status gnssStatusCb(const GnssStatusValue status) override;
+    binder::Status gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) override;
+    binder::Status gnssLocationCb(const hardware::gnss::GnssLocation& location) override;
+    binder::Status gnssNmeaCb(const int64_t timestamp, const std::string& nmea) override;
+    binder::Status gnssAcquireWakelockCb() override;
+    binder::Status gnssReleaseWakelockCb() override;
+    binder::Status gnssSetSystemInfoCb(const GnssSystemInfo& info) override;
+    binder::Status gnssRequestTimeCb() override;
+    binder::Status gnssRequestLocationCb(const bool independentFromGnss,
+                                         const bool isUserEmergency) override;
+};
+
+/*
+ * GnssCallbackHidl class implements the callback methods for HIDL IGnssCallback interface.
+ */
+struct GnssCallbackHidl : public hardware::gnss::V2_1::IGnssCallback {
+    hardware::Return<void> gnssLocationCb(
+            const hardware::gnss::V1_0::GnssLocation& location) override;
+    hardware::Return<void> gnssStatusCb(
+            const hardware::gnss::V1_0::IGnssCallback::GnssStatusValue status) override;
+    hardware::Return<void> gnssSvStatusCb(
+            const hardware::gnss::V1_0::IGnssCallback::GnssSvStatus& svStatus) override {
+        return gnssSvStatusCbImpl<hardware::gnss::V1_0::IGnssCallback::GnssSvStatus,
+                                  hardware::gnss::V1_0::IGnssCallback::GnssSvInfo>(svStatus);
+    }
+    hardware::Return<void> gnssNmeaCb(int64_t timestamp,
+                                      const hardware::hidl_string& nmea) override;
+    hardware::Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override;
+    hardware::Return<void> gnssAcquireWakelockCb() override;
+    hardware::Return<void> gnssReleaseWakelockCb() override;
+    hardware::Return<void> gnssRequestTimeCb() override;
+    hardware::Return<void> gnssRequestLocationCb(const bool independentFromGnss) override;
+
+    hardware::Return<void> gnssSetSystemInfoCb(
+            const hardware::gnss::V1_0::IGnssCallback::GnssSystemInfo& info) override;
+
+    // New in 1.1
+    hardware::Return<void> gnssNameCb(const hardware::hidl_string& name) override;
+
+    // New in 2.0
+    hardware::Return<void> gnssRequestLocationCb_2_0(const bool independentFromGnss,
+                                                     const bool isUserEmergency) override;
+    hardware::Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override;
+    hardware::Return<void> gnssLocationCb_2_0(
+            const hardware::gnss::V2_0::GnssLocation& location) override;
+    hardware::Return<void> gnssSvStatusCb_2_0(
+            const hardware::hidl_vec<hardware::gnss::V2_0::IGnssCallback::GnssSvInfo>& svInfoList)
+            override {
+        return gnssSvStatusCbImpl<
+                hardware::hidl_vec<hardware::gnss::V2_0::IGnssCallback::GnssSvInfo>,
+                hardware::gnss::V1_0::IGnssCallback::GnssSvInfo>(svInfoList);
+    }
+
+    // New in 2.1
+    hardware::Return<void> gnssSvStatusCb_2_1(
+            const hardware::hidl_vec<hardware::gnss::V2_1::IGnssCallback::GnssSvInfo>& svInfoList)
+            override {
+        return gnssSvStatusCbImpl<
+                hardware::hidl_vec<hardware::gnss::V2_1::IGnssCallback::GnssSvInfo>,
+                hardware::gnss::V1_0::IGnssCallback::GnssSvInfo>(svInfoList);
+    }
+    hardware::Return<void> gnssSetCapabilitiesCb_2_1(uint32_t capabilities) override;
+
+    // TODO: Reconsider allocation cost vs threadsafety on these statics
+    static const char* sNmeaString;
+    static size_t sNmeaStringLength;
+
+    template <class T>
+    static hardware::Return<void> gnssLocationCbImpl(const T& location);
+
+    template <class T_list, class T_sv_info>
+    static hardware::Return<void> gnssSvStatusCbImpl(const T_list& svStatus);
+
+private:
+    template <class T>
+    static uint32_t getHasBasebandCn0DbHzFlag(const T& svStatus) {
+        return 0;
+    }
+
+    template <class T>
+    static double getBasebandCn0DbHz(const T& svStatus, size_t i) {
+        return 0.0;
+    }
+
+    template <class T>
+    static uint32_t getGnssSvInfoListSize(const T& svInfoList) {
+        return svInfoList.size();
+    }
+
+    static const hardware::gnss::IGnssCallback::GnssSvInfo& getGnssSvInfoOfIndex(
+            const std::vector<hardware::gnss::IGnssCallback::GnssSvInfo>& svInfoList, size_t i) {
+        return svInfoList[i];
+    }
+
+    static const hardware::gnss::V1_0::IGnssCallback::GnssSvInfo& getGnssSvInfoOfIndex(
+            const hardware::gnss::V1_0::IGnssCallback::GnssSvStatus& svStatus, size_t i) {
+        return svStatus.gnssSvList.data()[i];
+    }
+
+    static const hardware::gnss::V1_0::IGnssCallback::GnssSvInfo& getGnssSvInfoOfIndex(
+            const hardware::hidl_vec<hardware::gnss::V2_0::IGnssCallback::GnssSvInfo>& svInfoList,
+            size_t i) {
+        return svInfoList[i].v1_0;
+    }
+
+    static const hardware::gnss::V1_0::IGnssCallback::GnssSvInfo& getGnssSvInfoOfIndex(
+            const hardware::hidl_vec<hardware::gnss::V2_1::IGnssCallback::GnssSvInfo>& svInfoList,
+            size_t i) {
+        return svInfoList[i].v2_0.v1_0;
+    }
+
+    template <class T>
+    static uint32_t getConstellationType(const T& svInfoList, size_t i) {
+        return static_cast<uint32_t>(svInfoList[i].constellation);
+    }
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSCALLBACK_H
\ No newline at end of file
diff --git a/services/core/jni/gnss/GnssDebug.cpp b/services/core/jni/gnss/GnssDebug.cpp
index da53317..263a6c6 100644
--- a/services/core/jni/gnss/GnssDebug.cpp
+++ b/services/core/jni/gnss/GnssDebug.cpp
@@ -104,4 +104,10 @@
     return satelliteDataArray[i];
 }
 
+template <>
+int64_t GnssDebugUtil::getTimeEstimateMs(
+        const android::hardware::gnss::IGnssDebug::DebugData& data) {
+    return data.time.timeEstimateMs;
+}
+
 } // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssDebug.h b/services/core/jni/gnss/GnssDebug.h
index 1e1a7b4..e02c2ac 100644
--- a/services/core/jni/gnss/GnssDebug.h
+++ b/services/core/jni/gnss/GnssDebug.h
@@ -113,12 +113,6 @@
     return data.time.timeEstimate;
 }
 
-template <>
-int64_t GnssDebugUtil::getTimeEstimateMs(
-        const android::hardware::gnss::IGnssDebug::DebugData& data) {
-    return data.time.timeEstimateMs;
-}
-
 template <class T_DebugData, class T_SatelliteData>
 jstring GnssDebugUtil::parseDebugData(JNIEnv* env, std::stringstream& internalState,
                                       const T_DebugData& data) {
diff --git a/services/core/jni/gnss/GnssPsds.cpp b/services/core/jni/gnss/GnssPsds.cpp
new file mode 100644
index 0000000..51a1450
--- /dev/null
+++ b/services/core/jni/gnss/GnssPsds.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Define LOG_TAG before <log/log.h> to overwrite the default value.
+#define LOG_TAG "GnssPsdsJni"
+
+#include "GnssPsds.h"
+
+#include "Utils.h"
+
+using android::hardware::hidl_bitfield;
+using android::hardware::gnss::PsdsType;
+using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
+using IGnssPsdsHidl = android::hardware::gnss::V1_0::IGnssXtra;
+
+namespace android::gnss {
+
+// Implementation of GnssPsds (AIDL HAL)
+
+GnssPsdsAidl::GnssPsdsAidl(const sp<IGnssPsdsAidl>& iGnssPsds) : mIGnssPsds(iGnssPsds) {
+    assert(mIGnssPsds != nullptr);
+}
+
+jboolean GnssPsdsAidl::setCallback(const std::unique_ptr<GnssPsdsCallback>& callback) {
+    auto status = mIGnssPsds->setCallback(callback->getAidl());
+    return checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.");
+}
+
+void GnssPsdsAidl::injectPsdsData(const jbyteArray& data, const jint& length,
+                                  const jint& psdsType) {
+    JNIEnv* env = getJniEnv();
+    jbyte* bytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(data, 0));
+    auto status = mIGnssPsds->injectPsdsData(static_cast<PsdsType>(psdsType),
+                                             std::vector<uint8_t>((const uint8_t*)bytes,
+                                                                  (const uint8_t*)bytes + length));
+    checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.");
+    env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
+}
+
+// Implementation of GnssPsdsHidl
+
+GnssPsdsHidl::GnssPsdsHidl(const sp<android::hardware::gnss::V1_0::IGnssXtra>& iGnssXtra)
+      : mIGnssXtra(iGnssXtra) {
+    assert(mIGnssXtra != nullptr);
+}
+
+jboolean GnssPsdsHidl::setCallback(const std::unique_ptr<GnssPsdsCallback>& callback) {
+    auto result = mIGnssXtra->setCallback(callback->getHidl());
+    return checkHidlReturn(result, "IGnssPsdsHidl setCallback() failed.");
+}
+
+void GnssPsdsHidl::injectPsdsData(const jbyteArray& data, const jint& length, const jint&) {
+    JNIEnv* env = getJniEnv();
+    jbyte* bytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(data, 0));
+    auto result = mIGnssXtra->injectXtraData(std::string((const char*)bytes, length));
+    checkHidlReturn(result, "IGnssXtra injectXtraData() failed.");
+    env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssPsds.h b/services/core/jni/gnss/GnssPsds.h
new file mode 100644
index 0000000..6b65ee8
--- /dev/null
+++ b/services/core/jni/gnss/GnssPsds.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSPSDS_H
+#define _ANDROID_SERVER_GNSS_GNSSPSDS_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnssXtra.h>
+#include <android/hardware/gnss/BnGnssPsds.h>
+#include <log/log.h>
+
+#include "GnssPsdsCallback.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+class GnssPsdsInterface {
+public:
+    virtual ~GnssPsdsInterface() {}
+    virtual jboolean setCallback(const std::unique_ptr<GnssPsdsCallback>& callback);
+    virtual void injectPsdsData(const jbyteArray& data, const jint& length, const jint& psdsType);
+};
+
+class GnssPsdsAidl : public GnssPsdsInterface {
+public:
+    GnssPsdsAidl(const sp<android::hardware::gnss::IGnssPsds>& iGnssPsds);
+    jboolean setCallback(const std::unique_ptr<GnssPsdsCallback>& callback) override;
+    void injectPsdsData(const jbyteArray& data, const jint& length, const jint& psdsType) override;
+
+private:
+    const sp<android::hardware::gnss::IGnssPsds> mIGnssPsds;
+};
+
+class GnssPsdsHidl : public GnssPsdsInterface {
+public:
+    GnssPsdsHidl(const sp<android::hardware::gnss::V1_0::IGnssXtra>& iGnssXtra);
+    jboolean setCallback(const std::unique_ptr<GnssPsdsCallback>& callback) override;
+    void injectPsdsData(const jbyteArray& data, const jint& length, const jint& psdsType) override;
+
+private:
+    const sp<android::hardware::gnss::V1_0::IGnssXtra> mIGnssXtra;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSPSDS_H
diff --git a/services/core/jni/gnss/GnssPsdsCallback.cpp b/services/core/jni/gnss/GnssPsdsCallback.cpp
new file mode 100644
index 0000000..1dd7022
--- /dev/null
+++ b/services/core/jni/gnss/GnssPsdsCallback.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GnssPsdsCbJni"
+
+#include "GnssPsdsCallback.h"
+
+#include <vector>
+
+#include "Utils.h"
+
+namespace android::gnss {
+
+namespace {
+jmethodID method_psdsDownloadRequest;
+} // anonymous namespace
+
+using binder::Status;
+using hardware::Return;
+using hardware::Void;
+using hardware::gnss::PsdsType;
+
+void GnssPsds_class_init_once(JNIEnv* env, jclass clazz) {
+    method_psdsDownloadRequest = env->GetMethodID(clazz, "psdsDownloadRequest", "(I)V");
+}
+
+// Implementation of android::hardware::gnss::IGnssPsdsCallback
+
+Status GnssPsdsCallbackAidl::downloadRequestCb(PsdsType psdsType) {
+    ALOGD("%s. psdsType: %d", __func__, static_cast<int32_t>(psdsType));
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, psdsType);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
+// Implementation of android::hardware::gnss::V1_0::IGnssPsdsCallback
+
+Return<void> GnssPsdsCallbackHidl::downloadRequestCb() {
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, /* psdsType= */ 1);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Void();
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssPsdsCallback.h b/services/core/jni/gnss/GnssPsdsCallback.h
new file mode 100644
index 0000000..3ac7473
--- /dev/null
+++ b/services/core/jni/gnss/GnssPsdsCallback.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSPSDSCALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSPSDSCALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnssXtraCallback.h>
+#include <android/hardware/gnss/BnGnssPsdsCallback.h>
+#include <log/log.h>
+
+#include "jni.h"
+
+namespace android::gnss {
+
+namespace {
+extern jmethodID method_psdsDownloadRequest;
+} // anonymous namespace
+
+void GnssPsds_class_init_once(JNIEnv* env, jclass clazz);
+
+class GnssPsdsCallbackAidl : public hardware::gnss::BnGnssPsdsCallback {
+public:
+    GnssPsdsCallbackAidl() {}
+    binder::Status downloadRequestCb(hardware::gnss::PsdsType psdsType) override;
+};
+
+class GnssPsdsCallbackHidl : public hardware::gnss::V1_0::IGnssXtraCallback {
+public:
+    GnssPsdsCallbackHidl() {}
+    hardware::Return<void> downloadRequestCb() override;
+};
+
+class GnssPsdsCallback {
+public:
+    GnssPsdsCallback() {}
+    sp<GnssPsdsCallbackAidl> getAidl() {
+        if (callbackAidl == nullptr) {
+            callbackAidl = sp<GnssPsdsCallbackAidl>::make();
+        }
+        return callbackAidl;
+    }
+
+    sp<GnssPsdsCallbackHidl> getHidl() {
+        if (callbackHidl == nullptr) {
+            callbackHidl = sp<GnssPsdsCallbackHidl>::make();
+        }
+        return callbackHidl;
+    }
+
+private:
+    sp<GnssPsdsCallbackAidl> callbackAidl;
+    sp<GnssPsdsCallbackHidl> callbackHidl;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSPSDSCALLBACK_H
\ No newline at end of file
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 33e97aa..5aa3dfe 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1999,9 +1999,6 @@
             mOwners.load();
             setDeviceOwnershipSystemPropertyLocked();
             findOwnerComponentIfNecessaryLocked();
-
-            // TODO PO may not have a class name either due to b/17652534.  Address that too.
-            updateDeviceOwnerLocked();
         }
     }
 
@@ -3142,23 +3139,6 @@
         }
     }
 
-    private void updateDeviceOwnerLocked() {
-        long ident = mInjector.binderClearCallingIdentity();
-        try {
-            // TODO This is to prevent DO from getting "clear data"ed, but it should also check the
-            // user id and also protect all other DAs too.
-            final ComponentName deviceOwnerComponent = mOwners.getDeviceOwnerComponent();
-            if (deviceOwnerComponent != null) {
-                mInjector.getIActivityManager()
-                        .updateDeviceOwner(deviceOwnerComponent.getPackageName());
-            }
-        } catch (RemoteException e) {
-            // Not gonna happen.
-        } finally {
-            mInjector.binderRestoreCallingIdentity(ident);
-        }
-    }
-
     static void validateQualityConstant(int quality) {
         switch (quality) {
             case PASSWORD_QUALITY_UNSPECIFIED:
@@ -8590,7 +8570,6 @@
 
             mOwners.setDeviceOwner(admin, ownerName, userId);
             mOwners.writeDeviceOwner();
-            updateDeviceOwnerLocked();
             setDeviceOwnershipSystemPropertyLocked();
 
             //TODO(b/180371154): when provisionFullyManagedDevice is used in tests, remove this
@@ -8951,7 +8930,6 @@
 
         mOwners.clearDeviceOwner();
         mOwners.writeDeviceOwner();
-        updateDeviceOwnerLocked();
 
         clearDeviceOwnerUserRestriction(UserHandle.of(userId));
         mInjector.securityLogSetLoggingEnabledProperty(false);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e895d37..5098abe 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -386,8 +386,8 @@
             "com.android.server.DeviceIdleController";
     private static final String BLOB_STORE_MANAGER_SERVICE_CLASS =
             "com.android.server.blob.BlobStoreManagerService";
-    private static final String APP_SEARCH_MANAGER_SERVICE_CLASS =
-            "com.android.server.appsearch.AppSearchManagerService";
+    private static final String APPSEARCH_MODULE_LIFECYCLE_CLASS =
+            "com.android.server.appsearch.AppSearchModule$Lifecycle";
     private static final String ISOLATED_COMPILATION_SERVICE_CLASS =
             "com.android.server.compos.IsolatedCompilationService";
     private static final String ROLLBACK_MANAGER_SERVICE_CLASS =
@@ -2764,8 +2764,8 @@
         mSystemServiceManager.startService(SAFETY_CENTER_SERVICE_CLASS);
         t.traceEnd();
 
-        t.traceBegin("AppSearchManagerService");
-        mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS);
+        t.traceBegin("AppSearchModule");
+        mSystemServiceManager.startService(APPSEARCH_MODULE_LIFECYCLE_CLASS);
         t.traceEnd();
 
         if (SystemProperties.getBoolean("ro.config.isolated_compilation_enabled", false)) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index d2358a0..023608c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -1167,7 +1167,14 @@
         startUser(gameManagerService, USER_ID_1);
         gameManagerService.setGameMode(
                 mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
-        GameState gameState = new GameState(isLoading, GameState.MODE_NONE);
+        int testMode = GameState.MODE_NONE;
+        int testLabel = 99;
+        int testQuality = 123;
+        GameState gameState = new GameState(isLoading, testMode, testLabel, testQuality);
+        assertEquals(isLoading, gameState.isLoading());
+        assertEquals(testMode, gameState.getMode());
+        assertEquals(testLabel, gameState.getLabel());
+        assertEquals(testQuality, gameState.getQuality());
         gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
         mTestLooper.dispatchAll();
         verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, isLoading);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 890a549..4e4854c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -333,6 +333,10 @@
 
     @Test
     public void testGetLastLocation_Bypass() {
+        mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
+                new PackageTagsList.Builder().add(
+                        IDENTITY.getPackageName()).build());
+
         assertThat(mManager.getLastLocation(new LastLocationRequest.Builder().build(), IDENTITY,
                 PERMISSION_FINE)).isNull();
         assertThat(mManager.getLastLocation(
@@ -381,6 +385,14 @@
                 new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
                 IDENTITY, PERMISSION_FINE)).isEqualTo(
                 loc);
+
+        mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
+                new PackageTagsList.Builder().build());
+        mProvider.setProviderAllowed(false);
+
+        assertThat(mManager.getLastLocation(
+                new LastLocationRequest.Builder().setLocationSettingsIgnored(true).build(),
+                IDENTITY, PERMISSION_FINE)).isNull();
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 2398e36..b601d14 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1137,9 +1137,6 @@
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
 
         // Verify internal calls.
-        verify(getServices().iactivityManager, times(1)).updateDeviceOwner(
-                eq(admin1.getPackageName()));
-
         verify(getServices().userManager, times(1)).setUserRestriction(
                 eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
                 eq(true), eq(UserHandle.SYSTEM));
@@ -1205,9 +1202,6 @@
         assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1);
 
         // Verify internal calls.
-        verify(getServices().iactivityManager).updateDeviceOwner(
-                eq(admin1.getPackageName()));
-
         verify(mContext.spiedContext).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
                 MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
@@ -1392,11 +1386,6 @@
         setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
         dpm.setActiveAdmin(admin1, /* replace =*/ false);
         assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
-
-        // Verify internal calls.
-        verify(getServices().iactivityManager, times(1)).updateDeviceOwner(
-                eq(admin1.getPackageName()));
-
         assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1);
 
         dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER);
@@ -1501,11 +1490,6 @@
         setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
         dpm.setActiveAdmin(admin1, /* replace =*/ false);
         assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
-
-        // Verify internal calls.
-        verify(getServices().iactivityManager, times(1)).updateDeviceOwner(
-                eq(admin1.getPackageName()));
-
         assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1);
 
         // Now call clear from the secondary user, which should throw.
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
index c771000..0287510 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
@@ -41,7 +41,9 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
+import android.os.HandlerThread;
 import android.os.LocaleList;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SimpleClock;
 import android.util.SparseArray;
@@ -78,6 +80,7 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class LocaleManagerBackupRestoreTest {
+    private static final String TAG = "LocaleManagerBackupRestoreTest";
     private static final String DEFAULT_PACKAGE_NAME = "com.android.myapp";
     private static final String DEFAULT_LOCALE_TAGS = "en-XC,ar-XB";
     private static final String TEST_LOCALES_XML_TAG = "locales";
@@ -131,12 +134,17 @@
 
         doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
 
+        HandlerThread broadcastHandlerThread = new HandlerThread(TAG,
+                Process.THREAD_PRIORITY_BACKGROUND);
+        broadcastHandlerThread.start();
+
         mBackupHelper = spy(new ShadowLocaleManagerBackupHelper(mMockContext,
-                mMockLocaleManagerService, mMockPackageManagerInternal, mClock, STAGE_DATA));
+                mMockLocaleManagerService, mMockPackageManagerInternal, mClock, STAGE_DATA,
+                broadcastHandlerThread));
         doNothing().when(mBackupHelper).notifyBackupManager();
 
         mUserMonitor = mBackupHelper.getUserMonitor();
-        mPackageMonitor = mBackupHelper.getPackageMonitor();
+        mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper);
         setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
index ca5b0cb..0b3ef45 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
@@ -46,6 +46,7 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.internal.content.PackageMonitor;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal.PackageConfig;
 
@@ -86,6 +87,8 @@
     private ActivityTaskManagerInternal mMockActivityTaskManager;
     @Mock
     private ActivityManagerInternal mMockActivityManager;
+    @Mock
+    PackageMonitor mMockPackageMonitor;
 
     @Before
     public void setUp() throws Exception {
@@ -93,6 +96,7 @@
         mMockActivityTaskManager = mock(ActivityTaskManagerInternal.class);
         mMockActivityManager = mock(ActivityManagerInternal.class);
         mMockPackageManagerInternal = mock(PackageManagerInternal.class);
+        mMockPackageMonitor = mock(PackageMonitor.class);
 
         // For unit tests, set the default installer info
         PackageManager mockPackageManager = mock(PackageManager.class);
@@ -113,7 +117,8 @@
 
         mMockBackupHelper = mock(ShadowLocaleManagerBackupHelper.class);
         mLocaleManagerService = new LocaleManagerService(mMockContext, mMockActivityTaskManager,
-                mMockActivityManager, mMockPackageManagerInternal, mMockBackupHelper);
+                mMockActivityManager, mMockPackageManagerInternal,
+                mMockBackupHelper, mMockPackageMonitor);
     }
 
     @Test(expected = SecurityException.class)
diff --git a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
index b0fc636..ad9be0d 100644
--- a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
+++ b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
+import android.os.HandlerThread;
 import android.util.SparseArray;
 
 import java.time.Clock;
@@ -31,7 +32,8 @@
     ShadowLocaleManagerBackupHelper(Context context,
             LocaleManagerService localeManagerService,
             PackageManagerInternal pmInternal, Clock clock,
-            SparseArray<LocaleManagerBackupHelper.StagedData> stagedData) {
-        super(context, localeManagerService, pmInternal, clock, stagedData);
+            SparseArray<LocaleManagerBackupHelper.StagedData> stagedData,
+            HandlerThread broadcastHandlerThread) {
+        super(context, localeManagerService, pmInternal, clock, stagedData, broadcastHandlerThread);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
index 16cfd13..6bdd88c 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
@@ -31,7 +31,6 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -49,7 +48,6 @@
 import android.os.IHwBinder;
 import android.os.IHwInterface;
 import android.os.RemoteException;
-import android.system.OsConstants;
 
 import org.junit.After;
 import org.junit.Before;
@@ -60,6 +58,7 @@
 
 import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
 
 @RunWith(Parameterized.class)
 public class SoundHw2CompatTest {
@@ -240,14 +239,15 @@
                 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
 
         final int handle = 29;
-        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
-                ArgumentCaptor.forClass(
-                        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+        AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> model =
+                new AtomicReference<>();
         ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
                 ArgumentCaptor.forClass(
                         android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
 
         doAnswer(invocation -> {
+            // We need to dup the model, as it gets invalidated after the call returns.
+            model.set(TestUtil.dupModel_2_1(invocation.getArgument(0)));
             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
                     resultCallback = invocation.getArgument(3);
 
@@ -259,10 +259,9 @@
         assertEquals(handle,
                 mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
 
-        verify(driver_2_1).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
-                anyInt(), any());
+        verify(driver_2_1).loadSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(), any());
 
-        TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
+        TestUtil.validateGenericSoundModel_2_1(model.get());
         validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
         return handle;
     }
@@ -355,14 +354,16 @@
                 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
 
         final int handle = 29;
-        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
-                modelCaptor = ArgumentCaptor.forClass(
-                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+        AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel> model =
+                new AtomicReference<>();
         ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
                 ArgumentCaptor.forClass(
                         android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
 
         doAnswer(invocation -> {
+            // We need to dup the model, as it gets invalidated after the call returns.
+            model.set(TestUtil.dupPhraseModel_2_1(invocation.getArgument(0)));
+
             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
                     resultCallback = invocation.getArgument(3);
 
@@ -374,10 +375,10 @@
         assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
                 canonicalCallback));
 
-        verify(driver_2_1).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
-                anyInt(), any());
+        verify(driver_2_1).loadPhraseSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(),
+                any());
 
-        TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
+        TestUtil.validatePhraseSoundModel_2_1(model.get());
         validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
         return handle;
     }
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
index e687a2a..30b4a59 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -47,6 +47,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.SharedMemory;
 
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.List;
 
@@ -82,6 +83,19 @@
                 HidlMemoryUtil.hidlMemoryToByteArray(model.data));
     }
 
+    static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dupModel_2_1(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel model) {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dup =
+                new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel();
+        dup.header = model.header;
+        try {
+            dup.data = model.data.dup();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        return dup;
+    }
+
     private static void validateSoundModel_2_0(
             android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model, int type) {
         assertEquals(type, model.type);
@@ -121,6 +135,15 @@
         validatePhrases_2_0(model.phrases);
     }
 
+    static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dupPhraseModel_2_1(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel model) {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dup =
+                new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel();
+        dup.common = dupModel_2_1(model.common);
+        dup.phrases = model.phrases;
+        return dup;
+    }
+
     static void validatePhraseSoundModel_2_0(
             android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model) {
         validateSoundModel_2_0(model.common,
@@ -190,31 +213,27 @@
         properties.maxKeyPhrases = 567;
         properties.maxUsers = 678;
         properties.recognitionModes =
-                RecognitionMode.VOICE_TRIGGER
-                        | RecognitionMode.USER_IDENTIFICATION
-                        | RecognitionMode.USER_AUTHENTICATION
-                        | RecognitionMode.GENERIC_TRIGGER;
+                RecognitionMode.VOICE_TRIGGER | RecognitionMode.USER_IDENTIFICATION
+                        | RecognitionMode.USER_AUTHENTICATION | RecognitionMode.GENERIC_TRIGGER;
         properties.captureTransition = true;
         properties.maxBufferMs = 321;
         properties.concurrentCapture = supportConcurrentCapture;
         properties.triggerInEvent = true;
         properties.powerConsumptionMw = 432;
         properties.supportedModelArch = "supportedModelArch";
-        properties.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
-                | AudioCapabilities.NOISE_SUPPRESSION;
+        properties.audioCapabilities =
+                AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
         return properties;
     }
 
-    static void validateDefaultProperties(Properties properties,
-            boolean supportConcurrentCapture) {
+    static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture) {
         validateDefaultProperties(properties, supportConcurrentCapture,
                 AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
                 "supportedModelArch");
     }
 
-    static void validateDefaultProperties(Properties properties,
-            boolean supportConcurrentCapture, @AudioCapabilities int audioCapabilities,
-            @NonNull String supportedModelArch) {
+    static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture,
+            @AudioCapabilities int audioCapabilities, @NonNull String supportedModelArch) {
         assertEquals("implementor", properties.implementor);
         assertEquals("description", properties.description);
         assertEquals(123, properties.version);
@@ -222,10 +241,9 @@
         assertEquals(456, properties.maxSoundModels);
         assertEquals(567, properties.maxKeyPhrases);
         assertEquals(678, properties.maxUsers);
-        assertEquals(RecognitionMode.GENERIC_TRIGGER
-                | RecognitionMode.USER_AUTHENTICATION
-                | RecognitionMode.USER_IDENTIFICATION
-                | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
+        assertEquals(RecognitionMode.GENERIC_TRIGGER | RecognitionMode.USER_AUTHENTICATION
+                        | RecognitionMode.USER_IDENTIFICATION | RecognitionMode.VOICE_TRIGGER,
+                properties.recognitionModes);
         assertTrue(properties.captureTransition);
         assertEquals(321, properties.maxBufferMs);
         assertEquals(supportConcurrentCapture, properties.concurrentCapture);
@@ -246,8 +264,8 @@
         config.phraseRecognitionExtras[0].levels[0].userId = 234;
         config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
         config.data = new byte[]{5, 4, 3, 2, 1};
-        config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
-                | AudioCapabilities.NOISE_SUPPRESSION;
+        config.audioCapabilities =
+                AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
         return config;
     }
 
@@ -295,13 +313,12 @@
             int captureHandle) {
         validateRecognitionConfig_2_1(config.base, captureDevice, captureHandle);
 
-        assertEquals(AudioCapabilities.ECHO_CANCELLATION
-                | AudioCapabilities.NOISE_SUPPRESSION, config.audioCapabilities);
+        assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
+                config.audioCapabilities);
     }
 
     static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
-            int hwHandle,
-            int status) {
+            int hwHandle, int status) {
         android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
                 new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
         halEvent.status = status;
@@ -351,8 +368,7 @@
         return event;
     }
 
-    static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
-            int hwHandle,
+    static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(int hwHandle,
             int status) {
         ISoundTriggerHwCallback.RecognitionEvent halEvent =
                 new ISoundTriggerHwCallback.RecognitionEvent();
@@ -386,8 +402,7 @@
         PhraseRecognitionExtra extra = new PhraseRecognitionExtra();
         extra.id = 123;
         extra.confidenceLevel = 52;
-        extra.recognitionModes = RecognitionMode.VOICE_TRIGGER
-                | RecognitionMode.GENERIC_TRIGGER;
+        extra.recognitionModes = RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER;
         ConfidenceLevel level = new ConfidenceLevel();
         level.userId = 31;
         level.levelPercent = 43;
@@ -396,8 +411,8 @@
         return event;
     }
 
-    static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent
-    createPhraseRecognitionEvent_2_0(int hwHandle, int status) {
+    static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0(
+            int hwHandle, int status) {
         android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
                 new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
         halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 8d60466..4cddd85 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -60,7 +60,7 @@
         }
         teardown {
             test {
-                testApp.exit()
+                testApp.exit(wmHelper)
             }
         }
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index 7ee6451..5bd365c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -64,7 +64,6 @@
             device.waitForIdle()
         } else {
             wmHelper.waitImeShown()
-            wmHelper.waitForAppTransitionIdle()
         }
     }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index b66c45c7..a135e0a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -53,8 +53,8 @@
         button.click()
 
         device.wait(Until.gone(launchActivityButton), FIND_TIMEOUT)
-        wmHelper.waitForFullScreenApp(secondActivityComponent)
         wmHelper.waitFor(
+            WindowManagerStateHelper.isAppFullScreen(secondActivityComponent),
             WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
             WindowManagerConditionsFactory.hasLayersAnimating().negate()
         )
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index ba5698c..a9564fd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -88,7 +88,7 @@
             }
             transitions {
                 device.reopenAppFromOverview(wmHelper)
-                require(wmHelper.waitImeShown()) { "IME didn't show in time" }
+                wmHelper.waitImeShown()
             }
             teardown {
                 test {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index 19e2c92..7e3ed82 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
+import android.view.Display
 import android.view.Surface
 import android.view.WindowManagerPolicyConstants
 import androidx.test.filters.RequiresDevice
@@ -35,6 +36,8 @@
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -64,12 +67,22 @@
                 eachRun {
                     this.setRotation(testSpec.startRotation)
                     testApp.launchViaIntent(wmHelper)
-                    wmHelper.waitForFullScreenApp(testApp.component)
-                    wmHelper.waitForAppTransitionIdle()
+                    val testAppVisible = wmHelper.waitFor(
+                        WindowManagerStateHelper.isAppFullScreen(testApp.component),
+                        WindowManagerConditionsFactory.isAppTransitionIdle(
+                            Display.DEFAULT_DISPLAY))
+                    require(testAppVisible) {
+                        "Expected ${testApp.component.toWindowName()} to be visible"
+                    }
 
                     imeTestApp.launchViaIntent(wmHelper)
-                    wmHelper.waitForFullScreenApp(testApp.component)
-                    wmHelper.waitForAppTransitionIdle()
+                    val imeAppVisible = wmHelper.waitFor(
+                        WindowManagerStateHelper.isAppFullScreen(imeTestApp.component),
+                        WindowManagerConditionsFactory.isAppTransitionIdle(
+                            Display.DEFAULT_DISPLAY))
+                    require(imeAppVisible) {
+                        "Expected ${imeTestApp.component.toWindowName()} to be visible"
+                    }
 
                     imeTestApp.openIME(device, wmHelper)
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index b5e13be..cc808a0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
+import android.view.Display
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.entireScreenCovered
@@ -30,7 +31,9 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
 import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
 import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -77,14 +80,16 @@
             }
             teardown {
                 test {
-                    testApp.exit()
+                    testApp.exit(wmHelper)
                 }
             }
             transitions {
                 testApp.openSecondActivity(device, wmHelper)
                 device.pressBack()
-                wmHelper.waitForAppTransitionIdle()
-                wmHelper.waitForFullScreenApp(testApp.component)
+                val firstActivityVisible = wmHelper.waitFor(
+                    WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
+                    WindowManagerStateHelper.isAppFullScreen(testApp.component))
+                require(firstActivityVisible) { "Expected ${testApp.component} to be visible" }
             }
         }
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 53560cc..4313b8d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -56,7 +56,7 @@
         }
         teardown {
             test {
-                testApp.exit()
+                testApp.exit(wmHelper)
             }
         }
     }