Merge "Dismiss keyguard when target user has no password"
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/TareBill.java b/apex/jobscheduler/service/java/com/android/server/alarm/TareBill.java
index e2f5ee1..a348136 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/TareBill.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/TareBill.java
@@ -37,7 +37,7 @@
 import java.util.List;
 
 /**
- * Container to maintain alarm TARE {@link ActionBill}s and their related methods.
+ * Container to maintain alarm TARE {@link ActionBill ActionBills} and their related methods.
  */
 final class TareBill {
     /**
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 714c90b..ef442f0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -2299,7 +2299,7 @@
     }
 
     /**
-     * Check if a job is restricted by any of the declared {@link JobRestriction}s.
+     * Check if a job is restricted by any of the declared {@link JobRestriction JobRestrictions}.
      * Note, that the jobs with {@link JobInfo#BIAS_FOREGROUND_SERVICE} bias or higher may not
      * be restricted, thus we won't even perform the check, but simply return null early.
      *
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 29c1108..b02e801 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -148,8 +148,9 @@
         public int bgJobCountInMaxPeriod;
 
         /**
-         * The number of {@link TimingSession}s within the bucket window size. This will include
-         * sessions that started before the window as long as they end within the window.
+         * The number of {@link TimingSession TimingSessions} within the bucket window size.
+         * This will include sessions that started before the window as long as they end within
+         * the window.
          */
         public int sessionCountInWindow;
 
@@ -183,7 +184,7 @@
         public long sessionRateLimitExpirationTimeElapsed;
 
         /**
-         * The number of {@link TimingSession}s that ran in at least the last
+         * The number of {@link TimingSession TimingSessions} that ran in at least the last
          * {@link #mRateLimitingWindowMs}. It may contain a few stale entries since cleanup won't
          * happen exactly every {@link #mRateLimitingWindowMs}. This should only be considered
          * valid before elapsed realtime has reached {@link #sessionRateLimitExpirationTimeElapsed}.
@@ -363,8 +364,8 @@
             QcConstants.DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
 
     /**
-     * The maximum number of {@link TimingSession}s that can run within the past {@link
-     * #mRateLimitingWindowMs}.
+     * The maximum number of {@link TimingSession TimingSessions} that can run within the past
+     * {@link #mRateLimitingWindowMs}.
      */
     private int mMaxSessionCountPerRateLimitingWindow =
             QcConstants.DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW;
@@ -434,9 +435,10 @@
     };
 
     /**
-     * The maximum number of {@link TimingSession}s based on its standby bucket. For each max value
-     * count in the array, the app will not be allowed to have more than that many number of
-     * {@link TimingSession}s within the latest time interval of its rolling window size.
+     * The maximum number of {@link TimingSession TimingSessions} based on its standby bucket.
+     * For each max value count in the array, the app will not be allowed to have more than that
+     * many number of {@link TimingSession TimingSessions} within the latest time interval of its
+     * rolling window size.
      *
      * @see #mBucketPeriodsMs
      */
@@ -450,8 +452,8 @@
     };
 
     /**
-     * Treat two distinct {@link TimingSession}s as the same if they start and end within this
-     * amount of time of each other.
+     * Treat two distinct {@link TimingSession TimingSessions} as the same if they start and end
+     * within this amount of time of each other.
      */
     private long mTimingSessionCoalescingDurationMs =
             QcConstants.DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS;
@@ -997,8 +999,8 @@
     /**
      * Returns the amount of time, in milliseconds, until the package would have reached its
      * duration quota, assuming it has a job counting towards its quota the entire time. This takes
-     * into account any {@link TimingSession}s that may roll out of the window as the job is
-     * running.
+     * into account any {@link TimingSession TimingSessions} that may roll out of the window as the
+     * job is running.
      */
     @VisibleForTesting
     long getTimeUntilQuotaConsumedLocked(final int userId, @NonNull final String packageName) {
@@ -3073,45 +3075,45 @@
                 DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
 
         /**
-         * The maximum number of {@link TimingSession}s an app can run within this particular
-         * standby bucket's window size.
+         * The maximum number of {@link TimingSession TimingSessions} an app can run within this
+         * particular standby bucket's window size.
          */
         public int MAX_SESSION_COUNT_ACTIVE = DEFAULT_MAX_SESSION_COUNT_ACTIVE;
 
         /**
-         * The maximum number of {@link TimingSession}s an app can run within this particular
-         * standby bucket's window size.
+         * The maximum number of {@link TimingSession TimingSessions} an app can run within this
+         * particular standby bucket's window size.
          */
         public int MAX_SESSION_COUNT_WORKING = DEFAULT_MAX_SESSION_COUNT_WORKING;
 
         /**
-         * The maximum number of {@link TimingSession}s an app can run within this particular
-         * standby bucket's window size.
+         * The maximum number of {@link TimingSession TimingSessions} an app can run within this
+         * particular standby bucket's window size.
          */
         public int MAX_SESSION_COUNT_FREQUENT = DEFAULT_MAX_SESSION_COUNT_FREQUENT;
 
         /**
-         * The maximum number of {@link TimingSession}s an app can run within this particular
-         * standby bucket's window size.
+         * The maximum number of {@link TimingSession TimingSessions} an app can run within this
+         * particular standby bucket's window size.
          */
         public int MAX_SESSION_COUNT_RARE = DEFAULT_MAX_SESSION_COUNT_RARE;
 
         /**
-         * The maximum number of {@link TimingSession}s an app can run within this particular
-         * standby bucket's window size.
+         * The maximum number of {@link TimingSession TimingSessions} an app can run within this
+         * particular standby bucket's window size.
          */
         public int MAX_SESSION_COUNT_RESTRICTED = DEFAULT_MAX_SESSION_COUNT_RESTRICTED;
 
         /**
-         * The maximum number of {@link TimingSession}s that can run within the past
+         * The maximum number of {@link TimingSession TimingSessions} that can run within the past
          * {@link #ALLOWED_TIME_PER_PERIOD_MS}.
          */
         public int MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
                 DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW;
 
         /**
-         * Treat two distinct {@link TimingSession}s as the same if they start and end within this
-         * amount of time of each other.
+         * Treat two distinct {@link TimingSession TimingSessions} as the same if they start and
+         * end within this amount of time of each other.
          */
         public long TIMING_SESSION_COALESCING_DURATION_MS =
                 DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS;
@@ -3125,8 +3127,8 @@
         private static final int MIN_BUCKET_JOB_COUNT = 10;
 
         /**
-         * The minimum number of {@link TimingSession}s that any bucket will be allowed to run
-         * within its window.
+         * The minimum number of {@link TimingSession TimingSessions} that any bucket will be
+         * allowed to run within its window.
          */
         private static final int MIN_BUCKET_SESSION_COUNT = 1;
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
index 3387b1d..4067541 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
@@ -54,7 +54,8 @@
 
     /**
      * Called by {@link JobSchedulerService} to check if it may proceed with scheduling the job (in
-     * case all constraints are satisfied and all other {@link JobRestriction}s are fine with it)
+     * case all constraints are satisfied and all other {@link JobRestriction JobRestrictions} are
+     * fine with it).
      *
      * @param job to be checked
      * @return false if the {@link JobSchedulerService} should not schedule this job at the moment,
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index c5f0f32..005c447 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -85,7 +85,7 @@
             mCurrentOngoingEvents = new SparseArrayMap<>();
 
     /**
-     * Set of {@link ActionAffordabilityNote}s keyed by userId-pkgName.
+     * Set of {@link ActionAffordabilityNote ActionAffordabilityNotes} keyed by userId-pkgName.
      *
      * Note: it would be nice/better to sort by base price since that doesn't change and simply
      * look at the change in the "insertion" of what would be affordable, but since CTP
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
index 630f1e7..0fa0c47 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
@@ -74,7 +74,7 @@
     }
 
     /**
-     * A collection of {@link AnticipatedAction}s that will be performed together.
+     * A collection of {@link AnticipatedAction AnticipatedActions} that will be performed together.
      */
     final class ActionBill {
         private static final Comparator<AnticipatedAction>
diff --git a/config/generate-preloaded-classes.sh b/config/generate-preloaded-classes.sh
deleted file mode 100755
index b17a366..0000000
--- a/config/generate-preloaded-classes.sh
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2017 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.
-if [ "$#" -lt 2 ]; then
-  echo "Usage $0 <input classes file> <denylist file> [extra classes files]"
-  exit 1
-fi
-
-# Write file headers first
-DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-cat "$DIR/copyright-header"
-echo "# Preloaded-classes filter file for phones.
-#
-# Classes in this file will be allocated into the boot image, and forcibly initialized in
-# the zygote during initialization. This is a trade-off, using virtual address space to share
-# common heap between apps.
-#
-# This file has been derived for mainline phone (and tablet) usage.
-#"
-
-input=$1
-denylist=$2
-shift 2
-extra_classes_files=("$@")
-
-# Disable locale to enable lexicographical sorting
-LC_ALL=C sort "$input" "${extra_classes_files[@]}" | uniq | grep -f "$denylist" -v -F -x | grep -v "\$NoPreloadHolder"
diff --git a/config/preloaded-classes-extra b/config/preloaded-classes-extra
deleted file mode 100644
index 09f393a..0000000
--- a/config/preloaded-classes-extra
+++ /dev/null
@@ -1,14 +0,0 @@
-android.icu.impl.coll.CollationRoot
-android.icu.impl.IDNA2003
-android.icu.impl.number.Parse
-android.icu.util.TimeZone
-android.media.ImageReader
-android.media.MediaCodecList
-android.media.MediaPlayer
-android.media.SoundPool
-android.text.format.Formatter
-android.text.Html$HtmlParser
-android.util.Log$PreloadHolder
-com.android.org.conscrypt.TrustedCertificateStore
-org.ccil.cowan.tagsoup.HTMLScanner
-sun.security.jca.Providers
diff --git a/core/api/Android.bp b/core/api/Android.bp
index 170febb..114a957 100644
--- a/core/api/Android.bp
+++ b/core/api/Android.bp
@@ -51,11 +51,17 @@
 filegroup {
     name: "non-updatable-module-lib-current.txt",
     srcs: ["module-lib-current.txt"],
-    visibility: ["//frameworks/base/api"],
+    visibility: [
+        "//frameworks/base/api",
+        "//cts/tests/signature/api",
+    ],
 }
 
 filegroup {
     name: "non-updatable-module-lib-removed.txt",
     srcs: ["module-lib-removed.txt"],
-    visibility: ["//frameworks/base/api"],
+    visibility: [
+        "//frameworks/base/api",
+        "//cts/tests/signature/api",
+    ],
 }
diff --git a/core/api/current.txt b/core/api/current.txt
index 5656c31..f699bbe 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1409,6 +1409,7 @@
     field public static final int summaryColumn = 16843426; // 0x10102a2
     field public static final int summaryOff = 16843248; // 0x10101f0
     field public static final int summaryOn = 16843247; // 0x10101ef
+    field public static final int supportedTypes;
     field public static final int supportsAssist = 16844016; // 0x10104f0
     field public static final int supportsInlineSuggestions = 16844301; // 0x101060d
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
@@ -20732,7 +20733,9 @@
     field public static final int GET_DEVICES_ALL = 3; // 0x3
     field public static final int GET_DEVICES_INPUTS = 1; // 0x1
     field public static final int GET_DEVICES_OUTPUTS = 2; // 0x2
+    field public static final int MODE_CALL_REDIRECT = 5; // 0x5
     field public static final int MODE_CALL_SCREENING = 4; // 0x4
+    field public static final int MODE_COMMUNICATION_REDIRECT = 6; // 0x6
     field public static final int MODE_CURRENT = -1; // 0xffffffff
     field public static final int MODE_INVALID = -2; // 0xfffffffe
     field public static final int MODE_IN_CALL = 2; // 0x2
@@ -26304,6 +26307,13 @@
   public final class TvIAppManager {
   }
 
+  public abstract class TvIAppService extends android.app.Service {
+    ctor public TvIAppService();
+    method public final android.os.IBinder onBind(android.content.Intent);
+    field public static final String SERVICE_INTERFACE = "android.media.tv.interactive.TvIAppService";
+    field public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
+  }
+
 }
 
 package android.mtp {
@@ -31656,9 +31666,9 @@
     method public byte[] marshall();
     method @NonNull public static android.os.Parcel obtain();
     method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder);
-    method @Nullable public Object[] readArray(@Nullable ClassLoader);
+    method @Deprecated @Nullable public Object[] readArray(@Nullable ClassLoader);
     method @Nullable public <T> T[] readArray(@Nullable ClassLoader, @NonNull Class<T>);
-    method @Nullable public java.util.ArrayList readArrayList(@Nullable ClassLoader);
+    method @Deprecated @Nullable public java.util.ArrayList readArrayList(@Nullable ClassLoader);
     method @Nullable public <T> java.util.ArrayList<T> readArrayList(@Nullable ClassLoader, @NonNull Class<? extends T>);
     method public void readBinderArray(@NonNull android.os.IBinder[]);
     method public void readBinderList(@NonNull java.util.List<android.os.IBinder>);
@@ -31676,32 +31686,32 @@
     method public android.os.ParcelFileDescriptor readFileDescriptor();
     method public float readFloat();
     method public void readFloatArray(@NonNull float[]);
-    method @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader);
+    method @Deprecated @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader);
     method @Nullable public <K, V> java.util.HashMap<K,V> readHashMap(@Nullable ClassLoader, @NonNull Class<? extends K>, @NonNull Class<? extends V>);
     method public int readInt();
     method public void readIntArray(@NonNull int[]);
     method public <T extends android.os.IInterface> void readInterfaceArray(@NonNull T[], @NonNull java.util.function.Function<android.os.IBinder,T>);
     method public <T extends android.os.IInterface> void readInterfaceList(@NonNull java.util.List<T>, @NonNull java.util.function.Function<android.os.IBinder,T>);
-    method public void readList(@NonNull java.util.List, @Nullable ClassLoader);
+    method @Deprecated public void readList(@NonNull java.util.List, @Nullable ClassLoader);
     method public <T> void readList(@NonNull java.util.List<? super T>, @Nullable ClassLoader, @NonNull Class<T>);
     method public long readLong();
     method public void readLongArray(@NonNull long[]);
-    method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
+    method @Deprecated public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
     method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>);
-    method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
+    method @Deprecated @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
     method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
     method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
     method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>);
-    method @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
+    method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
     method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
     method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
     method @Nullable public android.os.PersistableBundle readPersistableBundle();
     method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
-    method @Nullable public java.io.Serializable readSerializable();
+    method @Deprecated @Nullable public java.io.Serializable readSerializable();
     method @Nullable public <T extends java.io.Serializable> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>);
     method @NonNull public android.util.Size readSize();
     method @NonNull public android.util.SizeF readSizeF();
-    method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
+    method @Deprecated @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
     method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader, @NonNull Class<? extends T>);
     method @Nullable public android.util.SparseBooleanArray readSparseBooleanArray();
     method @Nullable public String readString();
@@ -31957,8 +31967,10 @@
     method public static final long getElapsedCpuTime();
     method public static final int[] getExclusiveCores();
     method public static final int getGidForName(String);
-    method public static final long getStartElapsedRealtime();
-    method public static final long getStartUptimeMillis();
+    method public static long getStartElapsedRealtime();
+    method public static long getStartRequestedElapsedRealtime();
+    method public static long getStartRequestedUptimeMillis();
+    method public static long getStartUptimeMillis();
     method public static final int getThreadPriority(int) throws java.lang.IllegalArgumentException;
     method public static final int getUidForName(String);
     method public static final boolean is64Bit();
@@ -31966,6 +31978,7 @@
     method public static final boolean isIsolated();
     method public static final void killProcess(int);
     method public static final int myPid();
+    method @NonNull public static String myProcessName();
     method public static final int myTid();
     method public static final int myUid();
     method public static android.os.UserHandle myUserHandle();
@@ -42697,6 +42710,7 @@
     method @Deprecated public int getMnc();
     method @Nullable public String getMncString();
     method public String getNumber();
+    method public int getPortIndex();
     method public int getSimSlotIndex();
     method public int getSubscriptionId();
     method public int getSubscriptionType();
@@ -42729,6 +42743,8 @@
     method @NonNull public java.util.List<android.net.Uri> getDeviceToDeviceStatusSharingContacts(int);
     method public int getDeviceToDeviceStatusSharingPreference(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_NUMBERS, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public String getPhoneNumber(int, int);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_NUMBERS, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public String getPhoneNumber(int);
     method public static int getSlotIndex(int);
     method @Nullable public int[] getSubscriptionIds(int);
     method @NonNull public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
@@ -42740,6 +42756,7 @@
     method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
     method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
+    method public void setCarrierPhoneNumber(int, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharingContacts(int, @NonNull java.util.List<android.net.Uri>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharingPreference(int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunistic(boolean, int);
@@ -42766,6 +42783,9 @@
     field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
     field public static final int INVALID_SIM_SLOT_INDEX = -1; // 0xffffffff
     field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
+    field public static final int PHONE_NUMBER_SOURCE_CARRIER = 2; // 0x2
+    field public static final int PHONE_NUMBER_SOURCE_IMS = 3; // 0x3
+    field public static final int PHONE_NUMBER_SOURCE_UICC = 1; // 0x1
     field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
     field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
   }
@@ -43240,14 +43260,28 @@
     method public int describeContents();
     method public int getCardId();
     method @Nullable public String getEid();
-    method @Nullable public String getIccId();
-    method public int getSlotIndex();
+    method @Deprecated @Nullable public String getIccId();
+    method public int getPhysicalSlotIndex();
+    method @NonNull public java.util.Collection<android.telephony.UiccPortInfo> getPorts();
+    method @Deprecated public int getSlotIndex();
     method public boolean isEuicc();
+    method public boolean isMultipleEnabledProfilesSupported();
     method public boolean isRemovable();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccCardInfo> CREATOR;
   }
 
+  public final class UiccPortInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public String getIccId();
+    method @IntRange(from=0) public int getLogicalSlotIndex();
+    method @IntRange(from=0) public int getPortIndex();
+    method public boolean isActive();
+    method public void writeToParcel(@Nullable android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccPortInfo> CREATOR;
+    field public static final String ICCID_REDACTED = "FFFFFFFFFFFFFFFFFFFF";
+  }
+
   public abstract class VisualVoicemailService extends android.app.Service {
     ctor public VisualVoicemailService();
     method public android.os.IBinder onBind(android.content.Intent);
@@ -43548,6 +43582,7 @@
     method @Nullable public String getEid();
     method @Nullable public android.telephony.euicc.EuiccInfo getEuiccInfo();
     method public boolean isEnabled();
+    method public boolean isSimPortAvailable(int);
     method public void startResolutionActivity(android.app.Activity, int, android.content.Intent, android.app.PendingIntent) throws android.content.IntentSender.SendIntentException;
     method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, android.app.PendingIntent);
     method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void updateSubscriptionNickname(int, @Nullable String, @NonNull android.app.PendingIntent);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2d762f0..4d6c1c8 100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -7530,7 +7530,7 @@
     method @NonNull public static android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings.Builder builder();
     method public int getCodeRate();
     method public int getModulation();
-    method @IntRange(from=0, to=255) public int getNumOfSegment();
+    method @IntRange(from=0, to=255) public int getNumberOfSegment();
     method public int getTimeInterleaveMode();
   }
 
@@ -7538,7 +7538,7 @@
     method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings build();
     method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings.Builder setCodeRate(int);
     method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings.Builder setModulation(int);
-    method @IntRange(from=0, to=255) @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings.Builder setNumOfSegment(int);
+    method @IntRange(from=0, to=255) @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings.Builder setNumberOfSegment(int);
     method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings.Builder setTimeInterleaveMode(int);
   }
 
@@ -8955,6 +8955,7 @@
     field public static final int SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED = 4; // 0x4
     field public static final int SWITCHABILITY_STATUS_USER_IN_CALL = 1; // 0x1
     field public static final int SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED = 2; // 0x2
+    field public static final String USER_TYPE_FULL_GUEST = "android.os.usertype.full.GUEST";
     field public static final String USER_TYPE_FULL_SECONDARY = "android.os.usertype.full.SECONDARY";
     field public static final String USER_TYPE_FULL_SYSTEM = "android.os.usertype.full.SYSTEM";
     field public static final String USER_TYPE_PROFILE_CLONE = "android.os.usertype.profile.CLONE";
@@ -12358,6 +12359,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerState(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerStateForSlot(int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerStateForSlot(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimSlotMapping(@NonNull java.util.Collection<android.telephony.UiccSlotMapping>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>);
     method @Deprecated public void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
@@ -12369,7 +12371,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPinReportResult(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPuk(String, String);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPukReportResult(String, String);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff();
     method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor);
     method public void updateServiceLocation();
@@ -12539,10 +12541,11 @@
     method public int describeContents();
     method public String getCardId();
     method public int getCardStateInfo();
-    method public boolean getIsActive();
+    method @Deprecated public boolean getIsActive();
     method public boolean getIsEuicc();
     method public boolean getIsExtendedApduSupported();
-    method public int getLogicalSlotIdx();
+    method @Deprecated public int getLogicalSlotIdx();
+    method @NonNull public java.util.Collection<android.telephony.UiccPortInfo> getPorts();
     method public boolean isRemovable();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
@@ -12552,6 +12555,15 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccSlotInfo> CREATOR;
   }
 
+  public final class UiccSlotMapping implements android.os.Parcelable {
+    method public int describeContents();
+    method @IntRange(from=0) public int getLogicalSlotIndex();
+    method @IntRange(from=0) public int getPhysicalSlotIndex();
+    method @IntRange(from=0) public int getPortIndex();
+    method public void writeToParcel(@Nullable android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.UiccSlotMapping> CREATOR;
+  }
+
   public abstract class VisualVoicemailService extends android.app.Service {
     method public static final void sendVisualVoicemailSms(android.content.Context, android.telecom.PhoneAccountHandle, String, short, String, android.app.PendingIntent);
     method public static final void setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings);
@@ -12843,7 +12855,8 @@
     method public void authenticateServer(String, String, byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
     method public void cancelSession(String, byte[], @android.telephony.euicc.EuiccCardManager.CancelReason int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
     method public void deleteProfile(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
-    method public void disableProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+    method @Deprecated public void disableProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
+    method public void disableProfile(@Nullable String, @Nullable String, int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
     method public void listNotifications(String, @android.telephony.euicc.EuiccNotification.Event int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
     method public void loadBoundProfilePackage(String, byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
     method public void prepareDownload(String, @Nullable byte[], byte[], byte[], byte[], java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
@@ -12861,7 +12874,8 @@
     method public void retrieveNotificationList(String, @android.telephony.euicc.EuiccNotification.Event int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.telephony.euicc.EuiccNotification[]>);
     method public void setDefaultSmdpAddress(String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
     method public void setNickname(String, String, String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
-    method public void switchToProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
+    method @Deprecated public void switchToProfile(String, String, boolean, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
+    method public void switchToProfile(@Nullable String, @Nullable String, int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
     field public static final int CANCEL_REASON_END_USER_REJECTED = 0; // 0x0
     field public static final int CANCEL_REASON_POSTPONED = 1; // 0x1
     field public static final int CANCEL_REASON_PPR_NOT_ALLOWED = 3; // 0x3
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 19223f9..ffa7cf1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -37,8 +37,10 @@
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UptimeMillisLong;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.backup.BackupAgent;
@@ -888,6 +890,9 @@
 
         SharedMemory mSerializedSystemFontMap;
 
+        long startRequestedElapsedTime;
+        long startRequestedUptime;
+
         @Override
         public String toString() {
             return "AppBindData{appInfo=" + appInfo + "}";
@@ -1099,7 +1104,8 @@
                 CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                 String buildSerial, AutofillOptions autofillOptions,
                 ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges,
-                SharedMemory serializedSystemFontMap) {
+                SharedMemory serializedSystemFontMap,
+                long startRequestedElapsedTime, long startRequestedUptime) {
             if (services != null) {
                 if (false) {
                     // Test code to make sure the app could see the passed-in services.
@@ -1149,6 +1155,8 @@
             data.contentCaptureOptions = contentCaptureOptions;
             data.disabledCompatChanges = disabledCompatChanges;
             data.mSerializedSystemFontMap = serializedSystemFontMap;
+            data.startRequestedElapsedTime = startRequestedElapsedTime;
+            data.startRequestedUptime = startRequestedUptime;
             sendMessage(H.BIND_APPLICATION, data);
         }
 
@@ -6407,7 +6415,8 @@
             DdmVmInternal.setRecentAllocationsTrackingEnabled(true);
         }
         // Note when this process has started.
-        Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
+        Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis(),
+                data.startRequestedElapsedTime, data.startRequestedUptime);
 
         AppCompatCallbacks.install(data.disabledCompatChanges);
         // Let libcore handle any compat changes after installing the list of compat changes.
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index d6ff6d3..0e42a79 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -80,7 +80,8 @@
             in CompatibilityInfo compatInfo, in Map services,
             in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions,
             in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges,
-            in SharedMemory serializedSystemFontMap);
+            in SharedMemory serializedSystemFontMap,
+            long startRequestedElapsedTime, long startRequestedUptime);
     void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs);
     void scheduleExit();
     void scheduleServiceArgs(IBinder token, in ParceledListSlice args);
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 95892aa..b06d076 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -96,7 +96,7 @@
     public void requestState(@NonNull DeviceStateRequest request,
             @Nullable @CallbackExecutor Executor executor,
             @Nullable DeviceStateRequest.Callback callback) {
-        mGlobal.requestState(request, callback, executor);
+        mGlobal.requestState(request, executor, callback);
     }
 
     /**
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index 904a54b..85e70b0 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -117,7 +117,7 @@
      * @see DeviceStateRequest
      */
     public void requestState(@NonNull DeviceStateRequest request,
-            @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+            @Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) {
         if (callback == null && executor != null) {
             throw new IllegalArgumentException("Callback must be supplied with executor.");
         } else if (executor == null && callback != null) {
@@ -149,7 +149,7 @@
 
     /**
      * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
-     * {@link #requestState(DeviceStateRequest, DeviceStateRequest.Callback, Executor)}.
+     * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
      *
      * @see DeviceStateManager#cancelRequest(DeviceStateRequest)
      */
@@ -408,7 +408,7 @@
                 return;
             }
 
-            mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest));
+            mExecutor.execute(() -> mCallback.onRequestCanceled(mRequest));
         }
     }
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 689c806..a4a76a8 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -58,6 +58,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.text.DecimalFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Formatter;
@@ -859,11 +860,13 @@
         /**
          * Returns cpu times of an uid at a particular process state.
          */
-        public abstract long[] getCpuFreqTimes(int which, int procState);
+        public abstract boolean getCpuFreqTimes(@NonNull long[] timesInFreqMs, int procState);
+
         /**
          * Returns cpu times of an uid while the screen if off at a particular process state.
          */
-        public abstract long[] getScreenOffCpuFreqTimes(int which, int procState);
+        public abstract boolean getScreenOffCpuFreqTimes(@NonNull long[] timesInFreqMs,
+                int procState);
 
         // Note: the following times are disjoint.  They can be added together to find the
         // total time a uid has had any processes running at all.
@@ -1575,6 +1578,11 @@
 
     public abstract long getNextMaxDailyDeadline();
 
+    /**
+     * Returns the total number of frequencies across all CPU clusters.
+     */
+    public abstract int getCpuFreqCount();
+
     public abstract long[] getCpuFreqs();
 
     public final static class HistoryTag {
@@ -4626,27 +4634,26 @@
                             cpuFreqTimeMs.length, sb.toString());
                 }
 
+                final long[] timesInFreqMs = new long[getCpuFreqCount()];
                 for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
-                    final long[] timesMs = u.getCpuFreqTimes(which, procState);
-                    if (timesMs != null && timesMs.length == cpuFreqs.length) {
+                    if (u.getCpuFreqTimes(timesInFreqMs, procState)) {
                         sb.setLength(0);
-                        for (int i = 0; i < timesMs.length; ++i) {
+                        for (int i = 0; i < timesInFreqMs.length; ++i) {
                             if (i != 0) sb.append(',');
-                            sb.append(timesMs[i]);
+                            sb.append(timesInFreqMs[i]);
                         }
-                        final long[] screenOffTimesMs = u.getScreenOffCpuFreqTimes(
-                                which, procState);
-                        if (screenOffTimesMs != null) {
-                            for (int i = 0; i < screenOffTimesMs.length; ++i) {
-                                sb.append(',').append(screenOffTimesMs[i]);
+                        if (u.getScreenOffCpuFreqTimes(timesInFreqMs, procState)) {
+                            for (int i = 0; i < timesInFreqMs.length; ++i) {
+                                sb.append(',').append(timesInFreqMs[i]);
                             }
                         } else {
-                            for (int i = 0; i < timesMs.length; ++i) {
+                            for (int i = 0; i < timesInFreqMs.length; ++i) {
                                 sb.append(",0");
                             }
                         }
                         dumpLine(pw, uid, category, CPU_TIMES_AT_FREQ_DATA,
-                                Uid.UID_PROCESS_TYPES[procState], timesMs.length, sb.toString());
+                                Uid.UID_PROCESS_TYPES[procState], timesInFreqMs.length,
+                                sb.toString());
                     }
                 }
             }
@@ -6243,25 +6250,24 @@
                 pw.println(sb.toString());
             }
 
+            final long[] timesInFreqMs = new long[getCpuFreqCount()];
             for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
-                final long[] cpuTimes = u.getCpuFreqTimes(which, procState);
-                if (cpuTimes != null) {
+                if (u.getCpuFreqTimes(timesInFreqMs, procState)) {
                     sb.setLength(0);
                     sb.append("    Cpu times per freq at state ")
                             .append(Uid.PROCESS_STATE_NAMES[procState]).append(':');
-                    for (int i = 0; i < cpuTimes.length; ++i) {
-                        sb.append(" " + cpuTimes[i]);
+                    for (int i = 0; i < timesInFreqMs.length; ++i) {
+                        sb.append(" ").append(timesInFreqMs[i]);
                     }
                     pw.println(sb.toString());
                 }
 
-                final long[] screenOffCpuTimes = u.getScreenOffCpuFreqTimes(which, procState);
-                if (screenOffCpuTimes != null) {
+                if (u.getScreenOffCpuFreqTimes(timesInFreqMs, procState)) {
                     sb.setLength(0);
                     sb.append("   Screen-off cpu times per freq at state ")
                             .append(Uid.PROCESS_STATE_NAMES[procState]).append(':');
-                    for (int i = 0; i < screenOffCpuTimes.length; ++i) {
-                        sb.append(" " + screenOffCpuTimes[i]);
+                    for (int i = 0; i < timesInFreqMs.length; ++i) {
+                        sb.append(" ").append(timesInFreqMs[i]);
                     }
                     pw.println(sb.toString());
                 }
@@ -7627,22 +7633,22 @@
                 }
             }
 
+            final long[] timesInFreqMs = new long[getCpuFreqCount()];
+            final long[] timesInFreqScreenOffMs = new long[getCpuFreqCount()];
             for (int procState = 0; procState < Uid.NUM_PROCESS_STATE; ++procState) {
-                final long[] timesMs = u.getCpuFreqTimes(which, procState);
-                if (timesMs != null && timesMs.length == cpuFreqs.length) {
-                    long[] screenOffTimesMs = u.getScreenOffCpuFreqTimes(which, procState);
-                    if (screenOffTimesMs == null) {
-                        screenOffTimesMs = new long[timesMs.length];
+                if (u.getCpuFreqTimes(timesInFreqMs, procState)) {
+                    if (!u.getScreenOffCpuFreqTimes(timesInFreqScreenOffMs, procState)) {
+                        Arrays.fill(timesInFreqScreenOffMs, 0);
                     }
                     final long procToken = proto.start(UidProto.Cpu.BY_PROCESS_STATE);
                     proto.write(UidProto.Cpu.ByProcessState.PROCESS_STATE, procState);
-                    for (int ic = 0; ic < timesMs.length; ++ic) {
+                    for (int ic = 0; ic < timesInFreqMs.length; ++ic) {
                         long cToken = proto.start(UidProto.Cpu.ByProcessState.BY_FREQUENCY);
                         proto.write(UidProto.Cpu.ByFrequency.FREQUENCY_INDEX, ic + 1);
                         proto.write(UidProto.Cpu.ByFrequency.TOTAL_DURATION_MS,
-                                timesMs[ic]);
+                                timesInFreqMs[ic]);
                         proto.write(UidProto.Cpu.ByFrequency.SCREEN_OFF_DURATION_MS,
-                                screenOffTimesMs[ic]);
+                                timesInFreqScreenOffMs[ic]);
                         proto.end(cToken);
                     }
                     proto.end(procToken);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 09e5a8f..7bdb6b9 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -2990,7 +2990,12 @@
      * Please use {@link #readBundle(ClassLoader)} instead (whose data must have
      * been written with {@link #writeBundle}.  Read into an existing Map object
      * from the parcel at the current dataPosition().
+     *
+     * @deprecated Consider using {@link #readBundle(ClassLoader)} as stated above, in case this
+     *      method is still preferred use the type-safer version {@link #readMap(Map, ClassLoader,
+     *      Class, Class)} starting from Android {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
         int n = readInt();
         readMapInternal(outVal, n, loader, /* clazzKey */ null, /* clazzValue */ null);
@@ -3016,7 +3021,14 @@
      * Read into an existing List object from the parcel at the current
      * dataPosition(), using the given class loader to load any enclosed
      * Parcelables.  If it is null, the default class loader is used.
+     *
+     * @deprecated Use the type-safer version {@link #readList(List, ClassLoader, Class)} starting
+     *      from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to
+     *      use {@link #readTypedList(List, Parcelable.Creator)} if possible (eg. if the items'
+     *      class is final) since this is also more performant. Note that changing to the latter
+     *      also requires changing the writes.
      */
+    @Deprecated
     public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) {
         int N = readInt();
         readListInternal(outVal, N, loader, /* clazz */ null);
@@ -3043,10 +3055,14 @@
      * object from the parcel at the current dataPosition(), using the given
      * class loader to load any enclosed Parcelables.  Returns null if
      * the previously written map object was null.
+     *
+     * @deprecated Consider using {@link #readBundle(ClassLoader)} as stated above, in case this
+     *      method is still preferred use the type-safer version {@link #readHashMap(ClassLoader,
+     *      Class, Class)} starting from Android {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     @Nullable
-    public final HashMap readHashMap(@Nullable ClassLoader loader)
-    {
+    public HashMap readHashMap(@Nullable ClassLoader loader) {
         int n = readInt();
         if (n < 0) {
             return null;
@@ -3247,7 +3263,14 @@
      * dataPosition().  Returns null if the previously written list object was
      * null.  The given class loader will be used to load any enclosed
      * Parcelables.
+     *
+     * @deprecated Use the type-safer version {@link #readArrayList(ClassLoader, Class)} starting
+     *      from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to
+     *      use {@link #createTypedArrayList(Parcelable.Creator)} if possible (eg. if the items'
+     *      class is final) since this is also more performant. Note that changing to the latter
+     *      also requires changing the writes.
      */
+    @Deprecated
     @Nullable
     public ArrayList readArrayList(@Nullable ClassLoader loader) {
         return readArrayListInternal(loader, /* clazz */ null);
@@ -3274,7 +3297,14 @@
      * dataPosition().  Returns null if the previously written array was
      * null.  The given class loader will be used to load any enclosed
      * Parcelables.
+     *
+     * @deprecated Use the type-safer version {@link #readArray(ClassLoader, Class)} starting from
+     *      Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to use
+     *      {@link #createTypedArray(Parcelable.Creator)} if possible (eg. if the items' class is
+     *      final) since this is also more performant. Note that changing to the latter also
+     *      requires changing the writes.
      */
+    @Deprecated
     @Nullable
     public Object[] readArray(@Nullable ClassLoader loader) {
         return readArrayInternal(loader, /* clazz */ null);
@@ -3300,7 +3330,14 @@
      * dataPosition().  Returns null if the previously written list object was
      * null.  The given class loader will be used to load any enclosed
      * Parcelables.
+     *
+     * @deprecated Use the type-safer version {@link #readSparseArray(ClassLoader, Class)} starting
+     *      from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to
+     *      use {@link #createTypedSparseArray(Parcelable.Creator)} if possible (eg. if the items'
+     *      class is final) since this is also more performant. Note that changing to the latter
+     *      also requires changing the writes.
      */
+    @Deprecated
     @Nullable
     public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
         return readSparseArrayInternal(loader, /* clazz */ null);
@@ -4107,7 +4144,13 @@
      * object has been written.
      * @throws BadParcelableException Throws BadParcelableException if there
      * was an error trying to instantiate the Parcelable.
+     *
+     * @deprecated Use the type-safer version {@link #readParcelable(ClassLoader, Class)} starting
+     *      from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to
+     *      use {@link Parcelable.Creator#createFromParcel(Parcel)} if possible since this is also
+     *      more performant. Note that changing to the latter also requires changing the writes.
      */
+    @Deprecated
     @Nullable
     public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
         return readParcelableInternal(loader, /* clazz */ null);
@@ -4176,7 +4219,11 @@
      * read the {@link Parcelable.Creator}.
      *
      * @see #writeParcelableCreator
+     *
+     * @deprecated Use the type-safer version {@link #readParcelableCreator(ClassLoader, Class)}
+     *       starting from Android {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     @Nullable
     public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
         return readParcelableCreatorInternal(loader, /* clazz */ null);
@@ -4337,7 +4384,11 @@
      * Read and return a new Serializable object from the parcel.
      * @return the Serializable object, or null if the Serializable name
      * wasn't found in the parcel.
+     *
+     * @deprecated Use the type-safer version {@link #readSerializable(ClassLoader, Class)} starting
+     *       from Android {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     @Nullable
     public Serializable readSerializable() {
         return readSerializableInternal(/* loader */ null, /* clazz */ null);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 9f37c48..c9670ff 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -18,11 +18,14 @@
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.annotation.UptimeMillisLong;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build.VERSION_CODES;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -553,9 +556,26 @@
     public static final int SIGNAL_KILL = 9;
     public static final int SIGNAL_USR1 = 10;
 
+    /**
+     * When the process started and ActivityThread.handleBindApplication() was executed.
+     */
     private static long sStartElapsedRealtime;
+
+    /**
+     * When the process started and ActivityThread.handleBindApplication() was executed.
+     */
     private static long sStartUptimeMillis;
 
+    /**
+     * When the activity manager was about to ask zygote to fork.
+     */
+    private static long sStartRequestedElapsedRealtime;
+
+    /**
+     * When the activity manager was about to ask zygote to fork.
+     */
+    private static long sStartRequestedUptimeMillis;
+
     private static final int PIDFD_UNKNOWN = 0;
     private static final int PIDFD_SUPPORTED = 1;
     private static final int PIDFD_UNSUPPORTED = 2;
@@ -605,6 +625,12 @@
      */
     public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();
 
+
+    /**
+     * The process name set via {@link #setArgV0(String)}.
+     */
+    private static String sArgV0;
+
     /**
      * Start a new process.
      * 
@@ -718,21 +744,44 @@
     /**
      * Return the {@link SystemClock#elapsedRealtime()} at which this process was started.
      */
-    public static final long getStartElapsedRealtime() {
+    @ElapsedRealtimeLong
+    public static long getStartElapsedRealtime() {
         return sStartElapsedRealtime;
     }
 
     /**
      * Return the {@link SystemClock#uptimeMillis()} at which this process was started.
      */
-    public static final long getStartUptimeMillis() {
+    @UptimeMillisLong
+    public static long getStartUptimeMillis() {
         return sStartUptimeMillis;
     }
 
+    /**
+     * Return the {@link SystemClock#elapsedRealtime()} at which the system decides to start
+     * this process.
+     */
+    @ElapsedRealtimeLong
+    public static long getStartRequestedElapsedRealtime() {
+        return sStartRequestedElapsedRealtime;
+    }
+
+    /**
+     * Return the {@link SystemClock#uptimeMillis()} at which the system decides to start
+     * this process.
+     */
+    @UptimeMillisLong
+    public static long getStartRequestedUptimeMillis() {
+        return sStartRequestedUptimeMillis;
+    }
+
     /** @hide */
-    public static final void setStartTimes(long elapsedRealtime, long uptimeMillis) {
+    public static final void setStartTimes(long elapsedRealtime, long uptimeMillis,
+            long startRequestedElapsedRealtime, long startRequestedUptime) {
         sStartElapsedRealtime = elapsedRealtime;
         sStartUptimeMillis = uptimeMillis;
+        sStartRequestedElapsedRealtime = startRequestedElapsedRealtime;
+        sStartRequestedUptimeMillis = startRequestedUptime;
     }
 
     /**
@@ -1135,8 +1184,26 @@
      * 
      * {@hide}
      */
-    @UnsupportedAppUsage
-    public static final native void setArgV0(String text);
+    @UnsupportedAppUsage(maxTargetSdk = VERSION_CODES.S, publicAlternatives = "Do not try to "
+            + "change the process name. (If you must, you could use {@code pthread_setname_np(3)}, "
+            + "but this could confuse the system)")
+    public static void setArgV0(@NonNull String text) {
+        sArgV0 = text;
+        setArgV0Native(text);
+    }
+
+    private static native void setArgV0Native(String text);
+
+    /**
+     * Return the name of this process.
+     */
+    @NonNull
+    public static String myProcessName() {
+        // Note this could be different from the actual process name if someone changes the
+        // process name using native code (using pthread_setname_np()). But sArgV0
+        // is the name that the system thinks this process has.
+        return sArgV0;
+    }
 
     /**
      * Kill the process with the given PID.
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 4e8418b..ba5ed43 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -298,6 +298,17 @@
     }
 
     /**
+     * Register callback for service registration notifications.
+     *
+     * @throws RemoteException for underlying error.
+     * @hide
+     */
+    public static void registerForNotifications(
+            @NonNull String name, @NonNull IServiceCallback callback) throws RemoteException {
+        getIServiceManager().registerForNotifications(name, callback);
+    }
+
+    /**
      * Return a list of all currently running services.
      * @return an array of all currently running services, or <code>null</code> in
      * case of an exception
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 3739040..2dcf674 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -78,7 +78,7 @@
 
     public void registerForNotifications(String name, IServiceCallback cb)
             throws RemoteException {
-        throw new RemoteException();
+        mServiceManager.registerForNotifications(name, cb);
     }
 
     public void unregisterForNotifications(String name, IServiceCallback cb)
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 14d7e82..447102b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -118,6 +118,7 @@
      * User type representing a guest user that may be transient.
      * @hide
      */
+    @SystemApi
     public static final String USER_TYPE_FULL_GUEST = "android.os.usertype.full.GUEST";
 
     /**
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index fbac954..bff5c62 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -90,9 +90,9 @@
      */
     int changeEncryptionPassword(int type, in String password) = 28;
     /**
-     * Returns list of all mountable volumes.
+     * Returns list of all mountable volumes for the specified userId
      */
-    StorageVolume[] getVolumeList(int uid, in String packageName, int flags) = 29;
+    StorageVolume[] getVolumeList(int userId, in String callingPackage, int flags) = 29;
     /**
      * Determines the encryption state of the volume.
      * @return a numerical value. See {@code ENCRYPTION_STATE_*} for possible
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 77c794c..627e09e 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1391,13 +1391,7 @@
                 }
                 packageName = packageNames[0];
             }
-            final int uid = ActivityThread.getPackageManager().getPackageUid(packageName,
-                    PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
-            if (uid <= 0) {
-                Log.w(TAG, "Missing UID; no storage volumes available");
-                return new StorageVolume[0];
-            }
-            return storageManager.getVolumeList(uid, packageName, flags);
+            return storageManager.getVolumeList(userId, packageName, flags);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 17272d3..3d7c026 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -15080,22 +15080,6 @@
                 "max_sound_trigger_detection_service_ops_per_day";
 
         /**
-         * Setting indicating the name of the Wear OS app package containing the device's sysui.
-         *
-         * @hide
-         */
-        public static final String CLOCKWORK_SYSUI_PACKAGE_NAME =
-                "clockwork_sysui_package_name";
-
-        /**
-         * Setting indicating the name of the main activity of the Wear OS sysui.
-         *
-         * @hide
-         */
-        public static final String CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME =
-                "clockwork_sysui_main_activity_name";
-
-        /**
          * Setting to determine if the Clockwork Home application is ready.
          *
          * <p>
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 89b95c0..f807da8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -10424,7 +10424,7 @@
 
     @Override
     public boolean applyTransactionOnDraw(@NonNull SurfaceControl.Transaction t) {
-        if (mRemoved) {
+        if (mRemoved || !isHardwareEnabled()) {
             t.apply();
         } else {
             registerRtFrameCallback(frame -> mergeWithNextTransaction(t, frame));
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index dc01990..91ef8a5 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -20,6 +20,7 @@
 import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_ACCESSIBILITY;
 
 import android.os.Build;
+import android.os.SystemClock;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.LongArray;
@@ -71,6 +72,11 @@
 
     private long mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
     private long mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
+    /**
+     * The event time of the {@link AccessibilityEvent} which presents the populated windows cache
+     * before it is stale.
+     */
+    private long mValidWindowCacheTimeStamp = 0;
 
     private int mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
     private int mInputFocusWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
@@ -97,13 +103,20 @@
      * The key of SparseArray is display ID.
      *
      * @param windowsOnAllDisplays The accessibility windows of all displays.
+     * @param populationTimeStamp The timestamp from {@link SystemClock#uptimeMillis()} when the
+     *                            client requests the data.
      */
     public void setWindowsOnAllDisplays(
-            SparseArray<List<AccessibilityWindowInfo>> windowsOnAllDisplays) {
+            SparseArray<List<AccessibilityWindowInfo>> windowsOnAllDisplays,
+            long populationTimeStamp) {
         synchronized (mLock) {
             if (DEBUG) {
                 Log.i(LOG_TAG, "Set windows");
             }
+            if (mValidWindowCacheTimeStamp > populationTimeStamp) {
+                // Discard the windows because it might be stale.
+                return;
+            }
             clearWindowCacheLocked();
             if (windowsOnAllDisplays == null) {
                 return;
@@ -224,6 +237,7 @@
                 } break;
 
                 case AccessibilityEvent.TYPE_WINDOWS_CHANGED:
+                    mValidWindowCacheTimeStamp = event.getEventTime();
                     if (event.getWindowChanges()
                             == AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED) {
                         // Don't need to clear all cache. Unless the changes are related to
@@ -232,6 +246,7 @@
                         break;
                     }
                 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
+                    mValidWindowCacheTimeStamp = event.getEventTime();
                     clear();
                 } break;
             }
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 6975bb2..bc21488 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -435,8 +435,10 @@
                     }
                 }
 
+                long populationTimeStamp;
                 final long identityToken = Binder.clearCallingIdentity();
                 try {
+                    populationTimeStamp = SystemClock.uptimeMillis();
                     windows = connection.getWindows();
                 } finally {
                     Binder.restoreCallingIdentity(identityToken);
@@ -446,7 +448,7 @@
                 }
                 if (windows != null) {
                     if (sAccessibilityCache != null) {
-                        sAccessibilityCache.setWindowsOnAllDisplays(windows);
+                        sAccessibilityCache.setWindowsOnAllDisplays(windows, populationTimeStamp);
                     }
                     return windows;
                 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 9140bb8..06d68e0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -468,6 +468,7 @@
             mPendingUids.clear();
         }
         final long timestampMs = mClock.elapsedRealtime();
+        LongArrayMultiStateCounter.LongArrayContainer deltaContainer = null;
         for (int i = uidStates.size() - 1; i >= 0; --i) {
             final int uid = uidStates.keyAt(i);
             final int procState = uidStates.valueAt(i);
@@ -493,6 +494,9 @@
                         isolatedUids[j] = u.mChildUids.keyAt(j);
                         isolatedUidTimeInFreqCounters[j] =
                                 u.mChildUids.valueAt(j).cpuTimeInFreqCounter;
+                        if (deltaContainer == null && isolatedUidTimeInFreqCounters[j] != null) {
+                            deltaContainer = getCpuTimeInFreqContainer();
+                        }
                     }
                 }
             }
@@ -509,8 +513,6 @@
             mKernelSingleUidTimeReader.addDelta(uid, onBatteryScreenOffCounter, timestampMs);
 
             if (isolatedUids != null) {
-                LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
-                        new LongArrayMultiStateCounter.LongArrayContainer(getCpuFreqCount());
                 for (int j = isolatedUids.length - 1; j >= 0; --j) {
                     if (isolatedUidTimeInFreqCounters[j] != null) {
                         mKernelSingleUidTimeReader.addDelta(isolatedUids[j],
@@ -612,13 +614,13 @@
                 }
 
                 if (u.mChildUids != null) {
-                    final LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
-                            new LongArrayMultiStateCounter.LongArrayContainer(getCpuFreqCount());
                     for (int j = u.mChildUids.size() - 1; j >= 0; --j) {
                         final LongArrayMultiStateCounter counter =
                                 u.mChildUids.valueAt(j).cpuTimeInFreqCounter;
                         if (counter != null) {
                             final int isolatedUid = u.mChildUids.keyAt(j);
+                            final LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
+                                    getCpuTimeInFreqContainer();
                             mKernelSingleUidTimeReader.addDelta(isolatedUid,
                                     counter, timestampMs, deltaContainer);
                             onBatteryCounter.addCounts(deltaContainer);
@@ -1186,7 +1188,9 @@
 
     private long mBatteryTimeToFullSeconds = -1;
 
+    private boolean mCpuFreqsInitialized;
     private long[] mCpuFreqs;
+    private LongArrayMultiStateCounter.LongArrayContainer mTmpCpuTimeInFreq;
 
     /**
      * Times spent by the system server threads handling incoming binder requests.
@@ -1931,23 +1935,22 @@
         }
 
         /**
-         * Returns accumulated counts for the specified state, or null if all counts are zero.
+         * Returns accumulated counts for the specified state, or false if all counts are zero.
          */
-        @Nullable
-        public long[] getCountsLocked(int which, int procState) {
-            LongArrayMultiStateCounter.LongArrayContainer longArrayContainer =
-                    new LongArrayMultiStateCounter.LongArrayContainer(mCounter.getArrayLength());
-            mCounter.getCounts(longArrayContainer, procState);
-            final long[] counts = new long[mCounter.getArrayLength()];
-            longArrayContainer.getValues(counts);
+        public boolean getCountsLocked(long[] counts, int procState) {
+            if (counts.length != mCounter.getArrayLength()) {
+                return false;
+            }
+
+            mCounter.getCounts(counts, procState);
 
             // Return counts only if at least one of the elements is non-zero.
             for (int i = counts.length - 1; i >= 0; --i) {
                 if (counts[i] != 0) {
-                    return counts;
+                    return true;
                 }
             }
-            return null;
+            return false;
         }
 
         public void logState(Printer pw, String prefix) {
@@ -8346,35 +8349,34 @@
 
         @GuardedBy("mBsi")
         @Override
-        public long[] getCpuFreqTimes(int which, int procState) {
+        public boolean getCpuFreqTimes(long[] timesInFreqMs, int procState) {
             if (procState < 0 || procState >= NUM_PROCESS_STATE) {
-                return null;
+                return false;
             }
             if (mProcStateTimeMs == null) {
-                return null;
+                return false;
             }
             if (!mBsi.mPerProcStateCpuTimesAvailable) {
                 mProcStateTimeMs = null;
-                return null;
+                return false;
             }
-
-            return mProcStateTimeMs.getCountsLocked(which, procState);
+            return mProcStateTimeMs.getCountsLocked(timesInFreqMs, procState);
         }
 
         @GuardedBy("mBsi")
         @Override
-        public long[] getScreenOffCpuFreqTimes(int which, int procState) {
+        public boolean getScreenOffCpuFreqTimes(long[] timesInFreqMs, int procState) {
             if (procState < 0 || procState >= NUM_PROCESS_STATE) {
-                return null;
+                return false;
             }
             if (mProcStateScreenOffTimeMs == null) {
-                return null;
+                return false;
             }
             if (!mBsi.mPerProcStateCpuTimesAvailable) {
                 mProcStateScreenOffTimeMs = null;
-                return null;
+                return false;
             }
-            return mProcStateScreenOffTimeMs.getCountsLocked(which, procState);
+            return mProcStateScreenOffTimeMs.getCountsLocked(timesInFreqMs, procState);
         }
 
         public long getBinderCallCount() {
@@ -11577,18 +11579,30 @@
         }
     }
 
+    @GuardedBy("this")
+    @Override
     public long[] getCpuFreqs() {
+        if (!mCpuFreqsInitialized) {
+            mCpuFreqs = mCpuUidFreqTimeReader.readFreqs(mPowerProfile);
+            mCpuFreqsInitialized = true;
+        }
         return mCpuFreqs;
     }
 
-    /**
-     * Returns the total number of frequencies across all CPU clusters.
-     */
-    private int getCpuFreqCount() {
-        if (mCpuFreqs == null) {
-            mCpuFreqs = mCpuUidFreqTimeReader.readFreqs(mPowerProfile);
+    @GuardedBy("this")
+    @Override
+    public int getCpuFreqCount() {
+        final long[] cpuFreqs = getCpuFreqs();
+        return cpuFreqs != null ? cpuFreqs.length : 0;
+    }
+
+    @GuardedBy("this")
+    private LongArrayMultiStateCounter.LongArrayContainer getCpuTimeInFreqContainer() {
+        if (mTmpCpuTimeInFreq == null) {
+            mTmpCpuTimeInFreq =
+                    new LongArrayMultiStateCounter.LongArrayContainer(getCpuFreqCount());
         }
-        return mCpuFreqs != null ? mCpuFreqs.length : 0;
+        return mTmpCpuTimeInFreq;
     }
 
     public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb,
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 4ab4fae..175f28f 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -56,6 +56,7 @@
         public long durationFgMs;
         public String packageWithHighestDrain;
         public double[] perProcStatePowerMah;
+        public long[] cpuFreqTimes;
     }
 
     public CpuPowerCalculator(PowerProfile profile) {
@@ -98,6 +99,9 @@
 
         BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
         Result result = new Result();
+        if (query.isProcessStateDataNeeded()) {
+            result.cpuFreqTimes = new long[batteryStats.getCpuFreqCount()];
+        }
         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
                 builder.getUidBatteryConsumerBuilders();
         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
@@ -187,11 +191,10 @@
             // TODO(b/191921016): use per-state CPU cluster times
             final long[] cpuClusterTimes = null;
 
-            final long[] cpuFreqTimes = u.getCpuFreqTimes(BatteryStats.STATS_SINCE_CHARGED,
-                    uidProcState);
-            if (cpuClusterTimes != null || cpuFreqTimes != null) {
+            boolean hasCpuFreqTimes = u.getCpuFreqTimes(result.cpuFreqTimes, uidProcState);
+            if (cpuClusterTimes != null || hasCpuFreqTimes) {
                 result.perProcStatePowerMah[procState] += calculateUidModeledPowerMah(u,
-                        0, cpuClusterTimes, cpuFreqTimes);
+                        0, cpuClusterTimes, result.cpuFreqTimes);
             }
         }
 
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index d98b73f..50dcca64 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -27,6 +27,7 @@
 import libcore.util.NativeAllocationRegistry;
 
 import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Performs per-state counting of multi-element values over time. The class' behavior is illustrated
@@ -120,6 +121,8 @@
     private static final NativeAllocationRegistry sRegistry =
             NativeAllocationRegistry.createMalloced(
                     LongArrayMultiStateCounter.class.getClassLoader(), native_getReleaseFunc());
+    private static final AtomicReference<LongArrayContainer> sTmpArrayContainer =
+            new AtomicReference<>();
 
     private final int mStateCount;
     private final int mLength;
@@ -204,6 +207,19 @@
     }
 
     /**
+     * Populates the array with the accumulated counts for the specified state.
+     */
+    public void getCounts(long[] counts, int state) {
+        LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
+        if (container == null || container.mLength != counts.length) {
+            container = new LongArrayContainer(counts.length);
+        }
+        getCounts(container, state);
+        container.getValues(counts);
+        sTmpArrayContainer.set(container);
+    }
+
+    /**
      * Populates longArrayContainer with the accumulated counts for the specified state.
      */
     public void getCounts(LongArrayContainer longArrayContainer, int state) {
diff --git a/core/java/com/android/internal/view/OWNERS b/core/java/com/android/internal/view/OWNERS
index eb2478f..7a590d0 100644
--- a/core/java/com/android/internal/view/OWNERS
+++ b/core/java/com/android/internal/view/OWNERS
@@ -15,7 +15,7 @@
 
 # WindowManager
 per-file AppearanceRegion = file:/services/core/java/com/android/server/wm/OWNERS
-per-file BaseIWIndow.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file BaseIWindow.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file RotationPolicy.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file WindowManagerPolicyThread.java = file:/services/core/java/com/android/server/wm/OWNERS
 
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 5c9b6df..18e85b6 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -750,8 +750,9 @@
     }
 
     const bool checkJni = GetBoolProperty("dalvik.vm.checkjni", false);
-    ALOGV("CheckJNI is %s\n", checkJni ? "ON" : "OFF");
     if (checkJni) {
+        ALOGD("CheckJNI is ON");
+
         /* extended JNI checking */
         addOption("-Xcheck:jni");
 
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 4d8d4db..7c67cbc 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -1346,7 +1346,7 @@
         {"createProcessGroup", "(II)I", (void*)android_os_Process_createProcessGroup},
         {"getExclusiveCores", "()[I", (void*)android_os_Process_getExclusiveCores},
         {"setSwappiness", "(IZ)Z", (void*)android_os_Process_setSwappiness},
-        {"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
+        {"setArgV0Native", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
         {"setUid", "(I)I", (void*)android_os_Process_setUid},
         {"setGid", "(I)I", (void*)android_os_Process_setGid},
         {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal},
diff --git a/core/res/res/layout-watch/alert_dialog_material.xml b/core/res/res/layout-watch/alert_dialog_material.xml
index 960b927..1d56845 100644
--- a/core/res/res/layout-watch/alert_dialog_material.xml
+++ b/core/res/res/layout-watch/alert_dialog_material.xml
@@ -51,7 +51,7 @@
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
                         android:gravity="center_horizontal|top"
-                        android:textAppearance="@style/TextAppearance.Material.Subhead"
+                        android:textAppearance="@style/TextAppearance.DeviceDefault.Body1"
                         android:paddingStart="?dialogPreferredPadding"
                         android:paddingEnd="?dialogPreferredPadding"
                         android:paddingTop="8dip"
diff --git a/core/res/res/layout-watch/progress_dialog_material.xml b/core/res/res/layout-watch/progress_dialog_material.xml
index 96bda1d..a7df948 100644
--- a/core/res/res/layout-watch/progress_dialog_material.xml
+++ b/core/res/res/layout-watch/progress_dialog_material.xml
@@ -40,7 +40,7 @@
 
         <TextView
             android:id="@+id/message"
-            android:textAppearance="?attr/textAppearanceListItem"
+            android:textAppearance="@style/TextAppearance.DeviceDefault.Subhead"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="center_vertical" />
diff --git a/core/res/res/values-w208dp/dimens_material.xml b/core/res/res/values-w208dp/dimens_material.xml
new file mode 100644
index 0000000..069eeb0
--- /dev/null
+++ b/core/res/res/values-w208dp/dimens_material.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+    <dimen name="screen_percentage_05">10.4dp</dimen>
+    <dimen name="screen_percentage_10">20.8dp</dimen>
+    <dimen name="screen_percentage_15">31.2dp</dimen>
+</resources>
diff --git a/core/res/res/values-w227dp/dimens_material.xml b/core/res/res/values-w227dp/dimens_material.xml
new file mode 100644
index 0000000..eb4df8a2
--- /dev/null
+++ b/core/res/res/values-w227dp/dimens_material.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+    <dimen name="screen_percentage_05">11.35dp</dimen>
+    <dimen name="screen_percentage_10">22.7dp</dimen>
+    <dimen name="screen_percentage_15">34.05dp</dimen>
+</resources>
diff --git a/core/res/res/values-watch/styles_device_default.xml b/core/res/res/values-watch/styles_device_default.xml
new file mode 100644
index 0000000..e2261af
--- /dev/null
+++ b/core/res/res/values-watch/styles_device_default.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <style name="DialogWindowTitle.DeviceDefault" parent="DialogWindowTitle.Material">
+      <item name="maxLines">2</item>
+      <item name="shadowRadius">0</item>
+      <item name="ellipsize">end</item>
+      <item name="textAppearance">@style/TextAppearance.DeviceDefault.Title</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Body1" parent="TextAppearance.Material.Body1">
+        <item name="android:textSize">15sp</item>
+        <item name="android:fontFamily">sans-serif</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Title" parent="TextAppearance.Material.Title">
+        <item name="android:textSize">16sp</item>
+        <item name="android:fontFamily">google-sans</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.Subhead" parent="TextAppearance.Material.Subhead">
+        <item name="android:textSize">16sp</item>
+        <item name="android:fontFamily">google-sans-medium</item>
+    </style>
+</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 819857f..1380165 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9316,6 +9316,18 @@
         <attr name="canPauseRecording" format="boolean" />
     </declare-styleable>
 
+    <!-- Use <code>tv-iapp</code> as the root tag of the XML resource that describes a
+         {@link android.media.tv.interactive.TvIAppService}, which is referenced from its
+         {@link android.media.tv.interactive.TvIAppService#SERVICE_META_DATA} meta-data entry.
+         Described here are the attributes that can be included in that tag. -->
+    <declare-styleable name="TvIAppService">
+        <!-- The interactive app types that the TV interactive app service supports.
+             Reference to a string array resource that describes the supported types,
+             e.g. HbbTv, Ginga. -->
+        <attr name="supportedTypes" format="reference" />
+    </declare-styleable>
+
+
     <!-- Attributes that can be used with <code>rating-system-definition</code> tags inside of the
          XML resource that describes TV content rating of a {@link android.media.tv.TvInputService},
          which is referenced from its
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 53ed0ec..2c60fbd8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1419,6 +1419,12 @@
     <integer name="config_screenBrightnessDim">10</integer>
     <item name="config_screenBrightnessDimFloat" format="float" type="dimen">0.05</item>
 
+    <!-- If the screen brightness is already set at or below config_screenBrightnessDim, and the
+         user activity timeout expires, we still want to dim the screen slightly to indicate that
+         the device is about to go to sleep. The screen will dim by this amount in that case.
+         -->
+    <item name="config_screenBrightnessMinimumDimAmountFloat" format="float" type="dimen">0.04</item>
+
     <!-- Minimum allowable screen brightness to use in a very dark room.
          This value sets the floor for the darkest possible auto-brightness
          adjustment.  It is expected to be somewhat less than the first entry in
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2820f86..a2dadb9 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3304,6 +3304,7 @@
     <public name="requiredSplitTypes" />
     <public name="splitTypes" />
     <public name="canDisplayOnRemoteDevices" />
+    <public name="supportedTypes" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01de0000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b0e4b1ff..12b1842 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2049,6 +2049,7 @@
   <java-symbol type="dimen" name="config_screenBrightnessSettingDefaultFloat" />
   <java-symbol type="dimen" name="config_screenBrightnessDozeFloat" />
   <java-symbol type="dimen" name="config_screenBrightnessDimFloat" />
+  <java-symbol type="dimen" name="config_screenBrightnessMinimumDimAmountFloat" />
   <java-symbol type="integer" name="config_screenBrightnessDark" />
   <java-symbol type="integer" name="config_screenBrightnessDim" />
   <java-symbol type="integer" name="config_screenBrightnessDoze" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 1f27063..5db6a3e 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -309,18 +309,22 @@
         final int numOfConfig = activity.mNumOfConfigChanges;
 
         final Configuration processConfigLandscape = new Configuration();
+        processConfigLandscape.orientation = ORIENTATION_LANDSCAPE;
         processConfigLandscape.windowConfiguration.setBounds(new Rect(0, 0, 100, 60));
         processConfigLandscape.seq = BASE_SEQ + 1;
 
         final Configuration activityConfigLandscape = new Configuration();
+        activityConfigLandscape.orientation = ORIENTATION_LANDSCAPE;
         activityConfigLandscape.windowConfiguration.setBounds(new Rect(0, 0, 100, 50));
         activityConfigLandscape.seq = BASE_SEQ + 2;
 
         final Configuration processConfigPortrait = new Configuration();
+        processConfigPortrait.orientation = ORIENTATION_PORTRAIT;
         processConfigPortrait.windowConfiguration.setBounds(new Rect(0, 0, 60, 100));
         processConfigPortrait.seq = BASE_SEQ + 3;
 
         final Configuration activityConfigPortrait = new Configuration();
+        activityConfigPortrait.orientation = ORIENTATION_PORTRAIT;
         activityConfigPortrait.windowConfiguration.setBounds(new Rect(0, 0, 50, 100));
         activityConfigPortrait.seq = BASE_SEQ + 4;
 
@@ -348,7 +352,8 @@
         assertEquals(activityConfigPortrait.windowConfiguration.getBounds(), bounds);
 
         // Ensure that Activity#onConfigurationChanged() not be called because the changes in
-        // WindowConfiguration shouldn't be reported.
+        // WindowConfiguration shouldn't be reported, and we only apply the latest Configuration
+        // update in transaction.
         assertEquals(numOfConfig, activity.mNumOfConfigChanges);
     }
 
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index df0c64c..207671e 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -453,7 +453,8 @@
                 boolean b2, boolean b3, Configuration configuration,
                 CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1,
                 AutofillOptions ao, ContentCaptureOptions co, long[] disableCompatChanges,
-                SharedMemory serializedSystemFontMap)
+                SharedMemory serializedSystemFontMap,
+                long startRequestedElapsedTime, long startRequestedUptime)
                 throws RemoteException {
         }
 
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index 0e78f87..33c6a4b 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.os.SystemClock;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.View;
@@ -299,7 +300,8 @@
             SparseArray<List<AccessibilityWindowInfo>> allWindows = new SparseArray<>();
             allWindows.put(Display.DEFAULT_DISPLAY, windowsIn1);
             allWindows.put(SECONDARY_DISPLAY_ID, windowsIn2);
-            mAccessibilityCache.setWindowsOnAllDisplays(allWindows);
+            final long populationTimeStamp = SystemClock.uptimeMillis();
+            mAccessibilityCache.setWindowsOnAllDisplays(allWindows, populationTimeStamp);
             // Gets windows at default display.
             windowsOut1 = getWindowsByDisplay(Display.DEFAULT_DISPLAY);
             window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1);
@@ -339,6 +341,46 @@
     }
 
     @Test
+    public void setInvalidWindowsAfterWindowsChangedEvent_notInCache() {
+        final AccessibilityEvent event = new AccessibilityEvent(
+                AccessibilityEvent.TYPE_WINDOWS_CHANGED);
+        final long eventTime = 1000L;
+        event.setEventTime(eventTime);
+        mAccessibilityCache.onAccessibilityEvent(event);
+
+        final AccessibilityWindowInfo windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1,
+                SPECIFIC_WINDOW_LAYER);
+        List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1);
+        setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn, eventTime - 10);
+
+        try {
+            assertNull(getWindowsByDisplay(Display.DEFAULT_DISPLAY));
+        } finally {
+            windowInfo1.recycle();
+        }
+    }
+
+    @Test
+    public void setInvalidWindowsAfterStateChangedEvent_notInCache() {
+        final AccessibilityEvent event = new AccessibilityEvent(
+                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+        final long eventTime = 1000L;
+        event.setEventTime(eventTime);
+        mAccessibilityCache.onAccessibilityEvent(event);
+
+        final AccessibilityWindowInfo windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1,
+                SPECIFIC_WINDOW_LAYER);
+        List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1);
+        setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn, eventTime - 10);
+
+        try {
+            assertNull(getWindowsByDisplay(Display.DEFAULT_DISPLAY));
+        } finally {
+            windowInfo1.recycle();
+        }
+    }
+
+    @Test
     public void addWindowThenStateChangedEvent_noLongerInCache() {
         putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY,
                 DEFAULT_WINDOW_LAYER);
@@ -1063,9 +1105,14 @@
     }
 
     private void setWindowsByDisplay(int displayId, List<AccessibilityWindowInfo> windows) {
+        setWindowsByDisplay(displayId, windows, SystemClock.uptimeMillis());
+    }
+
+    private void setWindowsByDisplay(int displayId, List<AccessibilityWindowInfo> windows,
+            long populationTimeStamp) {
         SparseArray<List<AccessibilityWindowInfo>> allWindows = new SparseArray<>();
         allWindows.put(displayId, windows);
-        mAccessibilityCache.setWindowsOnAllDisplays(allWindows);
+        mAccessibilityCache.setWindowsOnAllDisplays(allWindows, populationTimeStamp);
     }
 
     private List<AccessibilityWindowInfo> getWindowsByDisplay(int displayId) {
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index 8b060ff..8ec33bf 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.os;
 
-import static android.os.BatteryStats.STATS_SINCE_CHARGED;
 import static android.os.BatteryStats.Uid.NUM_PROCESS_STATE;
 import static android.os.BatteryStats.Uid.PROCESS_STATE_BACKGROUND;
 import static android.os.BatteryStats.Uid.PROCESS_STATE_CACHED;
@@ -25,8 +24,10 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -115,14 +116,17 @@
                 {989834, 384098, 98483, 23809, 4984},
                 {4859048, 348903, 4578967, 5973894, 298549}
         };
+
+        final long[] timeInFreqs = new long[NUM_CPU_FREQS];
+
         for (int i = 0; i < testUids.length; ++i) {
             mockKernelSingleUidTimeReader(testUids[i], cpuTimes[i]);
 
             // Verify there are no cpu times initially.
             final BatteryStats.Uid u = mBatteryStatsImpl.getUidStatsLocked(testUids[i]);
             for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
-                assertNull(u.getCpuFreqTimes(STATS_SINCE_CHARGED, procState));
-                assertNull(u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                assertFalse(u.getCpuFreqTimes(timeInFreqs, procState));
+                assertFalse(u.getScreenOffCpuFreqTimes(timeInFreqs, procState));
             }
         }
         addPendingUids(testUids, testProcStates);
@@ -134,13 +138,13 @@
         for (int i = 0; i < testUids.length; ++i) {
             final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
             for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
+                final boolean hasTimeInFreq = u.getCpuFreqTimes(timeInFreqs, procState);
                 if (procState == testProcStates[i]) {
-                    assertArrayEquals("Uid=" + testUids[i], cpuTimes[i],
-                            u.getCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                    assertArrayEquals("Uid=" + testUids[i], cpuTimes[i], timeInFreqs);
                 } else {
-                    assertNull(u.getCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                    assertFalse(hasTimeInFreq);
                 }
-                assertNull(u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                assertFalse(u.getScreenOffCpuFreqTimes(timeInFreqs, procState));
             }
         }
 
@@ -172,12 +176,12 @@
                     for (int j = 0; j < expectedCpuTimes.length; ++j) {
                         expectedCpuTimes[j] += delta1[i][j];
                     }
-                    assertArrayEquals("Uid=" + testUids[i], expectedCpuTimes,
-                            u.getCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                    assertTrue(u.getCpuFreqTimes(timeInFreqs, procState));
+                    assertArrayEquals("Uid=" + testUids[i], expectedCpuTimes, timeInFreqs);
                 } else {
-                    assertNull(u.getCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                    assertFalse(u.getCpuFreqTimes(timeInFreqs, procState));
                 }
-                assertNull(u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                assertFalse(u.getScreenOffCpuFreqTimes(timeInFreqs, procState));
             }
         }
 
@@ -214,13 +218,13 @@
                     for (int j = 0; j < expectedCpuTimes.length; ++j) {
                         expectedCpuTimes[j] += delta1[i][j] + delta2[i][j];
                     }
-                    assertArrayEquals("Uid=" + testUids[i], expectedCpuTimes,
-                            u.getCpuFreqTimes(STATS_SINCE_CHARGED, procState));
-                    assertArrayEquals("Uid=" + testUids[i], delta2[i],
-                            u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                    assertTrue(u.getCpuFreqTimes(timeInFreqs, procState));
+                    assertArrayEquals("Uid=" + testUids[i], expectedCpuTimes, timeInFreqs);
+                    assertTrue(u.getScreenOffCpuFreqTimes(timeInFreqs, procState));
+                    assertArrayEquals("Uid=" + testUids[i], delta2[i], timeInFreqs);
                 } else {
-                    assertNull(u.getCpuFreqTimes(STATS_SINCE_CHARGED, procState));
-                    assertNull(u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                    assertFalse(u.getCpuFreqTimes(timeInFreqs, procState));
+                    assertFalse(u.getScreenOffCpuFreqTimes(timeInFreqs, procState));
                 }
             }
         }
@@ -262,18 +266,18 @@
                         expectedCpuTimes[j] += delta1[i][j] + delta2[i][j] + delta3[i][j]
                                 + (testUids[i] == parentUid ? isolatedUidCpuTimes[j] : 0);
                     }
-                    assertArrayEquals("Uid=" + testUids[i], expectedCpuTimes,
-                            u.getCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                    assertTrue(u.getCpuFreqTimes(timeInFreqs, procState));
+                    assertArrayEquals("Uid=" + testUids[i], expectedCpuTimes, timeInFreqs);
                     long[] expectedScreenOffTimes = delta2[i].clone();
                     for (int j = 0; j < expectedScreenOffTimes.length; ++j) {
                         expectedScreenOffTimes[j] += delta3[i][j]
                                 + (testUids[i] == parentUid ? isolatedUidCpuTimes[j] : 0);
                     }
-                    assertArrayEquals("Uid=" + testUids[i], expectedScreenOffTimes,
-                            u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                    assertTrue(u.getScreenOffCpuFreqTimes(timeInFreqs, procState));
+                    assertArrayEquals("Uid=" + testUids[i], expectedScreenOffTimes, timeInFreqs);
                 } else {
-                    assertNull(u.getCpuFreqTimes(STATS_SINCE_CHARGED, procState));
-                    assertNull(u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                    assertFalse(u.getCpuFreqTimes(timeInFreqs, procState));
+                    assertFalse(u.getScreenOffCpuFreqTimes(timeInFreqs, procState));
                 }
             }
         }
@@ -329,16 +333,19 @@
         mBatteryStatsImpl.copyFromAllUidsCpuTimes(true, false);
 
         verifyNoPendingUids();
+
+        final long[] timeInFreqs = new long[NUM_CPU_FREQS];
+
         for (int i = 0; i < testUids.length; ++i) {
             final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
             for (int procState = 0; procState < NUM_PROCESS_STATE; ++procState) {
                 if (procState == testProcStates[i]) {
-                    assertArrayEquals("Uid=" + testUids[i], expectedCpuTimes[i],
-                            u.getCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                    assertTrue(u.getCpuFreqTimes(timeInFreqs, procState));
+                    assertArrayEquals("Uid=" + testUids[i], expectedCpuTimes[i], timeInFreqs);
                 } else {
-                    assertNull(u.getCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                    assertFalse(u.getCpuFreqTimes(timeInFreqs, procState));
                 }
-                assertNull(u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED, procState));
+                assertFalse(u.getScreenOffCpuFreqTimes(timeInFreqs, procState));
             }
         }
     }
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 055fc71..db63e6e 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -160,6 +160,34 @@
         verify(callback).onStateChanged(eq(mService.getBaseState()));
     }
 
+    @Test
+    public void verifyDeviceStateRequestCallbacksCalled() {
+        DeviceStateRequest.Callback callback = mock(TestDeviceStateRequestCallback.class);
+
+        DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
+        mDeviceStateManagerGlobal.requestState(request,
+                ConcurrentUtils.DIRECT_EXECUTOR /* executor */,
+                callback /* callback */);
+
+        verify(callback).onRequestActivated(eq(request));
+        Mockito.reset(callback);
+
+        mDeviceStateManagerGlobal.cancelRequest(request);
+
+        verify(callback).onRequestCanceled(eq(request));
+    }
+
+    public static class TestDeviceStateRequestCallback implements DeviceStateRequest.Callback {
+        @Override
+        public void onRequestActivated(DeviceStateRequest request) { }
+
+        @Override
+        public void onRequestCanceled(DeviceStateRequest request) { }
+
+        @Override
+        public void onRequestSuspended(DeviceStateRequest request) { }
+    }
+
     private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
         public static final class Request {
             public final IBinder token;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index be1e2b2..88228f2 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-
 // Sysconfig files
 
 package {
@@ -119,6 +118,13 @@
 }
 
 prebuilt_etc {
+    name: "privapp_whitelist_com.android.intentresolver",
+    sub_dir: "permissions",
+    src: "com.android.intentresolver.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
     name: "privapp_whitelist_com.android.launcher3",
     system_ext_specific: true,
     sub_dir: "permissions",
diff --git a/packages/SystemUI/res/drawable/assist_orb_navbar_scrim.xml b/data/etc/com.android.intentresolver.xml
similarity index 66%
rename from packages/SystemUI/res/drawable/assist_orb_navbar_scrim.xml
rename to data/etc/com.android.intentresolver.xml
index 52ed76d..0f1c467 100644
--- a/packages/SystemUI/res/drawable/assist_orb_navbar_scrim.xml
+++ b/data/etc/com.android.intentresolver.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-
 <!--
-  ~ Copyright (C) 2014 The Android Open Source Project
+  ~ 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.
@@ -15,11 +14,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <gradient
-            android:type="linear"
-            android:angle="90"
-            android:startColor="#33000000"
-            android:endColor="#00000000" />
-</shape>
\ No newline at end of file
+<permissions>
+    <privapp-permissions package="com.android.intentresolver">
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
index 194b633..89d7a40 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
@@ -39,6 +39,8 @@
 
     final Transformation mTransformation = new Transformation();
     final float[] mMatrix = new float[9];
+    final float[] mVecs = new float[4];
+    final Rect mRect = new Rect();
     private boolean mIsFirstFrame = true;
 
     TaskFragmentAnimationAdapter(@NonNull Animation animation,
@@ -76,6 +78,22 @@
                 mTarget.localBounds.left, mTarget.localBounds.top);
         t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
         t.setAlpha(mLeash, mTransformation.getAlpha());
+
+        // Open/close animation may scale up the surface. Apply an inverse scale to the window crop
+        // so that it will not be covering other windows.
+        mVecs[1] = mVecs[2] = 0;
+        mVecs[0] = mVecs[3] = 1;
+        mTransformation.getMatrix().mapVectors(mVecs);
+        mVecs[0] = 1.f / mVecs[0];
+        mVecs[3] = 1.f / mVecs[3];
+        final Rect clipRect = mTarget.localBounds;
+        mRect.left = (int) (clipRect.left * mVecs[0] + 0.5f);
+        mRect.right = (int) (clipRect.right * mVecs[0] + 0.5f);
+        mRect.top = (int) (clipRect.top * mVecs[3] + 0.5f);
+        mRect.bottom = (int) (clipRect.bottom * mVecs[3] + 0.5f);
+        mRect.offsetTo(Math.round(mTarget.localBounds.width() * (1 - mVecs[0]) / 2.f),
+                Math.round(mTarget.localBounds.height() * (1 - mVecs[3]) / 2.f));
+        t.setWindowCrop(mLeash, mRect);
     }
 
     /** Called after animation finished. */
@@ -157,8 +175,6 @@
      * Should be used for the animation of the {@link RemoteAnimationTarget} that has size change.
      */
     static class BoundsChangeAdapter extends TaskFragmentAnimationAdapter {
-        private final float[] mVecs = new float[4];
-        private final Rect mRect = new Rect();
 
         BoundsChangeAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
             super(animation, target);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 32d447e..fe9ce97 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -18,9 +18,12 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static androidx.window.common.DisplayFeature.COMMON_STATE_FLAT;
+import static androidx.window.common.DisplayFeature.COMMON_STATE_HALF_OPENED;
 import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
 import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Rect;
@@ -119,22 +122,45 @@
         return !mWindowLayoutChangeListeners.isEmpty();
     }
 
-    private int getFeatureState(DisplayFeature feature) {
+    /**
+     * Calculate the {@link DisplayFeature.State} from the feature or the device posture producer.
+     * If the given {@link DisplayFeature.State} is not valid then {@code null} will be returned.
+     * The {@link FoldingFeature} should be ignored in the case of an invalid
+     * {@link DisplayFeature.State}.
+     *
+     * @param feature a {@link DisplayFeature} to provide the feature state if present.
+     * @return {@link DisplayFeature.State} of the hinge if present or the state from the posture
+     * produce if present.
+     */
+    @Nullable
+    private Integer getFeatureState(DisplayFeature feature) {
         Integer featureState = feature.getState();
         Optional<Integer> posture = mDevicePostureProducer.getData();
-        int fallbackPosture = posture.orElse(DisplayFeature.COMMON_STATE_FLAT);
-        int displayFeatureState = featureState == null ? fallbackPosture : featureState;
-        return convertToExtensionState(displayFeatureState);
+        Integer state = featureState == null ? posture.orElse(null) : featureState;
+        return convertToExtensionState(state);
     }
 
-    private int convertToExtensionState(int state) {
-        switch (state) {
-            case DisplayFeature.COMMON_STATE_FLAT:
-                return FoldingFeature.STATE_FLAT;
-            case DisplayFeature.COMMON_STATE_HALF_OPENED:
-                return FoldingFeature.STATE_HALF_OPENED;
+    /**
+     * A convenience method to translate from the common feature state to the extensions feature
+     * state.  More specifically, translates from {@link DisplayFeature.State} to
+     * {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED}. If it is not
+     * possible to translate, then we will return a {@code null} value.
+     *
+     * @param state if it matches a value in {@link DisplayFeature.State}, {@code null} otherwise.
+     * @return a {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED} if
+     * the given state matches a value in {@link DisplayFeature.State} and {@code null} otherwise.
+     */
+    @Nullable
+    private Integer convertToExtensionState(@Nullable Integer state) {
+        if (state == null) { // The null check avoids a NullPointerException.
+            return null;
+        } else if (state == COMMON_STATE_FLAT) {
+            return FoldingFeature.STATE_FLAT;
+        } else if (state == COMMON_STATE_HALF_OPENED) {
+            return FoldingFeature.STATE_HALF_OPENED;
+        } else {
+            return null;
         }
-        return FoldingFeature.STATE_FLAT;
     }
 
     private void onDisplayFeaturesChanged() {
@@ -151,6 +177,25 @@
         return new WindowLayoutInfo(displayFeatures);
     }
 
+    /**
+     * Translate from the {@link DisplayFeature} to
+     * {@link androidx.window.extensions.layout.DisplayFeature} for a given {@link Activity}. If a
+     * {@link DisplayFeature} is not valid then it will be omitted.
+     *
+     * For a {@link FoldingFeature} the bounds are localized into the {@link Activity} window
+     * coordinate space and the state is calculated either from {@link DisplayFeature#getState()} or
+     * {@link #mDisplayFeatureProducer}. The state from {@link #mDisplayFeatureProducer} may not be
+     * valid since {@link #mDisplayFeatureProducer} is a general state controller. If the state is
+     * not valid, the {@link FoldingFeature} is omitted from the {@link List} of
+     * {@link androidx.window.extensions.layout.DisplayFeature}. If the bounds are not valid,
+     * constructing a {@link FoldingFeature} will throw an {@link IllegalArgumentException} since
+     * this can cause negative UI effects down stream.
+     *
+     * @param activity a proxy for the {@link android.view.Window} that contains the
+     * {@link androidx.window.extensions.layout.DisplayFeature}.
+     * @return a {@link List} of valid {@link androidx.window.extensions.layout.DisplayFeature} that
+     * are within the {@link android.view.Window} of the {@link Activity}
+     */
     private List<androidx.window.extensions.layout.DisplayFeature> getDisplayFeatures(
             @NonNull Activity activity) {
         List<androidx.window.extensions.layout.DisplayFeature> features = new ArrayList<>();
@@ -170,6 +215,10 @@
         if (storedFeatures.isPresent()) {
 
             for (DisplayFeature baseFeature : storedFeatures.get()) {
+                Integer state = getFeatureState(baseFeature);
+                if (state == null) {
+                    continue;
+                }
                 Rect featureRect = baseFeature.getRect();
                 rotateRectToDisplayRotation(displayId, featureRect);
                 transformToWindowSpaceRect(activity, featureRect);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index f567877..c3ce362 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -74,7 +74,7 @@
             Optional<PipTouchHandler> pipTouchHandlerOptional,
             FullscreenTaskListener fullscreenTaskListener,
             Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
-            Optional<Optional<FreeformTaskListener>> freeformTaskListenerOptional,
+            Optional<FreeformTaskListener> freeformTaskListenerOptional,
             Optional<RecentTasksController> recentTasks,
             Transitions transitions,
             StartingWindowController startingWindow,
@@ -90,7 +90,7 @@
         mFullscreenTaskListener = fullscreenTaskListener;
         mPipTouchHandlerOptional = pipTouchHandlerOptional;
         mFullscreenUnfoldController = fullscreenUnfoldTransitionController;
-        mFreeformTaskListenerOptional = freeformTaskListenerOptional.flatMap(f -> f);
+        mFreeformTaskListenerOptional = freeformTaskListenerOptional;
         mRecentTasks = recentTasks;
         mTransitions = transitions;
         mMainExecutor = mainExecutor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/DynamicOverride.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/DynamicOverride.java
new file mode 100644
index 0000000..806f795
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/DynamicOverride.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.dagger;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/**
+ * This is a qualifier that Shell uses to workaround an issue with providing nullable optionals
+ * which are by default unbound.
+ *
+ * For example, ideally we would have this scenario:
+ * BaseModule:
+ *   @BindsOptionalOf
+ *   abstract Optional<Interface> optionalInterface();
+ *
+ * SpecializedModule:
+ *   @Provides
+ *   static Interface providesInterface() {
+ *       return new InterfaceImpl();
+ *   }
+ *
+ * However, if the interface is supposed to be provided dynamically, then Dagger is not able to bind
+ * the optional interface to a null instance, and @BindsOptionalOf does not support @Nullable
+ * instances of the interface provided by the specialized module.
+ *
+ * For example, this does not work:
+ * BaseModule:
+ *   @BindsOptionalOf
+ *   abstract Optional<Interface> optionalInterface();
+ *
+ * SpecializedModule:
+ *   @Provides
+ *   static Interface providesInterface() {
+ *       if (systemSupportsInterfaceFeature) {
+ *           return new InterfaceImpl();
+ *       } else {
+ *           return null;
+ *       }
+ *   }
+ *
+ * To workaround this, we can instead upstream the check (assuming it can be upstreamed into the
+ * base module), and then always provide a non-null instance in the specialized module.
+ *
+ * For example:
+ * BaseModule:
+ *   @BindsOptionalOf
+ *   @DynamicOverride
+ *   abstract Interface dynamicInterface();
+ *
+ *   @Provides
+ *   static Optional<Interface> providesOptionalInterface(
+ *           @DynamicOverride Optional<Interface> interface) {
+ *       if (systemSupportsInterfaceFeature) {
+ *           return interface;
+ *       }
+ *       return Optional.empty();
+ *   }
+ *
+ * SpecializedModule:
+ *   @Provides
+ *   @DynamicOverride
+ *   static Interface providesInterface() {
+ *       return new InterfaceImpl();
+ *   }
+ *
+ * This is also useful in cases where there needs to be a default implementation in the base module
+ * which is also overridable in the specialized module.  This isn't generally recommended, but
+ * due to the nature of Shell modules being referenced from a number of various projects, this
+ * can be useful for *required* components that
+ * 1) clearly identifies which are intended for overriding in the base module, and
+ * 2) allows us to declare a default implementation in the base module, without having to force
+ *    every SysUI impl to explicitly provide it (if a large number of them share the default impl)
+ *
+ * For example, this uses the same setup as above, but the interface provided (if bound) is used
+ * otherwise the default is created:
+ *   @BindsOptionalOf
+ *   @DynamicOverride
+ *   abstract Interface dynamicInterface();
+ *
+ *   @Provides
+ *   static Optional<Interface> providesOptionalInterface(
+ *           @DynamicOverride Optional<Interface> overrideInterfaceImpl) {
+ *       if (overrideInterfaceImpl.isPresent()) {
+ *           return overrideInterfaceImpl.get();
+ *       }
+ *       return new DefaultImpl();
+ *   }
+ *
+ * SpecializedModule:
+ *   @Provides
+ *   @DynamicOverride
+ *   static Interface providesInterface() {
+ *       return new SuperSpecialImpl();
+ *   }
+ */
+@Documented
+@Inherited
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DynamicOverride {}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index 6997d60..15bfeb2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -16,25 +16,16 @@
 
 package com.android.wm.shell.dagger;
 
-import android.animation.AnimationHandler;
-import android.content.Context;
 import android.view.IWindowManager;
 
-import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.SystemWindows;
-import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
 import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
 import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
 import com.android.wm.shell.startingsurface.tv.TvStartingWindowTypeAlgorithm;
-import com.android.wm.shell.transition.Transitions;
 
 import dagger.Module;
 import dagger.Provides;
@@ -51,42 +42,12 @@
 public class TvWMShellModule {
 
     //
-    // Internal common - Components used internally by multiple shell features
-    //
-
-    @WMSingleton
-    @Provides
-    static DisplayImeController provideDisplayImeController(IWindowManager wmService,
-            DisplayController displayController, DisplayInsetsController displayInsetsController,
-            @ShellMainThread ShellExecutor mainExecutor, TransactionPool transactionPool) {
-        return new DisplayImeController(wmService, displayController, displayInsetsController,
-                mainExecutor, transactionPool);
-    }
-
-    //
-    // Split/multiwindow
-    //
-
-    @WMSingleton
-    @Provides
-    static LegacySplitScreenController provideSplitScreen(Context context,
-            DisplayController displayController, SystemWindows systemWindows,
-            DisplayImeController displayImeController, TransactionPool transactionPool,
-            ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
-            TaskStackListenerImpl taskStackListener, Transitions transitions,
-            @ShellMainThread ShellExecutor mainExecutor,
-            @ChoreographerSfVsync AnimationHandler sfVsyncAnimationHandler) {
-        return new LegacySplitScreenController(context, displayController, systemWindows,
-                displayImeController, transactionPool, shellTaskOrganizer, syncQueue,
-                taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler);
-    }
-
-    //
     // Starting Windows (Splash Screen)
     //
 
     @WMSingleton
     @Provides
+    @DynamicOverride
     static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm() {
         return new TvStartingWindowTypeAlgorithm();
     };
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index ac2e448..3f5b70b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -16,16 +16,16 @@
 
 package com.android.wm.shell.dagger;
 
+import static com.android.wm.shell.onehanded.OneHandedController.SUPPORT_ONE_HANDED_MODE;
+
 import android.app.ActivityTaskManager;
 import android.content.Context;
-import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.os.Handler;
+import android.os.SystemProperties;
 import android.view.IWindowManager;
-import android.view.WindowManager;
 
 import com.android.internal.logging.UiEventLogger;
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.RootDisplayAreaOrganizer;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
@@ -77,23 +77,19 @@
 import com.android.wm.shell.sizecompatui.SizeCompatUIController;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.splitscreen.StageTaskUnfoldController;
 import com.android.wm.shell.startingsurface.StartingSurface;
 import com.android.wm.shell.startingsurface.StartingWindowController;
 import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
+import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
 import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
 import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelperController;
 import com.android.wm.shell.transition.ShellTransitions;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
-import com.android.wm.shell.unfold.UnfoldBackgroundController;
 
 import java.util.Optional;
 
-import javax.inject.Provider;
-
 import dagger.BindsOptionalOf;
-import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
 
@@ -128,6 +124,28 @@
         return new DisplayInsetsController(wmService, displayController, mainExecutor);
     }
 
+    // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+    @BindsOptionalOf
+    @DynamicOverride
+    abstract DisplayImeController optionalDisplayImeController();
+
+    @WMSingleton
+    @Provides
+    static DisplayImeController provideDisplayImeController(
+            @DynamicOverride Optional<DisplayImeController> overrideDisplayImeController,
+            IWindowManager wmService,
+            DisplayController displayController,
+            DisplayInsetsController displayInsetsController,
+            @ShellMainThread ShellExecutor mainExecutor,
+            TransactionPool transactionPool
+    ) {
+        if (overrideDisplayImeController.isPresent()) {
+            return overrideDisplayImeController.get();
+        }
+        return new DisplayImeController(wmService, displayController, displayInsetsController,
+                mainExecutor, transactionPool);
+    }
+
     @WMSingleton
     @Provides
     static DisplayLayout provideDisplayLayout() {
@@ -202,7 +220,7 @@
     }
 
     //
-    // Bubbles
+    // Bubbles (optional feature)
     //
 
     @WMSingleton
@@ -211,27 +229,8 @@
         return bubbleController.map((controller) -> controller.asBubbles());
     }
 
-    // Note: Handler needed for LauncherApps.register
-    @WMSingleton
-    @Provides
-    static Optional<BubbleController> provideBubbleController(Context context,
-            FloatingContentCoordinator floatingContentCoordinator,
-            IStatusBarService statusBarService,
-            WindowManager windowManager,
-            WindowManagerShellWrapper windowManagerShellWrapper,
-            LauncherApps launcherApps,
-            TaskStackListenerImpl taskStackListener,
-            UiEventLogger uiEventLogger,
-            ShellTaskOrganizer organizer,
-            DisplayController displayController,
-            @ShellMainThread ShellExecutor mainExecutor,
-            @ShellMainThread Handler mainHandler,
-            SyncTransactionQueue syncQueue) {
-        return Optional.of(BubbleController.create(context, null /* synchronizer */,
-                floatingContentCoordinator, statusBarService, windowManager,
-                windowManagerShellWrapper, launcherApps, taskStackListener,
-                uiEventLogger, organizer, displayController, mainExecutor, mainHandler, syncQueue));
-    }
+    @BindsOptionalOf
+    abstract BubbleController optionalBubblesController();
 
     //
     // Fullscreen
@@ -252,59 +251,45 @@
     // Unfold transition
     //
 
+    @BindsOptionalOf
+    abstract ShellUnfoldProgressProvider optionalShellUnfoldProgressProvider();
+
+    // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+    @BindsOptionalOf
+    @DynamicOverride
+    abstract FullscreenUnfoldController optionalFullscreenUnfoldController();
+
     @WMSingleton
     @Provides
     static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController(
-            Context context,
-            Optional<ShellUnfoldProgressProvider> progressProvider,
-            Lazy<UnfoldBackgroundController> unfoldBackgroundController,
-            DisplayInsetsController displayInsetsController,
-            @ShellMainThread ShellExecutor mainExecutor
-    ) {
-        return progressProvider.map(shellUnfoldTransitionProgressProvider ->
-                new FullscreenUnfoldController(context, mainExecutor,
-                        unfoldBackgroundController.get(), shellUnfoldTransitionProgressProvider,
-                        displayInsetsController));
-    }
-
-    @Provides
-    static Optional<StageTaskUnfoldController> provideStageTaskUnfoldController(
-            Optional<ShellUnfoldProgressProvider> progressProvider,
-            Context context,
-            TransactionPool transactionPool,
-            Lazy<UnfoldBackgroundController> unfoldBackgroundController,
-            DisplayInsetsController displayInsetsController,
-            @ShellMainThread ShellExecutor mainExecutor
-    ) {
-        return progressProvider.map(shellUnfoldTransitionProgressProvider ->
-                new StageTaskUnfoldController(
-                        context,
-                        transactionPool,
-                        shellUnfoldTransitionProgressProvider,
-                        displayInsetsController,
-                        unfoldBackgroundController.get(),
-                        mainExecutor
-                ));
-    }
-
-    @WMSingleton
-    @Provides
-    static UnfoldBackgroundController provideUnfoldBackgroundController(
-            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
-            Context context
-    ) {
-        return new UnfoldBackgroundController(
-                context,
-                rootTaskDisplayAreaOrganizer
-        );
+            @DynamicOverride Optional<FullscreenUnfoldController> fullscreenUnfoldController,
+            Optional<ShellUnfoldProgressProvider> progressProvider) {
+        if (progressProvider.isPresent()
+                && progressProvider.get() != ShellUnfoldProgressProvider.NO_PROVIDER) {
+            return fullscreenUnfoldController;
+        }
+        return Optional.empty();
     }
 
     //
     // Freeform (optional feature)
     //
 
+    // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
     @BindsOptionalOf
-    abstract Optional<FreeformTaskListener> optionalFreeformTaskListener();
+    @DynamicOverride
+    abstract FreeformTaskListener optionalFreeformTaskListener();
+
+    @WMSingleton
+    @Provides
+    static Optional<FreeformTaskListener> provideFreeformTaskListener(
+            @DynamicOverride Optional<FreeformTaskListener> freeformTaskListener,
+            Context context) {
+        if (FreeformTaskListener.isFreeformEnabled(context)) {
+            return freeformTaskListener;
+        }
+        return Optional.empty();
+    }
 
     //
     // Hide display cutout
@@ -335,20 +320,22 @@
         return oneHandedController.map((controller) -> controller.asOneHanded());
     }
 
-    // Needs the shell main handler for ContentObserver callbacks
+    // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+    @BindsOptionalOf
+    @DynamicOverride
+    abstract OneHandedController optionalOneHandedController();
+
     @WMSingleton
     @Provides
-    static Optional<OneHandedController> provideOneHandedController(Context context,
-            WindowManager windowManager, DisplayController displayController,
-            DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
-            UiEventLogger uiEventLogger,
-            @ShellMainThread ShellExecutor mainExecutor,
-            @ShellMainThread Handler mainHandler) {
-        return Optional.ofNullable(OneHandedController.create(context, windowManager,
-                displayController, displayLayout, taskStackListener, uiEventLogger, mainExecutor,
-                mainHandler));
+    static Optional<OneHandedController> providesOneHandedController(
+            @DynamicOverride Optional<OneHandedController> oneHandedController) {
+        if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+            return oneHandedController;
+        }
+        return Optional.empty();
     }
 
+
     //
     // Task to Surface communication
     //
@@ -366,15 +353,6 @@
         return Optional.ofNullable(new TaskSurfaceHelperController(taskOrganizer, mainExecutor));
     }
 
-    @WMSingleton
-    @Provides
-    static Optional<DisplayAreaHelper> provideDisplayAreaHelper(
-            @ShellMainThread ShellExecutor mainExecutor,
-            RootDisplayAreaOrganizer rootDisplayAreaOrganizer) {
-        return Optional.ofNullable(new DisplayAreaHelperController(mainExecutor,
-                rootDisplayAreaOrganizer));
-    }
-
     //
     // Pip (optional feature)
     //
@@ -460,7 +438,7 @@
     }
 
     //
-    // Split/multiwindow
+    // Display areas
     //
 
     @WMSingleton
@@ -479,31 +457,38 @@
 
     @WMSingleton
     @Provides
+    static Optional<DisplayAreaHelper> provideDisplayAreaHelper(
+            @ShellMainThread ShellExecutor mainExecutor,
+            RootDisplayAreaOrganizer rootDisplayAreaOrganizer) {
+        return Optional.of(new DisplayAreaHelperController(mainExecutor,
+                rootDisplayAreaOrganizer));
+    }
+
+    //
+    // Splitscreen (optional feature)
+    //
+
+    @WMSingleton
+    @Provides
     static Optional<SplitScreen> provideSplitScreen(
             Optional<SplitScreenController> splitScreenController) {
         return splitScreenController.map((controller) -> controller.asSplitScreen());
     }
 
+    // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+    @BindsOptionalOf
+    @DynamicOverride
+    abstract SplitScreenController optionalSplitScreenController();
+
     @WMSingleton
     @Provides
-    static Optional<SplitScreenController> provideSplitScreenController(
-            ShellTaskOrganizer shellTaskOrganizer,
-            SyncTransactionQueue syncQueue, Context context,
-            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
-            @ShellMainThread ShellExecutor mainExecutor,
-            DisplayImeController displayImeController,
-            DisplayInsetsController displayInsetsController, Transitions transitions,
-            TransactionPool transactionPool, IconProvider iconProvider,
-            Optional<RecentTasksController> recentTasks,
-            Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
+    static Optional<SplitScreenController> providesSplitScreenController(
+            @DynamicOverride Optional<SplitScreenController> splitscreenController,
+            Context context) {
         if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
-            return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
-                    rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
-                    displayInsetsController, transitions, transactionPool, iconProvider,
-                    recentTasks, stageTaskUnfoldControllerProvider));
-        } else {
-            return Optional.empty();
+            return splitscreenController;
         }
+        return Optional.empty();
     }
 
     // Legacy split (optional feature)
@@ -529,7 +514,9 @@
     @BindsOptionalOf
     abstract AppPairsController optionalAppPairs();
 
+    //
     // Starting window
+    //
 
     @WMSingleton
     @Provides
@@ -548,6 +535,23 @@
                 startingWindowTypeAlgorithm, iconProvider, pool);
     }
 
+    // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+    @BindsOptionalOf
+    @DynamicOverride
+    abstract StartingWindowTypeAlgorithm optionalStartingWindowTypeAlgorithm();
+
+    @WMSingleton
+    @Provides
+    static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm(
+            @DynamicOverride Optional<StartingWindowTypeAlgorithm> startingWindowTypeAlgorithm
+    ) {
+        if (startingWindowTypeAlgorithm.isPresent()) {
+            return startingWindowTypeAlgorithm.get();
+        }
+        // Default to phone starting window type
+        return new PhoneStartingWindowTypeAlgorithm();
+    }
+
     //
     // Task view factory
     //
@@ -591,7 +595,7 @@
             Optional<PipTouchHandler> pipTouchHandlerOptional,
             FullscreenTaskListener fullscreenTaskListener,
             Optional<FullscreenUnfoldController> appUnfoldTransitionController,
-            Optional<Optional<FreeformTaskListener>> freeformTaskListener,
+            Optional<FreeformTaskListener> freeformTaskListener,
             Optional<RecentTasksController> recentTasksOptional,
             Transitions transitions,
             StartingWindowController startingWindow,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index ec70147..46c7b50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -18,15 +18,22 @@
 
 import android.animation.AnimationHandler;
 import android.content.Context;
+import android.content.pm.LauncherApps;
 import android.os.Handler;
-import android.view.IWindowManager;
+import android.view.WindowManager;
 
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.apppairs.AppPairsController;
+import com.android.wm.shell.bubbles.BubbleController;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
@@ -36,6 +43,7 @@
 import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.freeform.FreeformTaskListener;
+import com.android.wm.shell.fullscreen.FullscreenUnfoldController;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
 import com.android.wm.shell.onehanded.OneHandedController;
 import com.android.wm.shell.pip.Pip;
@@ -55,13 +63,18 @@
 import com.android.wm.shell.pip.phone.PipController;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
 import com.android.wm.shell.pip.phone.PipTouchHandler;
+import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
-import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
+import com.android.wm.shell.splitscreen.StageTaskUnfoldController;
 import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
+import com.android.wm.shell.unfold.UnfoldBackgroundController;
 
 import java.util.Optional;
 
+import javax.inject.Provider;
+
+import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
 
@@ -77,17 +90,29 @@
 public class WMShellModule {
 
     //
-    // Internal common - Components used internally by multiple shell features
+    // Bubbles
     //
 
+    // Note: Handler needed for LauncherApps.register
     @WMSingleton
     @Provides
-    static DisplayImeController provideDisplayImeController(IWindowManager wmService,
-            DisplayController displayController, DisplayInsetsController displayInsetsController,
+    static BubbleController provideBubbleController(Context context,
+            FloatingContentCoordinator floatingContentCoordinator,
+            IStatusBarService statusBarService,
+            WindowManager windowManager,
+            WindowManagerShellWrapper windowManagerShellWrapper,
+            LauncherApps launcherApps,
+            TaskStackListenerImpl taskStackListener,
+            UiEventLogger uiEventLogger,
+            ShellTaskOrganizer organizer,
+            DisplayController displayController,
             @ShellMainThread ShellExecutor mainExecutor,
-            TransactionPool transactionPool) {
-        return new DisplayImeController(wmService, displayController, displayInsetsController,
-                mainExecutor, transactionPool);
+            @ShellMainThread Handler mainHandler,
+            SyncTransactionQueue syncQueue) {
+        return BubbleController.create(context, null /* synchronizer */,
+                floatingContentCoordinator, statusBarService, windowManager,
+                windowManagerShellWrapper, launcherApps, taskStackListener,
+                uiEventLogger, organizer, displayController, mainExecutor, mainHandler, syncQueue);
     }
 
     //
@@ -96,16 +121,55 @@
 
     @WMSingleton
     @Provides
-    static Optional<FreeformTaskListener> provideFreeformTaskListener(
-            Context context,
+    @DynamicOverride
+    static FreeformTaskListener provideFreeformTaskListener(
             SyncTransactionQueue syncQueue) {
-        return Optional.ofNullable(FreeformTaskListener.create(context, syncQueue));
+        return new FreeformTaskListener(syncQueue);
     }
 
     //
-    // Split/multiwindow
+    // One handed mode
     //
 
+
+    // Needs the shell main handler for ContentObserver callbacks
+    @WMSingleton
+    @Provides
+    @DynamicOverride
+    static OneHandedController provideOneHandedController(Context context,
+            WindowManager windowManager, DisplayController displayController,
+            DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
+            UiEventLogger uiEventLogger,
+            @ShellMainThread ShellExecutor mainExecutor,
+            @ShellMainThread Handler mainHandler) {
+        return OneHandedController.create(context, windowManager,
+                displayController, displayLayout, taskStackListener, uiEventLogger, mainExecutor,
+                mainHandler);
+    }
+
+    //
+    // Splitscreen
+    //
+
+    @WMSingleton
+    @Provides
+    @DynamicOverride
+    static SplitScreenController provideSplitScreenController(
+            ShellTaskOrganizer shellTaskOrganizer,
+            SyncTransactionQueue syncQueue, Context context,
+            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+            @ShellMainThread ShellExecutor mainExecutor,
+            DisplayImeController displayImeController,
+            DisplayInsetsController displayInsetsController, Transitions transitions,
+            TransactionPool transactionPool, IconProvider iconProvider,
+            Optional<RecentTasksController> recentTasks,
+            Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
+        return new SplitScreenController(shellTaskOrganizer, syncQueue, context,
+                rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
+                displayInsetsController, transitions, transactionPool, iconProvider,
+                recentTasks, stageTaskUnfoldControllerProvider);
+    }
+
     @WMSingleton
     @Provides
     static LegacySplitScreenController provideLegacySplitScreen(Context context,
@@ -258,12 +322,53 @@
     }
 
     //
-    // Starting Windows (Splash Screen)
+    // Unfold transition
     //
 
     @WMSingleton
     @Provides
-    static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm() {
-        return new PhoneStartingWindowTypeAlgorithm();
+    @DynamicOverride
+    static FullscreenUnfoldController provideFullscreenUnfoldController(
+            Context context,
+            Optional<ShellUnfoldProgressProvider> progressProvider,
+            Lazy<UnfoldBackgroundController> unfoldBackgroundController,
+            DisplayInsetsController displayInsetsController,
+            @ShellMainThread ShellExecutor mainExecutor
+    ) {
+        return new FullscreenUnfoldController(context, mainExecutor,
+                unfoldBackgroundController.get(), progressProvider.get(),
+                displayInsetsController);
+    }
+
+    @Provides
+    static Optional<StageTaskUnfoldController> provideStageTaskUnfoldController(
+            Optional<ShellUnfoldProgressProvider> progressProvider,
+            Context context,
+            TransactionPool transactionPool,
+            Lazy<UnfoldBackgroundController> unfoldBackgroundController,
+            DisplayInsetsController displayInsetsController,
+            @ShellMainThread ShellExecutor mainExecutor
+    ) {
+        return progressProvider.map(shellUnfoldTransitionProgressProvider ->
+                new StageTaskUnfoldController(
+                        context,
+                        transactionPool,
+                        shellUnfoldTransitionProgressProvider,
+                        displayInsetsController,
+                        unfoldBackgroundController.get(),
+                        mainExecutor
+                ));
+    }
+
+    @WMSingleton
+    @Provides
+    static UnfoldBackgroundController provideUnfoldBackgroundController(
+            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+            Context context
+    ) {
+        return new UnfoldBackgroundController(
+                context,
+                rootTaskDisplayAreaOrganizer
+        );
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 0c12d6c..d2b4711 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -19,6 +19,7 @@
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.DragEvent.ACTION_DRAG_ENDED;
 import static android.view.DragEvent.ACTION_DRAG_ENTERED;
 import static android.view.DragEvent.ACTION_DRAG_EXITED;
@@ -34,13 +35,13 @@
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
-import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.PixelFormat;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.Display;
 import android.view.DragEvent;
 import android.view.LayoutInflater;
 import android.view.SurfaceControl;
@@ -55,7 +56,6 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.util.Optional;
@@ -91,6 +91,11 @@
     @Override
     public void onDisplayAdded(int displayId) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Display added: %d", displayId);
+        if (displayId != DEFAULT_DISPLAY) {
+            // Ignore non-default displays for now
+            return;
+        }
+
         final Context context = mDisplayController.getDisplayContext(displayId)
                 .createWindowContext(TYPE_APPLICATION_OVERLAY, null);
         final WindowManager wm = context.getSystemService(WindowManager.class);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 8a8d7c6..5c8e7d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -141,16 +141,4 @@
                 || Settings.Global.getInt(context.getContentResolver(),
                 DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
     }
-
-    /**
-     * Creates {@link FreeformTaskListener} if freeform is enabled.
-     */
-    public static FreeformTaskListener create(Context context,
-            SyncTransactionQueue syncQueue) {
-        if (!isFreeformEnabled(context)) {
-            return null;
-        }
-
-        return new FreeformTaskListener(syncQueue);
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
index 80ab166..67e487d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
@@ -172,6 +172,14 @@
                 };
 
         mWindowManager = new DividerWindowManager(mSystemWindows);
+
+        // No need to listen to display window container or create root tasks if the device is not
+        // using legacy split screen.
+        if (!context.getResources().getBoolean(com.android.internal.R.bool.config_useLegacySplit)) {
+            return;
+        }
+
+
         mDisplayController.addDisplayWindowListener(this);
         // Don't initialize the divider or anything until we get the default display.
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 90074371..e068614 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -46,7 +46,6 @@
 import android.window.WindowContainerTransaction;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.logging.UiEventLogger;
@@ -76,7 +75,7 @@
     private static final int OVERLAY_ENABLED_DELAY_MS = 250;
     private static final int DISPLAY_AREA_READY_RETRY_MS = 10;
 
-    static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
+    public static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
 
     private volatile boolean mIsOneHandedEnabled;
     private volatile boolean mIsSwipeToNotificationEnabled;
@@ -198,16 +197,10 @@
     /**
      * Creates {@link OneHandedController}, returns {@code null} if the feature is not supported.
      */
-    @Nullable
     public static OneHandedController create(
             Context context, WindowManager windowManager, DisplayController displayController,
             DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
             UiEventLogger uiEventLogger, ShellExecutor mainExecutor, Handler mainHandler) {
-        if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
-            Slog.w(TAG, "Device doesn't support OneHanded feature");
-            return null;
-        }
-
         OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
         OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context);
         OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 7457be2..8af72a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -130,19 +130,6 @@
 
     private StageCoordinator mStageCoordinator;
 
-    // TODO(b/205019015): Remove after we clean up downstream modules
-    public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
-            SyncTransactionQueue syncQueue, Context context,
-            RootTaskDisplayAreaOrganizer rootTDAOrganizer,
-            ShellExecutor mainExecutor, DisplayImeController displayImeController,
-            DisplayInsetsController displayInsetsController,
-            Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
-            Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
-        this(shellTaskOrganizer, syncQueue, context, rootTDAOrganizer, mainExecutor,
-                displayImeController, displayInsetsController, transitions, transactionPool,
-                iconProvider, Optional.empty(), unfoldControllerProvider);
-    }
-
     public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
             SyncTransactionQueue syncQueue, Context context,
             RootTaskDisplayAreaOrganizer rootTDAOrganizer,
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 050d255..d494191 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
@@ -78,7 +78,6 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
-import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.InstanceId;
 import com.android.internal.protolog.common.ProtoLog;
@@ -157,10 +156,6 @@
     private boolean mExitSplitScreenOnHide;
     private boolean mKeyguardOccluded;
 
-    // TODO(b/187041611): remove this flag after totally deprecated legacy split
-    /** Whether the device is supporting legacy split or not. */
-    private boolean mUseLegacySplit;
-
     @SplitScreen.StageType
     private int mDismissTop = NO_DISMISS;
 
@@ -735,17 +730,9 @@
 
     private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
         if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
-            mUseLegacySplit = mContext.getResources().getBoolean(R.bool.config_useLegacySplit);
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             // Make the stages adjacent to each other so they occlude what's behind them.
             wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
-
-            // Only sets side stage as launch-adjacent-flag-root when the device is not using legacy
-            // split to prevent new split behavior confusing users.
-            if (!mUseLegacySplit) {
-                wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
-            }
-
             mTaskOrganizer.applyTransaction(wct);
         }
     }
@@ -755,11 +742,6 @@
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             // Deactivate the main stage if it no longer has a root task.
             mMainStage.deactivate(wct);
-
-            if (!mUseLegacySplit) {
-                wct.clearLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
-            }
-
             mTaskOrganizer.applyTransaction(wct);
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 190006e..62b8638 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -37,6 +37,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.internal.R;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.SurfaceUtils;
@@ -102,7 +103,12 @@
         mSurfaceSession = surfaceSession;
         mIconProvider = iconProvider;
         mStageTaskUnfoldController = stageTaskUnfoldController;
-        taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
+
+        // No need to create root task if the device is using legacy split screen.
+        // TODO(b/199236198): Remove this check after totally deprecated legacy split.
+        if (!context.getResources().getBoolean(R.bool.config_useLegacySplit)) {
+            taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
+        }
     }
 
     int getChildCount() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 7abda99..a0d9d03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -52,9 +52,12 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityThread;
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
@@ -66,6 +69,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.WindowManager;
+import android.view.WindowManager.TransitionType;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.Transformation;
@@ -78,6 +82,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.AttributeCache;
+import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.policy.TransitionAnimation;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.common.DisplayController;
@@ -292,9 +297,12 @@
             finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
         };
 
+        boolean requireBackgroundForTransition = false;
+
         final int wallpaperTransit = getWallpaperTransitType(info);
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
+            final boolean isTask = change.getTaskInfo() != null;
 
             if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
                 int rotateDelta = change.getEndRotation() - change.getStartRotation();
@@ -342,7 +350,7 @@
                 startTransaction.setPosition(change.getLeash(),
                         change.getEndAbsBounds().left - change.getEndRelOffset().x,
                         change.getEndAbsBounds().top - change.getEndRelOffset().y);
-                if (change.getTaskInfo() != null) {
+                if (isTask) {
                     // Skip non-tasks since those usually have null bounds.
                     startTransaction.setWindowCrop(change.getLeash(),
                             change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
@@ -354,14 +362,34 @@
 
             Animation a = loadAnimation(info, change, wallpaperTransit);
             if (a != null) {
+                if (changeRequiresBackground(info, change)) {
+                    requireBackgroundForTransition = true;
+                }
+
+                float cornerRadius = 0;
+                if (a.hasRoundedCorners() && isTask) {
+                    // hasRoundedCorners is currently only enabled for tasks
+                    final Context displayContext =
+                            mDisplayController.getDisplayContext(change.getTaskInfo().displayId);
+                    cornerRadius =
+                            ScreenDecorationsUtils.getWindowCornerRadius(displayContext);
+                }
+
                 startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
-                        mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */);
+                        mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
+                        cornerRadius, change.getEndAbsBounds());
 
                 if (info.getAnimationOptions() != null) {
-                    attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions());
+                    attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions(),
+                            cornerRadius);
                 }
             }
         }
+
+        if (requireBackgroundForTransition) {
+            addBackgroundToTransition(info.getRootLeash(), startTransaction, finishTransaction);
+        }
+
         startTransaction.apply();
         TransitionMetrics.getInstance().reportAnimationStart(transition);
         // run finish now in-case there are no animations
@@ -369,6 +397,40 @@
         return true;
     }
 
+    private boolean changeRequiresBackground(TransitionInfo info,
+            TransitionInfo.Change change) {
+        final boolean isTask = change.getTaskInfo() != null;
+        final @TransitionType int type = info.getType();
+        final boolean isOpenOrCloseTransition = type == TRANSIT_OPEN || type == TRANSIT_CLOSE
+                || type == TRANSIT_TO_FRONT || type == TRANSIT_TO_BACK;
+        return isTask && isOpenOrCloseTransition;
+    }
+
+    private void addBackgroundToTransition(
+            @NonNull SurfaceControl rootLeash,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction
+    ) {
+        final Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();
+        final @ColorInt int overviewBackgroundColor =
+                uiContext.getColor(R.color.overview_background);
+        final Color bgColor = Color.valueOf(overviewBackgroundColor);
+        final float[] colorArray = new float[] { bgColor.red(), bgColor.green(), bgColor.blue() };
+
+        final SurfaceControl animationBackgroundSurface = new SurfaceControl.Builder()
+                .setName("Animation Background")
+                .setParent(rootLeash)
+                .setColorLayer()
+                .setOpaque(true)
+                .build();
+
+        startTransaction
+                .setLayer(animationBackgroundSurface, Integer.MIN_VALUE)
+                .setColor(animationBackgroundSurface, colorArray)
+                .show(animationBackgroundSurface);
+        finishTransaction.remove(animationBackgroundSurface);
+    }
+
     @Nullable
     @Override
     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@@ -508,7 +570,7 @@
             @NonNull Animation anim, @NonNull SurfaceControl leash,
             @NonNull Runnable finishCallback, @NonNull TransactionPool pool,
             @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor,
-            @Nullable Point position) {
+            @Nullable Point position, float cornerRadius, @Nullable Rect clipRect) {
         final SurfaceControl.Transaction transaction = pool.acquire();
         final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
         final Transformation transformation = new Transformation();
@@ -520,12 +582,12 @@
             final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
 
             applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
-                    position);
+                    position, cornerRadius, clipRect);
         });
 
         final Runnable finisher = () -> {
             applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
-                    position);
+                    position, cornerRadius, clipRect);
 
             pool.release(transaction);
             mainExecutor.execute(() -> {
@@ -550,23 +612,24 @@
 
     private void attachThumbnail(@NonNull ArrayList<Animator> animations,
             @NonNull Runnable finishCallback, TransitionInfo.Change change,
-            TransitionInfo.AnimationOptions options) {
+            TransitionInfo.AnimationOptions options, float cornerRadius) {
         final boolean isTask = change.getTaskInfo() != null;
         final boolean isOpen = Transitions.isOpeningType(change.getMode());
         final boolean isClose = Transitions.isClosingType(change.getMode());
         if (isOpen) {
             if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS && isTask) {
-                attachCrossProfileThunmbnailAnimation(animations, finishCallback, change);
+                attachCrossProfileThunmbnailAnimation(animations, finishCallback, change,
+                        cornerRadius);
             } else if (options.getType() == ANIM_THUMBNAIL_SCALE_UP) {
-                attachThumbnailAnimation(animations, finishCallback, change, options);
+                attachThumbnailAnimation(animations, finishCallback, change, options, cornerRadius);
             }
         } else if (isClose && options.getType() == ANIM_THUMBNAIL_SCALE_DOWN) {
-            attachThumbnailAnimation(animations, finishCallback, change, options);
+            attachThumbnailAnimation(animations, finishCallback, change, options, cornerRadius);
         }
     }
 
     private void attachCrossProfileThunmbnailAnimation(@NonNull ArrayList<Animator> animations,
-            @NonNull Runnable finishCallback, TransitionInfo.Change change) {
+            @NonNull Runnable finishCallback, TransitionInfo.Change change, float cornerRadius) {
         final int thumbnailDrawableRes = change.getTaskInfo().userId == mCurrentUserId
                 ? R.drawable.ic_account_circle : R.drawable.ic_corp_badge;
         final Rect bounds = change.getEndAbsBounds();
@@ -594,12 +657,13 @@
         a.restrictDuration(MAX_ANIMATION_DURATION);
         a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
         startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
-                mMainExecutor, mAnimExecutor, new Point(bounds.left, bounds.top));
+                mMainExecutor, mAnimExecutor, new Point(bounds.left, bounds.top),
+                cornerRadius, change.getEndAbsBounds());
     }
 
     private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
             @NonNull Runnable finishCallback, TransitionInfo.Change change,
-            TransitionInfo.AnimationOptions options) {
+            TransitionInfo.AnimationOptions options, float cornerRadius) {
         final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
         final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
                 change.getLeash(), options.getThumbnail(), transaction);
@@ -618,7 +682,8 @@
         a.restrictDuration(MAX_ANIMATION_DURATION);
         a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
         startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
-                mMainExecutor, mAnimExecutor, null /* position */);
+                mMainExecutor, mAnimExecutor, null /* position */,
+                cornerRadius, change.getEndAbsBounds());
     }
 
     private static int getWallpaperTransitType(TransitionInfo info) {
@@ -650,13 +715,19 @@
 
     private static void applyTransformation(long time, SurfaceControl.Transaction t,
             SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix,
-            Point position) {
+            Point position, float cornerRadius, @Nullable Rect clipRect) {
         anim.getTransformation(time, transformation);
         if (position != null) {
             transformation.getMatrix().postTranslate(position.x, position.y);
         }
         t.setMatrix(leash, transformation.getMatrix(), matrix);
         t.setAlpha(leash, transformation.getAlpha());
+        if (anim.hasRoundedCorners() && cornerRadius > 0 && clipRect != null) {
+            // We can only apply rounded corner if a crop is set
+            t.setWindowCrop(leash, clipRect);
+            t.setCornerRadius(leash, cornerRadius);
+        }
+
         t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
         t.apply();
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index 13c670a..45d8be1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -292,14 +292,16 @@
             @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
             @NonNull ShellExecutor animExecutor) {
         startSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
-                mTransactionPool, mainExecutor, animExecutor, null /* position */);
+                mTransactionPool, mainExecutor, animExecutor, null /* position */,
+                0 /* cornerRadius */, null /* clipRect */);
     }
 
     private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
             @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
             @NonNull ShellExecutor animExecutor) {
         startSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
-                mTransactionPool, mainExecutor, animExecutor, null /* position */);
+                mTransactionPool, mainExecutor, animExecutor, null /* position */,
+                0 /* cornerRadius */, null /* clipRect */);
     }
 
     private void startColorAnimation(float animationScale, @NonNull ShellExecutor animExecutor) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
index 74e4812..367676f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
@@ -26,10 +26,16 @@
  */
 public interface ShellUnfoldProgressProvider {
 
+    // This is a temporary workaround until we move the progress providers into the Shell or
+    // refactor the dependencies. TLDR, the base module depends on this provider to determine if the
+    // FullscreenUnfoldController is available, but this check can't rely on an optional component.
+    public static final ShellUnfoldProgressProvider NO_PROVIDER =
+            new ShellUnfoldProgressProvider() {};
+
     /**
      * Adds a transition listener
      */
-    void addListener(Executor executor, UnfoldListener listener);
+    default void addListener(Executor executor, UnfoldListener listener) {}
 
     /**
      * Listener for receiving unfold updates
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index ad4ccc0..574a9f4 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -16,10 +16,6 @@
         <!-- restart launcher to activate TAPL -->
         <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
     </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
-        <!-- reboot the device to teardown any crashed tests -->
-        <option name="cleanup-action" value="REBOOT" />
-    </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true"/>
         <option name="test-file-name" value="WMShellFlickerTests.apk"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index ecc2d31..1d463d5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -24,11 +24,13 @@
 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.flicker.helpers.isShellTransitionsEnabled
 import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import org.junit.After
+import org.junit.Assume.assumeFalse
 import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -82,7 +84,11 @@
 
     @FlakyTest
     @Test
-    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 04c82e5..10cf0b7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -24,10 +24,12 @@
 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.flicker.helpers.isShellTransitionsEnabled
 import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
 import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -67,7 +69,11 @@
 
     @FlakyTest
     @Test
-    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
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 b7d3ba6..722ec34 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
@@ -24,11 +24,13 @@
 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.flicker.helpers.isShellTransitionsEnabled
 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
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import org.junit.After
+import org.junit.Assume.assumeFalse
 import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -86,7 +88,11 @@
 
     @FlakyTest
     @Test
-    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index f6ce3d4..38c008c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -25,10 +25,12 @@
 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.flicker.helpers.isShellTransitionsEnabled
 import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
 import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -71,7 +73,11 @@
 
     @FlakyTest
     @Test
-    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 5fe13e0..a787f2b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -27,6 +27,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.exitSplitScreen
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
@@ -40,6 +41,7 @@
 import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
 import com.android.wm.shell.flicker.helpers.SimpleAppHelper
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -110,7 +112,11 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index a238bc2..6041e23 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -30,6 +30,7 @@
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.ImeAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.resizeSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
@@ -44,6 +45,7 @@
 import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
 import com.android.wm.shell.flicker.helpers.SimpleAppHelper
 import com.android.wm.shell.flicker.testapp.Components
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -134,7 +136,11 @@
     fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
 
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     @Test
     fun topAppLayerIsAlwaysVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 50cd548..e44d7d6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -25,6 +25,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -34,6 +35,7 @@
 import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -77,7 +79,11 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index 8d52225..d33d92b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -25,6 +25,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -34,6 +35,7 @@
 import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -76,7 +78,11 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index 070f636..ece68df 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -24,6 +24,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
@@ -35,6 +36,7 @@
 import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -85,7 +87,11 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index fabbd26..127301f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -25,6 +25,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
@@ -36,6 +37,7 @@
 import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -91,7 +93,11 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     @FlakyTest
     @Test
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 33626d0..c439922 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
@@ -26,6 +26,8 @@
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -65,6 +67,15 @@
             }
         }
 
+    /** {@inheritDoc}  */
+    @Presubmit
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
+
     /**
      * Checks [pipApp] window remains visible throughout the animation
      */
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 791505b..f923a23 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -27,6 +27,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.traces.common.FlickerComponentName
@@ -35,6 +36,7 @@
 import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
 import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
 import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -119,7 +121,11 @@
      */
     @Presubmit
     @Test
-    override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     /**
      * Checks that all parts of the screen are covered at the start and end of the transition
@@ -204,6 +210,7 @@
     @Presubmit
     @Test
     fun testAppPlusPipLayerCoversFullScreenOnEnd() {
+        // This test doesn't work in shell transitions because of b/206669574
         testSpec.assertLayersEnd {
             val pipRegion = visibleRegion(pipApp.component).region
             visibleRegion(testApp.component)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index 8267442..3e7e2f5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -18,7 +18,9 @@
 
 import android.platform.test.annotations.Presubmit
 import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import org.junit.Assume.assumeFalse
 import org.junit.Test
 
 /**
@@ -27,6 +29,15 @@
 abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
     protected val testApp = FixedAppHelper(instrumentation)
 
+    /** {@inheritDoc}  */
+    @Presubmit
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
+
     /**
      * Checks that the pip app window remains inside the display bounds throughout the whole
      * animation
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 5f29dbc..4d63d14 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
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -23,7 +24,10 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -75,6 +79,15 @@
             }
         }
 
+    /** {@inheritDoc}  */
+    @Presubmit
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
+
     companion object {
         /**
          * Creates the test configurations.
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 00ccf26..030e040 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
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -23,7 +24,10 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -72,6 +76,15 @@
             }
         }
 
+    /** {@inheritDoc}  */
+    @Presubmit
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index b0b11e9..2def979 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -23,7 +24,11 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
+import org.junit.Before
 import org.junit.FixMethodOrder
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -60,6 +65,21 @@
             }
         }
 
+    @Before
+    fun onBefore() {
+        // This CUJ don't work in shell transitions because of b/204570898 b/204562589
+        assumeFalse(isShellTransitionsEnabled)
+    }
+
+    /** {@inheritDoc}  */
+    @Presubmit
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index f4eb701..9191d0e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -25,7 +25,9 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -96,7 +98,11 @@
 
     @Presubmit
     @Test
-    override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index f196764..3511cc2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -26,6 +26,9 @@
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -62,6 +65,12 @@
             }
         }
 
+    @Before
+    fun onBefore() {
+        // This CUJ don't work in shell transitions because of b/204570898 b/204562589
+        assumeFalse(isShellTransitionsEnabled)
+    }
+
     /**
      * Checks that the pip app window remains inside the display bounds throughout the whole
      * animation
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
index d9685f3..10a542f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -23,8 +24,11 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.traces.RegionSubject
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -78,6 +82,15 @@
         current.isHigherOrEqual(previous.region)
     }
 
+    /** {@inheritDoc}  */
+    @Presubmit
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index c6b42ea..cb6ba0e6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -23,8 +24,11 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.traces.RegionSubject
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -78,6 +82,15 @@
         current.isLowerOrEqual(previous.region)
     }
 
+    /** {@inheritDoc}  */
+    @Presubmit
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 45cbdc8..81ac10f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -25,9 +25,11 @@
 import com.android.server.wm.flicker.annotation.Group4
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.wm.shell.flicker.helpers.ImeAppHelper
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -70,6 +72,15 @@
             }
         }
 
+    /** {@inheritDoc}  */
+    @Presubmit
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
+
     /**
      * Ensure the pip window remains visible throughout any keyboard interactions
      */
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 3e3ea16..70075dd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -89,6 +89,15 @@
             }
         }
 
+    /** {@inheritDoc}  */
+    @Presubmit
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(com.android.server.wm.flicker.helpers.isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
+
     @FlakyTest(bugId = 161435597)
     @Test
     fun pipWindowInsideDisplayBounds() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index af984b3..16fc048 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -27,10 +27,13 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import org.junit.Assume.assumeFalse
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -81,6 +84,12 @@
             }
         }
 
+    @Before
+    fun onBefore() {
+        // This CUJ don't work in shell transitions because of b/204570898 b/204562589 b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+    }
+
     /**
      * Checks that all parts of the screen are covered at the start and end of the transition
      */
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index f8e2d38..9c26105 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -25,10 +25,12 @@
 import com.android.server.wm.flicker.annotation.Group4
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
 import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
 import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
 import org.junit.Assert.assertEquals
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -102,7 +104,11 @@
 
     @FlakyTest
     @Test
-    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
 
     @FlakyTest
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
index 31e9167..9c3b0fa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
@@ -27,7 +27,7 @@
 import com.android.wm.shell.flicker.pip.PipTestBase
 import org.junit.After
 import org.junit.Assert.assertFalse
-import org.junit.Assume
+import org.junit.Assume.assumeTrue
 import org.junit.Before
 
 abstract class TvPipTestBase : PipTestBase(rotationToString(ROTATION_0), ROTATION_0) {
@@ -37,7 +37,7 @@
     @Before
     final override fun televisionSetUp() {
         // Should run only on TVs.
-        Assume.assumeTrue(isTelevision)
+        assumeTrue(isTelevision)
 
         systemUiProcessObserver.start()
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
new file mode 100644
index 0000000..bfa2c92
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.draganddrop;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.view.Display;
+import android.view.DragEvent;
+import android.view.View;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.wm.shell.common.DisplayController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for the drag and drop controller.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DragAndDropControllerTest {
+
+    @Mock
+    private Context mContext;
+
+    @Mock
+    private DisplayController mDisplayController;
+
+    @Mock
+    private UiEventLogger mUiEventLogger;
+
+    private DragAndDropController mController;
+
+    @Before
+    public void setUp() throws RemoteException {
+        MockitoAnnotations.initMocks(this);
+
+        mController = new DragAndDropController(mContext, mDisplayController, mUiEventLogger);
+    }
+
+    @Test
+    public void testIgnoreNonDefaultDisplays() {
+        final int nonDefaultDisplayId = 12345;
+        final View dragLayout = mock(View.class);
+        final Display display = mock(Display.class);
+        doReturn(nonDefaultDisplayId).when(display).getDisplayId();
+        doReturn(display).when(dragLayout).getDisplay();
+
+        // Expect no per-display layout to be added
+        mController.onDisplayAdded(nonDefaultDisplayId);
+        assertFalse(mController.onDrag(dragLayout, mock(DragEvent.class)));
+    }
+}
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 8f04cfb..6ea303c 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -17,24 +17,29 @@
 #define LOG_TAG "PointerController"
 //#define LOG_NDEBUG 0
 
-// Log debug messages about pointer updates
-#define DEBUG_POINTER_UPDATES 0
-
 #include "PointerController.h"
-#include "MouseCursorController.h"
 #include "PointerControllerContext.h"
-#include "TouchSpotController.h"
 
-#include <log/log.h>
-
-#include <SkBitmap.h>
 #include <SkBlendMode.h>
 #include <SkCanvas.h>
 #include <SkColor.h>
-#include <SkPaint.h>
 
 namespace android {
 
+namespace {
+
+const ui::Transform kIdentityTransform;
+
+} // namespace
+
+// --- PointerController::DisplayInfoListener ---
+
+void PointerController::DisplayInfoListener::onWindowInfosChanged(
+        const std::vector<android::gui::WindowInfo>&,
+        const std::vector<android::gui::DisplayInfo>& displayInfo) {
+    mPointerController.onDisplayInfosChanged(displayInfo);
+}
+
 // --- PointerController ---
 
 std::shared_ptr<PointerController> PointerController::create(
@@ -63,9 +68,12 @@
 PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
                                      const sp<Looper>& looper,
                                      const sp<SpriteController>& spriteController)
-      : mContext(policy, looper, spriteController, *this), mCursorController(mContext) {
+      : mContext(policy, looper, spriteController, *this),
+        mCursorController(mContext),
+        mDisplayInfoListener(new DisplayInfoListener(*this)) {
     std::scoped_lock lock(mLock);
     mLocked.presentation = Presentation::SPOT;
+    SurfaceComposerClient::getDefault()->addWindowInfosListener(mDisplayInfoListener);
 }
 
 bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
@@ -74,7 +82,14 @@
 }
 
 void PointerController::move(float deltaX, float deltaY) {
-    mCursorController.move(deltaX, deltaY);
+    const int32_t displayId = mCursorController.getDisplayId();
+    vec2 transformed;
+    {
+        std::scoped_lock lock(mLock);
+        const auto& transform = getTransformForDisplayLocked(displayId);
+        transformed = transformWithoutTranslation(transform, {deltaX, deltaY});
+    }
+    mCursorController.move(transformed.x, transformed.y);
 }
 
 void PointerController::setButtonState(int32_t buttonState) {
@@ -86,12 +101,26 @@
 }
 
 void PointerController::setPosition(float x, float y) {
-    std::scoped_lock lock(mLock);
-    mCursorController.setPosition(x, y);
+    const int32_t displayId = mCursorController.getDisplayId();
+    vec2 transformed;
+    {
+        std::scoped_lock lock(mLock);
+        const auto& transform = getTransformForDisplayLocked(displayId);
+        transformed = transform.transform(x, y);
+    }
+    mCursorController.setPosition(transformed.x, transformed.y);
 }
 
 void PointerController::getPosition(float* outX, float* outY) const {
+    const int32_t displayId = mCursorController.getDisplayId();
     mCursorController.getPosition(outX, outY);
+    {
+        std::scoped_lock lock(mLock);
+        const auto& transform = getTransformForDisplayLocked(displayId);
+        const auto xy = transform.inverse().transform(*outX, *outY);
+        *outX = xy.x;
+        *outY = xy.y;
+    }
 }
 
 int32_t PointerController::getDisplayId() const {
@@ -130,11 +159,25 @@
 void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
                                  BitSet32 spotIdBits, int32_t displayId) {
     std::scoped_lock lock(mLock);
+    std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
+    const ui::Transform& transform = getTransformForDisplayLocked(displayId);
+
+    for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+        const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()];
+
+        const vec2 xy = transform.transform(spotCoords[index].getXYValue());
+        outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+        outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+
+        float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
+        outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+    }
+
     auto it = mLocked.spotControllers.find(displayId);
     if (it == mLocked.spotControllers.end()) {
         mLocked.spotControllers.try_emplace(displayId, displayId, mContext);
     }
-    mLocked.spotControllers.at(displayId).setSpots(spotCoords, spotIdToIndex, spotIdBits);
+    mLocked.spotControllers.at(displayId).setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits);
 }
 
 void PointerController::clearSpots() {
@@ -194,7 +237,7 @@
 
 void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports) {
     std::unordered_set<int32_t> displayIdSet;
-    for (DisplayViewport viewport : viewports) {
+    for (const DisplayViewport& viewport : viewports) {
         displayIdSet.insert(viewport.displayId);
     }
 
@@ -214,4 +257,17 @@
     }
 }
 
+void PointerController::onDisplayInfosChanged(const std::vector<gui::DisplayInfo>& displayInfo) {
+    std::scoped_lock lock(mLock);
+    mLocked.mDisplayInfos = displayInfo;
+}
+
+const ui::Transform& PointerController::getTransformForDisplayLocked(int displayId) const {
+    const auto& di = mLocked.mDisplayInfos;
+    auto it = std::find_if(di.begin(), di.end(), [displayId](const gui::DisplayInfo& info) {
+        return info.displayId == displayId;
+    });
+    return it != di.end() ? it->transform : kIdentityTransform;
+}
+
 } // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 97567ba..58bb014 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -72,6 +72,8 @@
     void reloadPointerResources();
     void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports);
 
+    void onDisplayInfosChanged(const std::vector<gui::DisplayInfo>& displayInfos);
+
 private:
     friend PointerControllerContext::LooperCallback;
     friend PointerControllerContext::MessageHandler;
@@ -85,9 +87,23 @@
     struct Locked {
         Presentation presentation;
 
+        std::vector<gui::DisplayInfo> mDisplayInfos;
         std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers;
     } mLocked GUARDED_BY(mLock);
 
+    class DisplayInfoListener : public gui::WindowInfosListener {
+    public:
+        explicit DisplayInfoListener(PointerController& pc) : mPointerController(pc){};
+        void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>&,
+                                  const std::vector<android::gui::DisplayInfo>&) override;
+
+    private:
+        PointerController& mPointerController;
+    };
+    sp<DisplayInfoListener> mDisplayInfoListener;
+
+    const ui::Transform& getTransformForDisplayLocked(int displayId) const REQUIRES(mLock);
+
     PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
                       const sp<SpriteController>& spriteController);
     void clearSpotsLocked();
diff --git a/media/aidl/android/media/audio/common/AudioMode.aidl b/media/aidl/android/media/audio/common/AudioMode.aidl
index cc03813..701bcb6 100644
--- a/media/aidl/android/media/audio/common/AudioMode.aidl
+++ b/media/aidl/android/media/audio/common/AudioMode.aidl
@@ -45,4 +45,8 @@
     IN_COMMUNICATION = 3,
     /** Call screening in progress. */
     CALL_SCREEN = 4,
+    /** PSTN Call redirection  in progress. */
+    SYS_RESERVED_CALL_REDIRECT = 5,
+    /** VoIP Call redirection  in progress. */
+    SYS_RESERVED_COMMUNICATION_REDIRECT = 6,
 }
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl
index 8ae1c10..8256c1c 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl
@@ -42,4 +42,6 @@
   IN_CALL = 2,
   IN_COMMUNICATION = 3,
   CALL_SCREEN = 4,
+  SYS_RESERVED_CALL_REDIRECT = 5,
+  SYS_RESERVED_COMMUNICATION_REDIRECT = 6,
 }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e3ed703..c662223 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -30,8 +30,11 @@
 import android.annotation.TestApi;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.compat.CompatChanges;
 import android.bluetooth.BluetoothCodecConfig;
 import android.bluetooth.BluetoothDevice;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -2840,6 +2843,14 @@
     }
 
     /**
+     * This change id controls use of audio modes for call audio redirection.
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public static final long CALL_REDIRECTION_AUDIO_MODES = 189472651L; // buganizer id
+
+    /**
      * Returns the current audio mode.
      *
      * @return      the current audio mode.
@@ -2858,6 +2869,12 @@
             }
             if (mode == MODE_CALL_SCREENING && sdk <= Build.VERSION_CODES.Q) {
                 mode = MODE_IN_CALL;
+            } else if (mode == MODE_CALL_REDIRECT
+                    && !CompatChanges.isChangeEnabled(CALL_REDIRECTION_AUDIO_MODES)) {
+                mode = MODE_IN_CALL;
+            } else if (mode == MODE_COMMUNICATION_REDIRECT
+                    && !CompatChanges.isChangeEnabled(CALL_REDIRECTION_AUDIO_MODES)) {
+                mode = MODE_IN_COMMUNICATION;
             }
             return mode;
         } catch (RemoteException e) {
@@ -3075,13 +3092,26 @@
      */
     public static final int MODE_CALL_SCREENING     = AudioSystem.MODE_CALL_SCREENING;
 
+    /**
+     * A telephony call is established and its audio is being redirected to another device.
+     */
+    public static final int MODE_CALL_REDIRECT   = AudioSystem.MODE_CALL_REDIRECT;
+
+    /**
+     * n audio/video chat or VoIP call is established and its audio is being redirected to another
+     * device.
+     */
+    public static final int MODE_COMMUNICATION_REDIRECT = AudioSystem.MODE_COMMUNICATION_REDIRECT;
+
     /** @hide */
     @IntDef(flag = false, prefix = "MODE_", value = {
             MODE_NORMAL,
             MODE_RINGTONE,
             MODE_IN_CALL,
             MODE_IN_COMMUNICATION,
-            MODE_CALL_SCREENING }
+            MODE_CALL_SCREENING,
+            MODE_CALL_REDIRECT,
+            MODE_COMMUNICATION_REDIRECT}
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioMode {}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 60c9e1c..16cb5f4 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -199,7 +199,11 @@
     /** @hide */
     public static final int MODE_CALL_SCREENING     = 4;
     /** @hide */
-    public static final int NUM_MODES               = 5;
+    public static final int MODE_CALL_REDIRECT     = 5;
+    /** @hide */
+    public static final int MODE_COMMUNICATION_REDIRECT  = 6;
+    /** @hide */
+    public static final int NUM_MODES               = 7;
 
     /** @hide */
     public static String modeToString(int mode) {
@@ -211,6 +215,8 @@
             case MODE_NORMAL: return "MODE_NORMAL";
             case MODE_RINGTONE: return "MODE_RINGTONE";
             case MODE_CALL_SCREENING: return "MODE_CALL_SCREENING";
+            case MODE_CALL_REDIRECT: return "MODE_CALL_REDIRECT";
+            case MODE_COMMUNICATION_REDIRECT: return "MODE_COMMUNICATION_REDIRECT";
             default: return "unknown mode (" + mode + ")";
         }
     }
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.java b/media/java/android/media/tv/interactive/TvIAppInfo.java
new file mode 100644
index 0000000..b3b5aee
--- /dev/null
+++ b/media/java/android/media/tv/interactive/TvIAppInfo.java
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+
+package android.media.tv.interactive;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is used to specify meta information of a TV interactive app.
+ * @hide
+ */
+public final class TvIAppInfo implements Parcelable {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "TvIAppInfo";
+
+    private final ResolveInfo mService;
+    private final String mId;
+    private List<String> mTypes = new ArrayList<>();
+
+    private TvIAppInfo(ResolveInfo service, String id, List<String> types) {
+        mService = service;
+        mId = id;
+        mTypes = types;
+    }
+
+    protected TvIAppInfo(Parcel in) {
+        mService = ResolveInfo.CREATOR.createFromParcel(in);
+        mId = in.readString();
+        in.readStringList(mTypes);
+    }
+
+    public static final Creator<TvIAppInfo> CREATOR = new Creator<TvIAppInfo>() {
+        @Override
+        public TvIAppInfo createFromParcel(Parcel in) {
+            return new TvIAppInfo(in);
+        }
+
+        @Override
+        public TvIAppInfo[] newArray(int size) {
+            return new TvIAppInfo[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        mService.writeToParcel(dest, flags);
+        dest.writeString(mId);
+        dest.writeStringList(mTypes);
+    }
+
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the information of the service that implements this TV IApp service.
+     */
+    public ServiceInfo getServiceInfo() {
+        return mService.serviceInfo;
+    }
+
+    /**
+     * A convenience builder for creating {@link TvIAppInfo} objects.
+     */
+    public static final class Builder {
+        private static final String XML_START_TAG_NAME = "tv-iapp";
+        private final Context mContext;
+        private final ResolveInfo mResolveInfo;
+        private final List<String> mTypes = new ArrayList<>();
+
+        /**
+         * Constructs a new builder for {@link TvIAppInfo}.
+         *
+         * @param context A Context of the application package implementing this class.
+         * @param component The name of the application component to be used for the
+         *                  {@link TvIAppService}.
+         */
+        public Builder(Context context, ComponentName component) {
+            if (context == null) {
+                throw new IllegalArgumentException("context cannot be null.");
+            }
+            Intent intent = new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
+            mResolveInfo = context.getPackageManager().resolveService(intent,
+                    PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+            if (mResolveInfo == null) {
+                throw new IllegalArgumentException("Invalid component. Can't find the service.");
+            }
+            mContext = context;
+        }
+
+        /**
+         * Creates a {@link TvIAppInfo} instance with the specified fields. Most of the information
+         * is obtained by parsing the AndroidManifest and {@link TvIAppService#SERVICE_META_DATA}
+         * for the {@link TvIAppService} this TV IApp implements.
+         *
+         * @return TvIAppInfo containing information about this TV IApp service.
+         */
+        public TvIAppInfo build() {
+            ComponentName componentName = new ComponentName(mResolveInfo.serviceInfo.packageName,
+                    mResolveInfo.serviceInfo.name);
+            String id;
+            id = generateIAppServiceId(componentName);
+            parseServiceMetadata();
+            return new TvIAppInfo(mResolveInfo, id, mTypes);
+        }
+
+        private static String generateIAppServiceId(ComponentName name) {
+            return name.flattenToShortString();
+        }
+
+        private void parseServiceMetadata() {
+            ServiceInfo si = mResolveInfo.serviceInfo;
+            PackageManager pm = mContext.getPackageManager();
+            try (XmlResourceParser parser =
+                         si.loadXmlMetaData(pm, TvIAppService.SERVICE_META_DATA)) {
+                if (parser == null) {
+                    throw new IllegalStateException("No " + TvIAppService.SERVICE_META_DATA
+                            + " meta-data found for " + si.name);
+                }
+
+                Resources res = pm.getResourcesForApplication(si.applicationInfo);
+                AttributeSet attrs = Xml.asAttributeSet(parser);
+
+                int type;
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                        && type != XmlPullParser.START_TAG) {
+                    // move to the START_TAG
+                }
+
+                String nodeName = parser.getName();
+                if (!XML_START_TAG_NAME.equals(nodeName)) {
+                    throw new IllegalStateException("Meta-data does not start with "
+                            + XML_START_TAG_NAME + " tag for " + si.name);
+                }
+
+                TypedArray sa = res.obtainAttributes(attrs,
+                        com.android.internal.R.styleable.TvIAppService);
+                CharSequence[] types = sa.getTextArray(
+                        com.android.internal.R.styleable.TvIAppService_supportedTypes);
+                for (CharSequence cs : types) {
+                    mTypes.add(cs.toString());
+                }
+
+                sa.recycle();
+            } catch (IOException | XmlPullParserException e) {
+                throw new IllegalStateException(
+                        "Failed reading meta-data for " + si.packageName, e);
+            } catch (PackageManager.NameNotFoundException e) {
+                throw new IllegalStateException("No resources found for " + si.packageName, e);
+            }
+        }
+    }
+}
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index b385d9c..8863729 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -38,22 +38,32 @@
 
 /**
  * The TvIAppService class represents a TV interactive applications RTE.
- * @hide
  */
 public abstract class TvIAppService extends Service {
     private static final boolean DEBUG = false;
     private static final String TAG = "TvIAppService";
 
-    private final Handler mServiceHandler = new ServiceHandler();
+    // TODO: cleanup and unhide APIs.
 
     /**
-     * This is the interface name that a service implementing an environment to run Tv IApp should
-     * say that it support -- that is, this is the action it uses for its intent filter. To be
-     * supported, the service must also require the BIND_TV_IAPP permission so that other
-     * applications cannot abuse it.
+     * This is the interface name that a service implementing a TV IApp service should say that it
+     * supports -- that is, this is the action it uses for its intent filter. To be supported, the
+     * service must also require the android.Manifest.permission#BIND_TV_IAPP permission so
+     * that other applications cannot abuse it.
      */
-    public static final String SERVICE_INTERFACE = "android.media.tv.TvIAppService";
+    public static final String SERVICE_INTERFACE = "android.media.tv.interactive.TvIAppService";
 
+    /**
+     * Name under which a TvIAppService component publishes information about itself. This meta-data
+     * must reference an XML resource containing an
+     * <code>&lt;{@link android.R.styleable#TvIAppService tv-iapp}&gt;</code>
+     * tag.
+     */
+    public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
+
+    private final Handler mServiceHandler = new ServiceHandler();
+
+    /** @hide */
     @Override
     public final IBinder onBind(Intent intent) {
         ITvIAppService.Stub tvIAppServiceBinder = new ITvIAppService.Stub() {
@@ -83,12 +93,17 @@
      *
      * @param iAppServiceId The ID of the TV IApp associated with the session.
      * @param type The type of the TV IApp associated with the session.
+     * @hide
      */
     @Nullable
-    public abstract Session onCreateSession(@NonNull String iAppServiceId, int type);
+    public Session onCreateSession(@NonNull String iAppServiceId, int type) {
+        // TODO: make it abstract when unhide
+        return null;
+    }
 
     /**
      * Base class for derived classes to implement to provide a TV interactive app session.
+     * @hide
      */
     public abstract static class Session implements KeyEvent.Callback {
         private final Object mLock = new Object();
@@ -113,6 +128,7 @@
 
         /**
          * Starts TvIAppService session.
+         * @hide
          */
         public void onStartIApp() {
         }
@@ -144,6 +160,7 @@
 
         /**
          * Releases TvIAppService session.
+         * @hide
          */
         public void onRelease() {
         }
@@ -245,6 +262,7 @@
 
     /**
      * Implements the internal ITvIAppSession interface.
+     * @hide
      */
     public static class ITvIAppSessionWrapper extends ITvIAppSession.Stub {
         private final Session mSessionImpl;
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
index 1090f33..403bfa7 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
@@ -581,7 +581,7 @@
          * Gets Number of Segment.
          */
         @IntRange(from = 0, to = 0xff)
-        public int getNumOfSegment() {
+        public int getNumberOfSegment() {
             return mNumOfSegment;
         }
 
@@ -639,7 +639,7 @@
              */
             @NonNull
             @IntRange(from = 0, to = 0xff)
-            public Builder setNumOfSegment(int numOfSegment) {
+            public Builder setNumberOfSegment(int numOfSegment) {
                 mNumOfSegment = numOfSegment;
                 return this;
             }
diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp
index 923377c..f90796e 100644
--- a/media/native/midi/amidi.cpp
+++ b/media/native/midi/amidi.cpp
@@ -325,8 +325,8 @@
         }
 
         uint8_t readBuffer[AMIDI_PACKET_SIZE];
-        ssize_t readCount = read(mPort->ufd, readBuffer, sizeof(readBuffer));
-        if (readCount == EINTR || readCount < 1) {
+        ssize_t readCount = TEMP_FAILURE_RETRY(read(mPort->ufd, readBuffer, sizeof(readBuffer)));
+        if (readCount < 1) {
             return  AMEDIA_ERROR_UNKNOWN;
         }
 
@@ -407,7 +407,8 @@
 
         ssize_t numTransferBytes =
                 AMIDI_makeSendBuffer(writeBuffer, data + numSent, blockSize, timestamp);
-        ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, numTransferBytes);
+        ssize_t numWritten = TEMP_FAILURE_RETRY(write(((AMIDI_Port*)inputPort)->ufd, writeBuffer,
+                                                      numTransferBytes));
         if (numWritten < 0) {
             break;  // error so bail out.
         }
@@ -430,7 +431,8 @@
 
     uint8_t opCode = AMIDI_OPCODE_FLUSH;
     ssize_t numTransferBytes = 1;
-    ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, &opCode, numTransferBytes);
+    ssize_t numWritten = TEMP_FAILURE_RETRY(write(((AMIDI_Port*)inputPort)->ufd, &opCode,
+                                                  numTransferBytes));
 
     if (numWritten < numTransferBytes) {
         ALOGE("AMidiInputPort_flush Couldn't write MIDI flush. requested:%zd, written:%zd",
diff --git a/packages/SettingsLib/TopIntroPreference/Android.bp b/packages/SettingsLib/TopIntroPreference/Android.bp
index 9577281..cd0bdea 100644
--- a/packages/SettingsLib/TopIntroPreference/Android.bp
+++ b/packages/SettingsLib/TopIntroPreference/Android.bp
@@ -19,4 +19,8 @@
     ],
     sdk_version: "system_current",
     min_sdk_version: "21",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.cellbroadcast",
+    ],
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 71bf9f6..a781a62 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -175,9 +175,12 @@
     public UserIconDrawable setBadgeIfManagedUser(Context context, int userId) {
         Drawable badge = null;
         if (userId != UserHandle.USER_NULL) {
-            boolean isManaged = context.getSystemService(DevicePolicyManager.class)
-                    .getProfileOwnerAsUser(userId) != null;
-            if (isManaged) {
+            DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+            boolean isCorp =
+                    dpm.getProfileOwnerAsUser(userId) != null // has an owner
+                    && dpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(UserHandle.of(userId))
+                            == null; // and has no supervisor
+            if (isCorp) {
                 badge = getDrawableForDisplayDensity(
                         context, com.android.internal.R.drawable.ic_corp_badge_case);
             }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index 6f42d59..a50d4ae1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -40,9 +40,6 @@
     public static final String[] SETTINGS_TO_BACKUP = {
         Settings.Global.APPLY_RAMPING_RINGER,
         Settings.Global.BUGREPORT_IN_POWER_MENU,
-        Settings.Global.CLOCKWORK_SYSUI_PACKAGE_NAME,
-        Settings.Global.CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME,
-        Settings.Global.CLOCKWORK_HOME_READY,
         Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
         Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
         Settings.Global.AUTO_TIME,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 2bd5bdc..bf62f1d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -116,8 +116,6 @@
         VALIDATORS.put(
                 Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, PERCENTAGE_INTEGER_VALIDATOR);
         VALIDATORS.put(Global.BLUETOOTH_ON, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Global.CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME, ANY_STRING_VALIDATOR);
-        VALIDATORS.put(Global.CLOCKWORK_SYSUI_PACKAGE_NAME, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Global.CLOCKWORK_HOME_READY, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Global.ENABLE_TARE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.ENABLE_TARE_ALARM_MANAGER, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 53df2e82..324c05e 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -591,6 +591,7 @@
                     Settings.Global.CACHED_APPS_FREEZER_ENABLED,
                     Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT,
                     Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
+                    Settings.Global.CLOCKWORK_HOME_READY,
                     Settings.Global.Wearable.BATTERY_SAVER_MODE,
                     Settings.Global.Wearable.COMBINED_LOCATION_ENABLED,
                     Settings.Global.Wearable.HAS_PAY_TOKENS,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index b83ea4a..d5f858c 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -34,7 +34,7 @@
 
     String ACTION = "com.android.systemui.action.PLUGIN_QS";
 
-    int VERSION = 11;
+    int VERSION = 12;
 
     String TAG = "QS";
 
@@ -46,6 +46,8 @@
     void setHeightOverride(int desiredHeight);
     void setHeaderClickable(boolean qsExpansionEnabled);
     boolean isCustomizing();
+    /** Close the QS customizer, if it is open. */
+    void closeCustomizer();
     void setOverscrolling(boolean overscrolling);
     void setExpanded(boolean qsExpanded);
     void setListening(boolean listening);
diff --git a/packages/SystemUI/res/drawable/assist_orb_scrim.xml b/packages/SystemUI/res/drawable/assist_orb_scrim.xml
deleted file mode 100644
index bbb2617..0000000
--- a/packages/SystemUI/res/drawable/assist_orb_scrim.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-  ~ Copyright (C) 2014 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
-  -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <gradient
-            android:type="linear"
-            android:angle="90"
-            android:startColor="#55000000"
-            android:endColor="#00000000" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/assist_orb.xml b/packages/SystemUI/res/layout/assist_orb.xml
deleted file mode 100644
index 0036ed6..0000000
--- a/packages/SystemUI/res/layout/assist_orb.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, 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.
-*/
--->
-
-<!-- Extends FrameLayout -->
-<com.android.systemui.assist.AssistOrbContainer
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <com.android.systemui.statusbar.AlphaOptimizedView
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/assist_orb_scrim_height"
-        android:layout_gravity="bottom"
-        android:id="@+id/assist_orb_scrim"
-        android:background="@drawable/assist_orb_scrim"/>
-
-    <com.android.systemui.assist.AssistOrbView
-        android:id="@+id/assist_orb"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-
-        <ImageView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:id="@+id/search_logo"/>
-    </com.android.systemui.assist.AssistOrbView>
-
-    <com.android.systemui.statusbar.AlphaOptimizedView
-        android:id="@+id/assist_orb_navbar_scrim"
-        android:layout_height="@dimen/assist_orb_navbar_scrim_height"
-        android:layout_width="match_parent"
-        android:layout_gravity="bottom"
-        android:elevation="50dp"
-        android:outlineProvider="none"
-        android:background="@drawable/assist_orb_navbar_scrim"/>
-
-</com.android.systemui.assist.AssistOrbContainer>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index af5d85d..c5b47d0 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -87,8 +87,6 @@
 
     <color name="notification_section_clear_all_btn_color">@color/GM2_grey_700</color>
 
-    <color name="assist_orb_color">#ffffff</color>
-
     <color name="keyguard_user_switcher_background_gradient_color">#77000000</color>
 
     <!-- The color of the navigation bar icons. Need to be in sync with ic_sysbar_* -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8d0895c..059aad7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -678,25 +678,6 @@
          from Keyguard. -->
     <dimen name="go_to_full_shade_appearing_translation">200dp</dimen>
 
-    <!-- The diameter of the search panel circle. -->
-    <dimen name="assist_orb_size">144dp</dimen>
-
-    <!-- The margin to the edge of the screen from where the orb starts to appear -->
-    <dimen name="assist_orb_base_margin">22dp</dimen>
-
-    <!-- The amount the orb translates when appearing -->
-    <dimen name="assist_orb_travel_distance">26dp</dimen>
-
-    <!-- The elevation of the orb -->
-    <dimen name="assist_orb_elevation">12dp</dimen>
-
-    <!-- The height of the scrim behind the orb. -->
-    <dimen name="assist_orb_scrim_height">250dp</dimen>
-
-    <!-- The height of the scrim behind the search panel circle. Should be navigation_bar_height
-         + 8dp. -->
-    <dimen name="assist_orb_navbar_scrim_height">56dp</dimen>
-
     <!-- The width/height of the keyguard bottom area icon view on keyguard. -->
     <dimen name="keyguard_affordance_height">48dp</dimen>
     <dimen name="keyguard_affordance_width">48dp</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index e960e81..03f04d3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -38,10 +38,8 @@
     val shouldListenForFingerprintAssistant: Boolean,
     val switchingUser: Boolean,
     val udfps: Boolean,
-    val userDoesNotHaveTrust: Boolean,
-    val userNeedsStrongAuth: Boolean
+    val userDoesNotHaveTrust: Boolean
 ) : KeyguardListenModel()
-
 /**
  * Verbose debug information associated with [KeyguardUpdateMonitor.shouldListenForFace].
  */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d27bc67..e24f07c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2222,11 +2222,9 @@
                 !(mFingerprintLockedOut && mBouncer && mCredentialAttempted);
 
         final boolean isEncryptedOrLockdownForUser = isEncryptedOrLockdown(user);
-        final boolean userNeedsStrongAuth = userNeedsStrongAuth();
         final boolean shouldListenUdfpsState = !isUdfps
                 || (!userCanSkipBouncer
                     && !isEncryptedOrLockdownForUser
-                    && !userNeedsStrongAuth
                     && userDoesNotHaveTrust
                     && !mFingerprintLockedOut);
 
@@ -2257,8 +2255,7 @@
                         shouldListenForFingerprintAssistant,
                         mSwitchingUser,
                         isUdfps,
-                        userDoesNotHaveTrust,
-                        userNeedsStrongAuth));
+                        userDoesNotHaveTrust));
         }
 
         return shouldListen;
@@ -2362,7 +2359,15 @@
                 || (DEBUG_FINGERPRINT
                     && model instanceof KeyguardFingerprintListenModel
                     && mFingerprintRunningState != BIOMETRIC_STATE_RUNNING);
-        if (notYetRunning && model.getListening()) {
+        final boolean running =
+                (DEBUG_FACE
+                        && model instanceof KeyguardFaceListenModel
+                        && mFaceRunningState == BIOMETRIC_STATE_RUNNING)
+                        || (DEBUG_FINGERPRINT
+                        && model instanceof KeyguardFingerprintListenModel
+                        && mFingerprintRunningState == BIOMETRIC_STATE_RUNNING);
+        if (notYetRunning && model.getListening()
+                || running && !model.getListening()) {
             mListenModels.add(model);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index b1197e6..9d5b93c 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -7,7 +7,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.SearchManager;
 import android.content.ActivityNotFoundException;
@@ -27,17 +26,16 @@
 
 import com.android.internal.app.AssistUtils;
 import com.android.internal.app.IVoiceInteractionSessionListener;
-import com.android.internal.app.IVoiceInteractionSessionShowCallback;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
 import com.android.systemui.assist.ui.DefaultUiController;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 import javax.inject.Inject;
@@ -124,23 +122,8 @@
 
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final CommandQueue mCommandQueue;
-    private final AssistOrbController mOrbController;
     protected final AssistUtils mAssistUtils;
 
-    private IVoiceInteractionSessionShowCallback mShowCallback =
-            new IVoiceInteractionSessionShowCallback.Stub() {
-
-                @Override
-                public void onFailed() throws RemoteException {
-                    mOrbController.postHide();
-                }
-
-                @Override
-                public void onShown() throws RemoteException {
-                    mOrbController.postHide();
-                }
-            };
-
     @Inject
     public AssistManager(
             DeviceProvisionedController controller,
@@ -149,20 +132,18 @@
             CommandQueue commandQueue,
             PhoneStateMonitor phoneStateMonitor,
             OverviewProxyService overviewProxyService,
-            ConfigurationController configurationController,
             Lazy<SysUiState> sysUiState,
             DefaultUiController defaultUiController,
-            AssistLogger assistLogger) {
+            AssistLogger assistLogger,
+            @Main Handler uiHandler) {
         mContext = context;
         mDeviceProvisionedController = controller;
         mCommandQueue = commandQueue;
         mAssistUtils = assistUtils;
-        mAssistDisclosure = new AssistDisclosure(context, new Handler());
+        mAssistDisclosure = new AssistDisclosure(context, uiHandler);
         mPhoneStateMonitor = phoneStateMonitor;
         mAssistLogger = assistLogger;
 
-        mOrbController = new AssistOrbController(configurationController, context);
-
         registerVoiceInteractionSessionListener();
 
         mUiController = defaultUiController;
@@ -223,10 +204,6 @@
                 });
     }
 
-    protected boolean shouldShowOrb() {
-        return !ActivityManager.isLowRamDeviceStatic();
-    }
-
     public void startAssist(Bundle args) {
         final ComponentName assistComponent = getAssistInfo();
         if (assistComponent == null) {
@@ -234,10 +211,6 @@
         }
 
         final boolean isService = assistComponent.equals(getVoiceInteractorComponentName());
-        if (!isService || (!isVoiceSessionRunning() && shouldShowOrb())) {
-            mOrbController.showOrb(assistComponent, isService);
-            mOrbController.postHideDelayed(isService ? TIMEOUT_SERVICE : TIMEOUT_ACTIVITY);
-        }
 
         if (args == null) {
             args = new Bundle();
@@ -329,7 +302,7 @@
 
     private void startVoiceInteractor(Bundle args) {
         mAssistUtils.showSessionForActiveService(args,
-                VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, null);
+                VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, null, null);
     }
 
     public void launchVoiceAssistFromKeyguard() {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java
deleted file mode 100644
index 95b9e81..0000000
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.assist;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
-
-public class AssistOrbContainer extends FrameLayout {
-
-    private static final long EXIT_START_DELAY = 150;
-
-    private View mScrim;
-    private View mNavbarScrim;
-    private AssistOrbView mOrb;
-
-    private boolean mAnimatingOut;
-
-    public AssistOrbContainer(Context context) {
-        this(context, null);
-    }
-
-    public AssistOrbContainer(Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public AssistOrbContainer(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mScrim = findViewById(R.id.assist_orb_scrim);
-        mNavbarScrim = findViewById(R.id.assist_orb_navbar_scrim);
-        mOrb = (AssistOrbView) findViewById(R.id.assist_orb);
-    }
-
-    public void show(final boolean show, boolean animate, Runnable onDone) {
-        if (show) {
-            if (getVisibility() != View.VISIBLE) {
-                setVisibility(View.VISIBLE);
-                if (animate) {
-                    startEnterAnimation(onDone);
-                } else {
-                    reset();
-                    if (onDone != null) {
-                        onDone.run();
-                    }
-                }
-            }
-        } else {
-            if (animate) {
-                startExitAnimation(new Runnable() {
-                    @Override
-                    public void run() {
-                        mAnimatingOut = false;
-                        setVisibility(View.GONE);
-                        if (onDone != null) {
-                            onDone.run();
-                        }
-                    }
-                });
-            } else {
-                setVisibility(View.GONE);
-                if (onDone != null) {
-                    onDone.run();
-                }
-            }
-        }
-    }
-
-    private void reset() {
-        mAnimatingOut = false;
-        mOrb.reset();
-        mScrim.setAlpha(1f);
-        mNavbarScrim.setAlpha(1f);
-    }
-
-    private void startEnterAnimation(Runnable onDone) {
-        if (mAnimatingOut) {
-            return;
-        }
-        mOrb.startEnterAnimation();
-        mScrim.setAlpha(0f);
-        mNavbarScrim.setAlpha(0f);
-        post(new Runnable() {
-            @Override
-            public void run() {
-                mScrim.animate()
-                        .alpha(1f)
-                        .setDuration(300)
-                        .setStartDelay(0)
-                        .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-                mNavbarScrim.animate()
-                        .alpha(1f)
-                        .setDuration(300)
-                        .setStartDelay(0)
-                        .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
-                        .withEndAction(onDone);
-            }
-        });
-    }
-
-    private void startExitAnimation(final Runnable endRunnable) {
-        if (mAnimatingOut) {
-            if (endRunnable != null) {
-                endRunnable.run();
-            }
-            return;
-        }
-        mAnimatingOut = true;
-        mOrb.startExitAnimation(EXIT_START_DELAY);
-        mScrim.animate()
-                .alpha(0f)
-                .setDuration(250)
-                .setStartDelay(EXIT_START_DELAY)
-                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mNavbarScrim.animate()
-                .alpha(0f)
-                .setDuration(250)
-                .setStartDelay(EXIT_START_DELAY)
-                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                .withEndAction(endRunnable);
-    }
-
-    /**
-     * Whether the panel is showing, or, if it's animating, whether it will be
-     * when the animation is done.
-     */
-    public boolean isShowing() {
-        return getVisibility() == View.VISIBLE && !mAnimatingOut;
-    }
-
-    public AssistOrbView getOrb() {
-        return mOrb;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java
deleted file mode 100644
index 4082015..0000000
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.systemui.assist;
-
-import android.annotation.NonNull;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.os.Binder;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.ImageView;
-
-import com.android.settingslib.applications.InterestingConfigChanges;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-
-/**
- * AssistOrbController controls the showing and hiding of the assistant orb.
- */
-public class AssistOrbController {
-    private static final String ASSIST_ICON_METADATA_NAME =
-            "com.android.systemui.action_assist_icon";
-    private static final String TAG = "AssistOrbController";
-    private static final boolean VERBOSE = false;
-
-    private final InterestingConfigChanges mInterestingConfigChanges;
-    private AssistOrbContainer mView;
-    private final Context mContext;
-    private final WindowManager mWindowManager;
-
-    private Runnable mHideRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mView.removeCallbacks(this);
-            mView.show(false /* show */, true /* animate */, () -> {
-                if (mView.isAttachedToWindow()) {
-                    mWindowManager.removeView(mView);
-                }
-            });
-        }
-    };
-
-    private ConfigurationController.ConfigurationListener mConfigurationListener =
-            new ConfigurationController.ConfigurationListener() {
-                @Override
-                public void onConfigChanged(Configuration newConfig) {
-                    if (!mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
-                        return;
-                    }
-                    boolean visible = false;
-                    if (mView != null) {
-                        visible = mView.isShowing();
-                        if (mView.isAttachedToWindow()) {
-                            mWindowManager.removeView(mView);
-                        }
-                    }
-
-                    if (visible) {
-                        showOrb(false);
-                    }
-                }
-            };
-
-    AssistOrbController(ConfigurationController configurationController, Context context) {
-        mContext = context;
-        mWindowManager =  mContext.getSystemService(WindowManager.class);
-        mInterestingConfigChanges = new InterestingConfigChanges(ActivityInfo.CONFIG_ORIENTATION
-                | ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_UI_MODE
-                | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS);
-
-        configurationController.addCallback(mConfigurationListener);
-        mConfigurationListener.onConfigChanged(context.getResources().getConfiguration());
-    }
-
-    public void postHide() {
-        mView.post(mHideRunnable);
-    }
-
-    public void postHideDelayed(long delayMs) {
-        mView.postDelayed(mHideRunnable, delayMs);
-    }
-
-    private void showOrb(boolean animated) {
-        if (mView == null) {
-            mView = (AssistOrbContainer) LayoutInflater.from(mContext).inflate(
-                    R.layout.assist_orb, null);
-            mView.setVisibility(View.GONE);
-            mView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
-                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-        }
-        if (!mView.isAttachedToWindow()) {
-            WindowManager.LayoutParams params = getLayoutParams();
-            mWindowManager.addView(mView, params);
-        }
-        mView.show(true, animated, null);
-    }
-
-    private WindowManager.LayoutParams getLayoutParams() {
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                mContext.getResources().getDimensionPixelSize(R.dimen.assist_orb_scrim_height),
-                WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING,
-                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
-                PixelFormat.TRANSLUCENT);
-        lp.token = new Binder();
-        lp.gravity = Gravity.BOTTOM | Gravity.START;
-        lp.setTitle("AssistPreviewPanel");
-        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
-                | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
-        return lp;
-    }
-
-    public void showOrb(@NonNull ComponentName assistComponent, boolean isService) {
-        showOrb(true);
-        maybeSwapSearchIcon(assistComponent, isService);
-    }
-
-    private void maybeSwapSearchIcon(@NonNull ComponentName assistComponent, boolean isService) {
-        replaceDrawable(mView.getOrb().getLogo(), assistComponent, ASSIST_ICON_METADATA_NAME,
-                isService);
-    }
-
-    public void replaceDrawable(ImageView v, ComponentName component, String name,
-            boolean isService) {
-        if (component != null) {
-            try {
-                PackageManager packageManager = mContext.getPackageManager();
-                // Look for the search icon specified in the activity meta-data
-                Bundle metaData = isService
-                        ? packageManager.getServiceInfo(
-                        component, PackageManager.GET_META_DATA).metaData
-                        : packageManager.getActivityInfo(
-                                component, PackageManager.GET_META_DATA).metaData;
-                if (metaData != null) {
-                    int iconResId = metaData.getInt(name);
-                    if (iconResId != 0) {
-                        Resources res = packageManager.getResourcesForApplication(
-                                component.getPackageName());
-                        v.setImageDrawable(res.getDrawable(iconResId));
-                        return;
-                    }
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                if (VERBOSE) {
-                    Log.v(TAG, "Assistant component "
-                            + component.flattenToShortString() + " not found");
-                }
-            } catch (Resources.NotFoundException nfe) {
-                Log.w(TAG, "Failed to swap drawable from "
-                        + component.flattenToShortString(), nfe);
-            }
-        }
-        v.setImageDrawable(null);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java
deleted file mode 100644
index 16d9c84..0000000
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbView.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2014 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.assist;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-import android.view.animation.Interpolator;
-import android.view.animation.OvershootInterpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
-
-public class AssistOrbView extends FrameLayout {
-
-    private final int mCircleMinSize;
-    private final int mBaseMargin;
-    private final int mStaticOffset;
-    private final Paint mBackgroundPaint = new Paint();
-    private final Rect mCircleRect = new Rect();
-    private final Rect mStaticRect = new Rect();
-    private final Interpolator mOvershootInterpolator = new OvershootInterpolator();
-
-    private boolean mClipToOutline;
-    private final int mMaxElevation;
-    private float mOutlineAlpha;
-    private float mOffset;
-    private float mCircleSize;
-    private ImageView mLogo;
-    private float mCircleAnimationEndValue;
-
-    private ValueAnimator mOffsetAnimator;
-    private ValueAnimator mCircleAnimator;
-
-    private ValueAnimator.AnimatorUpdateListener mCircleUpdateListener
-            = new ValueAnimator.AnimatorUpdateListener() {
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            applyCircleSize((float) animation.getAnimatedValue());
-            updateElevation();
-        }
-    };
-    private AnimatorListenerAdapter mClearAnimatorListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            mCircleAnimator = null;
-        }
-    };
-    private ValueAnimator.AnimatorUpdateListener mOffsetUpdateListener
-            = new ValueAnimator.AnimatorUpdateListener() {
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            mOffset = (float) animation.getAnimatedValue();
-            updateLayout();
-        }
-    };
-
-
-    public AssistOrbView(Context context) {
-        this(context, null);
-    }
-
-    public AssistOrbView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public AssistOrbView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public AssistOrbView(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        setOutlineProvider(new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                if (mCircleSize > 0.0f) {
-                    outline.setOval(mCircleRect);
-                } else {
-                    outline.setEmpty();
-                }
-                outline.setAlpha(mOutlineAlpha);
-            }
-        });
-        setWillNotDraw(false);
-        mCircleMinSize = context.getResources().getDimensionPixelSize(
-                R.dimen.assist_orb_size);
-        mBaseMargin = context.getResources().getDimensionPixelSize(
-                R.dimen.assist_orb_base_margin);
-        mStaticOffset = context.getResources().getDimensionPixelSize(
-                R.dimen.assist_orb_travel_distance);
-        mMaxElevation = context.getResources().getDimensionPixelSize(
-                R.dimen.assist_orb_elevation);
-        mBackgroundPaint.setAntiAlias(true);
-        mBackgroundPaint.setColor(getResources().getColor(R.color.assist_orb_color));
-    }
-
-    public ImageView getLogo() {
-        return mLogo;
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-        drawBackground(canvas);
-    }
-
-    private void drawBackground(Canvas canvas) {
-        canvas.drawCircle(mCircleRect.centerX(), mCircleRect.centerY(), mCircleSize / 2,
-                mBackgroundPaint);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mLogo = findViewById(R.id.search_logo);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        mLogo.layout(0, 0, mLogo.getMeasuredWidth(), mLogo.getMeasuredHeight());
-        if (changed) {
-            updateCircleRect(mStaticRect, mStaticOffset, true);
-        }
-    }
-
-    public void animateCircleSize(float circleSize, long duration,
-            long startDelay, Interpolator interpolator) {
-        if (circleSize == mCircleAnimationEndValue) {
-            return;
-        }
-        if (mCircleAnimator != null) {
-            mCircleAnimator.cancel();
-        }
-        mCircleAnimator = ValueAnimator.ofFloat(mCircleSize, circleSize);
-        mCircleAnimator.addUpdateListener(mCircleUpdateListener);
-        mCircleAnimator.addListener(mClearAnimatorListener);
-        mCircleAnimator.setInterpolator(interpolator);
-        mCircleAnimator.setDuration(duration);
-        mCircleAnimator.setStartDelay(startDelay);
-        mCircleAnimator.start();
-        mCircleAnimationEndValue = circleSize;
-    }
-
-    private void applyCircleSize(float circleSize) {
-        mCircleSize = circleSize;
-        updateLayout();
-    }
-
-    private void updateElevation() {
-        float t = (mStaticOffset - mOffset) / (float) mStaticOffset;
-        t = 1.0f - Math.max(t, 0.0f);
-        float offset = t * mMaxElevation;
-        setElevation(offset);
-    }
-
-    /**
-     * Animates the offset to the edge of the screen.
-     *
-     * @param offset The offset to apply.
-     * @param startDelay The desired start delay if animated.
-     *
-     * @param interpolator The desired interpolator if animated. If null,
-     *                     a default interpolator will be taken designed for appearing or
-     *                     disappearing.
-     */
-    private void animateOffset(float offset, long duration, long startDelay,
-            Interpolator interpolator) {
-        if (mOffsetAnimator != null) {
-            mOffsetAnimator.removeAllListeners();
-            mOffsetAnimator.cancel();
-        }
-        mOffsetAnimator = ValueAnimator.ofFloat(mOffset, offset);
-        mOffsetAnimator.addUpdateListener(mOffsetUpdateListener);
-        mOffsetAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mOffsetAnimator = null;
-            }
-        });
-        mOffsetAnimator.setInterpolator(interpolator);
-        mOffsetAnimator.setStartDelay(startDelay);
-        mOffsetAnimator.setDuration(duration);
-        mOffsetAnimator.start();
-    }
-
-    private void updateLayout() {
-        updateCircleRect();
-        updateLogo();
-        invalidateOutline();
-        invalidate();
-        updateClipping();
-    }
-
-    private void updateClipping() {
-        boolean clip = mCircleSize < mCircleMinSize;
-        if (clip != mClipToOutline) {
-            setClipToOutline(clip);
-            mClipToOutline = clip;
-        }
-    }
-
-    private void updateLogo() {
-        float translationX = (mCircleRect.left + mCircleRect.right) / 2.0f - mLogo.getWidth() / 2.0f;
-        float translationY = (mCircleRect.top + mCircleRect.bottom) / 2.0f
-                - mLogo.getHeight() / 2.0f - mCircleMinSize / 7f;
-        float t = (mStaticOffset - mOffset) / (float) mStaticOffset;
-        translationY += t * mStaticOffset * 0.1f;
-        float alpha = 1.0f-t;
-        alpha = Math.max((alpha - 0.5f) * 2.0f, 0);
-        mLogo.setImageAlpha((int) (alpha * 255));
-        mLogo.setTranslationX(translationX);
-        mLogo.setTranslationY(translationY);
-    }
-
-    private void updateCircleRect() {
-        updateCircleRect(mCircleRect, mOffset, false);
-    }
-
-    private void updateCircleRect(Rect rect, float offset, boolean useStaticSize) {
-        int left, top;
-        float circleSize = useStaticSize ? mCircleMinSize : mCircleSize;
-        left = (int) (getWidth() - circleSize) / 2;
-        top = (int) (getHeight() - circleSize / 2 - mBaseMargin - offset);
-        rect.set(left, top, (int) (left + circleSize), (int) (top + circleSize));
-    }
-
-    public void startExitAnimation(long delay) {
-        animateCircleSize(0, 200, delay, Interpolators.FAST_OUT_LINEAR_IN);
-        animateOffset(0, 200, delay, Interpolators.FAST_OUT_LINEAR_IN);
-    }
-
-    public void startEnterAnimation() {
-        applyCircleSize(0);
-        post(new Runnable() {
-            @Override
-            public void run() {
-                animateCircleSize(mCircleMinSize, 300, 0 /* delay */, mOvershootInterpolator);
-                animateOffset(mStaticOffset, 400, 0 /* delay */, Interpolators.LINEAR_OUT_SLOW_IN);
-            }
-        });
-    }
-
-    public void reset() {
-        mClipToOutline = false;
-        mBackgroundPaint.setAlpha(255);
-        mOutlineAlpha = 1.0f;
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        // not really true but it's ok during an animation, as it's never permanent
-        return false;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 1bad19e..d60edbf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -47,6 +47,7 @@
 import android.os.Trace;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.provider.Settings;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -162,6 +163,7 @@
     private boolean mOnFingerDown;
     private boolean mAttemptedToDismissKeyguard;
     private Set<Callback> mCallbacks = new HashSet<>();
+    private final VibrationEffect mLowTick;
 
     @VisibleForTesting
     public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
@@ -171,6 +173,7 @@
                     .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)
                     .build();
 
+    // haptic to use for successful device entry
     public static final VibrationEffect EFFECT_CLICK =
             VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
 
@@ -276,6 +279,9 @@
                     return;
                 }
                 mGoodCaptureReceived = true;
+                if (mVibrator != null) {
+                    mVibrator.cancel();
+                }
                 mView.stopIllumination();
                 if (mServerRequest != null) {
                     mServerRequest.onAcquiredGood();
@@ -434,7 +440,7 @@
                 if (idx == event.getActionIndex()) {
                     boolean actionMoveWithinSensorArea =
                             isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx),
-                                fromUdfpsView);
+                                    fromUdfpsView);
                     if ((fromUdfpsView || actionMoveWithinSensorArea)
                             && shouldTryToDismissKeyguard()) {
                         Log.v(TAG, "onTouch | dismiss keyguard ACTION_MOVE");
@@ -508,9 +514,9 @@
 
     private boolean shouldTryToDismissKeyguard() {
         return mView.getAnimationViewController() != null
-            && mView.getAnimationViewController() instanceof UdfpsKeyguardViewController
-            && mKeyguardStateController.canDismissLockScreen()
-            && !mAttemptedToDismissKeyguard;
+                && mView.getAnimationViewController() instanceof UdfpsKeyguardViewController
+                && mKeyguardStateController.canDismissLockScreen()
+                && !mAttemptedToDismissKeyguard;
     }
 
     @Inject
@@ -542,7 +548,6 @@
             @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
         mContext = context;
         mExecution = execution;
-        // TODO (b/185124905): inject main handler and vibrator once done prototyping
         mVibrator = vibrator;
         mInflater = inflater;
         // The fingerprint manager is queried for UDFPS before this class is constructed, so the
@@ -567,6 +572,7 @@
         mConfigurationController = configurationController;
         mSystemClock = systemClock;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+        mLowTick = lowTick();
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -601,6 +607,27 @@
         udfpsHapticsSimulator.setUdfpsController(this);
     }
 
+    private VibrationEffect lowTick() {
+        float tickIntensity = Settings.Global.getFloat(
+                mContext.getContentResolver(), "low-tick-intensity", .5f);
+        VibrationEffect.Composition composition = VibrationEffect.startComposition();
+        composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
+                tickIntensity, 0);
+        int tickDelay = Settings.Global.getInt(
+                mContext.getContentResolver(), "low-tick-delay", 25);
+        int primitives = 1000 / tickDelay;
+        float[] rampUp = new float[]{.48f, .58f, .69f, .83f};
+        for (int i = 0; i < rampUp.length; i++) {
+            composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
+                    tickIntensity * rampUp[i], tickDelay);
+        }
+        for (int i = rampUp.length; i < primitives; i++) {
+            composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
+                    tickIntensity, tickDelay);
+        }
+        return composition.compose();
+    }
+
     /**
      * Play haptic to signal udfps scanning started.
      */
@@ -610,8 +637,8 @@
             mVibrator.vibrate(
                     Process.myUid(),
                     mContext.getOpPackageName(),
-                    EFFECT_CLICK,
-                    "udfps-onStart",
+                    mLowTick,
+                    "udfps-onStart-tick",
                     VIBRATION_SONIFICATION_ATTRIBUTES);
         }
     }
@@ -998,6 +1025,7 @@
             }
         }
         mOnFingerDown = false;
+        mVibrator.cancel();
         if (mView.isIlluminationRequested()) {
             mView.stopIllumination();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 3c86760..383c442 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -69,6 +69,7 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -129,6 +130,7 @@
         },
         subcomponents = {
             StatusBarComponent.class,
+            StatusBarFragmentComponent.class,
             NotificationRowComponent.class,
             DozeComponent.class,
             ExpandableNotificationRowComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 765c245..d27c39a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -16,6 +16,10 @@
 
 package com.android.systemui.doze;
 
+import static android.os.PowerManager.GO_TO_SLEEP_REASON_TIMEOUT;
+
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -31,11 +35,13 @@
 import android.provider.Settings;
 import android.util.IndentingPrintWriter;
 
+import com.android.internal.R;
 import com.android.systemui.doze.dagger.BrightnessSensor;
 import com.android.systemui.doze.dagger.DozeScope;
 import com.android.systemui.doze.dagger.WrappedService;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 
@@ -57,6 +63,12 @@
             "com.android.systemui.doze.AOD_BRIGHTNESS";
     protected static final String BRIGHTNESS_BUCKET = "brightness_bucket";
 
+    /**
+     * Just before the screen times out from user inactivity, DisplayPowerController dims the screen
+     * brightness to the lower of {@link #mScreenBrightnessDim}, or the current brightness minus
+     * this amount.
+     */
+    private final float mScreenBrightnessMinimumDimAmountFloat;
     private final Context mContext;
     private final DozeMachine.Service mDozeService;
     private final DozeHost mDozeHost;
@@ -87,6 +99,8 @@
      */
     private int mDebugBrightnessBucket = -1;
 
+    private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+
     @Inject
     public DozeScreenBrightness(
             Context context,
@@ -98,8 +112,8 @@
             WakefulnessLifecycle wakefulnessLifecycle,
             DozeParameters dozeParameters,
             DevicePostureController devicePostureController,
-            DozeLog dozeLog
-    ) {
+            DozeLog dozeLog,
+            UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
         mContext = context;
         mDozeService = service;
         mSensorManager = sensorManager;
@@ -111,6 +125,10 @@
         mDozeHost = host;
         mHandler = handler;
         mDozeLog = dozeLog;
+        mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+
+        mScreenBrightnessMinimumDimAmountFloat = context.getResources().getFloat(
+                R.dimen.config_screenBrightnessMinimumDimAmountFloat);
 
         mDefaultDozeBrightness = alwaysOnDisplayPolicy.defaultDozeBrightness;
         mScreenBrightnessDim = alwaysOnDisplayPolicy.dimBrightness;
@@ -163,14 +181,15 @@
         }
     }
 
-    private void updateBrightnessAndReady(boolean force) {
+    public void updateBrightnessAndReady(boolean force) {
         if (force || mRegistered || mDebugBrightnessBucket != -1) {
             int sensorValue = mDebugBrightnessBucket == -1
                     ? mLastSensorValue : mDebugBrightnessBucket;
             int brightness = computeBrightness(sensorValue);
             boolean brightnessReady = brightness > 0;
             if (brightnessReady) {
-                mDozeService.setDozeScreenBrightness(clampToUserSetting(brightness));
+                mDozeService.setDozeScreenBrightness(
+                        clampToDimBrightnessForScreenOff(clampToUserSetting(brightness)));
             }
 
             int scrimOpacity = -1;
@@ -243,13 +262,24 @@
     /**
      * Clamp the brightness to the dim brightness value used by PowerManagerService just before the
      * device times out and goes to sleep, if we are sleeping from a timeout. This ensures that we
-     * don't raise the brightness back to the user setting before playing the screen off animation.
+     * don't raise the brightness back to the user setting before or during the screen off
+     * animation.
      */
     private int clampToDimBrightnessForScreenOff(int brightness) {
-        if (mDozeParameters.shouldControlUnlockedScreenOff()
-                && mWakefulnessLifecycle.getLastSleepReason()
-                == PowerManager.GO_TO_SLEEP_REASON_TIMEOUT) {
-            return Math.min(mScreenBrightnessDim, brightness);
+        final boolean screenTurningOff =
+                mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
+                        || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_GOING_TO_SLEEP;
+        if (screenTurningOff
+                && mWakefulnessLifecycle.getLastSleepReason() == GO_TO_SLEEP_REASON_TIMEOUT) {
+            return Math.max(
+                    PowerManager.BRIGHTNESS_OFF,
+                    // Use the lower of either the dim brightness, or the current brightness reduced
+                    // by the minimum dim amount. This is the same logic used in
+                    // DisplayPowerController#updatePowerState to apply a minimum dim amount.
+                    Math.min(
+                            brightness - (int) Math.floor(
+                                    mScreenBrightnessMinimumDimAmountFloat * 255),
+                            mScreenBrightnessDim));
         } else {
             return brightness;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 908397b..3631938 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -77,6 +77,7 @@
     private final Provider<UdfpsController> mUdfpsControllerProvider;
     @Nullable private UdfpsController mUdfpsController;
     private final DozeLog mDozeLog;
+    private final DozeScreenBrightness mDozeScreenBrightness;
 
     private int mPendingScreenState = Display.STATE_UNKNOWN;
     private SettableWakeLock mWakeLock;
@@ -90,7 +91,8 @@
             WakeLock wakeLock,
             AuthController authController,
             Provider<UdfpsController> udfpsControllerProvider,
-            DozeLog dozeLog) {
+            DozeLog dozeLog,
+            DozeScreenBrightness dozeScreenBrightness) {
         mDozeService = service;
         mHandler = handler;
         mParameters = parameters;
@@ -99,6 +101,7 @@
         mAuthController = authController;
         mUdfpsControllerProvider = udfpsControllerProvider;
         mDozeLog = dozeLog;
+        mDozeScreenBrightness = dozeScreenBrightness;
 
         updateUdfpsController();
         if (mUdfpsController == null) {
@@ -209,6 +212,12 @@
         if (screenState != Display.STATE_UNKNOWN) {
             if (DEBUG) Log.d(TAG, "setDozeScreenState(" + screenState + ")");
             mDozeService.setDozeScreenState(screenState);
+            if (screenState == Display.STATE_DOZE) {
+                // If we're entering doze, update the doze screen brightness. We might have been
+                // clamping it to the dim brightness during the screen off animation, and we should
+                // now change it to the brightness we actually want according to the sensor.
+                mDozeScreenBrightness.updateBrightnessAndReady(false /* force */);
+            }
             mPendingScreenState = Display.STATE_UNKNOWN;
             mWakeLock.setAcquired(false);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index 5f7ad58..f8bdaf6 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -18,8 +18,6 @@
 
 import android.content.Context;
 import android.util.FeatureFlagUtils;
-import android.util.Log;
-import android.widget.Toast;
 
 import com.android.systemui.dagger.SysUISingleton;
 
@@ -33,12 +31,10 @@
 @SysUISingleton
 public class FeatureFlags {
     private final FlagReader mFlagReader;
-    private final Context mContext;
 
     @Inject
-    public FeatureFlags(FlagReader flagReader, Context context) {
+    public FeatureFlags(FlagReader flagReader) {
         mFlagReader = flagReader;
-        mContext = context;
     }
 
     /**
@@ -49,22 +45,6 @@
         return mFlagReader.isEnabled(flag);
     }
 
-    public void assertLegacyPipelineEnabled() {
-        if (isNewNotifPipelineRenderingEnabled()) {
-            throw new IllegalStateException("Old pipeline code running w/ new pipeline enabled");
-        }
-    }
-
-    public boolean checkLegacyPipelineEnabled() {
-        if (!isNewNotifPipelineRenderingEnabled()) {
-            return true;
-        }
-        Log.d("NotifPipeline", "Old pipeline code running w/ new pipeline enabled",
-                new Exception());
-        Toast.makeText(mContext, "Old pipeline code running!", Toast.LENGTH_SHORT).show();
-        return false;
-    }
-
     public boolean isNewNotifPipelineRenderingEnabled() {
         return isEnabled(Flags.NEW_NOTIFICATION_PIPELINE_RENDERING);
     }
@@ -129,8 +109,8 @@
     }
 
     /** System setting for provider model behavior */
-    public boolean isProviderModelSettingEnabled() {
-        return FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
+    public boolean isProviderModelSettingEnabled(Context context) {
+        return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
     }
 
     /**
@@ -146,9 +126,4 @@
     public boolean useCombinedQSHeaders() {
         return isEnabled(Flags.COMBINED_QS_HEADERS);
     }
-
-    /** static method for the system setting */
-    public static boolean isProviderModelSettingEnabled(Context context) {
-        return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index ee24f45..c60e098 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -92,6 +92,9 @@
 
     /***************************************/
     // 600- status bar
+    public static final BooleanFlag STATUS_BAR_PROVIDER_MODEL =
+            new BooleanFlag(600, false);
+
     public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS =
             new BooleanFlag(601, false);
 
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
index 4f5a969..7f57fcc 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -24,7 +24,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.qs.QSFragment;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
 import java.io.FileDescriptor;
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java
index c8afd72..9ca0293 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java
@@ -26,8 +26,8 @@
 import javax.inject.Qualifier;
 
 /**
- * A {@link LogBuffer} for
- * {@link com.android.systemui.statusbar.phone.CollapsedStatusBarFragment}-related messages.
+ * A {@link LogBuffer} for {@link
+ * com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment}-related messages.
  */
 @Qualifier
 @Documented
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 02beff9..d3bd0a5 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -110,7 +110,7 @@
 
     /**
      * Provides a logging buffer for
-     * {@link com.android.systemui.statusbar.phone.CollapsedStatusBarFragment}.
+     * {@link com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment}.
      */
     @Provides
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index fa37c86..5afefa1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -100,8 +100,7 @@
     private static final int MAX_NUM_LOGGED_PREDICTIONS = 10;
     private static final int MAX_NUM_LOGGED_GESTURES = 10;
 
-    // Temporary log until b/201642126 is resolved
-    static final boolean DEBUG_MISSING_GESTURE = true;
+    static final boolean DEBUG_MISSING_GESTURE = false;
     static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture";
 
     private ISystemGestureExclusionListener mGestureExclusionListener =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index cdf770f..b533ac4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -1,5 +1,7 @@
 package com.android.systemui.qs;
 
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -20,6 +22,7 @@
 import androidx.viewpager.widget.PagerAdapter;
 import androidx.viewpager.widget.ViewPager;
 
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QSTile;
@@ -533,6 +536,11 @@
 
     private final ViewPager.OnPageChangeListener mOnPageChangeListener =
             new ViewPager.SimpleOnPageChangeListener() {
+
+                private int mCurrentScrollState = SCROLL_STATE_IDLE;
+                // Flag to avoid redundant call InteractionJankMonitor::begin()
+                private boolean mIsScrollJankTraceBegin = false;
+
                 @Override
                 public void onPageSelected(int position) {
                     updateSelected();
@@ -546,6 +554,13 @@
                 @Override
                 public void onPageScrolled(int position, float positionOffset,
                         int positionOffsetPixels) {
+
+                    if (!mIsScrollJankTraceBegin && mCurrentScrollState == SCROLL_STATE_DRAGGING) {
+                        InteractionJankMonitor.getInstance().begin(PagedTileLayout.this,
+                                CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE);
+                        mIsScrollJankTraceBegin = true;
+                    }
+
                     if (mPageIndicator == null) return;
                     mPageIndicatorPosition = position + positionOffset;
                     mPageIndicator.setLocation(mPageIndicatorPosition);
@@ -554,6 +569,16 @@
                                 (isLayoutRtl() ? position == mPages.size() - 1 : position == 0));
                     }
                 }
+
+                @Override
+                public void onPageScrollStateChanged(int state) {
+                    if (state != mCurrentScrollState && state == SCROLL_STATE_IDLE) {
+                        InteractionJankMonitor.getInstance().end(
+                                CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE);
+                        mIsScrollJankTraceBegin = false;
+                    }
+                    mCurrentScrollState = state;
+                }
             };
 
     private final PagerAdapter mAdapter = new PagerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index dd876b7..e42c47b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -15,10 +15,10 @@
 package com.android.systemui.qs;
 
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
-import static com.android.systemui.statusbar.DisableFlagsLogger.DisableState;
 
 import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
 import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
+import static com.android.systemui.statusbar.DisableFlagsLogger.DisableState;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -696,6 +696,11 @@
         mQSPanelController.closeDetail();
     }
 
+    @Override
+    public void closeCustomizer() {
+        mQSCustomizerController.hide();
+    }
+
     public void notifyCustomizeChanged() {
         // The customize state changed, so our height changed.
         mContainer.updateExpansion();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
index 17a815e..8544f61 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
@@ -4,7 +4,6 @@
 import com.android.systemui.log.LogLevel
 import com.android.systemui.log.dagger.QSFragmentDisableLog
 import com.android.systemui.statusbar.DisableFlagsLogger
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment
 import javax.inject.Inject
 
 /** A helper class for logging disable flag changes made in [QSFragment]. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 0196769..af191a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -536,7 +536,7 @@
                 }
             }
         }
-        if (featureFlags.isProviderModelSettingEnabled()) {
+        if (featureFlags.isProviderModelSettingEnabled(context)) {
             if (!tiles.contains("internet")) {
                 if (tiles.contains("wifi")) {
                     // Replace the WiFi with Internet, and remove the Cell
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
index 8aa2d09..d2bc38e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
@@ -54,19 +54,23 @@
     }
 
     public int getValue() {
-        return mSecureSettings.getIntForUser(mSettingName, mDefaultValue, mUserId);
+        return mListening ? mObservedValue : getValueFromProvider();
     }
 
     public void setValue(int value) {
         mSecureSettings.putIntForUser(mSettingName, value, mUserId);
     }
 
+    private int getValueFromProvider() {
+        return mSecureSettings.getIntForUser(mSettingName, mDefaultValue, mUserId);
+    }
+
     @Override
     public void setListening(boolean listening) {
         if (listening == mListening) return;
         mListening = listening;
         if (listening) {
-            mObservedValue = getValue();
+            mObservedValue = getValueFromProvider();
             mSecureSettings.registerContentObserverForUser(
                     mSecureSettings.getUriFor(mSettingName), false, this, mUserId);
         } else {
@@ -77,9 +81,10 @@
 
     @Override
     public void onChange(boolean selfChange) {
-        final int value = getValue();
-        handleValueChanged(value, value != mObservedValue);
+        final int value = getValueFromProvider();
+        final boolean changed = value != mObservedValue;
         mObservedValue = value;
+        handleValueChanged(value, changed);
     }
 
     public void setUserId(int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 993bbd0..eef3b45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -121,7 +121,7 @@
         }
 
         final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
-        if (mFeatureFlags.isProviderModelSettingEnabled()) {
+        if (mFeatureFlags.isProviderModelSettingEnabled(mContext)) {
             possibleTiles.remove("cell");
             possibleTiles.remove("wifi");
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index e896c7c..b1cd68e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -61,10 +61,12 @@
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.SecureSetting;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.volume.ZenModePanel;
 
 import javax.inject.Inject;
@@ -81,6 +83,7 @@
     private final ZenModeController mController;
     private final DndDetailAdapter mDetailAdapter;
     private final SharedPreferences mSharedPreferences;
+    private final SecureSetting mSettingZenDuration;
 
     private boolean mListening;
     private boolean mShowingDetail;
@@ -96,7 +99,8 @@
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             ZenModeController zenModeController,
-            @Main SharedPreferences sharedPreferences
+            @Main SharedPreferences sharedPreferences,
+            SecureSettings secureSettings
     ) {
         super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
@@ -104,8 +108,17 @@
         mSharedPreferences = sharedPreferences;
         mDetailAdapter = new DndDetailAdapter();
         mController.observe(getLifecycle(), mZenCallback);
+        mSettingZenDuration = new SecureSetting(secureSettings, mUiHandler,
+                Settings.Secure.ZEN_DURATION, getHost().getUserId()) {
+            @Override
+            protected void handleValueChanged(int value, boolean observedChange) {
+                refreshState();
+            }
+        };
     }
 
+
+
     public static void setVisible(Context context, boolean visible) {
         Prefs.putBoolean(context, Prefs.Key.DND_TILE_VISIBLE, visible);
     }
@@ -144,14 +157,18 @@
         if (mState.value) {
             mController.setZen(ZEN_MODE_OFF, null, TAG);
         } else {
-            showDetail(true);
+            enableZenMode(view);
         }
     }
 
     @Override
-    public void showDetail(boolean show) {
-        int zenDuration = Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.ZEN_DURATION, 0);
+    protected void handleUserSwitch(int newUserId) {
+        super.handleUserSwitch(newUserId);
+        mSettingZenDuration.setUserId(newUserId);
+    }
+
+    private void enableZenMode(@Nullable View view) {
+        int zenDuration = mSettingZenDuration.getValue();
         boolean showOnboarding = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0
                 && Settings.Secure.getInt(mContext.getContentResolver(),
@@ -270,6 +287,8 @@
         state.dualLabelContentDescription = mContext.getResources().getString(
                 R.string.accessibility_quick_settings_open_settings, getTileLabel());
         state.expandedAccessibilityClassName = Switch.class.getName();
+        state.forceExpandIcon =
+                mSettingZenDuration.getValue() == Settings.Secure.ZEN_DURATION_PROMPT;
     }
 
     @Override
@@ -296,6 +315,13 @@
         } else {
             Prefs.unregisterListener(mContext, mPrefListener);
         }
+        mSettingZenDuration.setListening(listening);
+    }
+
+    @Override
+    protected void handleDestroy() {
+        super.handleDestroy();
+        mSettingZenDuration.setListening(false);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 563c4cd..77b9cc1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -306,12 +306,8 @@
         final boolean isWifiScanEnabled = mInternetDialogController.isWifiScanEnabled();
         updateWifiToggle(isWifiEnabled, isDeviceLocked);
         updateConnectedWifi(isWifiEnabled, isDeviceLocked);
+        updateWifiListAndSeeAll(isWifiEnabled, isDeviceLocked);
         updateWifiScanNotify(isWifiEnabled, isWifiScanEnabled, isDeviceLocked);
-
-        final int visibility = (isDeviceLocked || !isWifiEnabled || mWifiEntriesCount <= 0)
-                ? View.GONE : View.VISIBLE;
-        mWifiRecyclerView.setVisibility(visibility);
-        mSeeAllLayout.setVisibility(visibility);
     }
 
     private void setOnClickListener() {
@@ -414,6 +410,18 @@
     }
 
     @MainThread
+    private void updateWifiListAndSeeAll(boolean isWifiEnabled, boolean isDeviceLocked) {
+        if (!isWifiEnabled || isDeviceLocked) {
+            mWifiRecyclerView.setVisibility(View.GONE);
+            mSeeAllLayout.setVisibility(View.GONE);
+            return;
+        }
+        mWifiRecyclerView.setVisibility(mWifiEntriesCount > 0 ? View.VISIBLE : View.GONE);
+        mSeeAllLayout.setVisibility(
+                (mConnectedWifiEntry != null || mWifiEntriesCount > 0) ? View.VISIBLE : View.GONE);
+    }
+
+    @MainThread
     private void updateWifiScanNotify(boolean isWifiEnabled, boolean isWifiScanEnabled,
             boolean isDeviceLocked) {
         if (isWifiEnabled || !isWifiScanEnabled || isDeviceLocked) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 7cfa830..3fbed64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -33,11 +33,9 @@
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
-import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
 import android.os.AsyncTask;
 import android.os.Trace;
-import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
@@ -83,7 +81,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
@@ -133,7 +130,6 @@
     private final DelayableExecutor mMainExecutor;
 
     private final Context mContext;
-    private final MediaSessionManager mMediaSessionManager;
     private final ArrayList<MediaListener> mMediaListeners;
     private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private final MediaArtworkProcessor mMediaArtworkProcessor;
@@ -196,10 +192,6 @@
         mMediaArtworkProcessor = mediaArtworkProcessor;
         mKeyguardBypassController = keyguardBypassController;
         mMediaListeners = new ArrayList<>();
-        // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
-        // in session state
-        mMediaSessionManager = (MediaSessionManager) mContext.getSystemService(
-                Context.MEDIA_SESSION_SERVICE);
         // TODO: use KeyguardStateController#isOccluded to remove this dependency
         mStatusBarOptionalLazy = statusBarOptionalLazy;
         mNotificationShadeWindowController = notificationShadeWindowController;
@@ -476,35 +468,6 @@
                 }
             }
         }
-        if (mediaNotification == null) {
-            // Still nothing? OK, let's just look for live media sessions and see if they match
-            // one of our notifications. This will catch apps that aren't (yet!) using media
-            // notifications.
-
-            if (mMediaSessionManager != null) {
-                // TODO: Should this really be for all users? It appears that inactive users
-                //  can't have active sessions, which would mean it is fine.
-                final List<MediaController> sessions =
-                        mMediaSessionManager.getActiveSessionsForUser(null, UserHandle.ALL);
-
-                for (MediaController aController : sessions) {
-                    // now to see if we have one like this
-                    final String pkg = aController.getPackageName();
-
-                    for (NotificationEntry entry : allNotifications) {
-                        if (entry.getSbn().getPackageName().equals(pkg)) {
-                            if (DEBUG_MEDIA) {
-                                Log.v(TAG, "DEBUG_MEDIA: found controller matching "
-                                        + entry.getSbn().getKey());
-                            }
-                            controller = aController;
-                            mediaNotification = entry;
-                            break;
-                        }
-                    }
-                }
-            }
-        }
 
         if (controller != null && !sameSessions(mMediaController, controller)) {
             // We have a new media session
@@ -550,8 +513,6 @@
 
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
-        pw.print("    mMediaSessionManager=");
-        pw.println(mMediaSessionManager);
         pw.print("    mMediaNotificationKey=");
         pw.println(mMediaNotificationKey);
         pw.print("    mMediaController=");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index ff3e97a..18c8b7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -33,6 +33,7 @@
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.DynamicChildBindController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
@@ -91,6 +92,7 @@
     private final DynamicPrivacyController mDynamicPrivacyController;
     private final KeyguardBypassController mBypassController;
     private final ForegroundServiceSectionController mFgsSectionController;
+    private final NotifPipelineFlags mNotifPipelineFlags;
     private AssistantFeedbackController mAssistantFeedbackController;
     private final Context mContext;
 
@@ -121,7 +123,8 @@
             ForegroundServiceSectionController fgsSectionController,
             DynamicChildBindController dynamicChildBindController,
             LowPriorityInflationHelper lowPriorityInflationHelper,
-            AssistantFeedbackController assistantFeedbackController) {
+            AssistantFeedbackController assistantFeedbackController,
+            NotifPipelineFlags notifPipelineFlags) {
         mContext = context;
         mHandler = mainHandler;
         mFeatureFlags = featureFlags;
@@ -132,6 +135,7 @@
         mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
         mEntryManager = notificationEntryManager;
         mFgsSectionController = fgsSectionController;
+        mNotifPipelineFlags = notifPipelineFlags;
         Resources res = context.getResources();
         mAlwaysExpandNonGroupedNotification =
                 res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
@@ -157,7 +161,7 @@
     //TODO: Rewrite this to focus on Entries, or some other data object instead of views
     public void updateNotificationViews() {
         Assert.isMainThread();
-        if (!mFeatureFlags.checkLegacyPipelineEnabled()) {
+        if (!mNotifPipelineFlags.checkLegacyPipelineEnabled()) {
             return;
         }
 
@@ -435,7 +439,7 @@
      */
     public void updateRowStates() {
         Assert.isMainThread();
-        if (!mFeatureFlags.checkLegacyPipelineEnabled()) {
+        if (!mNotifPipelineFlags.checkLegacyPipelineEnabled()) {
             return;
         }
 
@@ -524,7 +528,9 @@
 
     @Override
     public void onDynamicPrivacyChanged() {
-        mFeatureFlags.assertLegacyPipelineEnabled();
+        if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+            throw new IllegalStateException("Old pipeline code running w/ new pipeline enabled");
+        }
         if (mPerformingUpdate) {
             Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call");
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index 9ae7ea2..82e1cf8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -227,7 +227,7 @@
         mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
                 info, mDefaults, mMobileCallback);
         mProviderModelBehavior = featureFlags.isCombinedStatusBarSignalIconsEnabled();
-        mProviderModelSetting = featureFlags.isProviderModelSettingEnabled();
+        mProviderModelSetting = featureFlags.isProviderModelSettingEnabled(mContext);
     }
 
     void setConfiguration(Config config) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 26780eb..a87efd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -442,7 +442,7 @@
 
         mDemoModeController.addCallback(this);
         mProviderModelBehavior = mFeatureFlags.isCombinedStatusBarSignalIconsEnabled();
-        mProviderModelSetting = mFeatureFlags.isProviderModelSettingEnabled();
+        mProviderModelSetting = mFeatureFlags.isProviderModelSettingEnabled(mContext);
 
         mDumpManager.registerDumpable(TAG, this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 103ca0e..b5d3637 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -68,7 +68,7 @@
                     new WifiTrafficStateCallback());
         }
         mCurrentState.iconGroup = mLastState.iconGroup = mUnmergedWifiIconGroup;
-        mProviderModelSetting = featureFlags.isProviderModelSettingEnabled();
+        mProviderModelSetting = featureFlags.isProviderModelSettingEnabled(mContext);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index d5cba72..77463fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -52,6 +52,7 @@
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.DynamicChildBindController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -211,7 +212,8 @@
             ForegroundServiceSectionController fgsSectionController,
             DynamicChildBindController dynamicChildBindController,
             LowPriorityInflationHelper lowPriorityInflationHelper,
-            AssistantFeedbackController assistantFeedbackController) {
+            AssistantFeedbackController assistantFeedbackController,
+            NotifPipelineFlags notifPipelineFlags) {
         return new NotificationViewHierarchyManager(
                 context,
                 mainHandler,
@@ -227,7 +229,8 @@
                 fgsSectionController,
                 dynamicChildBindController,
                 lowPriorityInflationHelper,
-                assistantFeedbackController);
+                assistantFeedbackController,
+                notifPipelineFlags);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
new file mode 100644
index 0000000..914697b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import android.content.Context
+import android.util.Log
+import android.widget.Toast
+import com.android.systemui.flags.FeatureFlags
+import javax.inject.Inject
+
+class NotifPipelineFlags @Inject constructor(
+        val context: Context,
+        val featureFlags: FeatureFlags
+) {
+    fun checkLegacyPipelineEnabled(): Boolean {
+        if (!featureFlags.isNewNotifPipelineRenderingEnabled) {
+            return true
+        }
+        Log.d("NotifPipeline", "Old pipeline code running w/ new pipeline enabled", Exception())
+        Toast.makeText(context, "Old pipeline code running!", Toast.LENGTH_SHORT).show()
+        return false
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 5c8e8b2..4c7b2bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -25,12 +25,12 @@
 
 import com.android.internal.util.NotificationMessagingUtil;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationUiAdjustment;
 import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationClicker;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
@@ -55,7 +55,6 @@
     private static final String TAG = "NotificationViewManager";
 
     private final Context mContext;
-    private final FeatureFlags mFeatureFlags;
     private final NotificationMessagingUtil mMessagingUtil;
     private final NotificationRemoteInputManager mNotificationRemoteInputManager;
     private final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@@ -66,6 +65,7 @@
             mExpandableNotificationRowComponentBuilder;
     private final IconManager mIconManager;
     private final LowPriorityInflationHelper mLowPriorityInflationHelper;
+    private final NotifPipelineFlags mNotifPipelineFlags;
 
     private NotificationPresenter mPresenter;
     private NotificationListContainer mListContainer;
@@ -75,7 +75,6 @@
     @Inject
     public NotificationRowBinderImpl(
             Context context,
-            FeatureFlags featureFlags,
             NotificationMessagingUtil notificationMessagingUtil,
             NotificationRemoteInputManager notificationRemoteInputManager,
             NotificationLockscreenUserManager notificationLockscreenUserManager,
@@ -84,9 +83,9 @@
             Provider<RowInflaterTask> rowInflaterTaskProvider,
             ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder,
             IconManager iconManager,
-            LowPriorityInflationHelper lowPriorityInflationHelper) {
+            LowPriorityInflationHelper lowPriorityInflationHelper,
+            NotifPipelineFlags notifPipelineFlags) {
         mContext = context;
-        mFeatureFlags = featureFlags;
         mNotifBindPipeline = notifBindPipeline;
         mRowContentBindStage = rowContentBindStage;
         mMessagingUtil = notificationMessagingUtil;
@@ -96,6 +95,7 @@
         mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder;
         mIconManager = iconManager;
         mLowPriorityInflationHelper = lowPriorityInflationHelper;
+        mNotifPipelineFlags = notifPipelineFlags;
     }
 
     /**
@@ -126,7 +126,7 @@
             throws InflationException {
         if (params == null) {
             // weak assert that the params should always be passed in the new pipeline
-            mFeatureFlags.checkLegacyPipelineEnabled();
+            mNotifPipelineFlags.checkLegacyPipelineEnabled();
         }
         ViewGroup parent = mListContainer.getViewParentForNotification(entry);
 
@@ -187,7 +187,7 @@
             NotificationUiAdjustment oldAdjustment,
             NotificationUiAdjustment newAdjustment,
             NotificationRowContentBinder.InflationCallback callback) {
-        mFeatureFlags.checkLegacyPipelineEnabled();
+        mNotifPipelineFlags.checkLegacyPipelineEnabled();
         if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
             if (entry.rowExists()) {
                 ExpandableNotificationRow row = entry.getRow();
@@ -238,7 +238,7 @@
             isLowPriority = inflaterParams.isLowPriority();
         } else {
             // LEGACY pipeline
-            mFeatureFlags.checkLegacyPipelineEnabled();
+            mNotifPipelineFlags.checkLegacyPipelineEnabled();
             // If this is our first time inflating, we don't actually know the groupings for real
             // yet, so we might actually inflate a low priority content view incorrectly here and
             // have to correct it later in the pipeline. On subsequent inflations (i.e. updates),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
index dd1f948..ae4f2bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.notification.collection.legacy;
 
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.RowContentBindParams;
@@ -31,18 +31,18 @@
  */
 @SysUISingleton
 public class LowPriorityInflationHelper {
-    private final FeatureFlags mFeatureFlags;
     private final NotificationGroupManagerLegacy mGroupManager;
     private final RowContentBindStage mRowContentBindStage;
+    private final NotifPipelineFlags mNotifPipelineFlags;
 
     @Inject
     LowPriorityInflationHelper(
-            FeatureFlags featureFlags,
             NotificationGroupManagerLegacy groupManager,
-            RowContentBindStage rowContentBindStage) {
-        mFeatureFlags = featureFlags;
+            RowContentBindStage rowContentBindStage,
+            NotifPipelineFlags notifPipelineFlags) {
         mGroupManager = groupManager;
         mRowContentBindStage = rowContentBindStage;
+        mNotifPipelineFlags = notifPipelineFlags;
     }
 
     /**
@@ -59,7 +59,7 @@
     public void recheckLowPriorityViewAndInflate(
             NotificationEntry entry,
             ExpandableNotificationRow row) {
-        mFeatureFlags.checkLegacyPipelineEnabled();
+        mNotifPipelineFlags.checkLegacyPipelineEnabled();
         RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
         final boolean shouldBeLowPriority = shouldUseLowPriorityView(entry);
         if (!row.isRemoved() && row.isLowPriority() != shouldBeLowPriority) {
@@ -73,7 +73,7 @@
      * Whether the notification should inflate a low priority version of its content views.
      */
     public boolean shouldUseLowPriorityView(NotificationEntry entry) {
-        mFeatureFlags.checkLegacyPipelineEnabled();
+        mNotifPipelineFlags.checkLegacyPipelineEnabled();
         return entry.isAmbient() && !mGroupManager.isChildInGroup(entry);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 408c457..88198f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -22,10 +22,10 @@
 import android.view.View
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.R
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.media.KeyguardMediaController
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
 import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager
@@ -54,12 +54,12 @@
  * TODO: Move remaining sections logic from NSSL into this class.
  */
 class NotificationSectionsManager @Inject internal constructor(
-    private val featureFlags: FeatureFlags,
     private val statusBarStateController: StatusBarStateController,
     private val configurationController: ConfigurationController,
     private val keyguardMediaController: KeyguardMediaController,
     private val sectionsFeatureManager: NotificationSectionsFeatureManager,
     private val logger: NotificationSectionsLogger,
+    private val notifPipelineFlags: NotifPipelineFlags,
     @IncomingHeader private val incomingHeaderController: SectionHeaderController,
     @PeopleHeader private val peopleHeaderController: SectionHeaderController,
     @AlertingHeader private val alertingHeaderController: SectionHeaderController,
@@ -201,7 +201,7 @@
                 override var targetPosition: Int? = null
 
                 override fun adjustViewPosition() {
-                    featureFlags.checkLegacyPipelineEnabled()
+                    notifPipelineFlags.checkLegacyPipelineEnabled()
                     val target = targetPosition
                     val current = currentPosition
                     if (target == null) {
@@ -228,7 +228,7 @@
     private fun <T : StackScrollerDecorView> decorViewHeaderState(
         header: T
     ): SectionUpdateState<T> {
-        featureFlags.checkLegacyPipelineEnabled()
+        notifPipelineFlags.checkLegacyPipelineEnabled()
         val inner = expandableViewHeaderState(header)
         return object : SectionUpdateState<T> by inner {
             override fun adjustViewPosition() {
@@ -245,7 +245,7 @@
      * bookkeeping and adds/moves/removes section headers if appropriate.
      */
     fun updateSectionBoundaries(reason: String) {
-        featureFlags.checkLegacyPipelineEnabled()
+        notifPipelineFlags.checkLegacyPipelineEnabled()
         if (!isUsingMultipleSections) {
             return
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index a66af04..c4ec086 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -183,6 +183,7 @@
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.phone.panelstate.PanelState;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -3019,6 +3020,10 @@
             mStatusBarTouchableRegionManager.setPanelExpanded(isExpanded);
             mStatusBar.setPanelExpanded(isExpanded);
             mPanelExpanded = isExpanded;
+
+            if (!isExpanded && mQs != null && mQs.isCustomizing()) {
+                mQs.closeCustomizer();
+            }
         }
     }
 
@@ -3317,10 +3322,20 @@
         return mQs.isShowingDetail();
     }
 
+    /** Returns whether the QS customizer is currently active. */
+    public boolean isQsCustomizing() {
+        return mQs.isCustomizing();
+    }
+
     public void closeQsDetail() {
         mQs.closeDetail();
     }
 
+    /** Close the QS customizer if it is open. */
+    public void closeQsCustomizer() {
+        mQs.closeCustomizer();
+    }
+
     public boolean isLaunchTransitionFinished() {
         return mIsLaunchTransitionFinished;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 06cf70d..2a54642 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -136,7 +136,6 @@
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DelegateLaunchAnimatorController;
 import com.android.systemui.assist.AssistManager;
-import com.android.systemui.battery.BatteryMeterViewController;
 import com.android.systemui.biometrics.AuthRippleController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.camera.CameraIntents;
@@ -199,6 +198,7 @@
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
@@ -216,6 +216,9 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -487,6 +490,7 @@
     private final DozeParameters mDozeParameters;
     private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
     private final StatusBarComponent.Factory mStatusBarComponentFactory;
+    private final StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
     private final PluginManager mPluginManager;
     private final Optional<LegacySplitScreen> mSplitScreenOptional;
     private final StatusBarNotificationActivityStarter.Builder
@@ -668,10 +672,10 @@
     private final Optional<Bubbles> mBubblesOptional;
     private final Bubbles.BubbleExpandListener mBubbleExpandListener;
     private final Optional<StartingSurface> mStartingSurfaceOptional;
+    private final NotifPipelineFlags mNotifPipelineFlags;
 
     private final ActivityIntentHelper mActivityIntentHelper;
     private NotificationStackScrollLayoutController mStackScrollerController;
-    private BatteryMeterViewController mBatteryMeterViewController;
 
     private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
             (extractor, which) -> updateTheme();
@@ -743,6 +747,7 @@
             CommandQueue commandQueue,
             CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
             StatusBarComponent.Factory statusBarComponentFactory,
+            StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
             PluginManager pluginManager,
             Optional<LegacySplitScreen> splitScreenOptional,
             LightsOutNotifController lightsOutNotifController,
@@ -783,7 +788,8 @@
             Optional<StartingSurface> startingSurfaceOptional,
             TunerService tunerService,
             DumpManager dumpManager,
-            ActivityLaunchAnimator activityLaunchAnimator) {
+            ActivityLaunchAnimator activityLaunchAnimator,
+            NotifPipelineFlags notifPipelineFlags) {
         super(context);
         mNotificationsController = notificationsController;
         mLightBarController = lightBarController;
@@ -847,6 +853,7 @@
         mCommandQueue = commandQueue;
         mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
         mStatusBarComponentFactory = statusBarComponentFactory;
+        mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
         mPluginManager = pluginManager;
         mSplitScreenOptional = splitScreenOptional;
         mStatusBarNotificationActivityStarterBuilder = statusBarNotificationActivityStarterBuilder;
@@ -880,6 +887,7 @@
 
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
         mStartingSurfaceOptional = startingSurfaceOptional;
+        mNotifPipelineFlags = notifPipelineFlags;
         lockscreenShadeTransitionController.setStatusbar(this);
 
         mPanelExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged);
@@ -1131,28 +1139,21 @@
         mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);
         mStatusBarWindowController.getFragmentHostManager()
                 .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
-                    CollapsedStatusBarFragment statusBarFragment =
-                            (CollapsedStatusBarFragment) fragment;
+                    StatusBarFragmentComponent statusBarFragmentComponent =
+                            ((CollapsedStatusBarFragment) fragment).getStatusBarFragmentComponent();
+                    if (statusBarFragmentComponent == null) {
+                        throw new IllegalStateException(
+                                "CollapsedStatusBarFragment should have a valid component");
+                    }
 
-                    PhoneStatusBarView oldStatusBarView = mStatusBarView;
-                    mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();
+                    mStatusBarView = statusBarFragmentComponent.getPhoneStatusBarView();
 
+                    // TODO(b/205609837): Migrate this to StatusBarFragmentComponent.
                     mPhoneStatusBarViewController = mPhoneStatusBarViewControllerFactory
                             .create(mStatusBarView, mNotificationPanelViewController
                                     .getStatusBarTouchEventHandler());
                     mPhoneStatusBarViewController.init();
 
-                    mBatteryMeterViewController = new BatteryMeterViewController(
-                            mStatusBarView.findViewById(R.id.battery),
-                            mConfigurationController,
-                            mTunerService,
-                            mBroadcastDispatcher,
-                            mMainHandler,
-                            mContext.getContentResolver(),
-                            mBatteryController
-                    );
-                    mBatteryMeterViewController.init();
-
                     // Ensure we re-propagate panel expansion values to the panel controller and
                     // any listeners it may have, such as PanelBar. This will also ensure we
                     // re-display the notification panel if necessary (for example, if
@@ -1166,8 +1167,8 @@
                         // This view is being recreated, let's destroy the old one
                         mHeadsUpAppearanceController.destroy();
                     }
-                    // TODO: this should probably be scoped to the StatusBarComponent
                     // TODO (b/136993073) Separate notification shade and status bar
+                    // TODO(b/205609837): Migrate this to StatusBarFragmentComponent.
                     mHeadsUpAppearanceController = new HeadsUpAppearanceController(
                             mNotificationIconAreaController, mHeadsUpManager,
                             mStackScrollerController,
@@ -1184,6 +1185,7 @@
                 .beginTransaction()
                 .replace(R.id.status_bar_container,
                         new CollapsedStatusBarFragment(
+                                mStatusBarFragmentComponentFactory,
                                 mOngoingCallController,
                                 mAnimationScheduler,
                                 mStatusBarLocationPublisher,
@@ -1475,7 +1477,8 @@
                 mInitController,
                 mNotificationInterruptStateProvider,
                 mRemoteInputManager,
-                mConfigurationController);
+                mConfigurationController,
+                mNotifPipelineFlags);
 
         mNotificationShelfController.setOnActivatedListener(mPresenter);
         mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController);
@@ -3280,6 +3283,10 @@
             }
             return true;
         }
+        if (mNotificationPanelViewController.isQsCustomizing()) {
+            mNotificationPanelViewController.closeQsCustomizer();
+            return true;
+        }
         if (mNotificationPanelViewController.isQsExpanded()) {
             if (mNotificationPanelViewController.isQsDetailShowing()) {
                 mNotificationPanelViewController.closeQsDetail();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 7c0f923..58a018c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -57,6 +57,7 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
@@ -108,6 +109,7 @@
     private final AccessibilityManager mAccessibilityManager;
     private final KeyguardManager mKeyguardManager;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
+    private final NotifPipelineFlags mNotifPipelineFlags;
     private final IStatusBarService mBarService;
     private final DynamicPrivacyController mDynamicPrivacyController;
     private boolean mReinflateNotificationsOnUserSwitched;
@@ -144,7 +146,8 @@
             InitController initController,
             NotificationInterruptStateProvider notificationInterruptStateProvider,
             NotificationRemoteInputManager remoteInputManager,
-            ConfigurationController configurationController) {
+            ConfigurationController configurationController,
+            NotifPipelineFlags notifPipelineFlags) {
         mKeyguardStateController = keyguardStateController;
         mNotificationPanel = panel;
         mHeadsUpManager = headsUp;
@@ -167,6 +170,7 @@
         mLockscreenGestureLogger = lockscreenGestureLogger;
         mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView());
         mNotificationShadeWindowController = notificationShadeWindowController;
+        mNotifPipelineFlags = notifPipelineFlags;
         mAboveShelfObserver.setListener(statusBarWindow.findViewById(
                 R.id.notification_container_parent));
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
@@ -305,7 +309,7 @@
 
     @Override
     public void updateNotificationViews(final String reason) {
-        if (!mFeatureFlags.checkLegacyPipelineEnabled()) {
+        if (!mNotifPipelineFlags.checkLegacyPipelineEnabled()) {
             return;
         }
         // The function updateRowStates depends on both of these being non-null, so check them here.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 3259f6b..1c3229f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -64,6 +64,7 @@
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -75,7 +76,6 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragmentLogger;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.DozeScrimController;
 import com.android.systemui.statusbar.phone.DozeServiceHost;
@@ -99,6 +99,8 @@
 import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
 import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -197,6 +199,7 @@
             CommandQueue commandQueue,
             CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
             StatusBarComponent.Factory statusBarComponentFactory,
+            StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
             PluginManager pluginManager,
             Optional<LegacySplitScreen> splitScreenOptional,
             LightsOutNotifController lightsOutNotifController,
@@ -237,7 +240,8 @@
             Optional<StartingSurface> startingSurfaceOptional,
             TunerService tunerService,
             DumpManager dumpManager,
-            ActivityLaunchAnimator activityLaunchAnimator) {
+            ActivityLaunchAnimator activityLaunchAnimator,
+            NotifPipelineFlags notifPipelineFlags) {
         return new StatusBar(
                 context,
                 notificationsController,
@@ -297,6 +301,7 @@
                 commandQueue,
                 collapsedStatusBarFragmentLogger,
                 statusBarComponentFactory,
+                statusBarFragmentComponentFactory,
                 pluginManager,
                 splitScreenOptional,
                 lightsOutNotifController,
@@ -336,7 +341,8 @@
                 startingSurfaceOptional,
                 tunerService,
                 dumpManager,
-                activityLaunchAnimator
+                activityLaunchAnimator,
+                notifPipelineFlags
         );
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 2b62e0b..2880e4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.statusbar.phone.fragment;
 
 import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
 import static android.app.StatusBarManager.DISABLE_CLOCK;
@@ -52,7 +52,14 @@
 import com.android.systemui.statusbar.connectivity.SignalCallback;
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
+import com.android.systemui.statusbar.phone.PhoneStatusBarView;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
+import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -83,6 +90,7 @@
     public static final String STATUS_BAR_ICON_MANAGER_TAG = "status_bar_icon_manager";
     public static final int FADE_IN_DURATION = 320;
     public static final int FADE_IN_DELAY = 50;
+    private StatusBarFragmentComponent mStatusBarFragmentComponent;
     private PhoneStatusBarView mStatusBar;
     private final StatusBarStateController mStatusBarStateController;
     private final KeyguardStateController mKeyguardStateController;
@@ -96,6 +104,7 @@
     private int mDisabled2;
     private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private DarkIconManager mDarkIconManager;
+    private final StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
     private final CommandQueue mCommandQueue;
     private final CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
     private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
@@ -127,6 +136,7 @@
 
     @Inject
     public CollapsedStatusBarFragment(
+            StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
             OngoingCallController ongoingCallController,
             SystemStatusAnimationScheduler animationScheduler,
             StatusBarLocationPublisher locationPublisher,
@@ -143,6 +153,7 @@
             CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
             OperatorNameViewController.Factory operatorNameViewControllerFactory
     ) {
+        mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
         mOngoingCallController = ongoingCallController;
         mAnimationScheduler = animationScheduler;
         mLocationPublisher = locationPublisher;
@@ -169,6 +180,9 @@
     @Override
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
+        mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(this);
+        mStatusBarFragmentComponent.init();
+
         mStatusBar = (PhoneStatusBarView) view;
         View contents = mStatusBar.findViewById(R.id.status_bar_contents);
         contents.addOnLayoutChangeListener(mStatusBarLayoutListener);
@@ -252,6 +266,17 @@
         updateNotificationIconAreaAndCallChip(mDisabled1, false);
     }
 
+    /**
+     * Returns the dagger component for this fragment.
+     *
+     * TODO(b/205609837): Eventually, the dagger component should encapsulate all status bar
+     *   fragment functionality and we won't need to expose it here anymore.
+     */
+    @Nullable
+    public StatusBarFragmentComponent getStatusBarFragmentComponent() {
+        return mStatusBarFragmentComponent;
+    }
+
     @Override
     public void disable(int displayId, int state1, int state2, boolean animate) {
         if (displayId != getContext().getDisplayId()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
index 4d472e4..9ae378f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone
+package com.android.systemui.statusbar.phone.fragment
 
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogLevel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
new file mode 100644
index 0000000..47c1875
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.phone.fragment.dagger;
+
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.statusbar.phone.PhoneStatusBarView;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * A subcomponent that gets re-created each time we create a new {@link CollapsedStatusBarFragment}.
+ *
+ * This component will also re-create all classes that depend on {@link CollapsedStatusBarFragment}
+ * and friends. Specifically, the fragment creates a new {@link PhoneStatusBarView} and multiple
+ * controllers need access to that view, so those controllers will be re-created whenever the
+ * fragment is recreated.
+ *
+ * Note that this is completely separate from
+ * {@link com.android.systemui.statusbar.phone.dagger.StatusBarComponent}. This component gets
+ * re-created on each new fragment creation, whereas
+ * {@link com.android.systemui.statusbar.phone.dagger.StatusBarComponent} is only created once in
+ * {@link com.android.systemui.statusbar.phone.StatusBar} and never re-created.
+ */
+
+@Subcomponent(modules = {StatusBarFragmentModule.class})
+@StatusBarFragmentScope
+public interface StatusBarFragmentComponent {
+    /** Simple factory. */
+    @Subcomponent.Factory
+    interface Factory {
+        StatusBarFragmentComponent create(
+                @BindsInstance CollapsedStatusBarFragment collapsedStatusBarFragment);
+    }
+
+    /**
+     * Initialize anything extra for the component. Must be called after the component is created.
+     */
+    default void init() {
+        // No one accesses this controller, so we need to make sure we reference it here so it does
+        // get initialized.
+        getBatteryMeterViewController().init();
+    }
+
+    /** */
+    @StatusBarFragmentScope
+    BatteryMeterViewController getBatteryMeterViewController();
+
+    /** */
+    @StatusBarFragmentScope
+    @RootView
+    PhoneStatusBarView getPhoneStatusBarView();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
new file mode 100644
index 0000000..969361b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.phone.fragment.dagger;
+
+import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
+import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.statusbar.phone.PhoneStatusBarView;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** Dagger module for {@link StatusBarFragmentComponent}. */
+@Module
+public interface StatusBarFragmentModule {
+    /** */
+    @Provides
+    @RootView
+    @StatusBarFragmentScope
+    static PhoneStatusBarView providePhoneStatusBarView(
+            CollapsedStatusBarFragment collapsedStatusBarFragment) {
+        return (PhoneStatusBarView) collapsedStatusBarFragment.getView();
+    }
+
+    /** */
+    @Provides
+    @StatusBarFragmentScope
+    static BatteryMeterView provideBatteryMeterView(@RootView PhoneStatusBarView view) {
+        return view.findViewById(R.id.battery);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentScope.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentScope.java
new file mode 100644
index 0000000..96cff59
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentScope.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.phone.fragment.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for singleton items within the {@link StatusBarFragmentComponent}.
+ */
+@Documented
+@Retention(RUNTIME)
+@Scope
+public @interface StatusBarFragmentScope {}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 6d176a7..50a0b80 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -176,6 +176,34 @@
                 ? WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM;
     }
 
+    private boolean isSeedColorSet(JSONObject jsonObject, WallpaperColors newWallpaperColors) {
+        if (newWallpaperColors == null) {
+            return false;
+        }
+        // Gets the color that was overridden in the theme setting if any.
+        String sysPaletteColor = (String) jsonObject.opt(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+        if (sysPaletteColor == null) {
+            return false;
+        }
+        if (!sysPaletteColor.startsWith("#")) {
+            sysPaletteColor = "#" + sysPaletteColor;
+        }
+        final int systemPaletteColorArgb = Color.parseColor(sysPaletteColor);
+        // Gets seed colors from incoming {@link WallpaperColors} instance.
+        List<Integer> seedColors = ColorScheme.getSeedColors(newWallpaperColors);
+        for (int seedColor : seedColors) {
+            // The seed color from incoming {@link WallpaperColors} instance
+            // was set as color override.
+            if (seedColor == systemPaletteColorArgb) {
+                if (DEBUG) {
+                    Log.d(TAG, "Same as previous set system palette: " + sysPaletteColor);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void handleWallpaperColors(WallpaperColors wallpaperColors, int flags) {
         final boolean hadWallpaperColors = mCurrentColors != null;
         int latestWallpaperType = getLatestWallpaperType();
@@ -213,8 +241,11 @@
         try {
             JSONObject jsonObject = (overlayPackageJson == null) ? new JSONObject()
                     : new JSONObject(overlayPackageJson);
+            // The latest applied wallpaper should be the source of system colors when:
+            // There is not preset color applied and the incoming wallpaper color is not applied
             if (!COLOR_SOURCE_PRESET.equals(jsonObject.optString(OVERLAY_COLOR_SOURCE))
-                    && ((flags & latestWallpaperType) != 0)) {
+                    && ((flags & latestWallpaperType) != 0 && !isSeedColorSet(jsonObject,
+                    wallpaperColors))) {
                 mSkipSettingChange = true;
                 if (jsonObject.has(OVERLAY_CATEGORY_ACCENT_COLOR) || jsonObject.has(
                         OVERLAY_CATEGORY_SYSTEM_PALETTE)) {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index cebc931..cd3e2d3 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -100,11 +100,11 @@
     fun provideShellProgressProvider(
         config: UnfoldTransitionConfig,
         provider: Optional<UnfoldTransitionProgressProvider>
-    ): Optional<ShellUnfoldProgressProvider> =
+    ): ShellUnfoldProgressProvider =
         if (config.isEnabled && provider.isPresent()) {
-            Optional.of(UnfoldProgressProvider(provider.get()))
+            UnfoldProgressProvider(provider.get())
         } else {
-            Optional.empty()
+            ShellUnfoldProgressProvider.NO_PROVIDER
         }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index db87c5d..4bdab76 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -75,8 +75,7 @@
     shouldListenForFingerprintAssistant = false,
     switchingUser = false,
     udfps = false,
-    userDoesNotHaveTrust = false,
-    userNeedsStrongAuth = false
+    userDoesNotHaveTrust = false
 )
 
 private fun faceModel(user: Int) = KeyguardFaceListenModel(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index ff5960b..de8cc89 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -19,6 +19,7 @@
 import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
 import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
 
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -961,6 +962,19 @@
     }
 
     @Test
+    public void testStartUdfpsServiceStrongAuthRequiredAfterTimeout() {
+        // GIVEN status bar state is on the keyguard
+        mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
+
+        // WHEN user loses smart unlock trust
+        when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+                .thenReturn(SOME_AUTH_REQUIRED_AFTER_USER_REQUEST);
+
+        // THEN we should still listen for udfps
+        assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(true);
+    }
+
+    @Test
     public void testShouldNotListenForUdfps_whenTrustEnabled() {
         // GIVEN a "we should listen for udfps" state
         mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index d90eb73..9e42ff3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -642,12 +642,12 @@
         mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
         moveEvent.recycle();
 
-        // THEN click haptic is played
+        // THEN low-tick haptic is played
         verify(mVibrator).vibrate(
                 anyInt(),
                 anyString(),
-                eq(mUdfpsController.EFFECT_CLICK),
-                eq("udfps-onStart"),
+                any(),
+                eq("udfps-onStart-tick"),
                 eq(UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES));
 
         // THEN make sure vibration attributes has so that it always will play the haptic,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 886f84e..5b472ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -51,6 +51,7 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.concurrency.FakeThreadFactory;
@@ -93,6 +94,8 @@
     DevicePostureController mDevicePostureController;
     @Mock
     DozeLog mDozeLog;
+    @Mock
+    private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
     private FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor);
 
@@ -130,7 +133,8 @@
                 mWakefulnessLifecycle,
                 mDozeParameters,
                 mDevicePostureController,
-                mDozeLog);
+                mDozeLog,
+                mUnlockedScreenOffAnimationController);
     }
 
     @Test
@@ -236,7 +240,8 @@
                 mWakefulnessLifecycle,
                 mDozeParameters,
                 mDevicePostureController,
-                mDozeLog);
+                mDozeLog,
+                mUnlockedScreenOffAnimationController);
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE);
         reset(mDozeHost);
@@ -273,7 +278,8 @@
                 mWakefulnessLifecycle,
                 mDozeParameters,
                 mDevicePostureController,
-                mDozeLog);
+                mDozeLog,
+                mUnlockedScreenOffAnimationController);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
@@ -304,7 +310,8 @@
                 mWakefulnessLifecycle,
                 mDozeParameters,
                 mDevicePostureController,
-                mDozeLog);
+                mDozeLog,
+                mUnlockedScreenOffAnimationController);
 
         // GIVEN the device is in AOD
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
@@ -342,7 +349,8 @@
                 mWakefulnessLifecycle,
                 mDozeParameters,
                 mDevicePostureController,
-                mDozeLog);
+                mDozeLog,
+                mUnlockedScreenOffAnimationController);
 
         // GIVEN device is in AOD
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
@@ -384,7 +392,8 @@
                 mWakefulnessLifecycle,
                 mDozeParameters,
                 mDevicePostureController,
-                mDozeLog);
+                mDozeLog,
+                mUnlockedScreenOffAnimationController);
         verify(mDevicePostureController).addCallback(postureCallbackCaptor.capture());
 
         // GIVEN device is in AOD
@@ -466,24 +475,26 @@
     }
 
     @Test
-    public void transitionToDoze_duringScreenOff_afterTimeout_clampsToDim() {
+    public void transitionToDoze_duringUnlockedScreenOff_afterTimeout_clampsToDim() {
         when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
                 PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
         when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true);
+        when(mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()).thenReturn(true);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE);
 
         // If we're dozing after a timeout, and playing the unlocked screen animation, we should
-        // stay at dim brightness, because the screen dims just before timeout.
-        assertEquals(mServiceFake.screenBrightness, DIM_BRIGHTNESS);
+        // stay at or below dim brightness, because the screen dims just before timeout.
+        assertTrue(mServiceFake.screenBrightness <= DIM_BRIGHTNESS);
     }
 
     @Test
-    public void transitionToDoze_duringScreenOff_notAfterTimeout_doesNotClampToDim() {
+    public void transitionToDoze_duringUnlockedScreenOff_notAfterTimeout_doesNotClampToDim() {
         when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
                 PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON);
         when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true);
+        when(mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()).thenReturn(true);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE);
@@ -494,10 +505,11 @@
     }
 
     @Test
-    public void transitionToDoze_duringScreenOff_afterTimeout_noScreenOff_doesNotClampToDim() {
+    public void transitionToDoze_duringUnlockedScreenOff_afterTimeout_noScreenOff_doesNotClampToDim() {
         when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
                 PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
         when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(false);
+        when(mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()).thenReturn(false);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE);
@@ -506,6 +518,36 @@
         assertEquals(mServiceFake.screenBrightness, DEFAULT_BRIGHTNESS);
     }
 
+    @Test
+    public void transitionToDoze_duringLockedScreenOff_afterTimeout_clampsToDim() {
+        when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
+                PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
+        when(mWakefulnessLifecycle.getWakefulness()).thenReturn(
+                WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP);
+        when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(false);
+        when(mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()).thenReturn(false);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE);
+
+        assertTrue(mServiceFake.screenBrightness <= DIM_BRIGHTNESS);
+    }
+
+    @Test
+    public void transitionToDoze_duringLockedScreenOff_notAfterTimeout_doesNotClampToDim() {
+        when(mWakefulnessLifecycle.getLastSleepReason()).thenReturn(
+                PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON);
+        when(mWakefulnessLifecycle.getWakefulness()).thenReturn(
+                WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP);
+        when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(false);
+        when(mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()).thenReturn(false);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE);
+
+        assertEquals(mServiceFake.screenBrightness, DEFAULT_BRIGHTNESS);
+    }
+
     private void waitForSensorManager() {
         mFakeExecutor.runAllReady();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index 150ab77..3e19cc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -82,6 +82,8 @@
     private UdfpsController mUdfpsController;
     @Mock
     private DozeLog mDozeLog;
+    @Mock
+    private DozeScreenBrightness mDozeScreenBrightness;
 
     @Before
     public void setUp() throws Exception {
@@ -96,7 +98,8 @@
         mHandlerFake = new FakeHandler(Looper.getMainLooper());
         mWakeLock = new WakeLockFake();
         mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeHost, mDozeParameters,
-                mWakeLock, mAuthController, mUdfpsControllerProvider, mDozeLog);
+                mWakeLock, mAuthController, mUdfpsControllerProvider, mDozeLog,
+                mDozeScreenBrightness);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index aba043b..ce36935 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -151,7 +151,7 @@
                 mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder,
                 mFeatureFlags);
         setUpTileFactory();
-        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false);
+        when(mFeatureFlags.isProviderModelSettingEnabled(mContext)).thenReturn(false);
     }
 
     private void setUpTileFactory() {
@@ -200,7 +200,7 @@
 
     @Test
     public void testRemoveWifiAndCellularWithoutInternet() {
-        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+        when(mFeatureFlags.isProviderModelSettingEnabled(mContext)).thenReturn(true);
         mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2");
 
         assertEquals("internet", mQSTileHost.mTileSpecs.get(0));
@@ -210,7 +210,7 @@
 
     @Test
     public void testRemoveWifiAndCellularWithInternet() {
-        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+        when(mFeatureFlags.isProviderModelSettingEnabled(mContext)).thenReturn(true);
         mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2, internet");
 
         assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
@@ -220,7 +220,7 @@
 
     @Test
     public void testRemoveWifiWithoutInternet() {
-        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+        when(mFeatureFlags.isProviderModelSettingEnabled(mContext)).thenReturn(true);
         mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, wifi, spec2");
 
         assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
@@ -230,7 +230,7 @@
 
     @Test
     public void testRemoveCellWithInternet() {
-        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+        when(mFeatureFlags.isProviderModelSettingEnabled(mContext)).thenReturn(true);
         mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, spec2, cell, internet");
 
         assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
@@ -240,7 +240,7 @@
 
     @Test
     public void testNoWifiNoCellularNoInternet() {
-        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+        when(mFeatureFlags.isProviderModelSettingEnabled(mContext)).thenReturn(true);
         mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
 
         assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 018806e..05db1eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -136,7 +136,7 @@
                     }
                 }
         ).when(mQSTileHost).createTile(anyString());
-        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false);
+        when(mFeatureFlags.isProviderModelSettingEnabled(mContext)).thenReturn(false);
         FakeSystemClock clock = new FakeSystemClock();
         mMainExecutor = new FakeExecutor(clock);
         mBgExecutor = new FakeExecutor(clock);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
new file mode 100644
index 0000000..f99703e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.content.ContextWrapper
+import android.content.SharedPreferences
+import android.os.Handler
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.statusbar.policy.ZenModeController
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.SecureSettings
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import java.io.File
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class DndTileTest : SysuiTestCase() {
+
+    companion object {
+        private const val DEFAULT_USER = 0
+        private const val KEY = Settings.Secure.ZEN_DURATION
+    }
+
+    @Mock
+    private lateinit var qsHost: QSHost
+    @Mock
+    private lateinit var metricsLogger: MetricsLogger
+    @Mock
+    private lateinit var statusBarStateController: StatusBarStateController
+    @Mock
+    private lateinit var activityStarter: ActivityStarter
+    @Mock
+    private lateinit var qsLogger: QSLogger
+    @Mock
+    private lateinit var uiEventLogger: UiEventLogger
+    @Mock
+    private lateinit var zenModeController: ZenModeController
+    @Mock
+    private lateinit var sharedPreferences: SharedPreferences
+
+    private lateinit var secureSettings: SecureSettings
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var tile: DndTile
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+        secureSettings = FakeSettings()
+
+        Mockito.`when`(qsHost.userId).thenReturn(DEFAULT_USER)
+        Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+
+        val wrappedContext = object : ContextWrapper(context) {
+            override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences {
+                return sharedPreferences
+            }
+        }
+        Mockito.`when`(qsHost.context).thenReturn(wrappedContext)
+
+        tile = DndTile(
+            qsHost,
+            testableLooper.looper,
+            Handler(testableLooper.looper),
+            FalsingManagerFake(),
+            metricsLogger,
+            statusBarStateController,
+            activityStarter,
+            qsLogger,
+            zenModeController,
+            sharedPreferences,
+            secureSettings
+        )
+    }
+
+    @After
+    fun tearDown() {
+        tile.handleSetListening(false)
+    }
+
+    @Test
+    fun testForceExpandIcon_durationAskAlways_true() {
+        secureSettings.putIntForUser(KEY, Settings.Secure.ZEN_DURATION_PROMPT, DEFAULT_USER)
+
+        tile.refreshState()
+        testableLooper.processAllMessages()
+
+        assertThat(tile.state.forceExpandIcon).isTrue()
+    }
+
+    @Test
+    fun testForceExpandIcon_durationNotAskAlways_false() {
+        secureSettings.putIntForUser(KEY, 60, DEFAULT_USER)
+
+        tile.refreshState()
+        testableLooper.processAllMessages()
+
+        assertThat(tile.state.forceExpandIcon).isFalse()
+    }
+
+    @Test
+    fun testForceExpandIcon_changeWhileListening() {
+        secureSettings.putIntForUser(KEY, 60, DEFAULT_USER)
+
+        tile.refreshState()
+        testableLooper.processAllMessages()
+
+        assertThat(tile.state.forceExpandIcon).isFalse()
+
+        tile.handleSetListening(true)
+
+        secureSettings.putIntForUser(KEY, Settings.Secure.ZEN_DURATION_PROMPT, DEFAULT_USER)
+        testableLooper.processAllMessages()
+
+        assertThat(tile.state.forceExpandIcon).isTrue()
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index b6e8979..b32b4d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -219,61 +219,89 @@
     }
 
     @Test
-    public void updateDialog_wifiOnAndNoWifiList_hideWifiListAndSeeAll() {
+    public void updateDialog_wifiOnAndNoWifiEntry_hideWifiEntryAndSeeAll() {
         // The precondition WiFi ON is already in setUp()
+        mInternetDialog.mConnectedWifiEntry = null;
         mInternetDialog.mWifiEntriesCount = 0;
 
         mInternetDialog.updateDialog(false);
 
+        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
         assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
         assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
     }
 
     @Test
-    public void updateDialog_wifiOnAndHasWifiList_showWifiListAndSeeAll() {
+    public void updateDialog_wifiOnAndHasConnectedWifi_showConnectedWifiAndSeeAll() {
         // The preconditions WiFi ON and WiFi entries are already in setUp()
+        mInternetDialog.mWifiEntriesCount = 0;
 
         mInternetDialog.updateDialog(false);
 
+        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void updateDialog_wifiOnAndHasWifiList_showWifiListAndSeeAll() {
+        // The preconditions WiFi ON and WiFi entries are already in setUp()
+        mInternetDialog.mConnectedWifiEntry = null;
+
+        mInternetDialog.updateDialog(false);
+
+        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
         assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
     }
 
     @Test
-    public void updateDialog_deviceLockedAndHasInternetWifi_showHighlightWifiToggle() {
-        // The preconditions WiFi ON and Internet WiFi are already in setUp()
-        when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+    public void updateDialog_wifiOnAndHasBothWifiEntry_showBothWifiEntryAndSeeAll() {
+        // The preconditions WiFi ON and WiFi entries are already in setUp()
 
         mInternetDialog.updateDialog(false);
 
-        assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mWifiToggle.getBackground()).isNotNull();
+        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
     }
 
     @Test
-    public void updateDialog_deviceLockedAndHasInternetWifi_hideConnectedWifi() {
-        // The preconditions WiFi ON and Internet WiFi are already in setUp()
-        when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
-
-        mInternetDialog.updateDialog(false);
-
-        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
-    }
-
-    @Test
-    public void updateDialog_deviceLockedAndHasWifiList_hideWifiListAndSeeAll() {
+    public void updateDialog_deviceLockedAndNoConnectedWifi_showWifiToggle() {
         // The preconditions WiFi entries are already in setUp()
         when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+        mInternetDialog.mConnectedWifiEntry = null;
 
         mInternetDialog.updateDialog(false);
 
+        // Show WiFi Toggle without background
+        assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mWifiToggle.getBackground()).isNull();
+        // Hide Wi-Fi networks and See all
+        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_deviceLockedAndHasConnectedWifi_showWifiToggleWithBackground() {
+        // The preconditions WiFi ON and WiFi entries are already in setUp()
+        when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+
+        mInternetDialog.updateDialog(false);
+
+        // Show WiFi Toggle with highlight background
+        assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mWifiToggle.getBackground()).isNotNull();
+        // Hide Wi-Fi networks and See all
+        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
         assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
         assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
     }
 
     @Test
     public void updateDialog_wifiOn_hideWifiScanNotify() {
-        // The preconditions WiFi ON and Internet WiFi are already in setUp()
+        // The preconditions WiFi ON and WiFi entries are already in setUp()
 
         mInternetDialog.updateDialog(false);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 9a23eb6..7210d9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -41,6 +41,7 @@
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.DynamicChildBindController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -77,6 +78,7 @@
 
     // Dependency mocks:
     @Mock private FeatureFlags mFeatureFlags;
+    @Mock private NotifPipelineFlags mNotifPipelineFlags;
     @Mock private NotificationEntryManager mEntryManager;
     @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
     @Mock private NotificationGroupManagerLegacy mGroupManager;
@@ -103,7 +105,8 @@
         when(mVisualStabilityManager.areGroupChangesAllowed()).thenReturn(true);
         when(mVisualStabilityManager.isReorderingAllowed()).thenReturn(true);
 
-        when(mFeatureFlags.checkLegacyPipelineEnabled()).thenReturn(true);
+        when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
+        when(mNotifPipelineFlags.checkLegacyPipelineEnabled()).thenReturn(true);
 
         mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
 
@@ -117,7 +120,8 @@
                 mock(ForegroundServiceSectionController.class),
                 mock(DynamicChildBindController.class),
                 mock(LowPriorityInflationHelper.class),
-                mock(AssistantFeedbackController.class));
+                mock(AssistantFeedbackController.class),
+                mNotifPipelineFlags);
         mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index 47a11fc..344d6f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -158,11 +158,9 @@
     public void setUp() throws Exception {
         mMockingSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT)
                 .mockStatic(FeatureFlags.class).startMocking();
-        ExtendedMockito.doReturn(true).when(() ->
-                FeatureFlags.isProviderModelSettingEnabled(mContext));
         mFeatureFlags = mock(FeatureFlags.class);
         when(mFeatureFlags.isCombinedStatusBarSignalIconsEnabled()).thenReturn(false);
-        when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+        when(mFeatureFlags.isProviderModelSettingEnabled(mContext)).thenReturn(true);
 
 
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index a53fb78..4521610 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -62,6 +62,7 @@
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
 import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationClicker;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -143,6 +144,7 @@
     @Mock private NotificationGroupManagerLegacy mGroupMembershipManager;
     @Mock private NotificationGroupManagerLegacy mGroupExpansionManager;
     @Mock private FeatureFlags mFeatureFlags;
+    @Mock private NotifPipelineFlags mNotifPipelineFlags;
     @Mock private LeakDetector mLeakDetector;
 
     @Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
@@ -179,6 +181,7 @@
                 .setNotification(notification)
                 .build();
 
+        when(mNotifPipelineFlags.checkLegacyPipelineEnabled()).thenReturn(true);
         when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
 
         mEntryManager = new NotificationEntryManager(
@@ -282,7 +285,6 @@
 
         mRowBinder = new NotificationRowBinderImpl(
                 mContext,
-                mFeatureFlags,
                 new NotificationMessagingUtil(mContext),
                 mRemoteInputManager,
                 mLockscreenUserManager,
@@ -294,7 +296,8 @@
                         mEntryManager,
                         mock(LauncherApps.class),
                         new IconBuilder(mContext)),
-                mock(LowPriorityInflationHelper.class));
+                mock(LowPriorityInflationHelper.class),
+                mNotifPipelineFlags);
 
         mEntryManager.setUpWithPresenter(mPresenter);
         mEntryManager.addNotificationEntryListener(mEntryListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 58e3cc8..276f246 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -52,6 +52,7 @@
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
@@ -80,11 +81,8 @@
     @Rule public final MockitoRule mockitoRule = MockitoJUnit.rule();
 
     @Mock private NotificationStackScrollLayout mNssl;
-    @Mock private FeatureFlags mFeatureFlags;
-    @Mock private ActivityStarterDelegate mActivityStarterDelegate;
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private ConfigurationController mConfigurationController;
-    @Mock private PeopleHubViewAdapter mPeopleHubAdapter;
     @Mock private KeyguardMediaController mKeyguardMediaController;
     @Mock private NotificationSectionsFeatureManager mSectionsFeatureManager;
     @Mock private NotificationRowComponent mNotificationRowComponent;
@@ -94,6 +92,7 @@
     @Mock private SectionHeaderController mPeopleHeaderController;
     @Mock private SectionHeaderController mAlertingHeaderController;
     @Mock private SectionHeaderController mSilentHeaderController;
+    @Mock private NotifPipelineFlags mNotifPipelineFlags;
 
     private NotificationSectionsManager mSectionsManager;
 
@@ -122,12 +121,12 @@
         when(mSilentHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
         mSectionsManager =
                 new NotificationSectionsManager(
-                        mFeatureFlags,
                         mStatusBarStateController,
                         mConfigurationController,
                         mKeyguardMediaController,
                         mSectionsFeatureManager,
                         mLogger,
+                        mNotifPipelineFlags,
                         mIncomingHeaderController,
                         mPeopleHeaderController,
                         mAlertingHeaderController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 4e6b0a2..ffef55b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -49,6 +49,7 @@
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -128,7 +129,8 @@
                 mInitController,
                 mNotificationInterruptStateProvider,
                 mock(NotificationRemoteInputManager.class),
-                mock(ConfigurationController.class));
+                mock(ConfigurationController.class),
+                mock(NotifPipelineFlags.class));
         mInitController.executePostInitTasks();
         ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
                 ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 371b91f..6cc6490 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -114,6 +114,7 @@
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
@@ -133,6 +134,8 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -243,6 +246,8 @@
     @Mock private CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
     @Mock private StatusBarComponent.Factory mStatusBarComponentFactory;
     @Mock private StatusBarComponent mStatusBarComponent;
+    @Mock private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
+    @Mock private StatusBarFragmentComponent mStatusBarFragmentComponent;
     @Mock private PluginManager mPluginManager;
     @Mock private LegacySplitScreen mLegacySplitScreen;
     @Mock private LightsOutNotifController mLightsOutNotifController;
@@ -277,6 +282,7 @@
     @Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
     @Mock private PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
     @Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
+    @Mock private NotifPipelineFlags mNotifPipelineFlags;
     private ShadeController mShadeController;
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -356,6 +362,8 @@
         when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
 
         when(mStatusBarComponentFactory.create()).thenReturn(mStatusBarComponent);
+        when(mStatusBarFragmentComponentFactory.create(any()))
+                .thenReturn(mStatusBarFragmentComponent);
         when(mStatusBarComponent.getNotificationShadeWindowViewController()).thenReturn(
                 mNotificationShadeWindowViewController);
 
@@ -425,6 +433,7 @@
                 mCommandQueue,
                 mCollapsedStatusBarFragmentLogger,
                 mStatusBarComponentFactory,
+                mStatusBarFragmentComponentFactory,
                 mPluginManager,
                 Optional.of(mLegacySplitScreen),
                 mLightsOutNotifController,
@@ -464,7 +473,8 @@
                 Optional.of(mStartingSurface),
                 mTunerService,
                 mDumpManager,
-                mActivityLaunchAnimator);
+                mActivityLaunchAnimator,
+                mNotifPipelineFlags);
         when(mKeyguardViewMediator.registerStatusBar(
                 any(StatusBar.class),
                 any(NotificationPanelViewController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
index bf8cc37..1ee8875 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone
+package com.android.systemui.statusbar.phone.fragment
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
similarity index 89%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 75a8624..0ee4e7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.statusbar.phone.fragment;
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
@@ -48,6 +48,13 @@
 import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -72,7 +79,6 @@
     private OngoingCallController mOngoingCallController;
     private SystemStatusAnimationScheduler mAnimationScheduler;
     private StatusBarLocationPublisher mLocationPublisher;
-
     // Set in instantiate()
     private StatusBarIconController mStatusBarIconController;
     private NetworkController mNetworkController;
@@ -84,6 +90,8 @@
     private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
     private OperatorNameViewController mOperatorNameViewController;
 
+    private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
+    private StatusBarFragmentComponent mStatusBarFragmentComponent;
 
     public CollapsedStatusBarFragmentTest() {
         super(CollapsedStatusBarFragment.class);
@@ -245,8 +253,22 @@
         Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
     }
 
+    @Test
+    public void setUp_fragmentCreatesDaggerComponent() {
+        mFragments.dispatchResume();
+        processAllMessages();
+        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+
+        assertEquals(mStatusBarFragmentComponent, fragment.getStatusBarFragmentComponent());
+    }
+
     @Override
     protected Fragment instantiate(Context context, String className, Bundle arguments) {
+        mStatusBarFragmentComponentFactory =
+                mock(StatusBarFragmentComponent.Factory.class);
+        mStatusBarFragmentComponent = mock(StatusBarFragmentComponent.class);
+        when(mStatusBarFragmentComponentFactory.create(any()))
+                .thenReturn(mStatusBarFragmentComponent);
         mOngoingCallController = mock(OngoingCallController.class);
         mAnimationScheduler = mock(SystemStatusAnimationScheduler.class);
         mLocationPublisher = mock(StatusBarLocationPublisher.class);
@@ -261,6 +283,7 @@
 
         setUpNotificationIconAreaController();
         return new CollapsedStatusBarFragment(
+                mStatusBarFragmentComponentFactory,
                 mOngoingCallController,
                 mAnimationScheduler,
                 mLocationPublisher,
@@ -282,7 +305,6 @@
                 mOperatorNameViewControllerFactory);
     }
 
-
     private void setUpNotificationIconAreaController() {
         mMockNotificationAreaController = mock(NotificationIconAreaController.class);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 766471b..3ff5666 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -248,8 +248,9 @@
                 Color.valueOf(Color.BLUE), null);
 
         String jsonString =
-                "{\"android.theme.customization.system_palette\":\"override.package.name\","
-                        + "\"android.theme.customization.color_source\":\"home_wallpaper\","
+                "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+                        + "\"android.theme.customization.system_palette\":\"A16B00\","
+                        + "\"android.theme.customization.accent_color\":\"A16B00\","
                         + "\"android.theme.customization.color_index\":\"2\"}";
 
         when(mSecureSettings.getStringForUser(
@@ -274,14 +275,15 @@
     }
 
     @Test
-    public void onWallpaperColorsChanged_ResetThemeWithDifferentWallpapers() {
+    public void onWallpaperColorsChanged_ResetThemeWithNewHomeWallpapers() {
         // Should ask for a new theme when wallpaper colors change
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
                 Color.valueOf(Color.BLUE), null);
 
         String jsonString =
-                "{\"android.theme.customization.system_palette\":\"override.package.name\","
-                        + "\"android.theme.customization.color_source\":\"home_wallpaper\","
+                "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+                        + "\"android.theme.customization.system_palette\":\"A16B00\","
+                        + "\"android.theme.customization.accent_color\":\"A16B00\","
                         + "\"android.theme.customization.color_index\":\"2\"}";
 
         when(mSecureSettings.getStringForUser(
@@ -304,14 +306,15 @@
     }
 
     @Test
-    public void onWallpaperColorsChanged_ResetThemeWithSameWallpaper() {
+    public void onWallpaperColorsChanged_ResetThemeWithNewHomeAndLockWallpaper() {
         // Should ask for a new theme when wallpaper colors change
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
                 Color.valueOf(Color.BLUE), null);
 
         String jsonString =
-                "{\"android.theme.customization.system_palette\":\"override.package.name\","
-                        + "\"android.theme.customization.color_source\":\"home_wallpaper\","
+                "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+                        + "\"android.theme.customization.system_palette\":\"A16B00\","
+                        + "\"android.theme.customization.accent_color\":\"A16B00\","
                         + "\"android.theme.customization.color_index\":\"2\"}";
 
         when(mSecureSettings.getStringForUser(
@@ -339,8 +342,9 @@
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
                 Color.valueOf(Color.BLUE), null);
         String jsonString =
-                "{\"android.theme.customization.system_palette\":\"override.package.name\","
-                        + "\"android.theme.customization.color_source\":\"home_wallpaper\","
+                "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+                        + "\"android.theme.customization.system_palette\":\"A16B00\","
+                        + "\"android.theme.customization.accent_color\":\"A16B00\","
                         + "\"android.theme.customization.color_index\":\"2\"}";
         when(mSecureSettings.getStringForUser(
                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
@@ -366,8 +370,9 @@
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
                 Color.valueOf(Color.BLUE), null);
         String jsonString =
-                "{\"android.theme.customization.system_palette\":\"override.package.name\","
-                        + "\"android.theme.customization.color_source\":\"lock_wallpaper\","
+                "{\"android.theme.customization.color_source\":\"lock_wallpaper\","
+                        + "\"android.theme.customization.system_palette\":\"A16B00\","
+                        + "\"android.theme.customization.accent_color\":\"A16B00\","
                         + "\"android.theme.customization.color_index\":\"2\"}";
         when(mSecureSettings.getStringForUser(
                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
@@ -394,8 +399,9 @@
                 Color.valueOf(Color.BLUE), null);
 
         String jsonString =
-                "{\"android.theme.customization.system_palette\":\"override.package.name\","
-                        + "\"android.theme.customization.color_source\":\"home_wallpaper\","
+                "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+                        + "\"android.theme.customization.system_palette\":\"A16B00\","
+                        + "\"android.theme.customization.accent_color\":\"A16B00\","
                         + "\"android.theme.customization.color_index\":\"2\"}";
 
         when(mSecureSettings.getStringForUser(
@@ -416,6 +422,36 @@
     }
 
     @Test
+    public void onWallpaperColorsChanged_keepThemeWhenFromLatestWallpaperAndSpecifiedColor() {
+        // Shouldn't ask for a new theme when the colors of the last applied wallpaper change
+        // with the same specified system palette one.
+        WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+                Color.valueOf(0xffa16b00), null);
+
+        String jsonString =
+                "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+                        + "\"android.theme.customization.system_palette\":\"A16B00\","
+                        + "\"android.theme.customization.accent_color\":\"A16B00\","
+                        + "\"android.theme.customization.color_index\":\"2\"}";
+
+        when(mSecureSettings.getStringForUser(
+                eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+                .thenReturn(jsonString);
+        when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK)).thenReturn(1);
+        // SYSTEM wallpaper is the last applied one
+        when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_SYSTEM)).thenReturn(2);
+
+        mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+
+        ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
+        verify(mSecureSettings, never()).putString(
+                eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
+
+        // Apply overlay by existing theme from secure setting
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+    }
+
+    @Test
     public void onWallpaperColorsChanged_keepThemeIfNotLatestWallpaper() {
         // Shouldn't ask for a new theme when the colors of the wallpaper that is not the last
         // applied one change
@@ -423,8 +459,9 @@
                 Color.valueOf(Color.BLUE), null);
 
         String jsonString =
-                "{\"android.theme.customization.system_palette\":\"override.package.name\","
-                        + "\"android.theme.customization.color_source\":\"home_wallpaper\","
+                "{\"android.theme.customization.color_source\":\"home_wallpaper\","
+                        + "\"android.theme.customization.system_palette\":\"A16B00\","
+                        + "\"android.theme.customization.accent_color\":\"A16B00\","
                         + "\"android.theme.customization.color_index\":\"2\"}";
 
         when(mSecureSettings.getStringForUser(
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 311555e..6f009b6 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3713,8 +3713,19 @@
     }
 
     @Override
-    public StorageVolume[] getVolumeList(int uid, String packageName, int flags) {
-        final int userId = UserHandle.getUserId(uid);
+    public StorageVolume[] getVolumeList(int userId, String callingPackage, int flags) {
+        final int callingUid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(callingUid);
+
+        if (!isUidOwnerOfPackageOrSystem(callingPackage, callingUid)) {
+            throw new SecurityException("callingPackage does not match UID");
+        }
+        if (callingUserId != userId) {
+            // Callers can ask for volumes of different users, but only with the correct permissions
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS,
+                    "Need INTERACT_ACROSS_USERS to get volumes for another user");
+        }
 
         final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0;
         final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0;
@@ -3730,7 +3741,7 @@
         // should never attempt to augment the actual storage volume state,
         // otherwise we risk confusing it with race conditions as users go
         // through various unlocked states
-        final boolean callerIsMediaStore = UserHandle.isSameApp(Binder.getCallingUid(),
+        final boolean callerIsMediaStore = UserHandle.isSameApp(callingUid,
                 mMediaStoreAuthorityAppId);
 
         final boolean userIsDemo;
@@ -3740,8 +3751,9 @@
         try {
             userIsDemo = LocalServices.getService(UserManagerInternal.class)
                     .getUserInfo(userId).isDemo();
+            storagePermission = mStorageManagerInternal.hasExternalStorage(callingUid,
+                    callingPackage);
             userKeyUnlocked = isUserKeyUnlocked(userId);
-            storagePermission = mStorageManagerInternal.hasExternalStorage(uid, packageName);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4c2cd53..5188a72 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4666,7 +4666,8 @@
                         app.getCompat(), getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
                         buildSerial, autofillOptions, contentCaptureOptions,
-                        app.getDisabledCompatChanges(), serializedSystemFontMap);
+                        app.getDisabledCompatChanges(), serializedSystemFontMap,
+                        app.getStartElapsedTime(), app.getStartUptime());
             } else {
                 thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
                         null, null, null, testMode,
@@ -4676,7 +4677,8 @@
                         app.getCompat(), getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
                         buildSerial, autofillOptions, contentCaptureOptions,
-                        app.getDisabledCompatChanges(), serializedSystemFontMap);
+                        app.getDisabledCompatChanges(), serializedSystemFontMap,
+                        app.getStartElapsedTime(), app.getStartUptime());
             }
             if (profilerInfo != null) {
                 profilerInfo.closeFd();
@@ -11951,6 +11953,15 @@
         // If this is a preceding instance of another process instance
         allowRestart = mProcessList.handlePrecedingAppDiedLocked(app);
 
+        // If somehow this process was still waiting for the death of its predecessor,
+        // (probably it's "killed" before starting for real), reset the bookkeeping.
+        final ProcessRecord predecessor = app.mPredecessor;
+        if (predecessor != null) {
+            predecessor.mSuccessor = null;
+            predecessor.mSuccessorStartRunnable = null;
+            app.mPredecessor = null;
+        }
+
         // If the caller is restarting this app, then leave it in its
         // current lists and let the caller take care of it.
         if (restarting) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 74c35e9..bbd41f7 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1794,13 +1794,14 @@
         if (app.isPendingStart()) {
             return true;
         }
-        long startTime = SystemClock.uptimeMillis();
+        final long startUptime = SystemClock.uptimeMillis();
+        final long startElapsedTime = SystemClock.elapsedRealtime();
         if (app.getPid() > 0 && app.getPid() != ActivityManagerService.MY_PID) {
-            checkSlow(startTime, "startProcess: removing from pids map");
+            checkSlow(startUptime, "startProcess: removing from pids map");
             mService.removePidLocked(app.getPid(), app);
             app.setBindMountPending(false);
             mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
-            checkSlow(startTime, "startProcess: done removing from pids map");
+            checkSlow(startUptime, "startProcess: done removing from pids map");
             app.setPid(0);
             app.setStartSeq(0);
         }
@@ -1813,9 +1814,9 @@
                 "startProcessLocked removing on hold: " + app);
         mService.mProcessesOnHold.remove(app);
 
-        checkSlow(startTime, "startProcess: starting to update cpu stats");
+        checkSlow(startUptime, "startProcess: starting to update cpu stats");
         mService.updateCpuStats();
-        checkSlow(startTime, "startProcess: done updating cpu stats");
+        checkSlow(startUptime, "startProcess: done updating cpu stats");
 
         try {
             final int userId = UserHandle.getUserId(app.uid);
@@ -1832,7 +1833,7 @@
             if (!app.isolated) {
                 int[] permGids = null;
                 try {
-                    checkSlow(startTime, "startProcess: getting gids from package manager");
+                    checkSlow(startUptime, "startProcess: getting gids from package manager");
                     final IPackageManager pm = AppGlobals.getPackageManager();
                     permGids = pm.getPackageGids(app.info.packageName,
                             MATCH_DIRECT_BOOT_AUTO, app.userId);
@@ -1870,7 +1871,7 @@
                 gids = computeGidsForProcess(mountExternal, uid, permGids, externalStorageAccess);
             }
             app.setMountMode(mountExternal);
-            checkSlow(startTime, "startProcess: building args");
+            checkSlow(startUptime, "startProcess: building args");
             if (mService.mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {
                 uid = 0;
             }
@@ -2028,7 +2029,7 @@
 
             return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,
                     runtimeFlags, zygotePolicyFlags, mountExternal, seInfo, requiredAbi,
-                    instructionSet, invokeWith, startTime);
+                    instructionSet, invokeWith, startUptime, startElapsedTime);
         } catch (RuntimeException e) {
             Slog.e(ActivityManagerService.TAG, "Failure starting process " + app.processName, e);
 
@@ -2048,7 +2049,7 @@
     boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app,
             int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal,
             String seInfo, String requiredAbi, String instructionSet, String invokeWith,
-            long startTime) {
+            long startUptime, long startElapsedTime) {
         app.setPendingStart(true);
         app.setRemoved(false);
         synchronized (mProcLock) {
@@ -2069,7 +2070,7 @@
         }
         final long startSeq = ++mProcStartSeqCounter;
         app.setStartSeq(startSeq);
-        app.setStartParams(uid, hostingRecord, seInfo, startTime);
+        app.setStartParams(uid, hostingRecord, seInfo, startUptime, startElapsedTime);
         app.setUsingWrapper(invokeWith != null
                 || Zygote.getWrapProperty(app.processName) != null);
         mPendingStarts.put(startSeq, app);
@@ -2086,7 +2087,7 @@
                 final Process.ProcessStartResult startResult = startProcess(hostingRecord,
                         entryPoint, app,
                         uid, gids, runtimeFlags, zygotePolicyFlags, mountExternal, seInfo,
-                        requiredAbi, instructionSet, invokeWith, startTime);
+                        requiredAbi, instructionSet, invokeWith, startUptime);
                 handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
                         startSeq, false);
             } catch (RuntimeException e) {
@@ -5144,7 +5145,8 @@
         }
 
         Watchdog.getInstance().processDied(app.processName, app.getPid());
-        if (app.getDeathRecipient() == null) {
+        if (app.getDeathRecipient() == null
+                && mDyingProcesses.get(app.processName, app.uid) == app) {
             // If we've done unlinkDeathRecipient before calling into this, remove from dying list.
             mDyingProcesses.remove(app.processName, app.uid);
             app.setDyingPid(0);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 8ae1259..8c55626 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -178,9 +178,14 @@
     private volatile String mSeInfo;
 
     /**
-     * When the process is started.
+     * When the process is started. (before zygote fork)
      */
-    private volatile long mStartTime;
+    private volatile long mStartUptime;
+
+    /**
+     * When the process is started. (before zygote fork)
+     */
+    private volatile long mStartElapsedTime;
 
     /**
      * This will be same as {@link #uid} usually except for some apps used during factory testing.
@@ -372,16 +377,18 @@
     Runnable mSuccessorStartRunnable;
 
     void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
-            long startTime) {
+            long startUptime, long startElapsedTime) {
         this.mStartUid = startUid;
         this.mHostingRecord = hostingRecord;
         this.mSeInfo = seInfo;
-        this.mStartTime = startTime;
+        this.mStartUptime = startUptime;
+        this.mStartElapsedTime = startElapsedTime;
     }
 
     @GuardedBy({"mService", "mProcLock"})
     void dump(PrintWriter pw, String prefix) {
         final long nowUptime = SystemClock.uptimeMillis();
+        final long nowElapsedTime = SystemClock.elapsedRealtime();
 
         pw.print(prefix); pw.print("user #"); pw.print(userId);
                 pw.print(" uid="); pw.print(info.uid);
@@ -442,6 +449,10 @@
         pw.print(prefix); pw.print("pid="); pw.println(mPid);
         pw.print(prefix); pw.print("lastActivityTime=");
         TimeUtils.formatDuration(mLastActivityTime, nowUptime, pw);
+        pw.print(prefix); pw.print("startUptimeTime=");
+        TimeUtils.formatDuration(mStartElapsedTime, nowUptime, pw);
+        pw.print(prefix); pw.print("startElapsedTime=");
+        TimeUtils.formatDuration(mStartElapsedTime, nowElapsedTime, pw);
         pw.println();
         if (mPersistent || mRemoved) {
             pw.print(prefix); pw.print("persistent="); pw.print(mPersistent);
@@ -671,12 +682,21 @@
         mSeInfo = seInfo;
     }
 
-    long getStartTime() {
-        return mStartTime;
+    long getStartUptime() {
+        return mStartUptime;
     }
 
-    void setStartTime(long startTime) {
-        mStartTime = startTime;
+    /**
+     * Same as {@link #getStartUptime()}.
+     * @deprecated use {@link #getStartUptime()} instead for clarity.
+     */
+    @Deprecated
+    long getStartTime() {
+        return mStartUptime;
+    }
+
+    long getStartElapsedTime() {
+        return mStartElapsedTime;
     }
 
     int getStartUid() {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 4dd1682..18be9a10 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4638,12 +4638,13 @@
          * or recording for VOICE_COMMUNICATION.
          *   or
          * - It requests a mode different from MODE_IN_COMMUNICATION or MODE_NORMAL
+         * Note: only privileged apps can request MODE_IN_CALL, MODE_CALL_REDIRECT
+         * or MODE_COMMUNICATION_REDIRECT.
          */
         public boolean isActive() {
             return mIsPrivileged
                     || ((mMode == AudioSystem.MODE_IN_COMMUNICATION)
                         && (mRecordingActive || mPlaybackActive))
-                    || mMode == AudioSystem.MODE_IN_CALL
                     || mMode == AudioSystem.MODE_RINGTONE
                     || mMode == AudioSystem.MODE_CALL_SCREENING;
         }
@@ -4752,9 +4753,13 @@
         final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.MODIFY_PHONE_STATE)
                 == PackageManager.PERMISSION_GRANTED;
-        if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) {
-            Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
-                    + pid + ", uid=" + Binder.getCallingUid());
+        if ((mode == AudioSystem.MODE_IN_CALL
+                || mode == AudioSystem.MODE_CALL_REDIRECT
+                || mode == AudioSystem.MODE_COMMUNICATION_REDIRECT)
+                && !hasModifyPhoneStatePermission) {
+            Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode("
+                    + AudioSystem.modeToString(mode) + ") from pid=" + pid
+                    + ", uid=" + Binder.getCallingUid());
             return;
         }
 
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index fb74170..e2e56ae 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -223,11 +223,18 @@
      * Overrides the enabled state for a given change and app.
      *
      *
-     * @param overrides   list of overrides to default changes config.
-     * @param packageName app for which the overrides will be applied.
+     * @param overrides            list of overrides to default changes config.
+     * @param packageName          app for which the overrides will be applied.
+     * @param skipUnknownChangeIds whether to skip unknown change IDs in {@code overrides}.
      */
-    synchronized void addOverrides(CompatibilityOverrideConfig overrides, String packageName) {
+    synchronized void addPackageOverrides(CompatibilityOverrideConfig overrides,
+            String packageName, boolean skipUnknownChangeIds) {
         for (Long changeId : overrides.overrides.keySet()) {
+            if (skipUnknownChangeIds && !isKnownChangeId(changeId)) {
+                Slog.w(TAG, "Trying to add overrides for unknown Change ID " + changeId + ". "
+                        + "Skipping Change ID.");
+                continue;
+            }
             addOverrideUnsafe(changeId, packageName, overrides.overrides.get(changeId));
         }
         saveOverrides();
@@ -338,7 +345,8 @@
 
     /**
      * Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or
-     * {@link #addOverrides(CompatibilityOverrideConfig, String)} for a certain package.
+     * {@link #addPackageOverrides(CompatibilityOverrideConfig, String, boolean)} for a certain
+     * package.
      *
      * <p>This restores the default behaviour for the given app.
      *
@@ -359,7 +367,8 @@
     /**
      * Removes overrides whose change ID is specified in {@code overridesToRemove} that were
      * previously added via {@link #addOverride(long, String, boolean)} or
-     * {@link #addOverrides(CompatibilityOverrideConfig, String)} for a certain package.
+     * {@link #addPackageOverrides(CompatibilityOverrideConfig, String, boolean)} for a certain
+     * package.
      *
      * <p>This restores the default behaviour for the given change IDs and app.
      *
@@ -370,6 +379,11 @@
             String packageName) {
         boolean shouldInvalidateCache = false;
         for (Long changeId : overridesToRemove.changeIds) {
+            if (!isKnownChangeId(changeId)) {
+                Slog.w(TAG, "Trying to remove overrides for unknown Change ID " + changeId + ". "
+                        + "Skipping Change ID.");
+                continue;
+            }
             shouldInvalidateCache |= removeOverrideUnsafe(changeId, packageName);
         }
         if (shouldInvalidateCache) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index b32d1d7..6ea89d4 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -205,7 +205,8 @@
             overridesMap.put(change, new PackageOverride.Builder().setEnabled(false)
                     .build());
         }
-        mCompatConfig.addOverrides(new CompatibilityOverrideConfig(overridesMap), packageName);
+        mCompatConfig.addPackageOverrides(new CompatibilityOverrideConfig(overridesMap),
+                packageName, /* skipUnknownChangeIds */ false);
         killPackage(packageName);
     }
 
@@ -220,7 +221,8 @@
             overridesMap.put(change, new PackageOverride.Builder().setEnabled(false)
                     .build());
         }
-        mCompatConfig.addOverrides(new CompatibilityOverrideConfig(overridesMap), packageName);
+        mCompatConfig.addPackageOverrides(new CompatibilityOverrideConfig(overridesMap),
+                packageName, /* skipUnknownChangeIds */ false);
     }
 
     @Override
@@ -229,7 +231,7 @@
         // TODO(b/183630314): Unify the permission enforcement with the other setOverrides* methods.
         checkCompatChangeOverrideOverridablePermission();
         checkAllCompatOverridesAreOverridable(overrides.overrides.keySet());
-        mCompatConfig.addOverrides(overrides, packageName);
+        mCompatConfig.addPackageOverrides(overrides, packageName, /* skipUnknownChangeIds= */ true);
     }
 
     @Override
@@ -435,7 +437,7 @@
 
     private void checkAllCompatOverridesAreOverridable(Collection<Long> changeIds) {
         for (Long changeId : changeIds) {
-            if (!mCompatConfig.isOverridable(changeId)) {
+            if (isKnownChangeId(changeId) && !mCompatConfig.isOverridable(changeId)) {
                 throw new SecurityException("Only change ids marked as Overridable can be "
                         + "overridden.");
             }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2f3342f..22dd2c0 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -108,8 +108,6 @@
     // screen state returns.  Playing the animation can also be somewhat slow.
     private static final boolean USE_COLOR_FADE_ON_ANIMATION = false;
 
-    // The minimum reduction in brightness when dimmed.
-    private static final float SCREEN_DIM_MINIMUM_REDUCTION_FLOAT = 0.04f;
     private static final float SCREEN_ANIMATION_RATE_MINIMUM = 0.0f;
 
     private static final int COLOR_FADE_ON_ANIMATION_DURATION_MILLIS = 250;
@@ -200,6 +198,10 @@
     // The dim screen brightness.
     private final float mScreenBrightnessDimConfig;
 
+    // The minimum dim amount to use if the screen brightness is already below
+    // mScreenBrightnessDimConfig.
+    private final float mScreenBrightnessMinimumDimAmount;
+
     private final float mScreenBrightnessDefault;
 
     // The minimum allowed brightness while in VR.
@@ -485,6 +487,9 @@
                 pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE));
         mScreenBrightnessDimConfig = clampAbsoluteBrightness(
                 pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM));
+        mScreenBrightnessMinimumDimAmount = resources.getFloat(
+                com.android.internal.R.dimen.config_screenBrightnessMinimumDimAmountFloat);
+
 
         // NORMAL SCREEN SETTINGS
         mScreenBrightnessDefault = clampAbsoluteBrightness(
@@ -1284,7 +1289,7 @@
         if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
             if (brightnessState > PowerManager.BRIGHTNESS_MIN) {
                 brightnessState = Math.max(
-                        Math.min(brightnessState - SCREEN_DIM_MINIMUM_REDUCTION_FLOAT,
+                        Math.min(brightnessState - mScreenBrightnessMinimumDimAmount,
                                 mScreenBrightnessDimConfig),
                         PowerManager.BRIGHTNESS_MIN);
                 mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED);
diff --git a/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java b/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java
index 769ea17..803b5a3 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java
@@ -133,7 +133,7 @@
             try {
                 LocaleList locales = mBinderService.getApplicationLocales(packageName, userId);
                 getOutPrintWriter().println("Locales for " + packageName
-                        + " for user " + userId + " are " + locales);
+                        + " for user " + userId + " are [" + locales.toLanguageTags() + "]");
             } catch (RemoteException e) {
                 getOutPrintWriter().println("Remote Exception: " + e);
             } catch (IllegalArgumentException e) {
diff --git a/services/core/java/com/android/server/locales/TEST_MAPPING b/services/core/java/com/android/server/locales/TEST_MAPPING
index 097c2bc..27d2851 100644
--- a/services/core/java/com/android/server/locales/TEST_MAPPING
+++ b/services/core/java/com/android/server/locales/TEST_MAPPING
@@ -10,6 +10,9 @@
     },
     {
       "name": "CtsLocaleManagerTestCases"
+    },
+    {
+      "name": "CtsLocaleManagerHostTestCases"
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7fd7505..c47ca4b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -558,7 +558,10 @@
                 Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
                 childSessions = getChildSessionsLocked();
             }
-            cleanStageDir(childSessions);
+            destroyInternal();
+            for (PackageInstallerSession child : childSessions) {
+                child.destroyInternal();
+            }
             mCallback.onStagedSessionChanged(PackageInstallerSession.this);
         }
 
@@ -576,7 +579,10 @@
                 Slog.d(TAG, "Marking session " + sessionId + " as applied");
                 childSessions = getChildSessionsLocked();
             }
-            cleanStageDir(childSessions);
+            destroyInternal();
+            for (PackageInstallerSession child : childSessions) {
+                child.destroyInternal();
+            }
             mCallback.onStagedSessionChanged(PackageInstallerSession.this);
         }
 
@@ -699,13 +705,11 @@
                     return;
                 }
                 mDestroyed = true;
-                List<PackageInstallerSession> childSessions = getChildSessionsLocked();
                 r = () -> {
                     assertNotLocked("abandonStaged");
                     if (mCommitted.get()) {
                         mStagingManager.abortCommittedSession(this);
                     }
-                    cleanStageDir(childSessions);
                     destroyInternal();
                     dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
                     maybeCleanUpChildSessions();
@@ -2102,10 +2106,6 @@
         destroyInternal();
         // Dispatch message to remove session from PackageInstallerService.
         dispatchSessionFinished(error, detailMessage, null);
-        // TODO(b/173194203): clean up staged session in destroyInternal() call instead
-        if (isStaged() && stageDir != null) {
-            cleanStageDir();
-        }
     }
 
     private void onSessionVerificationFailure(int error, String msg) {
@@ -4282,41 +4282,13 @@
             incrementalFileStorages = mIncrementalFileStorages;
             mIncrementalFileStorages = null;
         }
-        // For staged sessions, we don't delete the directory where the packages have been copied,
-        // since these packages are supposed to be read on reboot.
-        // Those dirs are deleted when the staged session has reached a final state.
-        if (stageDir != null && !params.isStaged) {
-            try {
-                if (incrementalFileStorages != null) {
-                    incrementalFileStorages.cleanUpAndMarkComplete();
-                }
-                mInstaller.rmPackageDir(stageDir.getAbsolutePath());
-            } catch (InstallerException ignored) {
-            }
-        }
-    }
-
-    private void cleanStageDir(List<PackageInstallerSession> childSessions) {
-        if (isMultiPackage()) {
-            for (PackageInstallerSession childSession : childSessions) {
-                childSession.cleanStageDir();
-            }
-        } else {
-            cleanStageDir();
-        }
-    }
-
-    private void cleanStageDir() {
-        final IncrementalFileStorages incrementalFileStorages;
-        synchronized (mLock) {
-            incrementalFileStorages = mIncrementalFileStorages;
-            mIncrementalFileStorages = null;
-        }
         try {
             if (incrementalFileStorages != null) {
                 incrementalFileStorages.cleanUpAndMarkComplete();
             }
-            mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+            if (stageDir != null) {
+                mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+            }
         } catch (InstallerException ignored) {
         }
     }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 3855e65..a01c358 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -4205,7 +4205,7 @@
         // Make sure all dynamic permissions have been assigned to a package,
         // and make sure there are no dangling permissions.
         boolean permissionSourcePackageChanged = updatePermissionSourcePackage(changingPkgName,
-                changingPkg, callback);
+                callback);
 
         if (permissionTreesSourcePackageChanged | permissionSourcePackageChanged) {
             // Permission ownership has changed. This e.g. changes which packages can get signature
@@ -4244,22 +4244,12 @@
     /**
      * Update which app declares a permission.
      *
-     * <p>Possible parameter combinations
-     * <table>
-     *     <tr><th></th><th>packageName != null</th><th>packageName == null</th></tr>
-     *     <tr><th>pkg != null</th><td>package is updated</td><td>invalid</td></tr>
-     *     <tr><th>pkg == null</th><td>package is deleted</td><td>all packages are updated</td></tr>
-     * </table>
-     *
      * @param packageName The package that is updated, or {@code null} if all packages should be
      *                    updated
-     * @param pkg The package that is updated, or {@code null} if all packages should be updated or
-     *            package is deleted
      *
      * @return {@code true} if a permission source package might have changed
      */
     private boolean updatePermissionSourcePackage(@Nullable String packageName,
-            @Nullable AndroidPackage pkg,
             final @Nullable PermissionCallback callback) {
         // Always need update if packageName is null
         if (packageName == null) {
@@ -4289,6 +4279,7 @@
             }
         }
         if (needsUpdate != null) {
+            final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
             for (final Permission bp : needsUpdate) {
                 // If the target package is being uninstalled, we need to revoke this permission
                 // From all other packages
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ea3ef650..c6ff8a0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -951,7 +951,7 @@
             powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
         } else if (count == 3) {
             powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
-        } else if (interactive && !beganFromNonInteractive) {
+        } else if (count == 1 && interactive && !beganFromNonInteractive) {
             if (mSideFpsEventHandler.onSinglePressDetected(eventTime)) {
                 Slog.i(TAG, "Suppressing power key because the user is interacting with the "
                         + "fingerprint sensor");
@@ -3479,13 +3479,23 @@
         if (!mSystemBooted) {
             // If we have not yet booted, don't let key events do anything.
             // Exception: Wake and power key events are forwarded to PowerManager to allow it to
-            // wake from quiescent mode during boot.
+            // wake from quiescent mode during boot. On these key events we also explicitly turn on
+            // the connected TV and switch HDMI input if we're a HDMI playback device.
+            boolean shouldTurnOnTv = false;
             if (down && (keyCode == KeyEvent.KEYCODE_POWER
                     || keyCode == KeyEvent.KEYCODE_TV_POWER)) {
                 wakeUpFromPowerKey(event.getDownTime());
+                shouldTurnOnTv = true;
             } else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)
                     && isWakeKeyWhenScreenOff(keyCode)) {
                 wakeUpFromWakeKey(event);
+                shouldTurnOnTv = true;
+            }
+            if (shouldTurnOnTv) {
+                final HdmiControl hdmiControl = getHdmiControl();
+                if (hdmiControl != null) {
+                    hdmiControl.turnOnTv();
+                }
             }
             return 0;
         }
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index b7fb6e5..0276b37 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -44,7 +44,7 @@
     private static final int MSG_KEY_VERY_LONG_PRESS = 1;
     private static final int MSG_KEY_DELAYED_PRESS = 2;
 
-    private volatile int mKeyPressCounter;
+    private int mKeyPressCounter;
     private boolean mBeganFromNonInteractive = false;
 
     private final ArrayList<SingleKeyRule> mRules = new ArrayList();
@@ -53,7 +53,6 @@
     // Key code of current key down event, reset when key up.
     private int mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
     private volatile boolean mHandledByLongPress = false;
-    private volatile boolean mHandledByMultiPress = false;
     private final Handler mHandler;
     private long mLastDownTime = 0;
     private static final long MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout();
@@ -223,7 +222,6 @@
             reset();
         }
         mDownKeyCode = keyCode;
-        mLastDownTime = event.getDownTime();
 
         // Picks a new rule, return if no rule picked.
         if (mActiveRule == null) {
@@ -238,12 +236,21 @@
                     break;
                 }
             }
+            mLastDownTime = 0;
         }
         if (mActiveRule == null) {
             return;
         }
 
-        if (mKeyPressCounter == 0) {
+        final long keyDownInterval = event.getDownTime() - mLastDownTime;
+        mLastDownTime = event.getDownTime();
+        if (keyDownInterval >= MULTI_PRESS_TIMEOUT) {
+            mKeyPressCounter = 1;
+        } else {
+            mKeyPressCounter++;
+        }
+
+        if (mKeyPressCounter == 1) {
             if (mActiveRule.supportLongPress()) {
                 final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, keyCode, 0,
                         mActiveRule);
@@ -263,17 +270,16 @@
             mHandler.removeMessages(MSG_KEY_DELAYED_PRESS);
 
             // Trigger multi press immediately when reach max count.( > 1)
-            if (mKeyPressCounter == mActiveRule.getMaxMultiPressCount() - 1) {
+            if (mActiveRule.getMaxMultiPressCount() > 1
+                    && mKeyPressCounter == mActiveRule.getMaxMultiPressCount()) {
                 if (DEBUG) {
                     Log.i(TAG, "Trigger multi press " + mActiveRule.toString() + " for it"
-                            + " reached the max count " + (mKeyPressCounter + 1));
+                            + " reached the max count " + mKeyPressCounter);
                 }
                 final Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, keyCode,
-                        mKeyPressCounter + 1, mActiveRule);
+                        mKeyPressCounter, mActiveRule);
                 msg.setAsynchronous(true);
                 mHandler.sendMessage(msg);
-                mHandledByMultiPress = true;
-                mKeyPressCounter = 0;
             }
         }
     }
@@ -286,10 +292,10 @@
             return false;
         }
 
-        if (mHandledByLongPress || mHandledByMultiPress) {
+        if (mHandledByLongPress) {
             mHandledByLongPress = false;
-            mHandledByMultiPress = false;
             mKeyPressCounter = 0;
+            mActiveRule = null;
             return true;
         }
 
@@ -303,16 +309,17 @@
                         1, mActiveRule);
                 msg.setAsynchronous(true);
                 mHandler.sendMessage(msg);
-                reset();
+                mActiveRule = null;
                 return true;
             }
 
             // This could be a multi-press.  Wait a little bit longer to confirm.
-            mKeyPressCounter++;
-            Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode,
-                    mKeyPressCounter, mActiveRule);
-            msg.setAsynchronous(true);
-            mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT);
+            if (mKeyPressCounter < mActiveRule.getMaxMultiPressCount()) {
+                Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode,
+                        mKeyPressCounter, mActiveRule);
+                msg.setAsynchronous(true);
+                mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT);
+            }
             return true;
         }
         reset();
@@ -342,7 +349,6 @@
         }
 
         mHandledByLongPress = false;
-        mHandledByMultiPress = false;
         mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
     }
 
@@ -373,9 +379,6 @@
                 Log.wtf(TAG, "No active rule.");
                 return;
             }
-            // We count the press count when interceptKeyUp. Reset the counter here to prevent if
-            // the multi-press or press happened but the count is less than max multi-press count.
-            mKeyPressCounter = 0;
 
             final int keyCode = msg.arg1;
             final int pressCount = msg.arg2;
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 0394d4c..7cdae58 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -93,8 +93,12 @@
 import android.content.pm.PermissionInfo;
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.display.DisplayManager;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
+import android.media.AudioManager;
+import android.media.MediaDrm;
+import android.media.UnsupportedSchemeException;
 import android.net.ConnectivityManager;
 import android.net.INetworkStatsService;
 import android.net.INetworkStatsSession;
@@ -163,6 +167,7 @@
 import android.util.SparseArray;
 import android.util.StatsEvent;
 import android.util.proto.ProtoOutputStream;
+import android.view.Display;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.procstats.IProcessStats;
@@ -743,6 +748,8 @@
                         return pullAccessibilityShortcutStatsLocked(atomTag, data);
                     case FrameworkStatsLog.ACCESSIBILITY_FLOATING_MENU_STATS:
                         return pullAccessibilityFloatingMenuStatsLocked(atomTag, data);
+                    case FrameworkStatsLog.MEDIA_CAPABILITIES:
+                        return pullMediaCapabilitiesStats(atomTag, data);
                     default:
                         throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
                 }
@@ -940,6 +947,7 @@
         registerKeystoreCrashStats();
         registerAccessibilityShortcutStats();
         registerAccessibilityFloatingMenuStats();
+        registerMediaCapabilitiesStats();
     }
 
     private void initAndRegisterNetworkStatsPullers() {
@@ -4188,6 +4196,16 @@
         );
     }
 
+    private void registerMediaCapabilitiesStats() {
+        int tagId = FrameworkStatsLog.MEDIA_CAPABILITIES;
+        mStatsManager.setPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                DIRECT_EXECUTOR,
+                mStatsCallbackImpl
+        );
+    }
+
     int parseKeystoreStorageStats(KeystoreAtom[] atoms, List<StatsEvent> pulledData) {
         for (KeystoreAtom atomWrapper : atoms) {
             if (atomWrapper.payload.getTag() != KeystoreAtomPayload.storageStats) {
@@ -4470,6 +4488,162 @@
         return StatsManager.PULL_SUCCESS;
     }
 
+    int pullMediaCapabilitiesStats(int atomTag, List<StatsEvent> pulledData) {
+        AudioManager audioManager = mContext.getSystemService(AudioManager.class);
+        if (audioManager == null) {
+            return StatsManager.PULL_SKIP;
+        }
+
+        // get the surround sound metrics information
+        Map<Integer, Boolean> surroundEncodingsMap = audioManager.getSurroundFormats();
+        byte[] surroundEncodings = toBytes(new ArrayList(surroundEncodingsMap.keySet()));
+        byte[] sinkSurroundEncodings = toBytes(audioManager.getReportedSurroundFormats());
+        List<Integer> disabledSurroundEncodingsList = new ArrayList<>();
+        List<Integer> enabledSurroundEncodingsList = new ArrayList<>();
+        for (int surroundEncoding:  surroundEncodingsMap.keySet()) {
+            if (!surroundEncodingsMap.get(surroundEncoding)) {
+                disabledSurroundEncodingsList.add(surroundEncoding);
+            } else {
+                enabledSurroundEncodingsList.add(surroundEncoding);
+            }
+        }
+        byte[] disabledSurroundEncodings = toBytes(disabledSurroundEncodingsList);
+        byte[] enabledSurroundEncodings = toBytes(enabledSurroundEncodingsList);
+        int surroundOutputMode = audioManager.getEncodedSurroundMode();
+
+        DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+        Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+        // get the display capabilities metrics information
+        Display.HdrCapabilities hdrCapabilities = display.getHdrCapabilities();
+        byte[] sinkHdrFormats = new byte[]{};
+        if (hdrCapabilities != null) {
+            sinkHdrFormats = toBytes(hdrCapabilities.getSupportedHdrTypes());
+        }
+        byte[] sinkDisplayModes = toBytes(display.getSupportedModes());
+        int hdcpLevel = -1;
+        List<UUID> uuids = MediaDrm.getSupportedCryptoSchemes();
+        try {
+            if (!uuids.isEmpty()) {
+                MediaDrm mediaDrm = new MediaDrm(uuids.get(0));
+                hdcpLevel = mediaDrm.getConnectedHdcpLevel();
+            }
+        } catch (UnsupportedSchemeException exception) {
+            Slog.e(TAG, "pulling hdcp level failed.", exception);
+            hdcpLevel = -1;
+        }
+
+        // get the display settings metrics information
+        int matchContentFrameRateUserPreference =
+                displayManager.getMatchContentFrameRateUserPreference();
+        byte[] userDisabledHdrTypes = toBytes(displayManager.getUserDisabledHdrTypes());
+        Display.Mode userPreferredDisplayMode = displayManager.getUserPreferredDisplayMode();
+        int userPreferredWidth = userPreferredDisplayMode != null
+                ? userPreferredDisplayMode.getPhysicalWidth() : -1;
+        int userPreferredHeight = userPreferredDisplayMode != null
+                ? userPreferredDisplayMode.getPhysicalHeight() : -1;
+        float userPreferredRefreshRate = userPreferredDisplayMode != null
+                ? userPreferredDisplayMode.getRefreshRate() : 0.0f;
+        boolean hasUserDisabledAllm = false;
+        try {
+            hasUserDisabledAllm = Settings.Secure.getIntForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED,
+                    1) == 0;
+        } catch (Settings.SettingNotFoundException exception) {
+            Slog.e(
+                    TAG, "unable to find setting for MINIMAL_POST_PROCESSING_ALLOWED.",
+                    exception);
+            hasUserDisabledAllm = false;
+        }
+
+        pulledData.add(
+                FrameworkStatsLog.buildStatsEvent(
+                        atomTag, surroundEncodings, sinkSurroundEncodings,
+                        disabledSurroundEncodings, enabledSurroundEncodings, surroundOutputMode,
+                        sinkHdrFormats, sinkDisplayModes, hdcpLevel,
+                        matchContentFrameRateUserPreference, userDisabledHdrTypes,
+                        userPreferredWidth, userPreferredHeight, userPreferredRefreshRate,
+                        hasUserDisabledAllm));
+
+        return StatsManager.PULL_SUCCESS;
+    }
+
+    private byte[] toBytes(List<Integer> audioEncodings) {
+        ProtoOutputStream protoOutputStream = new ProtoOutputStream();
+        for (int audioEncoding : audioEncodings) {
+            protoOutputStream.write(
+                    ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_ENUM | 1,
+                    audioEncoding);
+        }
+        return protoOutputStream.getBytes();
+    }
+
+    private byte[] toBytes(int[] array) {
+        ProtoOutputStream protoOutputStream = new ProtoOutputStream();
+        for (int element : array) {
+            protoOutputStream.write(
+                    ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_ENUM | 1,
+                    element);
+        }
+        return protoOutputStream.getBytes();
+    }
+
+    private byte[] toBytes(Display.Mode[] displayModes) {
+        Map<Integer, Integer> modeGroupIds = createModeGroups(displayModes);
+        ProtoOutputStream protoOutputStream = new ProtoOutputStream();
+        for (Display.Mode element : displayModes) {
+            ProtoOutputStream protoOutputStreamMode = new ProtoOutputStream();
+            protoOutputStreamMode.write(
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32 | 1,
+                    element.getPhysicalHeight());
+            protoOutputStreamMode.write(
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32 | 2,
+                    element.getPhysicalWidth());
+            protoOutputStreamMode.write(
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_FLOAT | 3,
+                    element.getRefreshRate());
+            protoOutputStreamMode.write(
+                    ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32 | 4,
+                    modeGroupIds.get(element.getModeId()));
+            protoOutputStream.write(
+                    ProtoOutputStream.FIELD_COUNT_REPEATED
+                            | ProtoOutputStream.FIELD_TYPE_MESSAGE | 1,
+                    protoOutputStreamMode.getBytes());
+        }
+        return protoOutputStream.getBytes();
+    }
+
+    // Returns map modeId -> groupId such that all modes with the same group have alternative
+    // refresh rates
+    private Map<Integer, Integer> createModeGroups(Display.Mode[] supportedModes) {
+        Map<Integer, Integer> modeGroupIds = new ArrayMap<>();
+        int groupId = 1;
+        for (Display.Mode mode : supportedModes) {
+            if (modeGroupIds.containsKey(mode.getModeId())) {
+                continue;
+            }
+            modeGroupIds.put(mode.getModeId(), groupId);
+            for (float refreshRate : mode.getAlternativeRefreshRates()) {
+                int alternativeModeId = findModeId(supportedModes, mode.getPhysicalWidth(),
+                        mode.getPhysicalHeight(), refreshRate);
+                if (alternativeModeId != -1 && !modeGroupIds.containsKey(alternativeModeId)) {
+                    modeGroupIds.put(alternativeModeId, groupId);
+                }
+            }
+            groupId++;
+        }
+        return modeGroupIds;
+    }
+
+    private int findModeId(Display.Mode[] modes, int width, int height, float refreshRate) {
+        for (Display.Mode mode : modes) {
+            if (mode.matches(width, height, refreshRate)) {
+                return mode.getModeId();
+            }
+        }
+        return -1;
+    }
+
     /**
      * Counts how many accessibility services (including features) there are in the colon-separated
      * string list.
diff --git a/services/core/java/com/android/server/timezonedetector/Dumpable.java b/services/core/java/com/android/server/timezonedetector/Dumpable.java
index 5603c38..1dd9637 100644
--- a/services/core/java/com/android/server/timezonedetector/Dumpable.java
+++ b/services/core/java/com/android/server/timezonedetector/Dumpable.java
@@ -24,18 +24,4 @@
 
     /** Dump internal state. */
     void dump(@NonNull IndentingPrintWriter pw, @Nullable String[] args);
-
-    /**
-     * An interface that can be used expose when one component allows another to be registered so
-     * that it is dumped at the same time.
-     */
-    interface Container {
-
-        /**
-         * Registers the supplied {@link Dumpable}. When the implementation is dumped
-         * {@link Dumpable#dump(IndentingPrintWriter, String[])} should be called on the
-         * {@code dumpable}.
-         */
-        void addDumpable(@NonNull Dumpable dumpable);
-    }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index b84f8a8..fc6e372 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -72,8 +72,8 @@
         mLocationManager = context.getSystemService(LocationManager.class);
         mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
 
-        // Wire up the config change listeners. All invocations are performed on the mHandler
-        // thread.
+        // Wire up the config change listeners for anything that could affect the return values from
+        // this object. All listener invocations are performed on the mHandler thread.
 
         // Listen for the user changing / the user's location mode changing.
         IntentFilter filter = new IntentFilter();
@@ -88,25 +88,19 @@
 
         // Add async callbacks for global settings being changed.
         ContentResolver contentResolver = mContext.getContentResolver();
+        ContentObserver contentObserver = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                handleConfigChangeOnHandlerThread();
+            }
+        };
         contentResolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
-                new ContentObserver(mHandler) {
-                    @Override
-                    public void onChange(boolean selfChange) {
-                        handleConfigChangeOnHandlerThread();
-                    }
-                });
+                Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true, contentObserver);
 
         // Add async callbacks for user scoped location settings being changed.
         contentResolver.registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED),
-                true,
-                new ContentObserver(mHandler) {
-                    @Override
-                    public void onChange(boolean selfChange) {
-                        handleConfigChangeOnHandlerThread();
-                    }
-                }, UserHandle.USER_ALL);
+                true, contentObserver, UserHandle.USER_ALL);
     }
 
     private void handleConfigChangeOnHandlerThread() {
diff --git a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
index 3b32549..f4a6ef0 100644
--- a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
+++ b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
@@ -39,12 +39,11 @@
  * <ul>
  *     <li>{@code effectiveFromElapsedMillis}: The time according to the elapsed realtime clock
  *     after which the suggestion should be considered in effect. For example, when a location fix
- *     used to establish the time zone is old, then the suggestion
- *     {@code effectiveFromElapsedMillis} should reflect this and indicates the time zone that was
- *     detected / correct at that time. The time_zone_detector is only expected to use the latest
- *     suggestion it has received, and so later suggestions always counteract previous suggestions.
- *     The inclusion of this information means that the time_zone_detector can take into account
- *     ordering when comparing suggestions from different sources.
+ *     used to establish the time zone is old, then the suggestion's {@code
+ *     effectiveFromElapsedMillis} should reflect this and indicates the time zone that was
+ *     detected / correct at that time. The inclusion of this information means that the
+ *     time_zone_detector <em>may</em> take this into account if comparing suggestions or signals
+ *     from different sources.
  *     <br />Note: Because the times can be back-dated, time_zone_detector can be sent a sequence of
  *     suggestions where the {@code effectiveFromElapsedMillis} of later suggestions is before
  *     the {@code effectiveFromElapsedMillis} of an earlier one.</li>
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
index 20f4fa1..f7ac9b6 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
-import android.os.SystemProperties;
 import android.util.ArraySet;
 
 import com.android.internal.R;
@@ -64,11 +63,11 @@
     public static final @ProviderMode String PROVIDER_MODE_ENABLED = "enabled";
 
     /**
-     * Device config keys that affect the {@link TimeZoneDetectorService} service and {@link
-     * com.android.server.timezonedetector.location.LocationTimeZoneManagerService}.
+     * Device config keys that can affect {@link
+     * com.android.server.timezonedetector.location.LocationTimeZoneManagerService} behavior.
      */
-    private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet(
-            new ArraySet<>(new String[] {
+    private static final Set<String> LOCATION_TIME_ZONE_MANAGER_SERVER_FLAGS_KEYS_TO_WATCH =
+            Collections.unmodifiableSet(new ArraySet<>(new String[] {
                     ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
                     ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
                     ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
@@ -94,14 +93,6 @@
 
     @NonNull private final Context mContext;
 
-    /**
-     * An ultimate "feature switch" for location-based time zone detection. If this is
-     * {@code false}, the device cannot support the feature without a config change or a reboot:
-     * This affects what services are started on boot to minimize expense when the feature is not
-     * wanted.
-     */
-    private final boolean mGeoDetectionFeatureSupportedInConfig;
-
     @NonNull private final ServerFlags mServerFlags;
 
     /**
@@ -148,14 +139,6 @@
     private ServiceConfigAccessor(@NonNull Context context) {
         mContext = Objects.requireNonNull(context);
 
-        // The config value is expected to be the main feature flag. Platform developers can also
-        // force enable the feature using a persistent system property. Because system properties
-        // can change, this value is cached and only changes on reboot.
-        mGeoDetectionFeatureSupportedInConfig = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection)
-                || SystemProperties.getBoolean(
-                "persist.sys.location_time_zone_detection_feature_supported", false);
-
         mServerFlags = ServerFlags.getInstance(mContext);
     }
 
@@ -170,14 +153,15 @@
     }
 
     /**
-     * Adds a listener that will be called when server flags related to this class change. The
-     * callbacks are delivered on the main looper thread.
+     * Adds a listener that will be called when server flags related to location_time_zone_manager
+     * change. The callbacks are delivered on the main looper thread.
      *
      * <p>Note: Only for use by long-lived objects. There is deliberately no associated remove
      * method.
      */
-    public void addListener(@NonNull ConfigurationChangeListener listener) {
-        mServerFlags.addListener(listener, SERVER_FLAGS_KEYS_TO_WATCH);
+    public void addLocationTimeZoneManagerConfigListener(
+            @NonNull ConfigurationChangeListener listener) {
+        mServerFlags.addListener(listener, LOCATION_TIME_ZONE_MANAGER_SERVER_FLAGS_KEYS_TO_WATCH);
     }
 
     /** Returns {@code true} if any form of automatic time zone detection is supported. */
@@ -197,11 +181,19 @@
     /**
      * Returns {@code true} if the location-based time zone detection feature can be supported on
      * this device at all according to config. When {@code false}, implies that various other
-     * location-based settings will be turned off or rendered meaningless. Typically {@link
-     * #isGeoTimeZoneDetectionFeatureSupported()} should be used instead.
+     * location-based services and settings will be turned off or rendered meaningless.
+     *
+     * <p>This is the ultimate "feature switch" for location-based time zone detection. If this is
+     * {@code false}, the device cannot support the feature without a config change or a reboot:
+     * This affects what services are started on boot to minimize expense when the feature is not
+     * wanted.
+     *
+     * Typically {@link #isGeoTimeZoneDetectionFeatureSupported()} should be used except during
+     * boot.
      */
     public boolean isGeoTimeZoneDetectionFeatureSupportedInConfig() {
-        return mGeoDetectionFeatureSupportedInConfig;
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection);
     }
 
     /**
@@ -213,7 +205,7 @@
         // 1) Be turned on in config.
         // 2) Not be turned off via a server flag.
         // 3) There must be at least one location time zone provider enabled / configured.
-        return mGeoDetectionFeatureSupportedInConfig
+        return isGeoTimeZoneDetectionFeatureSupportedInConfig()
                 && isGeoTimeZoneDetectionFeatureSupportedInternal()
                 && atLeastOneProviderIsEnabled();
     }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index d429b87..4161177 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -25,7 +25,7 @@
  * <p>The methods on this class can be called from any thread.
  * @hide
  */
-public interface TimeZoneDetectorInternal extends Dumpable.Container {
+public interface TimeZoneDetectorInternal {
 
     /** Adds a listener that will be invoked when {@link ConfigurationInternal} may have changed. */
     void addConfigurationListener(@NonNull ConfigurationChangeListener listener);
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index 4e78f5a..ca87811 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -57,11 +57,6 @@
     }
 
     @Override
-    public void addDumpable(@NonNull Dumpable dumpable) {
-        mTimeZoneDetectorStrategy.addDumpable(dumpable);
-    }
-
-    @Override
     public void addConfigurationListener(ConfigurationChangeListener listener) {
         synchronized (mConfigurationListeners) {
             mConfigurationListeners.add(Objects.requireNonNull(listener));
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index b1b537b..e6a58a1 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -45,6 +45,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -59,6 +61,7 @@
         implements IBinder.DeathRecipient {
 
     static final String TAG = "time_zone_detector";
+    static final boolean DBG = false;
 
     /**
      * Handles the service lifecycle for {@link TimeZoneDetectorService} and
@@ -112,17 +115,22 @@
      */
     @GuardedBy("mListeners")
     @NonNull
-    private final ArrayMap<IBinder, ITimeZoneDetectorListener> mListeners =
-            new ArrayMap<>();
+    private final ArrayMap<IBinder, ITimeZoneDetectorListener> mListeners = new ArrayMap<>();
+
+    /**
+     * References to components that should be dumped when {@link
+     * #dump(FileDescriptor, PrintWriter, String[])} is called on the service.
+     */
+    @GuardedBy("mDumpables")
+    private final List<Dumpable> mDumpables = new ArrayList<>();
 
     private static TimeZoneDetectorService create(
             @NonNull Context context, @NonNull Handler handler,
             @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
 
         CallerIdentityInjector callerIdentityInjector = CallerIdentityInjector.REAL;
-        TimeZoneDetectorService service = new TimeZoneDetectorService(
+        return new TimeZoneDetectorService(
                 context, handler, callerIdentityInjector, timeZoneDetectorStrategy);
-        return service;
     }
 
     @VisibleForTesting
@@ -251,7 +259,7 @@
             if (!removedListener) {
                 Slog.w(TAG, "Notified of binder death for who=" + who
                         + ", but did not remove any listeners."
-                        + " mConfigurationListeners=" + mListeners);
+                        + " mListeners=" + mListeners);
             }
         }
     }
@@ -314,8 +322,17 @@
     boolean isGeoTimeZoneDetectionSupported() {
         enforceManageTimeZoneDetectorPermission();
 
-        return ServiceConfigAccessor.getInstance(mContext)
-                .isGeoTimeZoneDetectionFeatureSupported();
+        return ServiceConfigAccessor.getInstance(mContext).isGeoTimeZoneDetectionFeatureSupported();
+    }
+
+    /**
+     * Registers the supplied {@link Dumpable} for dumping. When the service is dumped
+     * {@link Dumpable#dump(IndentingPrintWriter, String[])} will be called on the {@code dumpable}.
+     */
+    void addDumpable(@NonNull Dumpable dumpable) {
+        synchronized (mDumpables) {
+            mDumpables.add(dumpable);
+        }
     }
 
     @Override
@@ -325,6 +342,13 @@
 
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
         mTimeZoneDetectorStrategy.dump(ipw, args);
+
+        synchronized (mDumpables) {
+            for (Dumpable dumpable : mDumpables) {
+                dumpable.dump(ipw, args);
+            }
+        }
+
         ipw.flush();
     }
 
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index e3f31b6..e2cc679 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -30,7 +30,7 @@
  * Suggestions are acted on or ignored as needed, depending on previously received suggestions and
  * the current user's configuration (see {@link ConfigurationInternal}).
  *
- * <p>Devices can have zero, one or two automatic time zone detection algorithm available at any
+ * <p>Devices can have zero, one or two automatic time zone detection algorithms available at any
  * point in time.
  *
  * <p>The two automatic detection algorithms supported are "telephony" and "geolocation". Algorithm
@@ -63,6 +63,13 @@
  * have an empty suggestion submitted in order to "withdraw" their previous suggestion otherwise it
  * will remain in use.
  *
+ * <p>The strategy uses only one algorithm at a time and does not attempt consensus even when
+ * more than one is available on a device. This "use only one" behavior is deliberate as different
+ * algorithms have edge cases and blind spots that lead to incorrect answers or uncertainty;
+ * different algorithms aren't guaranteed to agree, and algorithms may frequently lose certainty as
+ * users enter areas without the necessary signals. Ultimately, with no perfect algorithm available,
+ * the user is left to choose which algorithm works best for their circumstances.
+ *
  * <p>Threading:
  *
  * <p>Suggestion calls with a void return type may be handed off to a separate thread and handled
@@ -73,7 +80,7 @@
  *
  * @hide
  */
-public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container {
+public interface TimeZoneDetectorStrategy extends Dumpable {
 
     /**
      * Adds a listener that will be triggered whenever {@link ConfigurationInternal} may have
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index ab2a88b..1b09f44 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -98,7 +98,7 @@
     }
 
     private static final String LOG_TAG = TimeZoneDetectorService.TAG;
-    private static final boolean DBG = false;
+    private static final boolean DBG = TimeZoneDetectorService.DBG;
 
     /**
      * The abstract score for an empty or invalid telephony suggestion.
@@ -168,7 +168,7 @@
 
     @GuardedBy("this")
     @NonNull
-    private List<ConfigurationChangeListener> mConfigChangeListeners = new ArrayList<>();
+    private final List<ConfigurationChangeListener> mConfigChangeListeners = new ArrayList<>();
 
     /**
      * A log that records the decisions / decision metadata that affected the device's time zone.
@@ -183,7 +183,7 @@
      * to be stable.
      */
     @GuardedBy("this")
-    private ArrayMapWithHistory<Integer, QualifiedTelephonyTimeZoneSuggestion>
+    private final ArrayMapWithHistory<Integer, QualifiedTelephonyTimeZoneSuggestion>
             mTelephonySuggestionsBySlotIndex =
             new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
@@ -192,18 +192,16 @@
      * detection then the latest suggestion is cleared.
      */
     @GuardedBy("this")
-    private ReferenceWithHistory<GeolocationTimeZoneSuggestion> mLatestGeoLocationSuggestion =
+    private final ReferenceWithHistory<GeolocationTimeZoneSuggestion> mLatestGeoLocationSuggestion =
             new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
     /**
      * The latest manual suggestion received.
      */
     @GuardedBy("this")
-    private ReferenceWithHistory<ManualTimeZoneSuggestion> mLatestManualSuggestion =
+    private final ReferenceWithHistory<ManualTimeZoneSuggestion> mLatestManualSuggestion =
             new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
-    @GuardedBy("this")
-    private final List<Dumpable> mDumpables = new ArrayList<>();
 
     /**
      * Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
@@ -291,16 +289,19 @@
         }
         Objects.requireNonNull(suggestion);
 
-        if (currentUserConfig.getGeoDetectionEnabledBehavior()) {
-            // Only store a geolocation suggestion if geolocation detection is currently enabled.
-            // See also clearGeolocationSuggestionIfNeeded().
-            mLatestGeoLocationSuggestion.set(suggestion);
+        // Geolocation suggestions may be stored but not used during time zone detection if the
+        // configuration doesn't have geo time zone detection enabled. The caller is expected to
+        // withdraw a previous suggestion (i.e. submit an "uncertain" suggestion, when geo time zone
+        // detection is disabled.
 
-            // Now perform auto time zone detection. The new suggestion may be used to modify the
-            // time zone setting.
-            String reason = "New geolocation time zone suggested. suggestion=" + suggestion;
-            doAutoTimeZoneDetection(currentUserConfig, reason);
-        }
+        // The suggestion's "effective from" time is ignored: we currently assume suggestions
+        // are made in a sensible order and the most recent is always the best one to use.
+        mLatestGeoLocationSuggestion.set(suggestion);
+
+        // Now perform auto time zone detection. The new suggestion may be used to modify the
+        // time zone setting.
+        String reason = "New geolocation time zone suggested. suggestion=" + suggestion;
+        doAutoTimeZoneDetection(currentUserConfig, reason);
     }
 
     @Override
@@ -365,10 +366,8 @@
 
         // Now perform auto time zone detection. The new suggestion may be used to modify the time
         // zone setting.
-        if (!currentUserConfig.getGeoDetectionEnabledBehavior()) {
-            String reason = "New telephony time zone suggested. suggestion=" + suggestion;
-            doAutoTimeZoneDetection(currentUserConfig, reason);
-        }
+        String reason = "New telephony time zone suggested. suggestion=" + suggestion;
+        doAutoTimeZoneDetection(currentUserConfig, reason);
     }
 
     @Override
@@ -427,7 +426,8 @@
             return;
         }
 
-        // Use the right suggestions based on the current configuration.
+        // Use the correct algorithm based on the user's current configuration. If it changes, then
+        // detection will be re-run.
         if (currentUserConfig.getGeoDetectionEnabledBehavior()) {
             doGeolocationTimeZoneDetection(detectionReason);
         } else  {
@@ -598,15 +598,6 @@
             Slog.d(LOG_TAG, "handleConfigChanged()");
         }
 
-        clearGeolocationSuggestionIfNeeded();
-
-        for (ConfigurationChangeListener listener : mConfigChangeListeners) {
-            listener.onChange();
-        }
-    }
-
-    @GuardedBy("this")
-    private void clearGeolocationSuggestionIfNeeded() {
         // This method is called whenever the user changes or the config for any user changes. We
         // don't know what happened, so we capture the current user's config, check to see if we
         // need to clear state associated with a previous user, and rerun detection.
@@ -614,24 +605,14 @@
         ConfigurationInternal currentUserConfig =
                 mEnvironment.getConfigurationInternal(currentUserId);
 
-        GeolocationTimeZoneSuggestion latestGeoLocationSuggestion =
-                mLatestGeoLocationSuggestion.get();
-        if (latestGeoLocationSuggestion != null
-                && !currentUserConfig.getGeoDetectionEnabledBehavior()) {
-            // The current user's config has geodetection disabled, so clear the latest suggestion.
-            // This is done to ensure we only ever keep a geolocation suggestion if the user has
-            // said it is ok to do so.
-            mLatestGeoLocationSuggestion.set(null);
-            mTimeZoneChangesLog.log(
-                    "clearGeolocationSuggestionIfNeeded: Cleared latest Geolocation suggestion.");
+        // The configuration change may have changed available suggestions or the way suggestions
+        // are used, so re-run detection.
+        doAutoTimeZoneDetection(currentUserConfig, "handleConfigChanged()");
+
+        // Pass on the signal to sub-components.
+        for (ConfigurationChangeListener listener : mConfigChangeListeners) {
+            listener.onChange();
         }
-
-        doAutoTimeZoneDetection(currentUserConfig, "clearGeolocationSuggestionIfNeeded()");
-    }
-
-    @Override
-    public synchronized void addDumpable(@NonNull Dumpable dumpable) {
-        mDumpables.add(dumpable);
     }
 
     /**
@@ -671,10 +652,6 @@
         mTelephonySuggestionsBySlotIndex.dump(ipw);
         ipw.decreaseIndent(); // level 2
         ipw.decreaseIndent(); // level 1
-
-        for (Dumpable dumpable : mDumpables) {
-            dumpable.dump(ipw, args);
-        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index c5c59ce..942df53 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -168,7 +168,8 @@
     // According to the SystemService docs: All lifecycle methods are called from the system
     // server's main looper thread.
     void onSystemReady() {
-        mServiceConfigAccessor.addListener(this::handleServiceConfigurationChangedOnMainThread);
+        mServiceConfigAccessor.addLocationTimeZoneManagerConfigListener(
+                this::handleServiceConfigurationChangedOnMainThread);
     }
 
     private void handleServiceConfigurationChangedOnMainThread() {
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index cf212df..8bbac7a 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -18,31 +18,41 @@
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.media.tv.interactive.ITvIAppClient;
 import android.media.tv.interactive.ITvIAppManager;
 import android.media.tv.interactive.ITvIAppService;
 import android.media.tv.interactive.ITvIAppServiceCallback;
 import android.media.tv.interactive.ITvIAppSession;
 import android.media.tv.interactive.ITvIAppSessionCallback;
+import android.media.tv.interactive.TvIAppInfo;
 import android.media.tv.interactive.TvIAppService;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.SparseArray;
 import android.view.Surface;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
 import com.android.server.SystemService;
 import com.android.server.utils.Slogf;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -82,6 +92,75 @@
         mContext = context;
     }
 
+    @GuardedBy("mLock")
+    private void buildTvIAppServiceListLocked(int userId, String[] updatedPackages) {
+        UserState userState = getOrCreateUserStateLocked(userId);
+        userState.mPackageSet.clear();
+
+        if (DEBUG) {
+            Slogf.d(TAG, "buildTvIAppServiceListLocked");
+        }
+        PackageManager pm = mContext.getPackageManager();
+        List<ResolveInfo> services = pm.queryIntentServicesAsUser(
+                new Intent(TvIAppService.SERVICE_INTERFACE),
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+                userId);
+        List<TvIAppInfo> iAppList = new ArrayList<>();
+
+        for (ResolveInfo ri : services) {
+            ServiceInfo si = ri.serviceInfo;
+            // TODO: add BIND_TV_IAPP permission and check it here
+
+            ComponentName component = new ComponentName(si.packageName, si.name);
+            try {
+                TvIAppInfo info = new TvIAppInfo.Builder(mContext, component).build();
+                iAppList.add(info);
+            } catch (Exception e) {
+                Slogf.e(TAG, "failed to load TV IApp service " + si.name, e);
+                continue;
+            }
+            userState.mPackageSet.add(si.packageName);
+        }
+
+        // sort the iApp list by iApp service id
+        Collections.sort(iAppList, Comparator.comparing(TvIAppInfo::getId));
+        Map<String, TvIAppState> iAppMap = new HashMap<>();
+        ArrayMap<String, Integer> tiasAppCount = new ArrayMap<>(iAppMap.size());
+        for (TvIAppInfo info : iAppList) {
+            String iAppServiceId = info.getId();
+            if (DEBUG) {
+                Slogf.d(TAG, "add " + iAppServiceId);
+            }
+            // Running count of IApp for each IApp service
+            Integer count = tiasAppCount.get(iAppServiceId);
+            count = count == null ? 1 : count + 1;
+            tiasAppCount.put(iAppServiceId, count);
+            TvIAppState iAppState = userState.mIAppMap.get(iAppServiceId);
+            if (iAppState == null) {
+                iAppState = new TvIAppState();
+            }
+            iAppState.mInfo = info;
+            iAppState.mUid = getIAppUid(info);
+            iAppMap.put(iAppServiceId, iAppState);
+            iAppState.mIAppNumber = count;
+        }
+
+        // TODO: notify iApp added / removed
+
+        userState.mIAppMap.clear();
+        userState.mIAppMap = iAppMap;
+    }
+
+    private int getIAppUid(TvIAppInfo info) {
+        try {
+            return getContext().getPackageManager().getApplicationInfo(
+                    info.getServiceInfo().packageName, 0).uid;
+        } catch (PackageManager.NameNotFoundException e) {
+            Slogf.w(TAG, "Unable to get UID for  " + info, e);
+            return Process.INVALID_UID;
+        }
+    }
+
     @Override
     public void onStart() {
         if (DEBUG) {
@@ -90,6 +169,96 @@
         publishBinderService(Context.TV_IAPP_SERVICE, new BinderService());
     }
 
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            registerBroadcastReceivers();
+        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+            synchronized (mLock) {
+                buildTvIAppServiceListLocked(mCurrentUserId, null);
+            }
+        }
+    }
+
+    private void registerBroadcastReceivers() {
+        PackageMonitor monitor = new PackageMonitor() {
+            private void buildTvIAppServiceList(String[] packages) {
+                int userId = getChangingUserId();
+                synchronized (mLock) {
+                    if (mCurrentUserId == userId || mRunningProfiles.contains(userId)) {
+                        buildTvIAppServiceListLocked(userId, packages);
+                    }
+                }
+            }
+
+            @Override
+            public void onPackageUpdateFinished(String packageName, int uid) {
+                if (DEBUG) Slogf.d(TAG, "onPackageUpdateFinished(packageName=" + packageName + ")");
+                // This callback is invoked when the TV iApp service is reinstalled.
+                // In this case, isReplacing() always returns true.
+                buildTvIAppServiceList(new String[] { packageName });
+            }
+
+            @Override
+            public void onPackagesAvailable(String[] packages) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onPackagesAvailable(packages=" + Arrays.toString(packages) + ")");
+                }
+                // This callback is invoked when the media on which some packages exist become
+                // available.
+                if (isReplacing()) {
+                    buildTvIAppServiceList(packages);
+                }
+            }
+
+            @Override
+            public void onPackagesUnavailable(String[] packages) {
+                // This callback is invoked when the media on which some packages exist become
+                // unavailable.
+                if (DEBUG)  {
+                    Slogf.d(TAG, "onPackagesUnavailable(packages=" + Arrays.toString(packages)
+                            + ")");
+                }
+                if (isReplacing()) {
+                    buildTvIAppServiceList(packages);
+                }
+            }
+
+            @Override
+            public void onSomePackagesChanged() {
+                if (DEBUG) Slogf.d(TAG, "onSomePackagesChanged()");
+                if (isReplacing()) {
+                    if (DEBUG) Slogf.d(TAG, "Skipped building TV iApp list due to replacing");
+                    // When the package is updated, buildTvIAppServiceListLocked is called in other
+                    // methods instead.
+                    return;
+                }
+                buildTvIAppServiceList(null);
+            }
+
+            @Override
+            public boolean onPackageChanged(String packageName, int uid, String[] components) {
+                // The iApp list needs to be updated in any cases, regardless of whether
+                // it happened to the whole package or a specific component. Returning true so that
+                // the update can be handled in {@link #onSomePackagesChanged}.
+                return true;
+            }
+        };
+        monitor.register(mContext, null, UserHandle.ALL, true);
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
+        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+        intentFilter.addAction(Intent.ACTION_USER_STARTED);
+        intentFilter.addAction(Intent.ACTION_USER_STOPPED);
+        mContext.registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                // TODO: handle switch / start / stop user
+            }
+        }, UserHandle.ALL, intentFilter, null, null);
+    }
+
     private SessionState getSessionState(IBinder sessionToken) {
         // TODO: implement user state and get session from it.
         return null;
@@ -462,19 +631,20 @@
         // A mapping from the token of a TV IApp session to its state.
         private final Map<IBinder, SessionState> mSessionStateMap = new HashMap<>();
 
+        // A set of all TV IApp service packages.
+        private final Set<String> mPackageSet = new HashSet<>();
+
         private UserState(int userId) {
             mUserId = userId;
         }
     }
 
     private static final class TvIAppState {
-        private final String mIAppServiceId;
-        private final ComponentName mComponentName;
-
-        TvIAppState(String id, ComponentName componentName) {
-            mIAppServiceId = id;
-            mComponentName = componentName;
-        }
+        private String mIAppServiceId;
+        private ComponentName mComponentName;
+        private TvIAppInfo mInfo;
+        private int mUid;
+        private int mIAppNumber;
     }
 
     private final class SessionState implements IBinder.DeathRecipient {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index b1cad7c..318ad06 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -295,6 +295,9 @@
         // surface for this token.
         if (mSurfaceControl == null) {
             createSurfaceControl(true /* force */);
+
+            // Layers could have been assigned before the surface was created, update them again
+            reassignLayer(getSyncTransaction());
         }
         if (!mChildren.contains(win)) {
             ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 6be872f..0a55003 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -28,7 +28,6 @@
 #include <android/hardware/gnss/2.1/IGnssAntennaInfo.h>
 #include <android/hardware/gnss/2.1/IGnssMeasurement.h>
 #include <android/hardware/gnss/BnGnss.h>
-#include <android/hardware/gnss/BnGnssBatchingCallback.h>
 #include <android/hardware/gnss/BnGnssCallback.h>
 #include <android/hardware/gnss/BnGnssMeasurementCallback.h>
 #include <android/hardware/gnss/BnGnssPowerIndicationCallback.h>
@@ -52,6 +51,7 @@
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
 #include "gnss/GnssAntennaInfoCallback.h"
+#include "gnss/GnssBatching.h"
 #include "gnss/GnssConfiguration.h"
 #include "gnss/GnssMeasurement.h"
 #include "gnss/Utils.h"
@@ -60,12 +60,9 @@
 #include "utils/Log.h"
 #include "utils/misc.h"
 
-static jclass class_location;
 static jclass class_gnssNavigationMessage;
 static jclass class_gnssPowerStats;
 
-jobject android::mCallbacksObj = nullptr;
-
 static jmethodID method_reportLocation;
 static jmethodID method_reportStatus;
 static jmethodID method_reportSvStatus;
@@ -87,7 +84,6 @@
 static jmethodID method_reportGeofencePauseStatus;
 static jmethodID method_reportGeofenceResumeStatus;
 static jmethodID method_reportNavigationMessages;
-static jmethodID method_reportLocationBatch;
 static jmethodID method_reportGnssServiceDied;
 static jmethodID method_reportGnssPowerStats;
 static jmethodID method_setSubHalMeasurementCorrectionsCapabilities;
@@ -117,7 +113,6 @@
 static jmethodID method_correctionPlaneAzimDeg;
 static jmethodID method_reportNfwNotification;
 static jmethodID method_isInEmergencySession;
-static jmethodID method_locationCtor;
 static jmethodID method_gnssNavigationMessageCtor;
 static jmethodID method_gnssPowerStatsCtor;
 static jmethodID method_setSubHalPowerIndicationCapabilities;
@@ -179,10 +174,6 @@
 using IAGnss_V2_0 = android::hardware::gnss::V2_0::IAGnss;
 using IAGnssCallback_V1_0 = android::hardware::gnss::V1_0::IAGnssCallback;
 using IAGnssCallback_V2_0 = android::hardware::gnss::V2_0::IAGnssCallback;
-using IGnssBatching_V1_0 = android::hardware::gnss::V1_0::IGnssBatching;
-using IGnssBatching_V2_0 = android::hardware::gnss::V2_0::IGnssBatching;
-using IGnssBatchingCallback_V1_0 = android::hardware::gnss::V1_0::IGnssBatchingCallback;
-using IGnssBatchingCallback_V2_0 = android::hardware::gnss::V2_0::IGnssBatchingCallback;
 
 using IMeasurementCorrections_V1_0 = android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrections;
 using IMeasurementCorrections_V1_1 = android::hardware::gnss::measurement_corrections::V1_1::IMeasurementCorrections;
@@ -199,8 +190,6 @@
 using android::hardware::gnss::IGnssPowerIndicationCallback;
 using android::hardware::gnss::PsdsType;
 using IGnssAidl = android::hardware::gnss::IGnss;
-using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching;
-using IGnssBatchingCallbackAidl = android::hardware::gnss::IGnssBatchingCallback;
 using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
 using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
 using IGnssPsdsCallbackAidl = android::hardware::gnss::IGnssPsdsCallback;
@@ -227,7 +216,6 @@
 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<IAGnssRil_V1_0> agnssRilIface = nullptr;
@@ -235,8 +223,6 @@
 sp<IGnssGeofencing> gnssGeofencingIface = nullptr;
 sp<IAGnss_V1_0> agnssIface = nullptr;
 sp<IAGnss_V2_0> agnssIface_V2_0 = nullptr;
-sp<IGnssBatching_V1_0> gnssBatchingIface = nullptr;
-sp<IGnssBatching_V2_0> gnssBatchingIface_V2_0 = nullptr;
 sp<IGnssDebug_V1_0> gnssDebugIface = nullptr;
 sp<IGnssDebug_V2_0> gnssDebugIface_V2_0 = nullptr;
 sp<IGnssNi> gnssNiIface = nullptr;
@@ -249,6 +235,7 @@
 
 std::unique_ptr<GnssConfigurationInterface> gnssConfigurationIface = nullptr;
 std::unique_ptr<android::gnss::GnssMeasurementInterface> gnssMeasurementIface = nullptr;
+std::unique_ptr<android::gnss::GnssBatchingInterface> gnssBatchingIface = nullptr;
 
 #define WAKE_LOCK_NAME  "GPS"
 
@@ -301,103 +288,6 @@
     const char* mNativeString;
 };
 
-static jobject translateGnssLocation(JNIEnv* env, const GnssLocationAidl& location) {
-    JavaObject object(env, class_location, method_locationCtor, "gps");
-
-    uint32_t flags = static_cast<uint32_t>(location.gnssLocationFlags);
-    if (flags & GnssLocationAidl::HAS_LAT_LONG) {
-        SET(Latitude, location.latitudeDegrees);
-        SET(Longitude, location.longitudeDegrees);
-    }
-    if (flags & GnssLocationAidl::HAS_ALTITUDE) {
-        SET(Altitude, location.altitudeMeters);
-    }
-    if (flags & GnssLocationAidl::HAS_SPEED) {
-        SET(Speed, (float)location.speedMetersPerSec);
-    }
-    if (flags & GnssLocationAidl::HAS_BEARING) {
-        SET(Bearing, (float)location.bearingDegrees);
-    }
-    if (flags & GnssLocationAidl::HAS_HORIZONTAL_ACCURACY) {
-        SET(Accuracy, (float)location.horizontalAccuracyMeters);
-    }
-    if (flags & GnssLocationAidl::HAS_VERTICAL_ACCURACY) {
-        SET(VerticalAccuracyMeters, (float)location.verticalAccuracyMeters);
-    }
-    if (flags & GnssLocationAidl::HAS_SPEED_ACCURACY) {
-        SET(SpeedAccuracyMetersPerSecond, (float)location.speedAccuracyMetersPerSecond);
-    }
-    if (flags & GnssLocationAidl::HAS_BEARING_ACCURACY) {
-        SET(BearingAccuracyDegrees, (float)location.bearingAccuracyDegrees);
-    }
-    SET(Time, location.timestampMillis);
-
-    flags = static_cast<uint32_t>(location.elapsedRealtime.flags);
-    if (flags & android::hardware::gnss::ElapsedRealtime::HAS_TIMESTAMP_NS) {
-        SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
-    }
-    if (flags & android::hardware::gnss::ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS) {
-        SET(ElapsedRealtimeUncertaintyNanos,
-            static_cast<double>(location.elapsedRealtime.timeUncertaintyNs));
-    }
-
-    return object.get();
-}
-
-static jobject translateGnssLocation(JNIEnv* env,
-                                     const GnssLocation_V1_0& location) {
-    JavaObject object(env, class_location, method_locationCtor, "gps");
-
-    uint16_t flags = static_cast<uint16_t>(location.gnssLocationFlags);
-    if (flags & GnssLocationFlags::HAS_LAT_LONG) {
-        SET(Latitude, location.latitudeDegrees);
-        SET(Longitude, location.longitudeDegrees);
-    }
-    if (flags & GnssLocationFlags::HAS_ALTITUDE) {
-        SET(Altitude, location.altitudeMeters);
-    }
-    if (flags & GnssLocationFlags::HAS_SPEED) {
-        SET(Speed, location.speedMetersPerSec);
-    }
-    if (flags & GnssLocationFlags::HAS_BEARING) {
-        SET(Bearing, location.bearingDegrees);
-    }
-    if (flags & GnssLocationFlags::HAS_HORIZONTAL_ACCURACY) {
-        SET(Accuracy, location.horizontalAccuracyMeters);
-    }
-    if (flags & GnssLocationFlags::HAS_VERTICAL_ACCURACY) {
-        SET(VerticalAccuracyMeters, location.verticalAccuracyMeters);
-    }
-    if (flags & GnssLocationFlags::HAS_SPEED_ACCURACY) {
-        SET(SpeedAccuracyMetersPerSecond, location.speedAccuracyMetersPerSecond);
-    }
-    if (flags & GnssLocationFlags::HAS_BEARING_ACCURACY) {
-        SET(BearingAccuracyDegrees, location.bearingAccuracyDegrees);
-    }
-    SET(Time, location.timestamp);
-    SET(ElapsedRealtimeNanos, android::elapsedRealtimeNano());
-
-    return object.get();
-}
-
-static jobject translateGnssLocation(JNIEnv* env,
-                                     const GnssLocation_V2_0& location) {
-    JavaObject object(env, class_location, translateGnssLocation(env, location.v1_0));
-
-    const uint16_t flags = static_cast<uint16_t>(location.elapsedRealtime.flags);
-
-    // Overwrite ElapsedRealtimeNanos when available from HAL.
-    if (flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) {
-        SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
-    }
-
-    if (flags & ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS) {
-        SET(ElapsedRealtimeUncertaintyNanos, static_cast<double>(location.elapsedRealtime.timeUncertaintyNs));
-    }
-
-    return object.get();
-}
-
 static GnssLocation_V1_0 createGnssLocation_V1_0(
         jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees,
         jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees,
@@ -1219,66 +1109,6 @@
     return Void();
 }
 
-struct GnssBatchingCallbackUtil {
-    template<class T>
-    static Return<void> gnssLocationBatchCbImpl(const hidl_vec<T>& locations);
-private:
-    GnssBatchingCallbackUtil() = delete;
-};
-
-template<class T>
-Return<void> GnssBatchingCallbackUtil::gnssLocationBatchCbImpl(const hidl_vec<T>& locations) {
-    JNIEnv* env = getJniEnv();
-
-    jobjectArray jLocations = env->NewObjectArray(locations.size(), class_location, nullptr);
-
-    for (uint16_t i = 0; i < locations.size(); ++i) {
-        jobject jLocation = translateGnssLocation(env, locations[i]);
-        env->SetObjectArrayElement(jLocations, i, jLocation);
-        env->DeleteLocalRef(jLocation);
-    }
-
-    env->CallVoidMethod(mCallbacksObj, method_reportLocationBatch, jLocations);
-    checkAndClearExceptionFromCallback(env, __FUNCTION__);
-
-    env->DeleteLocalRef(jLocations);
-
-    return Void();
-}
-
-/*
- * GnssBatchingCallbackAidl class implements the callback methods required by the
- * android::hardware::gnss::IGnssBatching interface.
- */
-struct GnssBatchingCallbackAidl : public android::hardware::gnss::BnGnssBatchingCallback {
-    Status gnssLocationBatchCb(const std::vector<GnssLocationAidl>& locations) {
-        GnssBatchingCallbackUtil::gnssLocationBatchCbImpl(hidl_vec<GnssLocationAidl>(locations));
-        return Status::ok();
-    }
-};
-
-/*
- * GnssBatchingCallback_V1_0 class implements the callback methods required by the
- * IGnssBatching 1.0 interface.
- */
-struct GnssBatchingCallback_V1_0 : public IGnssBatchingCallback_V1_0 {
-    /** Methods from ::android::hardware::gps::V1_0::IGnssBatchingCallback follow. */
-    Return<void> gnssLocationBatchCb(const hidl_vec<GnssLocation_V1_0>& locations) override {
-        return GnssBatchingCallbackUtil::gnssLocationBatchCbImpl(locations);
-    }
-};
-
-/*
- * GnssBatchingCallback_V2_0 class implements the callback methods required by the
- * IGnssBatching 2.0 interface.
- */
-struct GnssBatchingCallback_V2_0 : public IGnssBatchingCallback_V2_0 {
-    /** Methods from ::android::hardware::gps::V2_0::IGnssBatchingCallback follow. */
-    Return<void> gnssLocationBatchCb(const hidl_vec<GnssLocation_V2_0>& locations) override {
-        return GnssBatchingCallbackUtil::gnssLocationBatchCbImpl(locations);
-    }
-};
-
 /* Initializes the GNSS service handle. */
 static void android_location_gnss_hal_GnssNative_set_gps_service_handle() {
     gnssHalAidl = waitForVintfService<IGnssAidl>();
@@ -1354,10 +1184,6 @@
             clazz,
             "reportNavigationMessage",
             "(Landroid/location/GnssNavigationMessage;)V");
-    method_reportLocationBatch = env->GetMethodID(
-            clazz,
-            "reportLocationBatch",
-            "([Landroid/location/Location;)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");
@@ -1427,17 +1253,15 @@
     class_gnssPowerStats = (jclass)env->NewGlobalRef(gnssPowerStatsClass);
     method_gnssPowerStatsCtor = env->GetMethodID(class_gnssPowerStats, "<init>", "(IJDDDDDD[D)V");
 
-    jclass locationClass = env->FindClass("android/location/Location");
-    class_location = (jclass) env->NewGlobalRef(locationClass);
-    method_locationCtor = env->GetMethodID(class_location, "<init>", "(Ljava/lang/String;)V");
-
     jclass gnssNavigationMessageClass = env->FindClass("android/location/GnssNavigationMessage");
     class_gnssNavigationMessage = (jclass) env->NewGlobalRef(gnssNavigationMessageClass);
     method_gnssNavigationMessageCtor = env->GetMethodID(class_gnssNavigationMessage, "<init>", "()V");
 
+    gnss::GnssAntennaInfo_class_init_once(env, clazz);
+    gnss::GnssBatching_class_init_once(env, clazz);
     gnss::GnssConfiguration_class_init_once(env);
     gnss::GnssMeasurement_class_init_once(env, clazz);
-    gnss::GnssAntennaInfo_class_init_once(env, clazz);
+    gnss::Utils_class_init_once(env);
 }
 
 /* Initialization needed at system boot and whenever GNSS service dies. */
@@ -1672,25 +1496,21 @@
     }
 
     if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
-        sp<IGnssBatchingAidl> gnssBatchingAidl;
+        sp<android::hardware::gnss::IGnssBatching> gnssBatchingAidl;
         auto status = gnssHalAidl->getExtensionGnssBatching(&gnssBatchingAidl);
         if (checkAidlStatus(status, "Unable to get a handle to GnssBatching interface.")) {
-            gnssBatchingAidlIface = gnssBatchingAidl;
+            gnssBatchingIface = std::make_unique<gnss::GnssBatching>(gnssBatchingAidl);
         }
     } else if (gnssHal_V2_0 != nullptr) {
         auto gnssBatching_V2_0 = gnssHal_V2_0->getExtensionGnssBatching_2_0();
-        if (!gnssBatching_V2_0.isOk()) {
-            ALOGD("Unable to get a handle to GnssBatching_V2_0");
-        } else {
-            gnssBatchingIface_V2_0 = gnssBatching_V2_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 (gnssBatchingIface_V2_0 == nullptr ) {
+    if (gnssBatchingIface == nullptr) {
         auto gnssBatching_V1_0 = gnssHal->getExtensionGnssBatching();
-        if (!gnssBatching_V1_0.isOk()) {
-            ALOGD("Unable to get a handle to GnssBatching");
-        } else {
-            gnssBatchingIface = gnssBatching_V1_0;
+        if (checkHidlReturn(gnssBatching_V1_0, "Unable to get a handle to GnssBatching")) {
+            gnssBatchingIface = std::make_unique<gnss::GnssBatching_V1_0>(gnssBatching_V1_0);
         }
     }
 
@@ -2786,92 +2606,46 @@
 }
 
 static jint android_location_gnss_hal_GnssNative_get_batch_size(JNIEnv*) {
-    if (gnssBatchingAidlIface != nullptr) {
-        int size = 0;
-        auto status = gnssBatchingAidlIface->getBatchSize(&size);
-        if (!checkAidlStatus(status, "IGnssBatchingAidl getBatchSize() failed")) {
-            return 0;
-        }
-        return size;
-    } else if (gnssBatchingIface != nullptr) {
-        auto result = gnssBatchingIface->getBatchSize();
-        if (!checkHidlReturn(result, "IGnssBatching getBatchSize() failed.")) {
-            return 0; // failure in binder, don't support batching
-        }
-        return static_cast<jint>(result);
+    if (gnssBatchingIface == nullptr) {
+        return 0; // batching not supported, size = 0
     }
-    return 0; // batching not supported, size = 0
+    return gnssBatchingIface->getBatchSize();
 }
 
 static jboolean android_location_gnss_hal_GnssNative_init_batching(JNIEnv*, jclass) {
-    if (gnssBatchingAidlIface != nullptr) {
-        sp<IGnssBatchingCallbackAidl> gnssBatchingCbIface = new GnssBatchingCallbackAidl();
-        auto status = gnssBatchingAidlIface->init(gnssBatchingCbIface);
-        return checkAidlStatus(status, "IGnssBatchingAidl init() failed.");
-    } else if (gnssBatchingIface_V2_0 != nullptr) {
-        sp<IGnssBatchingCallback_V2_0> gnssBatchingCbIface_V2_0 = new GnssBatchingCallback_V2_0();
-        auto result = gnssBatchingIface_V2_0->init_2_0(gnssBatchingCbIface_V2_0);
-        return checkHidlReturn(result, "IGnssBatching init_2_0() failed.");
-    } else if (gnssBatchingIface != nullptr) {
-        sp<IGnssBatchingCallback_V1_0> gnssBatchingCbIface_V1_0 = new GnssBatchingCallback_V1_0();
-        auto result = gnssBatchingIface->init(gnssBatchingCbIface_V1_0);
-        return checkHidlReturn(result, "IGnssBatching init() failed.");
-    } else {
+    if (gnssBatchingIface == nullptr) {
         return JNI_FALSE; // batching not supported
     }
+    return gnssBatchingIface->init(std::make_unique<gnss::GnssBatchingCallback>());
 }
 
 static void android_location_gnss_hal_GnssNative_cleanup_batching(JNIEnv*, jclass) {
-    if (gnssBatchingAidlIface != nullptr) {
-        auto status = gnssBatchingAidlIface->cleanup();
-        checkAidlStatus(status, "IGnssBatchingAidl cleanup() failed");
-    } else if (gnssBatchingIface != nullptr) {
-        auto result = gnssBatchingIface->cleanup();
-        checkHidlReturn(result, "IGnssBatching cleanup() failed.");
+    if (gnssBatchingIface == nullptr) {
+        return; // batching not supported
     }
-    return;
+    gnssBatchingIface->cleanup();
 }
 
 static jboolean android_location_gnss_hal_GnssNative_start_batch(JNIEnv*, jclass, jlong periodNanos,
                                                                  jboolean wakeOnFifoFull) {
-    IGnssBatching_V1_0::Options options;
-    options.periodNanos = periodNanos;
-    if (wakeOnFifoFull) {
-        options.flags = static_cast<uint8_t>(IGnssBatching_V1_0::Flag::WAKEUP_ON_FIFO_FULL);
-    } else {
-        options.flags = 0;
+    if (gnssBatchingIface == nullptr) {
+        return JNI_FALSE; // batching not supported
     }
-
-    if (gnssBatchingAidlIface != nullptr) {
-        auto status = gnssBatchingAidlIface->start(periodNanos, (int)options.flags);
-        return checkAidlStatus(status, "IGnssBatchingAidl start() failed.");
-    } else if (gnssBatchingIface != nullptr) {
-        auto result = gnssBatchingIface->start(options);
-        return checkHidlReturn(result, "IGnssBatching start() failed.");
-    }
-    return JNI_FALSE; // batching not supported
+    return gnssBatchingIface->start(periodNanos, wakeOnFifoFull);
 }
 
 static void android_location_gnss_hal_GnssNative_flush_batch(JNIEnv*, jclass) {
-    if (gnssBatchingAidlIface != nullptr) {
-        auto status = gnssBatchingAidlIface->flush();
-        checkAidlStatus(status, "IGnssBatchingAidl flush() failed.");
-    } else if (gnssBatchingIface != nullptr) {
-        auto result = gnssBatchingIface->flush();
-        checkHidlReturn(result, "IGnssBatching flush() failed.");
+    if (gnssBatchingIface == nullptr) {
+        return; // batching not supported
     }
-    return;
+    gnssBatchingIface->flush();
 }
 
 static jboolean android_location_gnss_hal_GnssNative_stop_batch(JNIEnv*, jclass) {
-    if (gnssBatchingAidlIface != nullptr) {
-        auto status = gnssBatchingAidlIface->stop();
-        return checkAidlStatus(status, "IGnssBatchingAidl stop() failed.");
-    } else if (gnssBatchingIface != nullptr) {
-        auto result = gnssBatchingIface->stop();
-        return checkHidlReturn(result, "IGnssBatching stop() failed.");
+    if (gnssBatchingIface == nullptr) {
+        return JNI_FALSE; // batching not supported
     }
-    return JNI_FALSE; // batching not supported
+    return gnssBatchingIface->stop();
 }
 
 static jboolean android_location_GnssVisibilityControl_enable_nfw_location_access(
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index 9085fa7..090166a 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -24,6 +24,8 @@
 
     srcs: [
         "GnssAntennaInfoCallback.cpp",
+        "GnssBatching.cpp",
+        "GnssBatchingCallback.cpp",
         "GnssConfiguration.cpp",
         "GnssMeasurement.cpp",
         "GnssMeasurementCallback.cpp",
diff --git a/services/core/jni/gnss/GnssBatching.cpp b/services/core/jni/gnss/GnssBatching.cpp
new file mode 100644
index 0000000..b66bf21
--- /dev/null
+++ b/services/core/jni/gnss/GnssBatching.cpp
@@ -0,0 +1,131 @@
+/*
+ * 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 before <log/log.h> to overwrite the default value.
+#define LOG_TAG "GnssBatchingJni"
+
+#include "GnssBatching.h"
+
+#include "Utils.h"
+
+using android::hardware::gnss::IGnssBatching;
+using IGnssBatching_V1_0 = android::hardware::gnss::V1_0::IGnssBatching;
+using IGnssBatching_V2_0 = android::hardware::gnss::V2_0::IGnssBatching;
+
+namespace android::gnss {
+
+// Implementation of GnssBatching (AIDL HAL)
+
+GnssBatching::GnssBatching(const sp<IGnssBatching>& iGnssBatching) : mIGnssBatching(iGnssBatching) {
+    assert(mIGnssBatching != nullptr);
+}
+
+jboolean GnssBatching::init(const std::unique_ptr<GnssBatchingCallback>& callback) {
+    auto status = mIGnssBatching->init(callback->getAidl());
+    return checkAidlStatus(status, "IGnssBatchingAidl init() failed.");
+}
+
+jint GnssBatching::getBatchSize() {
+    int size = 0;
+    auto status = mIGnssBatching->getBatchSize(&size);
+    if (!checkAidlStatus(status, "IGnssBatchingAidl getBatchSize() failed")) {
+        return 0;
+    }
+    return size;
+}
+
+jboolean GnssBatching::start(long periodNanos, bool wakeOnFifoFull) {
+    int flags = (wakeOnFifoFull) ? IGnssBatching::WAKEUP_ON_FIFO_FULL : 0;
+    auto status = mIGnssBatching->start(periodNanos, flags);
+    return checkAidlStatus(status, "IGnssBatchingAidl start() failed.");
+}
+
+jboolean GnssBatching::stop() {
+    auto status = mIGnssBatching->stop();
+    return checkAidlStatus(status, "IGnssBatchingAidl stop() failed.");
+}
+
+jboolean GnssBatching::flush() {
+    auto status = mIGnssBatching->flush();
+    return checkAidlStatus(status, "IGnssBatchingAidl flush() failed.");
+}
+
+jboolean GnssBatching::cleanup() {
+    auto status = mIGnssBatching->cleanup();
+    return checkAidlStatus(status, "IGnssBatchingAidl cleanup() failed");
+}
+
+// Implementation of GnssBatching_V1_0
+
+GnssBatching_V1_0::GnssBatching_V1_0(const sp<IGnssBatching_V1_0>& iGnssBatching)
+      : mIGnssBatching_V1_0(iGnssBatching) {
+    assert(mIGnssBatching_V1_0 != nullptr);
+}
+
+jboolean GnssBatching_V1_0::init(const std::unique_ptr<GnssBatchingCallback>& callback) {
+    auto result = mIGnssBatching_V1_0->init(callback->getV1_0());
+    return checkHidlReturn(result, "IGnssBatching_V1_0 init() failed.");
+}
+
+jint GnssBatching_V1_0::getBatchSize() {
+    auto result = mIGnssBatching_V1_0->getBatchSize();
+    if (!checkHidlReturn(result, "IGnssBatching getBatchSize() failed.")) {
+        return 0; // failure in binder, don't support batching
+    }
+    return static_cast<jint>(result);
+}
+
+jboolean GnssBatching_V1_0::start(long periodNanos, bool wakeOnFifoFull) {
+    IGnssBatching_V1_0::Options options;
+    options.periodNanos = periodNanos;
+    if (wakeOnFifoFull) {
+        options.flags = static_cast<uint8_t>(IGnssBatching_V1_0::Flag::WAKEUP_ON_FIFO_FULL);
+    } else {
+        options.flags = 0;
+    }
+
+    auto result = mIGnssBatching_V1_0->start(options);
+    return checkHidlReturn(result, "IGnssBatching start() failed.");
+}
+
+jboolean GnssBatching_V1_0::stop() {
+    auto result = mIGnssBatching_V1_0->stop();
+    return checkHidlReturn(result, "IGnssBatching stop() failed.");
+}
+
+jboolean GnssBatching_V1_0::flush() {
+    auto result = mIGnssBatching_V1_0->flush();
+    return checkHidlReturn(result, "IGnssBatching flush() failed.");
+}
+
+jboolean GnssBatching_V1_0::cleanup() {
+    auto result = mIGnssBatching_V1_0->cleanup();
+    return checkHidlReturn(result, "IGnssBatching cleanup() failed.");
+}
+
+// Implementation of GnssBatching_V2_0
+
+GnssBatching_V2_0::GnssBatching_V2_0(const sp<IGnssBatching_V2_0>& iGnssBatching)
+      : GnssBatching_V1_0{iGnssBatching}, mIGnssBatching_V2_0(iGnssBatching) {
+    assert(mIGnssBatching_V2_0 != nullptr);
+}
+
+jboolean GnssBatching_V2_0::init(const std::unique_ptr<GnssBatchingCallback>& callback) {
+    auto result = mIGnssBatching_V2_0->init_2_0(callback->getV2_0());
+    return checkHidlReturn(result, "IGnssBatching_V2_0 init() failed.");
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssBatching.h b/services/core/jni/gnss/GnssBatching.h
new file mode 100644
index 0000000..a98ca9b
--- /dev/null
+++ b/services/core/jni/gnss/GnssBatching.h
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSBATCHING_H
+#define _ANDROID_SERVER_GNSS_GNSSBATCHING_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnssBatching.h>
+#include <android/hardware/gnss/2.0/IGnssBatching.h>
+#include <android/hardware/gnss/BnGnssBatching.h>
+#include <log/log.h>
+
+#include "GnssBatchingCallback.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+class GnssBatchingInterface {
+public:
+    virtual ~GnssBatchingInterface() {}
+    virtual jboolean init(const std::unique_ptr<GnssBatchingCallback>& callback) = 0;
+    virtual jint getBatchSize() = 0;
+    virtual jboolean start(long periodNanos, bool wakeupOnFifoFull) = 0;
+    virtual jboolean stop() = 0;
+    virtual jboolean flush() = 0;
+    virtual jboolean cleanup() = 0;
+};
+
+class GnssBatching : public GnssBatchingInterface {
+public:
+    GnssBatching(const sp<android::hardware::gnss::IGnssBatching>& iGnssBatching);
+    jboolean init(const std::unique_ptr<GnssBatchingCallback>& callback) override;
+    jint getBatchSize() override;
+    jboolean start(long periodNanos, bool wakeupOnFifoFull) override;
+    jboolean stop() override;
+    jboolean flush() override;
+    jboolean cleanup() override;
+
+private:
+    const sp<android::hardware::gnss::IGnssBatching> mIGnssBatching;
+};
+
+class GnssBatching_V1_0 : public GnssBatchingInterface {
+public:
+    GnssBatching_V1_0(const sp<android::hardware::gnss::V1_0::IGnssBatching>& iGnssBatching);
+    jboolean init(const std::unique_ptr<GnssBatchingCallback>& callback) override;
+    jint getBatchSize() override;
+    jboolean start(long periodNanos, bool wakeupOnFifoFull) override;
+    jboolean stop() override;
+    jboolean flush() override;
+    jboolean cleanup() override;
+
+private:
+    const sp<android::hardware::gnss::V1_0::IGnssBatching> mIGnssBatching_V1_0;
+};
+
+class GnssBatching_V2_0 : public GnssBatching_V1_0 {
+public:
+    GnssBatching_V2_0(const sp<android::hardware::gnss::V2_0::IGnssBatching>& iGnssBatching);
+    jboolean init(const std::unique_ptr<GnssBatchingCallback>& callback) override;
+
+private:
+    const sp<android::hardware::gnss::V2_0::IGnssBatching> mIGnssBatching_V2_0;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSBATCHING_H
diff --git a/services/core/jni/gnss/GnssBatchingCallback.cpp b/services/core/jni/gnss/GnssBatchingCallback.cpp
new file mode 100644
index 0000000..d2a5547
--- /dev/null
+++ b/services/core/jni/gnss/GnssBatchingCallback.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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 "GnssBatchingCbJni"
+
+#include "GnssBatchingCallback.h"
+
+namespace android::gnss {
+
+namespace {
+
+jmethodID method_reportLocationBatch;
+
+} // anonymous namespace
+
+using android::hardware::hidl_vec;
+using binder::Status;
+using hardware::Return;
+
+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;
+
+void GnssBatching_class_init_once(JNIEnv* env, jclass clazz) {
+    method_reportLocationBatch =
+            env->GetMethodID(clazz, "reportLocationBatch", "([Landroid/location/Location;)V");
+}
+
+Status GnssBatchingCallbackAidl::gnssLocationBatchCb(
+        const std::vector<android::hardware::gnss::GnssLocation>& locations) {
+    GnssBatchingCallbackUtil::gnssLocationBatchCbImpl(hidl_vec<GnssLocationAidl>(locations));
+    return Status::ok();
+}
+
+Return<void> GnssBatchingCallback_V1_0::gnssLocationBatchCb(
+        const hidl_vec<GnssLocation_V1_0>& locations) {
+    return GnssBatchingCallbackUtil::gnssLocationBatchCbImpl(locations);
+}
+
+Return<void> GnssBatchingCallback_V2_0::gnssLocationBatchCb(
+        const hidl_vec<GnssLocation_V2_0>& locations) {
+    return GnssBatchingCallbackUtil::gnssLocationBatchCbImpl(locations);
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssBatchingCallback.h b/services/core/jni/gnss/GnssBatchingCallback.h
new file mode 100644
index 0000000..a9dd4304
--- /dev/null
+++ b/services/core/jni/gnss/GnssBatchingCallback.h
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSBATCHCALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSBATCHCALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnssBatching.h>
+#include <android/hardware/gnss/2.0/IGnssBatching.h>
+#include <android/hardware/gnss/BnGnssBatchingCallback.h>
+#include <log/log.h>
+
+#include <vector>
+
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+namespace {
+
+extern jmethodID method_reportLocationBatch;
+
+} // anonymous namespace
+
+void GnssBatching_class_init_once(JNIEnv* env, jclass clazz);
+
+class GnssBatchingCallbackAidl : public hardware::gnss::BnGnssBatchingCallback {
+public:
+    GnssBatchingCallbackAidl() {}
+    android::binder::Status gnssLocationBatchCb(
+            const std::vector<android::hardware::gnss::GnssLocation>& locations) override;
+};
+
+class GnssBatchingCallback_V1_0 : public hardware::gnss::V1_0::IGnssBatchingCallback {
+public:
+    GnssBatchingCallback_V1_0() {}
+    hardware::Return<void> gnssLocationBatchCb(
+            const hardware::hidl_vec<hardware::gnss::V1_0::GnssLocation>& locations) override;
+};
+
+class GnssBatchingCallback_V2_0 : public hardware::gnss::V2_0::IGnssBatchingCallback {
+public:
+    GnssBatchingCallback_V2_0() {}
+    hardware::Return<void> gnssLocationBatchCb(
+            const hardware::hidl_vec<hardware::gnss::V2_0::GnssLocation>& locations) override;
+};
+
+class GnssBatchingCallback {
+public:
+    GnssBatchingCallback() {}
+    sp<GnssBatchingCallbackAidl> getAidl() {
+        if (callbackAidl == nullptr) {
+            callbackAidl = sp<GnssBatchingCallbackAidl>::make();
+        }
+        return callbackAidl;
+    }
+
+    sp<GnssBatchingCallback_V1_0> getV1_0() {
+        if (callbackV1_0 == nullptr) {
+            callbackV1_0 = sp<GnssBatchingCallback_V1_0>::make();
+        }
+        return callbackV1_0;
+    }
+
+    sp<GnssBatchingCallback_V2_0> getV2_0() {
+        if (callbackV2_0 == nullptr) {
+            callbackV2_0 = sp<GnssBatchingCallback_V2_0>::make();
+        }
+        return callbackV2_0;
+    }
+
+private:
+    sp<GnssBatchingCallbackAidl> callbackAidl;
+    sp<GnssBatchingCallback_V1_0> callbackV1_0;
+    sp<GnssBatchingCallback_V2_0> callbackV2_0;
+};
+
+struct GnssBatchingCallbackUtil {
+    template <class T>
+    static hardware::Return<void> gnssLocationBatchCbImpl(const hardware::hidl_vec<T>& locations);
+
+private:
+    GnssBatchingCallbackUtil() = delete;
+};
+
+template <class T>
+hardware::Return<void> GnssBatchingCallbackUtil::gnssLocationBatchCbImpl(
+        const hardware::hidl_vec<T>& locations) {
+    JNIEnv* env = getJniEnv();
+
+    jobjectArray jLocations = env->NewObjectArray(locations.size(), class_location, nullptr);
+
+    for (uint16_t i = 0; i < locations.size(); ++i) {
+        jobject jLocation = translateGnssLocation(env, locations[i]);
+        env->SetObjectArrayElement(jLocations, i, jLocation);
+        env->DeleteLocalRef(jLocation);
+    }
+
+    env->CallVoidMethod(android::getCallbacksObj(), method_reportLocationBatch, jLocations);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+
+    env->DeleteLocalRef(jLocations);
+
+    return hardware::Void();
+}
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSBATCHCALLBACK_H
\ No newline at end of file
diff --git a/services/core/jni/gnss/Utils.cpp b/services/core/jni/gnss/Utils.cpp
index 8cbdfb8..40a94ce 100644
--- a/services/core/jni/gnss/Utils.cpp
+++ b/services/core/jni/gnss/Utils.cpp
@@ -18,6 +18,9 @@
 
 #include "Utils.h"
 
+#include <android/hardware/gnss/1.0/IGnss.h>
+#include <android/hardware/gnss/2.0/IGnss.h>
+#include <utils/SystemClock.h>
 /*
  * Save a pointer to JavaVm to attach/detach threads executing
  * callback methods that need to make JNI calls.
@@ -29,9 +32,29 @@
 namespace {
 
 thread_local std::unique_ptr<ScopedJniThreadAttach> tJniThreadAttacher;
+jmethodID method_locationCtor;
 
 } // anonymous namespace
 
+jclass class_location;
+
+namespace gnss {
+void Utils_class_init_once(JNIEnv* env) {
+    jclass locationClass = env->FindClass("android/location/Location");
+    class_location = (jclass)env->NewGlobalRef(locationClass);
+    method_locationCtor = env->GetMethodID(class_location, "<init>", "(Ljava/lang/String;)V");
+}
+} // namespace gnss
+
+jobject mCallbacksObj = nullptr;
+
+jobject& getCallbacksObj() {
+    return mCallbacksObj;
+}
+
+using GnssLocation_V1_0 = android::hardware::gnss::V1_0::GnssLocation;
+using GnssLocation_V2_0 = android::hardware::gnss::V2_0::GnssLocation;
+
 // Define Java method signatures for all known types.
 template <>
 const char* const JavaMethodHelper<uint8_t>::signature_ = "(B)V";
@@ -130,4 +153,103 @@
     return env;
 }
 
+template <>
+jobject translateGnssLocation(JNIEnv* env, const android::hardware::gnss::GnssLocation& location) {
+    JavaObject object(env, class_location, method_locationCtor, "gps");
+
+    uint32_t flags = static_cast<uint32_t>(location.gnssLocationFlags);
+    if (flags & android::hardware::gnss::GnssLocation::HAS_LAT_LONG) {
+        SET(Latitude, location.latitudeDegrees);
+        SET(Longitude, location.longitudeDegrees);
+    }
+    if (flags & android::hardware::gnss::GnssLocation::HAS_ALTITUDE) {
+        SET(Altitude, location.altitudeMeters);
+    }
+    if (flags & android::hardware::gnss::GnssLocation::HAS_SPEED) {
+        SET(Speed, (float)location.speedMetersPerSec);
+    }
+    if (flags & android::hardware::gnss::GnssLocation::HAS_BEARING) {
+        SET(Bearing, (float)location.bearingDegrees);
+    }
+    if (flags & android::hardware::gnss::GnssLocation::HAS_HORIZONTAL_ACCURACY) {
+        SET(Accuracy, (float)location.horizontalAccuracyMeters);
+    }
+    if (flags & android::hardware::gnss::GnssLocation::HAS_VERTICAL_ACCURACY) {
+        SET(VerticalAccuracyMeters, (float)location.verticalAccuracyMeters);
+    }
+    if (flags & android::hardware::gnss::GnssLocation::HAS_SPEED_ACCURACY) {
+        SET(SpeedAccuracyMetersPerSecond, (float)location.speedAccuracyMetersPerSecond);
+    }
+    if (flags & android::hardware::gnss::GnssLocation::HAS_BEARING_ACCURACY) {
+        SET(BearingAccuracyDegrees, (float)location.bearingAccuracyDegrees);
+    }
+    SET(Time, location.timestampMillis);
+
+    flags = static_cast<uint32_t>(location.elapsedRealtime.flags);
+    if (flags & android::hardware::gnss::ElapsedRealtime::HAS_TIMESTAMP_NS) {
+        SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
+    }
+    if (flags & android::hardware::gnss::ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS) {
+        SET(ElapsedRealtimeUncertaintyNanos,
+            static_cast<double>(location.elapsedRealtime.timeUncertaintyNs));
+    }
+
+    return object.get();
+}
+
+template <>
+jobject translateGnssLocation(JNIEnv* env, const GnssLocation_V1_0& location) {
+    JavaObject object(env, class_location, method_locationCtor, "gps");
+
+    uint16_t flags = static_cast<uint16_t>(location.gnssLocationFlags);
+    if (flags & android::hardware::gnss::V1_0::GnssLocationFlags::HAS_LAT_LONG) {
+        SET(Latitude, location.latitudeDegrees);
+        SET(Longitude, location.longitudeDegrees);
+    }
+    if (flags & android::hardware::gnss::V1_0::GnssLocationFlags::HAS_ALTITUDE) {
+        SET(Altitude, location.altitudeMeters);
+    }
+    if (flags & android::hardware::gnss::V1_0::GnssLocationFlags::HAS_SPEED) {
+        SET(Speed, location.speedMetersPerSec);
+    }
+    if (flags & android::hardware::gnss::V1_0::GnssLocationFlags::HAS_BEARING) {
+        SET(Bearing, location.bearingDegrees);
+    }
+    if (flags & android::hardware::gnss::V1_0::GnssLocationFlags::HAS_HORIZONTAL_ACCURACY) {
+        SET(Accuracy, location.horizontalAccuracyMeters);
+    }
+    if (flags & android::hardware::gnss::V1_0::GnssLocationFlags::HAS_VERTICAL_ACCURACY) {
+        SET(VerticalAccuracyMeters, location.verticalAccuracyMeters);
+    }
+    if (flags & android::hardware::gnss::V1_0::GnssLocationFlags::HAS_SPEED_ACCURACY) {
+        SET(SpeedAccuracyMetersPerSecond, location.speedAccuracyMetersPerSecond);
+    }
+    if (flags & android::hardware::gnss::V1_0::GnssLocationFlags::HAS_BEARING_ACCURACY) {
+        SET(BearingAccuracyDegrees, location.bearingAccuracyDegrees);
+    }
+    SET(Time, location.timestamp);
+    SET(ElapsedRealtimeNanos, android::elapsedRealtimeNano());
+
+    return object.get();
+}
+
+template <>
+jobject translateGnssLocation(JNIEnv* env, const GnssLocation_V2_0& location) {
+    JavaObject object(env, class_location, translateGnssLocation(env, location.v1_0));
+
+    const uint16_t flags = static_cast<uint16_t>(location.elapsedRealtime.flags);
+
+    // Overwrite ElapsedRealtimeNanos when available from HAL.
+    if (flags & android::hardware::gnss::V2_0::ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) {
+        SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
+    }
+
+    if (flags & android::hardware::gnss::V2_0::ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS) {
+        SET(ElapsedRealtimeUncertaintyNanos,
+            static_cast<double>(location.elapsedRealtime.timeUncertaintyNs));
+    }
+
+    return object.get();
+}
+
 } // namespace android
diff --git a/services/core/jni/gnss/Utils.h b/services/core/jni/gnss/Utils.h
index 0938a1b..1bd69c4 100644
--- a/services/core/jni/gnss/Utils.h
+++ b/services/core/jni/gnss/Utils.h
@@ -37,11 +37,19 @@
 
 // Must match the value from GnssMeasurement.java
 const uint32_t ADR_STATE_HALF_CYCLE_REPORTED = (1 << 4);
+extern jmethodID method_locationCtor;
 
 } // anonymous namespace
 
+extern jclass class_location;
 extern jobject mCallbacksObj;
 
+namespace gnss {
+void Utils_class_init_once(JNIEnv* env);
+} // namespace gnss
+
+jobject& getCallbacksObj();
+
 jboolean checkHidlReturn(hardware::Return<bool>& result, const char* errorMessage);
 
 jboolean checkAidlStatus(const android::binder::Status& status, const char* errorMessage);
@@ -190,6 +198,9 @@
 
 JNIEnv* getJniEnv();
 
+template <class T>
+jobject translateGnssLocation(JNIEnv* env, const T& location);
+
 } // namespace android
 
 #endif // _ANDROID_SERVER_GNSS_UTILS_H
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 37a84f3..df9ab50 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -16,7 +16,7 @@
 
 package com.android.server.devicepolicy;
 
-import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_DISABLED;
+import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY;
 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
 
@@ -161,10 +161,10 @@
     int mPasswordComplexity = PASSWORD_COMPLEXITY_NONE;
 
     @DevicePolicyManager.NearbyStreamingPolicy
-    int mNearbyNotificationStreamingPolicy = NEARBY_STREAMING_DISABLED;
+    int mNearbyNotificationStreamingPolicy = NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY;
 
     @DevicePolicyManager.NearbyStreamingPolicy
-    int mNearbyAppStreamingPolicy = NEARBY_STREAMING_DISABLED;
+    int mNearbyAppStreamingPolicy = NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY;
 
     @Nullable
     FactoryResetProtectionPolicy mFactoryResetProtectionPolicy = null;
@@ -553,11 +553,11 @@
         if (mPasswordComplexity != PASSWORD_COMPLEXITY_NONE) {
             writeAttributeValueToXml(out, TAG_PASSWORD_COMPLEXITY, mPasswordComplexity);
         }
-        if (mNearbyNotificationStreamingPolicy != NEARBY_STREAMING_DISABLED) {
+        if (mNearbyNotificationStreamingPolicy != NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY) {
             writeAttributeValueToXml(out, TAG_NEARBY_NOTIFICATION_STREAMING_POLICY,
                     mNearbyNotificationStreamingPolicy);
         }
-        if (mNearbyAppStreamingPolicy != NEARBY_STREAMING_DISABLED) {
+        if (mNearbyAppStreamingPolicy != NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY) {
             writeAttributeValueToXml(out, TAG_NEARBY_APP_STREAMING_POLICY,
                     mNearbyAppStreamingPolicy);
         }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 752ad0b..7ef80ae 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -631,7 +631,8 @@
         mStartCount = SystemProperties.getInt(SYSPROP_START_COUNT, 0) + 1;
         mRuntimeStartElapsedTime = SystemClock.elapsedRealtime();
         mRuntimeStartUptime = SystemClock.uptimeMillis();
-        Process.setStartTimes(mRuntimeStartElapsedTime, mRuntimeStartUptime);
+        Process.setStartTimes(mRuntimeStartElapsedTime, mRuntimeStartUptime,
+                mRuntimeStartElapsedTime, mRuntimeStartUptime);
 
         // Remember if it's runtime restart(when sys.boot_completed is already set) or reboot
         // We don't use "mStartCount > 1" here because it'll be wrong on a FDE device.
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 0248b9b..d926dcb 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -265,9 +265,10 @@
     }
 
     @Test
-    public void testInstallerCanSetOverrides() throws Exception {
+    public void testInstallerCanAddOverrides() throws Exception {
         final long disabledChangeId1 = 1234L;
         final long disabledChangeId2 = 1235L;
+        final long unknownChangeId = 1236L;
         // We make disabledChangeId2 non-overridable to make sure it is ignored.
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addDisabledOverridableChangeWithId(disabledChangeId1)
@@ -284,19 +285,25 @@
         // Force the validator to prevent overriding non-overridable changes by using a user build.
         when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
         when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+        Map<Long, PackageOverride> overrides = new HashMap<>();
+        overrides.put(disabledChangeId1, new PackageOverride.Builder()
+                .setMaxVersionCode(99L)
+                .setEnabled(true)
+                .build());
+        // Adding an unknown change ID to make sure it's skipped if skipUnknownChangeIds is true.
+        overrides.put(unknownChangeId, new PackageOverride.Builder().setEnabled(false).build());
+        CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides);
 
-        CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(
-                Collections.singletonMap(disabledChangeId1,
-                        new PackageOverride.Builder()
-                                .setMaxVersionCode(99L)
-                                .setEnabled(true)
-                                .build()));
-
-        compatConfig.addOverrides(config, "com.some.package");
+        compatConfig.addPackageOverrides(config, "com.some.package", /* skipUnknownChangeIds */
+                true);
         assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo)).isTrue();
         assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo)).isFalse();
+        // Making sure the unknown change ID is still unknown and isChangeEnabled returns true.
+        assertThat(compatConfig.isKnownChangeId(unknownChangeId)).isFalse();
+        assertThat(compatConfig.isChangeEnabled(unknownChangeId, applicationInfo)).isTrue();
     }
 
+
     @Test
     public void testPreventInstallerSetNonOverridable() throws Exception {
         final long disabledChangeId1 = 1234L;
@@ -326,7 +333,8 @@
         CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides);
 
         assertThrows(SecurityException.class,
-                () -> compatConfig.addOverrides(config, "com.some.package")
+                () -> compatConfig.addPackageOverrides(config, "com.some.package",
+                        /* skipUnknownChangeIds */ true)
         );
         assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo)).isTrue();
         assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo)).isFalse();
@@ -334,6 +342,38 @@
     }
 
     @Test
+    public void testCanAddOverridesForUnknownChangeIdOnDebugBuild() throws Exception {
+        final long disabledChangeId = 1234L;
+        final long unknownChangeId = 1235L;
+        // We make disabledChangeId2 non-overridable to make sure it is ignored.
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(disabledChangeId)
+                .build();
+        ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+                .withPackageName("com.some.package")
+                .build();
+        PackageManager packageManager = mock(PackageManager.class);
+        when(mContext.getPackageManager()).thenReturn(packageManager);
+        when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+                .thenReturn(applicationInfo);
+
+        when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
+        Map<Long, PackageOverride> overrides = new HashMap<>();
+        overrides.put(disabledChangeId, new PackageOverride.Builder().setEnabled(true).build());
+        // Adding an unknown change ID to make sure it isn't skipped if skipUnknownChangeIds is
+        // false.
+        overrides.put(unknownChangeId, new PackageOverride.Builder().setEnabled(false).build());
+        CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides);
+
+        compatConfig.addPackageOverrides(config, "com.some.package", /* skipUnknownChangeIds */
+                false);
+        assertThat(compatConfig.isChangeEnabled(disabledChangeId, applicationInfo)).isTrue();
+        // Making sure the unknown change ID is now known and has an override.
+        assertThat(compatConfig.isKnownChangeId(unknownChangeId)).isTrue();
+        assertThat(compatConfig.isChangeEnabled(unknownChangeId, applicationInfo)).isFalse();
+    }
+
+    @Test
     public void testApplyDeferredOverridesAfterInstallingApp() throws Exception {
         ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
                 .withPackageName("com.notinstalled.foo")
@@ -377,7 +417,8 @@
                                 .setMaxVersionCode(99L)
                                 .setEnabled(true)
                                 .build()));
-        compatConfig.addOverrides(config, "com.installed.foo");
+        compatConfig.addPackageOverrides(config, "com.installed.foo", /* skipUnknownChangeIds */
+                true);
         assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
 
         // Add override that does include the installed app version
@@ -388,7 +429,8 @@
                                 .setMaxVersionCode(100L)
                                 .setEnabled(true)
                                 .build()));
-        compatConfig.addOverrides(config, "com.installed.foo");
+        compatConfig.addPackageOverrides(config, "com.installed.foo", /* skipUnknownChangeIds */
+                true);
         assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
     }
 
@@ -411,7 +453,8 @@
                         .setMaxVersionCode(99L)
                         .setEnabled(true)
                         .build()));
-        compatConfig.addOverrides(config, "com.notinstalled.foo");
+        compatConfig.addPackageOverrides(config, "com.notinstalled.foo", /* skipUnknownChangeIds */
+                true);
         assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
 
         // Pretend the app is now installed.
@@ -557,6 +600,7 @@
         final long disabledChangeId1 = 1234L;
         final long disabledChangeId2 = 1235L;
         final long enabledChangeId = 1236L;
+        final long unknownChangeId = 1237L;
         // We make disabledChangeId2 non-overridable to make sure it is ignored.
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addDisabledOverridableChangeWithId(disabledChangeId1)
@@ -583,6 +627,8 @@
         Set<Long> overridesToRemove = new HashSet<>();
         overridesToRemove.add(disabledChangeId1);
         overridesToRemove.add(enabledChangeId);
+        // Adding an unknown change ID to make sure it's skipped.
+        overridesToRemove.add(unknownChangeId);
         CompatibilityOverridesToRemoveConfig config = new CompatibilityOverridesToRemoveConfig(
                 overridesToRemove);
 
@@ -590,6 +636,8 @@
         assertThat(compatConfig.isChangeEnabled(disabledChangeId1, applicationInfo)).isFalse();
         assertThat(compatConfig.isChangeEnabled(disabledChangeId2, applicationInfo)).isTrue();
         assertThat(compatConfig.isChangeEnabled(enabledChangeId, applicationInfo)).isTrue();
+        // Making sure the unknown change ID is still unknown.
+        assertThat(compatConfig.isKnownChangeId(unknownChangeId)).isFalse();
     }
 
     @Test
@@ -797,18 +845,18 @@
                         .build());
         when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
                 .thenThrow(new NameNotFoundException());
-        compatConfig.addOverrides(
+        compatConfig.addPackageOverrides(
                 new CompatibilityOverrideConfig(
                         Collections.singletonMap(
                                 1L,
                                 new PackageOverride.Builder().setEnabled(true).build())),
-                "foo.bar");
-        compatConfig.addOverrides(
+                "foo.bar", /* skipUnknownChangeIds */ true);
+        compatConfig.addPackageOverrides(
                 new CompatibilityOverrideConfig(
                         Collections.singletonMap(
                                 2L,
                                 new PackageOverride.Builder().setEnabled(false).build())),
-                "bar.baz");
+                "bar.baz", /* skipUnknownChangeIds */ true);
 
         assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                 + "<overrides>\n"
@@ -847,12 +895,12 @@
         compatConfig.forceNonDebuggableFinalForTest(true);
         compatConfig.initOverrides(overridesFile, new File(""));
 
-        compatConfig.addOverrides(new CompatibilityOverrideConfig(Collections.singletonMap(1L,
-                new PackageOverride.Builder()
+        compatConfig.addPackageOverrides(new CompatibilityOverrideConfig(
+                Collections.singletonMap(1L, new PackageOverride.Builder()
                         .setMinVersionCode(99L)
                         .setMaxVersionCode(101L)
                         .setEnabled(true)
-                        .build())), "foo.bar");
+                        .build())), "foo.bar", /* skipUnknownChangeIds */ true);
 
         assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                 + "<overrides>\n"
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index 51f627a..11d5f3f 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -29,9 +29,6 @@
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.util.IndentingPrintWriter;
 
-import java.util.ArrayList;
-import java.util.List;
-
 class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
 
     private ConfigurationChangeListener mConfigurationChangeListener;
@@ -44,7 +41,6 @@
     private ManualTimeZoneSuggestion mLastManualSuggestion;
     private TelephonyTimeZoneSuggestion mLastTelephonySuggestion;
     private boolean mDumpCalled;
-    private final List<Dumpable> mDumpables = new ArrayList<>();
 
     @Override
     public void addConfigChangeListener(@NonNull ConfigurationChangeListener listener) {
@@ -123,11 +119,6 @@
     }
 
     @Override
-    public void addDumpable(Dumpable dumpable) {
-        mDumpables.add(dumpable);
-    }
-
-    @Override
     public void dump(IndentingPrintWriter pw, String[] args) {
         mDumpCalled = true;
     }
@@ -159,8 +150,4 @@
     void verifyDumpCalled() {
         assertTrue(mDumpCalled);
     }
-
-    void verifyHasDumpable(Dumpable expected) {
-        assertTrue(mDumpables.contains(expected));
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
index 5864620..45c5b6c 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
@@ -78,16 +78,6 @@
     }
 
     @Test
-    public void testAddDumpable() throws Exception {
-        Dumpable stubbedDumpable = mock(Dumpable.class);
-
-        mTimeZoneDetectorInternal.addDumpable(stubbedDumpable);
-        mTestHandler.assertTotalMessagesEnqueued(0);
-
-        mFakeTimeZoneDetectorStrategy.verifyHasDumpable(stubbedDumpable);
-    }
-
-    @Test
     public void testAddConfigurationListener() throws Exception {
         boolean[] changeCalled = new boolean[2];
         mTimeZoneDetectorInternal.addConfigurationListener(() -> changeCalled[0] = true);
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 773abf8..1e55c7b 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -60,12 +60,12 @@
     private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234L;
 
     private Context mMockContext;
-    private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy;
 
     private TimeZoneDetectorService mTimeZoneDetectorService;
     private HandlerThread mHandlerThread;
     private TestHandler mTestHandler;
     private TestCallerIdentityInjector mTestCallerIdentityInjector;
+    private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy;
 
 
     @Before
@@ -349,11 +349,15 @@
         when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
                 .thenReturn(PackageManager.PERMISSION_GRANTED);
 
+        Dumpable dumpable = mock(Dumpable.class);
+        mTimeZoneDetectorService.addDumpable(dumpable);
+
         PrintWriter pw = new PrintWriter(new StringWriter());
         mTimeZoneDetectorService.dump(null, pw, null);
 
         verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
         mFakeTimeZoneDetectorStrategy.verifyDumpCalled();
+        verify(dumpable).dump(any(), any());
     }
 
     private static TimeZoneConfiguration createTimeZoneConfiguration(boolean autoDetectionEnabled) {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index e2e8755..1826812 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -47,18 +47,15 @@
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality;
-import android.util.IndentingPrintWriter;
 
 import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion;
 
 import org.junit.Before;
 import org.junit.Test;
 
-import java.io.StringWriter;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
 
 /**
@@ -66,8 +63,8 @@
  */
 public class TimeZoneDetectorStrategyImplTest {
 
-    /** A time zone used for initialization that does not occur elsewhere in tests. */
     private static final @UserIdInt int USER_ID = 9876;
+    /** A time zone used for initialization that does not occur elsewhere in tests. */
     private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
     private static final int SLOT_INDEX1 = 10000;
     private static final int SLOT_INDEX2 = 20000;
@@ -790,7 +787,7 @@
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         script.simulateGeolocationTimeZoneSuggestion(suggestion)
-                .verifyTimeZoneChangedAndReset("Europe/London");
+                .verifyTimeZoneChangedAndReset(suggestion);
 
         // Assert internal service state.
         assertEquals(suggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
@@ -815,7 +812,7 @@
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         script.simulateGeolocationTimeZoneSuggestion(londonOnlySuggestion)
-                .verifyTimeZoneChangedAndReset("Europe/London");
+                .verifyTimeZoneChangedAndReset(londonOnlySuggestion);
         assertEquals(londonOnlySuggestion,
                 mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
 
@@ -826,7 +823,7 @@
                 mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
 
         script.simulateGeolocationTimeZoneSuggestion(parisOnlySuggestion)
-                .verifyTimeZoneChangedAndReset("Europe/Paris");
+                .verifyTimeZoneChangedAndReset(parisOnlySuggestion);
         assertEquals(parisOnlySuggestion,
                 mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
 
@@ -838,29 +835,6 @@
                 mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
     }
 
-    @Test
-    public void testGeoSuggestion_togglingGeoDetectionClearsLastSuggestion() {
-        GeolocationTimeZoneSuggestion suggestion =
-                createCertainGeolocationSuggestion("Europe/London");
-
-        Script script = new Script()
-                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
-
-        script.simulateGeolocationTimeZoneSuggestion(suggestion)
-                .verifyTimeZoneChangedAndReset("Europe/London");
-
-        // Assert internal service state.
-        assertEquals(suggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
-
-        // Turn off geo detection and verify the latest suggestion is cleared.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_GEO_DETECTION_DISABLED, true)
-                .verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
-
-        // Assert internal service state.
-        assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
-    }
-
     /**
      * Confirms that changing the geolocation time zone detection enabled setting has the expected
      * behavior, i.e. immediately recompute the detected time zone using different signals.
@@ -881,13 +855,12 @@
         script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
                 .verifyTimeZoneNotChanged();
 
-        // Geolocation suggestions are only stored when geolocation detection is enabled.
-        assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+        assertEquals(geolocationSuggestion,
+                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
 
         script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
                 .verifyTimeZoneNotChanged();
 
-        // Telephony suggestions are always stored.
         assertEquals(telephonySuggestion,
                 mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1).suggestion);
 
@@ -897,13 +870,11 @@
         script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
                 .verifyTimeZoneChangedAndReset(telephonySuggestion);
 
-        // Changing the detection to enable geo detection won't cause the device tz setting to
-        // change because the geo suggestion is empty.
+        // Changing the detection to enable geo detection will cause the device tz setting to
+        // change to use the latest geolocation suggestion.
         script.simulateUpdateConfiguration(
                 USER_ID, CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */)
-                .verifyTimeZoneNotChanged()
-                .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
-                .verifyTimeZoneChangedAndReset(geolocationSuggestion.getZoneIds().get(0));
+                .verifyTimeZoneChangedAndReset(geolocationSuggestion);
 
         // Changing the detection to disable geo detection should cause the device tz setting to
         // change to the telephony suggestion.
@@ -911,29 +882,8 @@
                 USER_ID, CONFIG_GEO_DETECTION_DISABLED, true /* expectedResult */)
                 .verifyTimeZoneChangedAndReset(telephonySuggestion);
 
-        assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
-    }
-
-    @Test
-    public void testAddDumpable() {
-        new Script()
-                .initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED)
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
-
-        AtomicBoolean dumpCalled = new AtomicBoolean(false);
-        class FakeDumpable implements Dumpable {
-            @Override
-            public void dump(IndentingPrintWriter pw, String[] args) {
-                dumpCalled.set(true);
-            }
-        }
-
-        mTimeZoneDetectorStrategy.addDumpable(new FakeDumpable());
-        IndentingPrintWriter ipw = new IndentingPrintWriter(new StringWriter());
-        String[] args = {"ArgOne", "ArgTwo"};
-        mTimeZoneDetectorStrategy.dump(ipw, args);
-
-        assertTrue(dumpCalled.get());
+        assertEquals(geolocationSuggestion,
+                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
     }
 
     @Test
@@ -972,7 +922,7 @@
                 .verifyTimeZoneNotChanged();
 
         assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
-                manualSuggestion, telephonySuggestion, null /* expectedGeoSuggestion */,
+                manualSuggestion, telephonySuggestion, geolocationTimeZoneSuggestion,
                 MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
 
         // Update the config and confirm that the config metrics state updates also.
@@ -982,16 +932,9 @@
                 .setAutoDetectionEnabled(true)
                 .setGeoDetectionEnabled(true)
                 .build();
-        script.simulateUpdateConfiguration(USER_ID, configUpdate, true /* expectedResult */)
-                .verifyConfigurationChangedAndReset(expectedInternalConfig)
-                .verifyTimeZoneNotChanged();
-        assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
-                manualSuggestion, telephonySuggestion, null  /* expectedGeoSuggestion */,
-                MetricsTimeZoneDetectorState.DETECTION_MODE_GEO);
 
-        // Now simulate a geo suggestion and confirm it is used and reported in the metrics too.
         expectedDeviceTimeZoneId = geolocationTimeZoneSuggestion.getZoneIds().get(0);
-        script.simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion)
+        script.simulateUpdateConfiguration(USER_ID, configUpdate, true /* expectedResult */)
                 .verifyTimeZoneChangedAndReset(expectedDeviceTimeZoneId);
         assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
                 manualSuggestion, telephonySuggestion, geolocationTimeZoneSuggestion,
@@ -1254,6 +1197,14 @@
             return this;
         }
 
+        Script verifyTimeZoneChangedAndReset(GeolocationTimeZoneSuggestion suggestion) {
+            assertEquals("Only use this method with unambiguous geo suggestions",
+                    1, suggestion.getZoneIds().size());
+            mFakeEnvironment.assertTimeZoneChangedTo(suggestion.getZoneIds().get(0));
+            mFakeEnvironment.commitAllChanges();
+            return this;
+        }
+
         /**
          * Verifies that the configuration has been changed to the expected value.
          */
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index 1ff67240..00bc546 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -150,11 +150,12 @@
 
     }
 
-    private void pressKey(long eventTime, int keyCode, long pressTime) {
-        pressKey(eventTime, keyCode, pressTime, true /* interactive */);
+    private void pressKey(int keyCode, long pressTime) {
+        pressKey(keyCode, pressTime, true /* interactive */);
     }
 
-    private void pressKey(long eventTime, int keyCode, long pressTime, boolean interactive) {
+    private void pressKey(int keyCode, long pressTime, boolean interactive) {
+        long eventTime = SystemClock.uptimeMillis();
         final KeyEvent keyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN,
                 keyCode, 0 /* repeat */, 0 /* metaState */);
         mDetector.interceptKey(keyDown, interactive);
@@ -175,54 +176,48 @@
 
     @Test
     public void testShortPress() throws InterruptedException {
-        final long eventTime = SystemClock.uptimeMillis();
-        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
+        pressKey(KEYCODE_POWER, 0 /* pressTime */);
         assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
     }
 
     @Test
     public void testLongPress() throws InterruptedException {
-        final long eventTime = SystemClock.uptimeMillis();
-        pressKey(eventTime, KEYCODE_POWER, mLongPressTime);
+        pressKey(KEYCODE_POWER, mLongPressTime);
         assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
     }
 
     @Test
     public void testVeryLongPress() throws InterruptedException {
-        final long eventTime = SystemClock.uptimeMillis();
-        pressKey(eventTime, KEYCODE_POWER, mVeryLongPressTime);
+        pressKey(KEYCODE_POWER, mVeryLongPressTime);
         assertTrue(mVeryLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
     }
 
     @Test
     public void testMultiPress() throws InterruptedException {
-        final long eventTime = SystemClock.uptimeMillis();
         // Double presses.
         mExpectedMultiPressCount = 2;
-        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
-        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
+        pressKey(KEYCODE_POWER, 0 /* pressTime */);
+        pressKey(KEYCODE_POWER, 0 /* pressTime */);
         assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
 
         // Triple presses.
         mExpectedMultiPressCount = 3;
         mMultiPressed = new CountDownLatch(1);
-        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
-        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
-        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
+        pressKey(KEYCODE_POWER, 0 /* pressTime */);
+        pressKey(KEYCODE_POWER, 0 /* pressTime */);
+        pressKey(KEYCODE_POWER, 0 /* pressTime */);
         assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
     }
 
     @Test
     public void testNonInteractive() throws InterruptedException {
-        long eventTime = SystemClock.uptimeMillis();
         // Disallow short press behavior from non interactive.
         mAllowNonInteractiveForPress = false;
-        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */, false /* interactive */);
+        pressKey(KEYCODE_POWER, 0 /* pressTime */, false /* interactive */);
         assertFalse(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
 
         // Allow long press behavior from non interactive.
-        eventTime = SystemClock.uptimeMillis();
-        pressKey(eventTime, KEYCODE_POWER, mLongPressTime, false /* interactive */);
+        pressKey(KEYCODE_POWER, mLongPressTime, false /* interactive */);
         assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
     }
 
@@ -238,9 +233,8 @@
             for (int i = 0; i < 100; i++) {
                 mShortPressed = new CountDownLatch(2);
                 newHandler.runWithScissors(() -> {
-                    final long eventTime = SystemClock.uptimeMillis();
-                    pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
-                    pressKey(eventTime, KEYCODE_BACK, 0 /* pressTime */);
+                    pressKey(KEYCODE_POWER, 0 /* pressTime */);
+                    pressKey(KEYCODE_BACK, 0 /* pressTime */);
                 }, mWaitTimeout);
                 assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
             }
@@ -261,15 +255,13 @@
                 mMultiPressed = new CountDownLatch(1);
                 mShortPressed = new CountDownLatch(1);
                 newHandler.runWithScissors(() -> {
-                    final long eventTime = SystemClock.uptimeMillis();
-                    pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
-                    pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
+                    pressKey(KEYCODE_POWER, 0 /* pressTime */);
+                    pressKey(KEYCODE_POWER, 0 /* pressTime */);
                 }, mWaitTimeout);
                 assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
 
                 newHandler.runWithScissors(() -> {
-                    final long eventTime = SystemClock.uptimeMillis();
-                    pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
+                    pressKey(KEYCODE_POWER, 0 /* pressTime */);
                 }, mWaitTimeout);
                 assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
             }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 589f913..74cff10 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -94,6 +94,20 @@
     }
 
     @Test
+    public void testAddWindow_assignsLayers() {
+        final TestWindowToken token1 = createTestWindowToken(0, mDisplayContent);
+        final TestWindowToken token2 = createTestWindowToken(0, mDisplayContent);
+        final WindowState window1 = createWindow(null, TYPE_STATUS_BAR, token1, "window1");
+        final WindowState window2 = createWindow(null, TYPE_STATUS_BAR, token2, "window2");
+
+        token1.addWindow(window1);
+        token2.addWindow(window2);
+
+        assertEquals(token1.getLastLayer(), 0);
+        assertEquals(token2.getLastLayer(), 1);
+    }
+
+    @Test
     public void testChildRemoval() {
         final DisplayContent dc = mDisplayContent;
         final TestWindowToken token = createTestWindowToken(0, dc);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2dfa9a45..f700d79 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5984,7 +5984,7 @@
         sDefaults.putString(KEY_CARRIER_PROVISIONING_APP_STRING, "");
         sDefaults.putBoolean(KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL, false);
         sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false);
-        sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, false);
+        sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, true);
         sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false);
     }
 
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 63a7acf..d6d6775 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -222,6 +222,11 @@
     private boolean mAreUiccApplicationsEnabled = true;
 
     /**
+     * The port index of the Uicc card.
+     */
+    private final int mPortIndex;
+
+    /**
      * Public copy constructor.
      * @hide
      */
@@ -274,6 +279,22 @@
             int carrierId, int profileClass, int subType, @Nullable String groupOwner,
             @Nullable UiccAccessRule[] carrierConfigAccessRules,
             boolean areUiccApplicationsEnabled) {
+        this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString,
+                cardId, isOpportunistic, groupUUID, isGroupDisabled, carrierId, profileClass,
+                subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, 0);
+    }
+    /**
+     * @hide
+     */
+    public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+            CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+            Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
+            @Nullable UiccAccessRule[] nativeAccessRules, String cardString, int cardId,
+            boolean isOpportunistic, @Nullable String groupUUID, boolean isGroupDisabled,
+            int carrierId, int profileClass, int subType, @Nullable String groupOwner,
+            @Nullable UiccAccessRule[] carrierConfigAccessRules,
+            boolean areUiccApplicationsEnabled, int portIndex) {
         this.mId = id;
         this.mIccId = iccId;
         this.mSimSlotIndex = simSlotIndex;
@@ -300,8 +321,8 @@
         this.mGroupOwner = groupOwner;
         this.mCarrierConfigAccessRules = carrierConfigAccessRules;
         this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
+        this.mPortIndex = portIndex;
     }
-
     /**
      * @return the subscription ID.
      */
@@ -737,6 +758,14 @@
     public int getCardId() {
         return this.mCardId;
     }
+    /**
+     * Returns the port index of the SIM card which contains the subscription.
+     *
+     * @return the portIndex
+     */
+    public int getPortIndex() {
+        return this.mPortIndex;
+    }
 
     /**
      * Set whether the subscription's group is disabled.
@@ -783,6 +812,7 @@
             UiccAccessRule[] nativeAccessRules = source.createTypedArray(UiccAccessRule.CREATOR);
             String cardString = source.readString();
             int cardId = source.readInt();
+            int portId = source.readInt();
             boolean isOpportunistic = source.readBoolean();
             String groupUUID = source.readString();
             boolean isGroupDisabled = source.readBoolean();
@@ -800,7 +830,7 @@
                     carrierName, nameSource, iconTint, number, dataRoaming, /* icon= */ null,
                     mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, cardId,
                     isOpportunistic, groupUUID, isGroupDisabled, carrierid, profileClass, subType,
-                    groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled);
+                    groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, portId);
             info.setAssociatedPlmns(ehplmns, hplmns);
             return info;
         }
@@ -830,6 +860,7 @@
         dest.writeTypedArray(mNativeAccessRules, flags);
         dest.writeString(mCardString);
         dest.writeInt(mCardId);
+        dest.writeInt(mPortIndex);
         dest.writeBoolean(mIsOpportunistic);
         dest.writeString(mGroupUUID == null ? null : mGroupUUID.toString());
         dest.writeBoolean(mIsGroupDisabled);
@@ -876,6 +907,7 @@
                 + " mnc=" + mMnc + " countryIso=" + mCountryIso + " isEmbedded=" + mIsEmbedded
                 + " nativeAccessRules=" + Arrays.toString(mNativeAccessRules)
                 + " cardString=" + cardStringToPrint + " cardId=" + mCardId
+                + " portIndex=" + mPortIndex
                 + " isOpportunistic=" + mIsOpportunistic + " groupUUID=" + mGroupUUID
                 + " isGroupDisabled=" + mIsGroupDisabled
                 + " profileClass=" + mProfileClass
@@ -892,7 +924,7 @@
         return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
                 mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc, mCountryIso, mCardString,
                 mCardId, mDisplayName, mCarrierName, mNativeAccessRules, mIsGroupDisabled,
-                mCarrierId, mProfileClass, mGroupOwner, mAreUiccApplicationsEnabled);
+                mCarrierId, mProfileClass, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex);
     }
 
     @Override
@@ -925,6 +957,7 @@
                 && Objects.equals(mCountryIso, toCompare.mCountryIso)
                 && Objects.equals(mCardString, toCompare.mCardString)
                 && Objects.equals(mCardId, toCompare.mCardId)
+                && mPortIndex == toCompare.mPortIndex
                 && Objects.equals(mGroupOwner, toCompare.mGroupOwner)
                 && TextUtils.equals(mDisplayName, toCompare.mDisplayName)
                 && TextUtils.equals(mCarrierName, toCompare.mCarrierName)
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d5315ac..1fab89e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1128,6 +1128,52 @@
      */
     public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
 
+    /**
+     * A source of phone number: the EF-MSISDN (see 3GPP TS 31.102),
+     * or EF-MDN for CDMA (see 3GPP2 C.P0065-B), from UICC application.
+     *
+     * <p>The availability and a of the number depends on the carrier.
+     * The number may be updated by over-the-air update to UICC applications
+     * from the carrier, or by other means with physical access to the SIM.
+     */
+    public static final int PHONE_NUMBER_SOURCE_UICC = 1;
+
+    /**
+     * A source of phone number: provided by an app that has carrier privilege.
+     *
+     * <p>The number is intended to be set by a carrier app knowing the correct number
+     * which is, for example, different from the number in {@link #PHONE_NUMBER_SOURCE_UICC UICC}
+     * for some reason.
+     * The number is not available until a carrier app sets one via
+     * {@link #setCarrierPhoneNumber(int, String)}.
+     * The app can update the number with the same API should the number change.
+     */
+    public static final int PHONE_NUMBER_SOURCE_CARRIER = 2;
+
+    /**
+     * A source of phone number: provided by IMS (IP Multimedia Subsystem) implementation.
+     * When IMS service is registered (as indicated by
+     * {@link android.telephony.ims.RegistrationManager.RegistrationCallback#onRegistered(int)})
+     * the IMS implementation may return P-Associated-Uri SIP headers (RFC 3455). The URIs
+     * are the user’s public user identities known to the network (see 3GPP TS 24.229 5.4.1.2),
+     * and the phone number is typically one of them (see “global number” in 3GPP TS 23.003 13.4).
+     *
+     * <p>This source provides the phone number from the last IMS registration.
+     * IMS registration may happen on every device reboot or other network condition changes.
+     * The number will be updated should the associated URI change after an IMS registration.
+     */
+    public static final int PHONE_NUMBER_SOURCE_IMS = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"PHONE_NUMBER_SOURCE"},
+            value = {
+                    PHONE_NUMBER_SOURCE_UICC,
+                    PHONE_NUMBER_SOURCE_CARRIER,
+                    PHONE_NUMBER_SOURCE_IMS,
+            })
+    public @interface PhoneNumberSource {}
+
     private final Context mContext;
 
     // Cache of Resource that has been created in getResourcesForSubId. Key is a Pair containing
@@ -3763,4 +3809,132 @@
                 RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
                 null, bundle);
     }
+
+    /**
+     * Returns the phone number for the given {@code subId} and {@code source},
+     * or an empty string if not available.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS}, or
+     * READ_PRIVILEGED_PHONE_STATE permission (can only be granted to apps preloaded on device),
+     * or that the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
+     *                       for the default one.
+     * @param source the source of the phone number, one of the PHONE_NUMBER_SOURCE_* constants.
+     * @return the phone number, or an empty string if not available.
+     * @throws IllegalArgumentException if {@code source} is invalid.
+     * @throws IllegalStateException if the telephony process is not currently available.
+     * @throws SecurityException if the caller doesn't have permissions required.
+     * @see #PHONE_NUMBER_SOURCE_UICC
+     * @see #PHONE_NUMBER_SOURCE_CARRIER
+     * @see #PHONE_NUMBER_SOURCE_IMS
+     */
+    @SuppressAutoDoc // No support for carrier privileges (b/72967236)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PHONE_NUMBERS,
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+    })
+    @NonNull
+    public String getPhoneNumber(int subscriptionId, @PhoneNumberSource int source) {
+        if (subscriptionId == DEFAULT_SUBSCRIPTION_ID) {
+            subscriptionId = getDefaultSubscriptionId();
+        }
+        if (source != PHONE_NUMBER_SOURCE_UICC
+                && source != PHONE_NUMBER_SOURCE_CARRIER
+                && source != PHONE_NUMBER_SOURCE_IMS) {
+            throw new IllegalArgumentException("invalid source " + source);
+        }
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                return iSub.getPhoneNumber(subscriptionId, source,
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
+            } else {
+                throw new IllegalStateException("subscription service unavailable.");
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Returns the phone number for the given {@code subId}, or an empty string if
+     * not available.
+     *
+     * <p>This API is built up on {@link #getPhoneNumber(int, int)}, but picks
+     * from available sources in the following order: {@link #PHONE_NUMBER_SOURCE_CARRIER}
+     * > {@link #PHONE_NUMBER_SOURCE_UICC} > {@link #PHONE_NUMBER_SOURCE_IMS}.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS}, or
+     * READ_PRIVILEGED_PHONE_STATE permission (can only be granted to apps preloaded on device),
+     * or that the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
+     *                       for the default one.
+     * @return the phone number, or an empty string if not available.
+     * @throws IllegalStateException if the telephony process is not currently available.
+     * @throws SecurityException if the caller doesn't have permissions required.
+     * @see #getPhoneNumber(int, int)
+     */
+    @SuppressAutoDoc // No support for carrier privileges (b/72967236)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PHONE_NUMBERS,
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+    })
+    @NonNull
+    public String getPhoneNumber(int subscriptionId) {
+        if (subscriptionId == DEFAULT_SUBSCRIPTION_ID) {
+            subscriptionId = getDefaultSubscriptionId();
+        }
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                return iSub.getPhoneNumberFromFirstAvailableSource(subscriptionId,
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
+            } else {
+                throw new IllegalStateException("subscription service unavailable.");
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Sets the phone number for the given {@code subId} for source
+     * {@link #PHONE_NUMBER_SOURCE_CARRIER carrier}.
+     * Sets an empty string to remove the previously set phone number.
+     *
+     * <p>Requires Permission: the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
+     *                       for the default one.
+     * @param number the phone number, or an empty string to remove the previously set number.
+     * @throws IllegalStateException if the telephony process is not currently available.
+     * @throws NullPointerException if {@code number} is {@code null}.
+     * @throws SecurityException if the caller doesn't have permissions required.
+     */
+    public void setCarrierPhoneNumber(int subscriptionId, @NonNull String number) {
+        if (subscriptionId == DEFAULT_SUBSCRIPTION_ID) {
+            subscriptionId = getDefaultSubscriptionId();
+        }
+        if (number == null) {
+            throw new NullPointerException("invalid number null");
+        }
+        try {
+            ISub iSub = TelephonyManager.getSubscriptionService();
+            if (iSub != null) {
+                iSub.setPhoneNumber(subscriptionId, PHONE_NUMBER_SOURCE_CARRIER, number,
+                        mContext.getOpPackageName(), mContext.getAttributionTag());
+            } else {
+                throw new IllegalStateException("subscription service unavailable.");
+            }
+        } catch (RemoteException ex) {
+            throw ex.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 50f2abd..04e7b7c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -131,6 +131,8 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -144,6 +146,7 @@
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
+
 /**
  * Provides access to information about the telephony services on
  * the device. Applications can use the methods in this class to
@@ -3931,8 +3934,8 @@
      * <p>
      * If the caller has carrier priviliges on any active subscription, then they have permission to
      * get simple information like the card ID ({@link UiccCardInfo#getCardId()}), whether the card
-     * is an eUICC ({@link UiccCardInfo#isEuicc()}), and the slot index where the card is inserted
-     * ({@link UiccCardInfo#getSlotIndex()}).
+     * is an eUICC ({@link UiccCardInfo#isEuicc()}), and the physical slot index where the card is
+     * inserted ({@link UiccCardInfo#getPhysicalSlotIndex()}.
      * <p>
      * To get private information such as the EID ({@link UiccCardInfo#getEid()}) or ICCID
      * ({@link UiccCardInfo#getIccId()}), the caller must have carrier priviliges on that specific
@@ -3976,7 +3979,7 @@
             if (telephony == null) {
                 return null;
             }
-            return telephony.getUiccSlotsInfo();
+            return telephony.getUiccSlotsInfo(mContext.getOpPackageName());
         } catch (RemoteException e) {
             return null;
         }
@@ -4009,8 +4012,13 @@
      *        size should be same as {@link #getUiccSlotsInfo()}.
      * @return boolean Return true if the switch succeeds, false if the switch fails.
      * @hide
+     * @deprecated {@link #setSimSlotMapping(Collection, Executor, Consumer)}
      */
+     // TODO: once integrating the HAL changes we can  convert int[] to List<UiccSlotMapping> and
+     // converge API's in ITelephony.aidl and PhoneInterfaceManager
+
     @SystemApi
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean switchSlots(int[] physicalSlots) {
         try {
@@ -4025,6 +4033,109 @@
     }
 
     /**
+     * @param slotMapping Logical to physical slot and port mapping.
+     * @return {@code true} if slotMapping is valid.
+     * @return {@code false} if slotMapping is invalid.
+     *
+     * slotMapping is invalid if there are different entries (physical slot + port) mapping to the
+     * same logical slot or if there are same {physical slot + port} mapping to the different
+     * logical slot
+     * @hide
+     */
+    private static boolean isSlotMappingValid(@NonNull Collection<UiccSlotMapping> slotMapping) {
+        // Grouping the collection by logicalSlotIndex, finding different entries mapping to the
+        // same logical slot
+        Map<Integer, List<UiccSlotMapping>> slotMappingInfo = slotMapping.stream().collect(
+                Collectors.groupingBy(UiccSlotMapping::getLogicalSlotIndex));
+        for (Map.Entry<Integer, List<UiccSlotMapping>> entry : slotMappingInfo.entrySet()) {
+            List<UiccSlotMapping> logicalSlotMap = entry.getValue();
+            if (logicalSlotMap.size() > 1) {
+                // duplicate logicalSlotIndex found
+                return false;
+            }
+        }
+
+        // Grouping the collection by physical slot and port, finding same entries mapping to the
+        // different logical slot
+        Map<List<Integer>, List<UiccSlotMapping>> slotMapInfos = slotMapping.stream().collect(
+                Collectors.groupingBy(
+                        slot -> Arrays.asList(slot.getPhysicalSlotIndex(), slot.getPortIndex())));
+        for (Map.Entry<List<Integer>, List<UiccSlotMapping>> entry : slotMapInfos.entrySet()) {
+            List<UiccSlotMapping> portAndPhysicalSlotList = entry.getValue();
+            if (portAndPhysicalSlotList.size() > 1) {
+                // duplicate pair of portIndex and physicalSlotIndex found
+                return false;
+            }
+        }
+        return true;
+    }
+    /**
+     * Maps the logical slots to physical slots and ports. Mapping is specified from
+     * {@link UiccSlotMapping} which consist of both physical slot index and port index.
+     * Logical slot is the slot that is seen by modem. Physical slot is the actual physical slot.
+     * Port index is the index (enumerated value) for the associated port available on the SIM.
+     * Each physical slot can have multiple ports if multi-enabled profile(MEP) is supported.
+     *
+     * Example: no. of logical slots 1 and physical slots 2 do not support MEP, each physical slot
+     * has one port:
+     * The only logical slot (index 0) can be mapped to first physical slot (value 0), port(index
+     * 0) or
+     * second physical slot(value 1), port (index 0), while the other physical slot remains unmapped
+     * and inactive.
+     * slotMapping[0] = UiccSlotMapping{0 //logical slot, 0 //physical slot//, 0 //port//}
+     * slotMapping[0] = UiccSlotMapping{1 // logical slot, 1 //physical slot//, 0 //port//}
+     *
+     * Example no. of logical slots 2 and physical slots 2 supports MEP with 2 ports available:
+     * Each logical slot must be mapped to a port (physical slot and port combination).
+     * First logical slot (index 0) can be mapped to physical slot 1 and the second logical slot
+     * can be mapped to either port from physical slot 2.
+     *
+     * slotMapping[0] = UiccSlotMapping{0, 0, 0} and slotMapping[1] = UiccSlotMapping{1, 0, 0} or
+     * slotMapping[0] = UiccSlotMapping{0, 0, 0} and slotMapping[1] = UiccSlotMapping{1, 1, 1}
+     *
+     * or the other way around, the second logical slot(index 1) can be mapped to physical slot 1
+     * and the first logical slot can be mapped to either port from physical slot 2.
+     *
+     * slotMapping[1] = UiccSlotMapping{0, 0, 0} and slotMapping[0] = UiccSlotMapping{1, 0, 0} or
+     * slotMapping[1] = UiccSlotMapping{0, 0, 0} and slotMapping[0] = UiccSlotMapping{1, 1, 1}
+     *
+     * another possible mapping is each logical slot maps to each port of physical slot 2 and there
+     * is no active logical modem mapped to physical slot 1.
+     *
+     * slotMapping[0] = UiccSlotMapping{1, 0, 0} and slotMapping[1] = UiccSlotMapping{1, 1, 1} or
+     * slotMapping[0] = UiccSlotMapping{1, 1, 1} and slotMapping[1] = UiccSlotMapping{1, 0, 0}
+     *
+     * @param slotMapping Logical to physical slot and port mapping.
+     * @throws IllegalStateException if telephony service is null or slot mapping was sent when the
+     *         radio in middle of a silent restart or other invalid states to handle the command
+     * @throws IllegalArgumentException if the caller passes in an invalid collection of
+     *         UiccSlotMapping like duplicate data, etc
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setSimSlotMapping(@NonNull Collection<UiccSlotMapping> slotMapping) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                if (isSlotMappingValid(slotMapping)) {
+                    boolean result = telephony.setSimSlotMapping(new ArrayList(slotMapping));
+                    if (!result) {
+                        throw new IllegalStateException("setSimSlotMapping has failed");
+                    }
+                } else {
+                    throw new IllegalArgumentException("Duplicate UiccSlotMapping data found");
+                }
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
      * Get the mapping from logical slots to physical slots. The key of the map is the logical slot
      * id and the value is the physical slots id mapped to this logical slot id.
      *
@@ -4041,7 +4152,7 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                int[] slotMappingArray = telephony.getSlotsMapping();
+                int[] slotMappingArray = telephony.getSlotsMapping(mContext.getOpPackageName());
                 for (int i = 0; i < slotMappingArray.length; i++) {
                     slotMapping.put(i, slotMappingArray[i]);
                 }
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index b4389203..7dfe450 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -20,21 +20,27 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 /**
  * The UiccCardInfo represents information about a currently inserted UICC or embedded eUICC.
  */
 public final class UiccCardInfo implements Parcelable {
-
     private final boolean mIsEuicc;
     private final int mCardId;
     private final String mEid;
     private final String mIccId;
-    private final int mSlotIndex;
+    private final int mPhysicalSlotIndex;
     private final boolean mIsRemovable;
+    private final boolean mIsMultipleEnabledProfilesSupported;
+    private final List<UiccPortInfo> mPortList;
+    private boolean mIccIdAccessRestricted = false;
 
-    public static final @android.annotation.NonNull Creator<UiccCardInfo> CREATOR = new Creator<UiccCardInfo>() {
+    public static final @NonNull Creator<UiccCardInfo> CREATOR = new Creator<UiccCardInfo>() {
         @Override
         public UiccCardInfo createFromParcel(Parcel in) {
             return new UiccCardInfo(in);
@@ -47,22 +53,29 @@
     };
 
     private UiccCardInfo(Parcel in) {
-        mIsEuicc = in.readByte() != 0;
+        mIsEuicc = in.readBoolean();
         mCardId = in.readInt();
-        mEid = in.readString();
-        mIccId = in.readString();
-        mSlotIndex = in.readInt();
-        mIsRemovable = in.readByte() != 0;
+        mEid = in.readString8();
+        mIccId = in.readString8();
+        mPhysicalSlotIndex = in.readInt();
+        mIsRemovable = in.readBoolean();
+        mIsMultipleEnabledProfilesSupported = in.readBoolean();
+        mPortList = new ArrayList<UiccPortInfo>();
+        in.readTypedList(mPortList, UiccPortInfo.CREATOR);
+        mIccIdAccessRestricted = in.readBoolean();
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeByte((byte) (mIsEuicc ? 1 : 0));
+        dest.writeBoolean(mIsEuicc);
         dest.writeInt(mCardId);
-        dest.writeString(mEid);
-        dest.writeString(mIccId);
-        dest.writeInt(mSlotIndex);
-        dest.writeByte((byte) (mIsRemovable ? 1 : 0));
+        dest.writeString8(mEid);
+        dest.writeString8(mIccId);
+        dest.writeInt(mPhysicalSlotIndex);
+        dest.writeBoolean(mIsRemovable);
+        dest.writeBoolean(mIsMultipleEnabledProfilesSupported);
+        dest.writeTypedList(mPortList, flags);
+        dest.writeBoolean(mIccIdAccessRestricted);
     }
 
     @Override
@@ -71,20 +84,34 @@
     }
 
     /**
+     * Construct a UiccCardInfo.
+     *
+     * @param isEuicc is a flag to check is eUICC or not
+     * @param cardId is unique ID used to identify a UiccCard.
+     * @param eid is unique eUICC Identifier
+     * @param physicalSlotIndex is unique index referring to a physical SIM slot.
+     * @param isRemovable is a flag to check is removable or embedded
+     * @param isMultipleEnabledProfilesSupported is a flag to check is MEP enabled or not
+     * @param portList has the information regarding port, ICCID and its active status
+     *
      * @hide
      */
-    public UiccCardInfo(boolean isEuicc, int cardId, String eid, String iccId, int slotIndex,
-            boolean isRemovable) {
+    public UiccCardInfo(boolean isEuicc, int cardId, String eid, int physicalSlotIndex,
+            boolean isRemovable, boolean isMultipleEnabledProfilesSupported,
+            @NonNull List<UiccPortInfo> portList) {
         this.mIsEuicc = isEuicc;
         this.mCardId = cardId;
         this.mEid = eid;
-        this.mIccId = iccId;
-        this.mSlotIndex = slotIndex;
+        this.mIccId = null;
+        this.mPhysicalSlotIndex = physicalSlotIndex;
         this.mIsRemovable = isRemovable;
+        this.mIsMultipleEnabledProfilesSupported = isMultipleEnabledProfilesSupported;
+        this.mPortList = portList;
     }
 
     /**
      * Return whether the UICC is an eUICC.
+     *
      * @return true if the UICC is an eUICC.
      */
     public boolean isEuicc() {
@@ -119,40 +146,83 @@
      * <p>
      * Note that this field may be omitted if the caller does not have the correct permissions
      * (see {@link TelephonyManager#getUiccCardsInfo()}).
+     *
+     * @deprecated with support for MEP(multiple enabled profile), a SIM card can have more than one
+     * ICCID active at the same time.Instead use {@link UiccPortInfo#getIccId()} to retrieve ICCID.
+     * To find {@link UiccPortInfo} use {@link UiccCardInfo#getPorts()}
+     *
+     * @throws UnsupportedOperationException if the calling app's target SDK is T and beyond.
      */
     @Nullable
+    @Deprecated
     public String getIccId() {
-        return mIccId;
+        if (mIccIdAccessRestricted) {
+            throw new UnsupportedOperationException("getIccId from UiccPortInfo");
+        }
+        //always return ICCID from first port.
+        return getPorts().stream().findFirst().get().getIccId();
     }
 
     /**
      * Gets the slot index for the slot that the UICC is currently inserted in.
+     *
+     * @deprecated use {@link #getPhysicalSlotIndex()}
      */
+    @Deprecated
     public int getSlotIndex() {
-        return mSlotIndex;
+        return mPhysicalSlotIndex;
     }
 
     /**
-     * Returns a copy of the UiccCardinfo with the EID and ICCID set to null. These values are
-     * generally private and require carrier privileges to view.
-     *
-     * @hide
+     * Gets the physical slot index for the slot that the UICC is currently inserted in.
      */
-    @NonNull
-    public UiccCardInfo getUnprivileged() {
-        return new UiccCardInfo(mIsEuicc, mCardId, null, null, mSlotIndex, mIsRemovable);
+    public int getPhysicalSlotIndex() {
+        return mPhysicalSlotIndex;
     }
 
     /**
      * Return whether the UICC or eUICC is removable.
      * <p>
      * UICCs are generally removable, but eUICCs may be removable or built in to the device.
+     *
      * @return true if the UICC or eUICC is removable
      */
     public boolean isRemovable() {
         return mIsRemovable;
     }
 
+    /*
+     * Whether the UICC card supports multiple enable profile(MEP)
+     * UICCs are generally MEP disabled, there can be only one active profile on the physical
+     * sim card.
+     *
+     * @return {@code true} if the eUICC is supporting multiple enabled profile(MEP).
+     */
+    public boolean isMultipleEnabledProfilesSupported() {
+        return mIsMultipleEnabledProfilesSupported;
+    }
+
+    /**
+     * Get information regarding port, ICCID and its active status.
+     *
+     * @return Collection of {@link UiccPortInfo}
+     */
+    public @NonNull Collection<UiccPortInfo> getPorts() {
+        return Collections.unmodifiableList(mPortList);
+    }
+
+    /**
+     * if the flag is set to {@code true} the calling app is not allowed to access deprecated
+     * {@link #getIccId()}
+     * @param iccIdAccessRestricted is the flag to check if app is allowed to access ICCID
+     *
+     * @hide
+     */
+    public void setIccIdAccessRestricted(boolean iccIdAccessRestricted) {
+        this.mIccIdAccessRestricted = iccIdAccessRestricted;
+    }
+
+
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
@@ -167,13 +237,16 @@
                 && (mCardId == that.mCardId)
                 && (Objects.equals(mEid, that.mEid))
                 && (Objects.equals(mIccId, that.mIccId))
-                && (mSlotIndex == that.mSlotIndex)
-                && (mIsRemovable == that.mIsRemovable));
+                && (mPhysicalSlotIndex == that.mPhysicalSlotIndex)
+                && (mIsRemovable == that.mIsRemovable)
+                && (mIsMultipleEnabledProfilesSupported == that.mIsMultipleEnabledProfilesSupported)
+                && (Objects.equals(mPortList, that.mPortList)));
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mSlotIndex, mIsRemovable);
+        return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mPhysicalSlotIndex, mIsRemovable,
+                mIsMultipleEnabledProfilesSupported, mPortList);
     }
 
     @Override
@@ -185,11 +258,17 @@
                 + ", mEid="
                 + mEid
                 + ", mIccId="
-                + mIccId
-                + ", mSlotIndex="
-                + mSlotIndex
+                + SubscriptionInfo.givePrintableIccid(mIccId)
+                + ", mPhysicalSlotIndex="
+                + mPhysicalSlotIndex
                 + ", mIsRemovable="
                 + mIsRemovable
+                + ", mIsMultipleEnabledProfilesSupported="
+                + mIsMultipleEnabledProfilesSupported
+                + ", mPortList="
+                + mPortList
+                + ", mIccIdAccessRestricted="
+                + mIccIdAccessRestricted
                 + ")";
     }
-}
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/UiccPortInfo.aidl b/telephony/java/android/telephony/UiccPortInfo.aidl
new file mode 100644
index 0000000..7fff4ba
--- /dev/null
+++ b/telephony/java/android/telephony/UiccPortInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+parcelable UiccPortInfo;
diff --git a/telephony/java/android/telephony/UiccPortInfo.java b/telephony/java/android/telephony/UiccPortInfo.java
new file mode 100644
index 0000000..d1838c0
--- /dev/null
+++ b/telephony/java/android/telephony/UiccPortInfo.java
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+package android.telephony;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * UiccPortInfo class represents information about a single port contained on {@link UiccCardInfo}.
+ * Per GSMA SGP.22 V3.0, a port is a logical entity to which an active UICC profile can be bound on
+ * a UICC card. If UICC supports 2 ports, then the port index is numbered 0,1.
+ * Each port index is unique within an UICC, but not necessarily unique across UICC’s.
+ * For UICC's does not support MEP(Multi-enabled profile), just return the default port index 0.
+ */
+public final class UiccPortInfo implements Parcelable{
+    private final String mIccId;
+    private final int mPortIndex;
+    private final int mLogicalSlotIndex;
+    private final boolean mIsActive;
+
+    /**
+     * A redacted String if caller does not have permission to read ICCID.
+     */
+    public static final String ICCID_REDACTED = "FFFFFFFFFFFFFFFFFFFF";
+
+    public static final @NonNull Creator<UiccPortInfo> CREATOR =
+            new Creator<UiccPortInfo>() {
+                @Override
+                public UiccPortInfo createFromParcel(Parcel in) {
+                    return new UiccPortInfo(in);
+                }
+                @Override
+                public UiccPortInfo[] newArray(int size) {
+                    return new UiccPortInfo[size];
+                }
+            };
+
+    private UiccPortInfo(Parcel in) {
+        mIccId = in.readString8();
+        mPortIndex = in.readInt();
+        mLogicalSlotIndex = in.readInt();
+        mIsActive = in.readBoolean();
+    }
+
+    @Override
+    public void writeToParcel(@Nullable Parcel dest, int flags) {
+        dest.writeString8(mIccId);
+        dest.writeInt(mPortIndex);
+        dest.writeInt(mLogicalSlotIndex);
+        dest.writeBoolean(mIsActive);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Construct a UiccPortInfo.
+     *
+     * @param iccId The ICCID of the profile.
+     * @param portIndex The port index is an enumeration of the ports available on the UICC.
+     * @param logicalSlotIndex is unique index referring to a logical SIM slot.
+     * @param isActive is flag to check if port was tied to a modem stack.
+     *
+     * @hide
+     */
+    public UiccPortInfo(String iccId, int portIndex, int logicalSlotIndex, boolean isActive) {
+        this.mIccId = iccId;
+        this.mPortIndex = portIndex;
+        this.mLogicalSlotIndex = logicalSlotIndex;
+        this.mIsActive = isActive;
+    }
+
+    /**
+     * Get the ICCID of the profile associated with this port.
+     * If this port is not {@link #isActive()}, returns {@code null}.
+     * If the caller does not have access to the ICCID for this port, it will be redacted and
+     * {@link #ICCID_REDACTED} will be returned.
+     */
+    public @Nullable String getIccId() {
+        return mIccId;
+    }
+
+    /**
+     * The port index is an enumeration of the ports available on the UICC.
+     * Example: if eUICC1 supports 2 ports, then the port index is numbered 0,1.
+     * Each port index is unique within an UICC, but not necessarily unique across UICC’s.
+     * For UICC's does not support MEP(Multi-enabled profile), just return the default port index 0.
+     */
+    @IntRange(from = 0)
+    public int getPortIndex() {
+        return mPortIndex;
+    }
+
+    /**
+     * @return {@code true} if port was tied to a modem stack.
+     */
+    public boolean isActive() {
+        return mIsActive;
+    }
+
+    /**
+     * Gets logical slot index for the slot that the UICC is currently attached.
+     * Logical slot index or ID: unique index referring to a logical SIM slot.
+     * Logical slot IDs start at 0 and go up depending on the number of supported active slots on
+     * a device.
+     * For example, a dual-SIM device typically has slot 0 and slot 1.
+     * If a device has multiple physical slots but only supports one active slot,
+     * it will have only the logical slot ID 0.
+     *
+     * @return the logical slot index for UICC port, if there is no logical slot index it returns
+     * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX}
+     */
+    @IntRange(from = 0)
+    public int getLogicalSlotIndex() {
+        return mLogicalSlotIndex;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        UiccPortInfo that = (UiccPortInfo) obj;
+        return (Objects.equals(mIccId, that.mIccId))
+                && (mPortIndex == that.mPortIndex)
+                && (mLogicalSlotIndex == that.mLogicalSlotIndex)
+                && (mIsActive == that.mIsActive);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIccId, mPortIndex, mLogicalSlotIndex, mIsActive);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "UiccPortInfo (isActive="
+                + mIsActive
+                + ", iccId="
+                + SubscriptionInfo.givePrintableIccid(mIccId)
+                + ", portIndex="
+                + mPortIndex
+                + ", mLogicalSlotIndex="
+                + mLogicalSlotIndex
+                + ")";
+    }
+}
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index a0e949a..2b1c8c8 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -24,6 +24,10 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -64,8 +68,10 @@
     private final int mLogicalSlotIdx;
     private final boolean mIsExtendedApduSupported;
     private final boolean mIsRemovable;
+    private final List<UiccPortInfo> mPortList;
+    private boolean mLogicalSlotAccessRestricted = false;
 
-    public static final @android.annotation.NonNull Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
+    public static final @NonNull Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
         @Override
         public UiccSlotInfo createFromParcel(Parcel in) {
             return new UiccSlotInfo(in);
@@ -78,24 +84,29 @@
     };
 
     private UiccSlotInfo(Parcel in) {
-        mIsActive = in.readByte() != 0;
-        mIsEuicc = in.readByte() != 0;
-        mCardId = in.readString();
+        mIsActive = in.readBoolean();
+        mIsEuicc = in.readBoolean();
+        mCardId = in.readString8();
         mCardStateInfo = in.readInt();
         mLogicalSlotIdx = in.readInt();
-        mIsExtendedApduSupported = in.readByte() != 0;
-        mIsRemovable = in.readByte() != 0;
+        mIsExtendedApduSupported = in.readBoolean();
+        mIsRemovable = in.readBoolean();
+        mPortList = new ArrayList<UiccPortInfo>();
+        in.readTypedList(mPortList, UiccPortInfo.CREATOR);
+        mLogicalSlotAccessRestricted = in.readBoolean();
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeByte((byte) (mIsActive ? 1 : 0));
-        dest.writeByte((byte) (mIsEuicc ? 1 : 0));
-        dest.writeString(mCardId);
+        dest.writeBoolean(mIsActive);
+        dest.writeBoolean(mIsEuicc);
+        dest.writeString8(mCardId);
         dest.writeInt(mCardStateInfo);
         dest.writeInt(mLogicalSlotIdx);
-        dest.writeByte((byte) (mIsExtendedApduSupported ? 1 : 0));
-        dest.writeByte((byte) (mIsRemovable ? 1 : 0));
+        dest.writeBoolean(mIsExtendedApduSupported);
+        dest.writeBoolean(mIsRemovable);
+        dest.writeTypedList(mPortList, flags);
+        dest.writeBoolean(mLogicalSlotAccessRestricted);
     }
 
     @Override
@@ -117,25 +128,42 @@
         this.mLogicalSlotIdx = logicalSlotIdx;
         this.mIsExtendedApduSupported = isExtendedApduSupported;
         this.mIsRemovable = false;
+        this.mPortList = null;
     }
 
     /**
+     * Construct a UiccSlotInfo.
      * @hide
      */
-    public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
-            @CardStateInfo int cardStateInfo, int logicalSlotIdx, boolean isExtendedApduSupported,
-            boolean isRemovable) {
-        this.mIsActive = isActive;
+    public UiccSlotInfo(boolean isEuicc, String cardId,
+            @CardStateInfo int cardStateInfo, boolean isExtendedApduSupported,
+            boolean isRemovable, @NonNull List<UiccPortInfo> portList) {
+        this.mIsActive = portList.get(0).isActive();
         this.mIsEuicc = isEuicc;
         this.mCardId = cardId;
         this.mCardStateInfo = cardStateInfo;
-        this.mLogicalSlotIdx = logicalSlotIdx;
+        this.mLogicalSlotIdx = portList.get(0).getLogicalSlotIndex();
         this.mIsExtendedApduSupported = isExtendedApduSupported;
         this.mIsRemovable = isRemovable;
+        this.mPortList = portList;
     }
 
+    /**
+     * @deprecated There is no longer isActive state for each slot because ports belonging
+     * to the physical slot could have different states
+     * we instead use {@link UiccPortInfo#isActive()}
+     * To get UiccPortInfo use {@link UiccSlotInfo#getPorts()}
+     *
+     * @return {@code true} if status is active.
+     * @throws UnsupportedOperationException if the calling app's target SDK is T and beyond.
+     */
+    @Deprecated
     public boolean getIsActive() {
-        return mIsActive;
+        if (mLogicalSlotAccessRestricted) {
+            throw new UnsupportedOperationException("get port status from UiccPortInfo");
+        }
+        //always return status from first port.
+        return getPorts().stream().findFirst().get().isActive();
     }
 
     public boolean getIsEuicc() {
@@ -159,8 +187,21 @@
         return mCardStateInfo;
     }
 
+    /**
+     * @deprecated There is no longer getLogicalSlotIndex
+     * There is no longer getLogicalSlotIdx as each port belonging to this physical slot could have
+     * different logical slot index. Use {@link UiccPortInfo#getLogicalSlotIndex()} instead
+     *
+     * @throws UnsupportedOperationException if the calling app's target SDK is T and beyond.
+     */
+    @Deprecated
     public int getLogicalSlotIdx() {
-        return mLogicalSlotIdx;
+        if (mLogicalSlotAccessRestricted) {
+            throw new UnsupportedOperationException("get logical slot index from UiccPortInfo");
+        }
+        //always return logical slot index from first port.
+        //portList always have at least one element.
+        return getPorts().stream().findFirst().get().getLogicalSlotIndex();
     }
 
     /**
@@ -170,16 +211,37 @@
         return mIsExtendedApduSupported;
     }
 
-   /**
+    /**
      * Return whether the UICC slot is for a removable UICC.
      * <p>
      * UICCs are generally removable, but eUICCs may be removable or built in to the device.
+     *
      * @return true if the slot is for removable UICCs
      */
     public boolean isRemovable() {
         return mIsRemovable;
     }
 
+    /**
+     * Get Information regarding port, iccid and its active status.
+     *
+     * @return Collection of {@link UiccPortInfo}
+     */
+    public @NonNull Collection<UiccPortInfo> getPorts() {
+        return Collections.unmodifiableList(mPortList);
+    }
+
+    /**
+     * Set the flag to check compatibility of the calling app's target SDK is T and beyond.
+     *
+     * @param logicalSlotAccessRestricted is the flag to check compatibility.
+     *
+     * @hide
+     */
+    public void setLogicalSlotAccessRestricted(boolean logicalSlotAccessRestricted) {
+        this.mLogicalSlotAccessRestricted = logicalSlotAccessRestricted;
+    }
+
     @Override
     public boolean equals(@Nullable Object obj) {
         if (this == obj) {
@@ -196,20 +258,14 @@
                 && (mCardStateInfo == that.mCardStateInfo)
                 && (mLogicalSlotIdx == that.mLogicalSlotIdx)
                 && (mIsExtendedApduSupported == that.mIsExtendedApduSupported)
-                && (mIsRemovable == that.mIsRemovable);
+                && (mIsRemovable == that.mIsRemovable)
+                && (Objects.equals(mPortList, that.mPortList));
     }
 
     @Override
     public int hashCode() {
-        int result = 1;
-        result = 31 * result + (mIsActive ? 1 : 0);
-        result = 31 * result + (mIsEuicc ? 1 : 0);
-        result = 31 * result + Objects.hashCode(mCardId);
-        result = 31 * result + mCardStateInfo;
-        result = 31 * result + mLogicalSlotIdx;
-        result = 31 * result + (mIsExtendedApduSupported ? 1 : 0);
-        result = 31 * result + (mIsRemovable ? 1 : 0);
-        return result;
+        return Objects.hash(mIsActive, mIsEuicc, mCardId, mCardStateInfo, mLogicalSlotIdx,
+                mIsExtendedApduSupported, mIsRemovable, mPortList);
     }
 
     @NonNull
@@ -229,6 +285,10 @@
                 + mIsExtendedApduSupported
                 + ", mIsRemovable="
                 + mIsRemovable
+                + ", mPortList="
+                + mPortList
+                + ", mLogicalSlotAccessRestricted="
+                + mLogicalSlotAccessRestricted
                 + ")";
     }
 }
diff --git a/telephony/java/android/telephony/UiccSlotMapping.aidl b/telephony/java/android/telephony/UiccSlotMapping.aidl
new file mode 100644
index 0000000..3b19499
--- /dev/null
+++ b/telephony/java/android/telephony/UiccSlotMapping.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+parcelable UiccSlotMapping;
diff --git a/telephony/java/android/telephony/UiccSlotMapping.java b/telephony/java/android/telephony/UiccSlotMapping.java
new file mode 100644
index 0000000..87e7acd
--- /dev/null
+++ b/telephony/java/android/telephony/UiccSlotMapping.java
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+package android.telephony;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * <p>Provides information for a SIM slot mapping, which establishes a unique mapping between a
+ * logical SIM slot and a physical SIM slot and port index.  A logical SIM slot represents a
+ * potentially active SIM slot, where a physical SIM slot and port index represent a hardware SIM
+ * slot and port (capable of having an active profile) which can be mapped to a logical sim slot.
+ * <p>It contains the following parameters:
+ * <ul>
+ * <li>Port index: unique index referring to a port belonging to the physical SIM slot.
+ * If the SIM does not support multiple enabled profiles, the port index is default index 0.</li>
+ * <li>Physical slot index: unique index referring to a physical SIM slot. Physical slot IDs start
+ * at 0 and go up depending on the number of physical slots on the device.
+ * This differs from the number of logical slots a device has, which corresponds to the number of
+ * active slots a device is capable of using. For example, a device which switches between dual-SIM
+ * and single-SIM mode may always have two physical slots, but in single-SIM mode it will have only
+ * one logical slot.</li>
+ * <li>Logical slot index: unique index referring to a logical SIM slot, Logical slot IDs start at 0
+ * and go up depending on the number of supported active slots on a device.
+ * For example, a dual-SIM device typically has slot 0 and slot 1. If a device has multiple physical
+ * slots but only supports one active slot, it will have only the logical slot ID 0</li>
+ * </ul>
+ *
+ * <p> This configurations tells a specific logical slot is mapped to a port from an actual physical
+ * sim slot @see <a href="https://developer.android.com/guide/topics/connectivity/telecom/telephony-ids">the Android Developer Site</a>
+ * for more information.
+ * @hide
+ */
+@SystemApi
+public final class UiccSlotMapping implements Parcelable {
+    private final int mPortIndex;
+    private final int mPhysicalSlotIndex;
+    private final int mLogicalSlotIndex;
+
+    public static final @NonNull Creator<UiccSlotMapping> CREATOR =
+            new Creator<UiccSlotMapping>() {
+        @Override
+        public UiccSlotMapping createFromParcel(Parcel in) {
+            return new UiccSlotMapping(in);
+        }
+
+        @Override
+        public UiccSlotMapping[] newArray(int size) {
+            return new UiccSlotMapping[size];
+        }
+    };
+
+    private UiccSlotMapping(Parcel in) {
+        mPortIndex = in.readInt();
+        mPhysicalSlotIndex = in.readInt();
+        mLogicalSlotIndex = in.readInt();
+    }
+
+    @Override
+    public void writeToParcel(@Nullable Parcel dest, int flags) {
+        dest.writeInt(mPortIndex);
+        dest.writeInt(mPhysicalSlotIndex);
+        dest.writeInt(mLogicalSlotIndex);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     *
+     * @param portIndex The port index is an enumeration of the ports available on the UICC.
+     * @param physicalSlotIndex is unique index referring to a physical SIM slot.
+     * @param logicalSlotIndex is unique index referring to a logical SIM slot.
+     *
+     * @hide
+     */
+    public UiccSlotMapping(int portIndex, int physicalSlotIndex, int logicalSlotIndex) {
+        this.mPortIndex = portIndex;
+        this.mPhysicalSlotIndex = physicalSlotIndex;
+        this.mLogicalSlotIndex = logicalSlotIndex;
+    }
+
+    /**
+     * Port index is the unique index referring to a port belonging to the physical SIM slot.
+     * If the SIM does not support multiple enabled profiles, the port index is default index 0.
+     *
+     * @return port index.
+     */
+    @IntRange(from = 0)
+    public int getPortIndex() {
+        return mPortIndex;
+    }
+
+    /**
+     * Gets the physical slot index for the slot that the UICC is currently inserted in.
+     *
+     * @return physical slot index which is the index of actual physical UICC slot.
+     */
+    @IntRange(from = 0)
+    public int getPhysicalSlotIndex() {
+        return mPhysicalSlotIndex;
+    }
+
+    /**
+     * Gets logical slot index for the slot that the UICC is currently attached.
+     * Logical slot index is the unique index referring to a logical slot(logical modem stack).
+     *
+     * @return logical slot index;
+     */
+    @IntRange(from = 0)
+    public int getLogicalSlotIndex() {
+        return mLogicalSlotIndex;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        UiccSlotMapping that = (UiccSlotMapping) obj;
+        return (mPortIndex == that.mPortIndex)
+                && (mPhysicalSlotIndex == that.mPhysicalSlotIndex)
+                && (mLogicalSlotIndex == that.mLogicalSlotIndex);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPortIndex, mPhysicalSlotIndex, mLogicalSlotIndex);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "UiccSlotMapping (mPortIndex="
+                + mPortIndex
+                + ", mPhysicalSlotIndex="
+                + mPhysicalSlotIndex
+                + ", mLogicalSlotIndex="
+                + mLogicalSlotIndex
+                + ")";
+    }
+}
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index e1aec0a..ab35d77 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -17,6 +17,7 @@
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.Context;
@@ -24,6 +25,7 @@
 import android.os.RemoteException;
 import android.service.euicc.EuiccProfileInfo;
 import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import com.android.internal.telephony.euicc.IAuthenticateServerCallback;
@@ -122,7 +124,6 @@
 
     /** Result code indicating the caller is not the active LPA. */
     public static final int RESULT_CALLER_NOT_ALLOWED = -3;
-
     /**
      * Callback to receive the result of an eUICC card API.
      *
@@ -220,12 +221,48 @@
      * @param refresh Whether sending the REFRESH command to modem.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code.
+     *
+     * @deprecated instead use {@link #disableProfile(String, String, int, boolean, Executor,
+     *             ResultCallback)}
      */
+    @Deprecated
     public void disableProfile(String cardId, String iccid, boolean refresh,
             @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
         try {
             getIEuiccCardController().disableProfile(mContext.getOpPackageName(), cardId, iccid,
-                    refresh, new IDisableProfileCallback.Stub() {
+                    TelephonyManager.DEFAULT_PORT_INDEX, refresh,
+                    new IDisableProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling disableProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+    /**
+     * Disables the profile of the given ICCID.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param iccid The iccid of the profile.
+     * @param portIndex the Port index is the unique index referring to a port.
+     * @param refresh Whether sending the REFRESH command to modem.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code.
+     */
+    public void disableProfile(@Nullable String cardId, @Nullable String iccid, int portIndex,
+            boolean refresh, @NonNull @CallbackExecutor Executor executor,
+            @NonNull ResultCallback<Void> callback) {
+        try {
+            getIEuiccCardController().disableProfile(mContext.getOpPackageName(), cardId, iccid,
+                    portIndex, refresh, new IDisableProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
                             final long token = Binder.clearCallingIdentity();
@@ -251,12 +288,51 @@
      * @param refresh Whether sending the REFRESH command to modem.
      * @param executor The executor through which the callback should be invoked.
      * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
+     *
+     * @deprecated instead use {@link #switchToProfile(String, String, int, boolean, Executor,
+     *             ResultCallback)}
      */
+    @Deprecated
     public void switchToProfile(String cardId, String iccid, boolean refresh,
             @CallbackExecutor Executor executor, ResultCallback<EuiccProfileInfo> callback) {
         try {
             getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid,
-                    refresh, new ISwitchToProfileCallback.Stub() {
+                    TelephonyManager.DEFAULT_PORT_INDEX, refresh,
+                    new ISwitchToProfileCallback.Stub() {
+                        @Override
+                        public void onComplete(int resultCode, EuiccProfileInfo profile) {
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profile));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling switchToProfile", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Switches from the current profile to another profile. The current profile will be disabled
+     * and the specified profile will be enabled. Here portIndex specifies on which port the
+     * profile is to be enabled.
+     *
+     * @param cardId The Id of the eUICC.
+     * @param iccid The iccid of the profile to switch to.
+     * @param portIndex The Port index is the unique index referring to a port.
+     * @param refresh Whether sending the REFRESH command to modem.
+     * @param executor The executor through which the callback should be invoked.
+     * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
+     */
+    public void switchToProfile(@Nullable String cardId, @Nullable String iccid, int portIndex,
+            boolean refresh, @NonNull @CallbackExecutor Executor executor,
+            @NonNull ResultCallback<EuiccProfileInfo> callback) {
+        try {
+            getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid,
+                    portIndex, refresh, new ISwitchToProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo profile) {
                             final long token = Binder.clearCallingIdentity();
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 2edb564c..45022a6 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -1418,4 +1418,22 @@
                         .getEuiccControllerService()
                         .get());
     }
+
+    /**
+     * Returns whether the passing portIndex is available.
+     * A port is available if it has no profiles enabled on it or calling app has carrier privilege
+     * over the profile installed on the selected port.
+     * Always returns false if the cardId is a physical card.
+     *
+     * @param portIndex is an enumeration of the ports available on the UICC.
+     * @return {@code true} if port is available
+     */
+    public boolean isSimPortAvailable(int portIndex) {
+        try {
+            return getIEuiccController().isSimPortAvailable(mCardId, portIndex,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 6493772..a900c84 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -304,4 +304,13 @@
     int setDeviceToDeviceStatusSharing(int sharing, int subId);
 
     int setDeviceToDeviceStatusSharingContacts(String contacts, int subscriptionId);
+
+    String getPhoneNumber(int subId, int source,
+            String callingPackage, String callingFeatureId);
+
+    String getPhoneNumberFromFirstAvailableSource(int subId,
+            String callingPackage, String callingFeatureId);
+
+    void setPhoneNumber(int subId, int source, String number,
+            String callingPackage, String callingFeatureId);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2e59fc7..dbc6cb6 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -77,6 +77,7 @@
 
 import android.telephony.UiccCardInfo;
 import android.telephony.UiccSlotInfo;
+import android.telephony.UiccSlotMapping;
 
 /**
  * Interface used to interact with the phone.  Mostly this is used by the
@@ -1729,17 +1730,35 @@
      * @return UiccSlotInfo array.
      * @hide
      */
-    UiccSlotInfo[] getUiccSlotsInfo();
+    UiccSlotInfo[] getUiccSlotsInfo(String callingPackage);
 
     /**
      * Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive.
      * @param physicalSlots Index i in the array representing physical slot for phone i. The array
      *        size should be same as getPhoneCount().
+     * @deprecated Use {@link #setSimSlotMapping(in List<UiccSlotMapping> slotMapping)} instead.
      * @return boolean Return true if the switch succeeds, false if the switch fails.
      */
     boolean switchSlots(in int[] physicalSlots);
 
     /**
+     * Maps the logical slots to the SlotPortMapping which consist of both physical slot index and
+     * port index. Logical slot is the slot that is seen by modem. Physical slot is the actual
+     * physical slot. Port index is the index (enumerated value) for the associated port available
+     * on the SIM. Each physical slot can have multiple ports which enables multi-enabled profile
+     * (MEP). If eUICC physical slot supports 2 ports, then the port index is numbered 0,1 and if
+     * eUICC2 supports 4 ports then the port index is numbered 0,1,2,3. Each portId is unique within
+     * a UICC physical slot but not necessarily unique across UICC’s. SEP(Single enabled profile)
+     * eUICC and non-eUICC will only have port Index 0.
+     *
+     * Logical slots that are already mapped to the requested SlotPortMapping are not impacted.
+     * @param slotMapping Index i in the list representing slot mapping for phone i.
+     *
+     * @return {@code true} if the switch succeeds, {@code false} if the switch fails.
+     */
+    boolean setSimSlotMapping(in List<UiccSlotMapping> slotMapping);
+
+    /**
      * Returns whether mobile data roaming is enabled on the subscription with id {@code subId}.
      *
      * @param subId the subscription id
@@ -2117,7 +2136,7 @@
     /**
      * Get the mapping from logical slots to physical slots.
      */
-    int[] getSlotsMapping();
+    int[] getSlotsMapping(String callingPackage);
 
     /**
      * Get the IRadio HAL Version encoded as 100 * MAJOR_VERSION + MINOR_VERSION or -1 if unknown
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 3a99f0e0..f650246 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -171,6 +171,8 @@
 
     public static final String SLOT_KEY  = "slot";
 
+    public static final String PORT_KEY = "port";
+
     // FIXME: This is used to pass a subId via intents, we need to look at its usage, which is
     // FIXME: extensive, and see if this should be an array of all active subId's or ...?
     /**
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
index e33f44c..c717c09 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
@@ -45,10 +45,10 @@
         in IGetAllProfilesCallback callback);
     oneway void getProfile(String callingPackage, String cardId, String iccid,
         in IGetProfileCallback callback);
-    oneway void disableProfile(String callingPackage, String cardId, String iccid, boolean refresh,
-        in IDisableProfileCallback callback);
-    oneway void switchToProfile(String callingPackage, String cardId, String iccid, boolean refresh,
-        in ISwitchToProfileCallback callback);
+    oneway void disableProfile(String callingPackage, String cardId, String iccid, int portIndex,
+            boolean refresh, in IDisableProfileCallback callback);
+    oneway void switchToProfile(String callingPackage, String cardId, String iccid, int portIndex,
+            boolean refresh, in ISwitchToProfileCallback callback);
     oneway void setNickname(String callingPackage, String cardId, String iccid, String nickname,
         in ISetNicknameCallback callback);
     oneway void deleteProfile(String callingPackage, String cardId, String iccid,
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 35e8a12..944ce348 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -51,4 +51,5 @@
     void setSupportedCountries(boolean isSupported, in List<String> countriesList);
     List<String> getSupportedCountries(boolean isSupported);
     boolean isSupportedCountry(String countryIso);
+    boolean isSimPortAvailable(int cardId, int portIndex, String callingPackage);
 }
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 5b44dba..21c3f76 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -23,6 +23,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.os.Build;
+import android.telephony.UiccPortInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet;
@@ -44,8 +45,7 @@
     static final int FPLMN_BYTE_SIZE = 3;
 
     // ICCID used for tests by some OEMs
-    // TODO(b/159354974): Replace the constant here with UiccPortInfo.ICCID_REDACTED once ready
-    private static final String TEST_ICCID = "FFFFFFFFFFFFFFFFFFFF";
+    public static final String TEST_ICCID = UiccPortInfo.ICCID_REDACTED;
 
     // A table mapping from a number to a hex character for fast encoding hex strings.
     private static final char[] HEX_CHARS = {
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index 896ec9a..566c725 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -16,10 +16,6 @@
         <!-- restart launcher to activate TAPL -->
         <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
     </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
-        <!-- reboot the device to teardown any crashed tests -->
-        <option name="cleanup-action" value="REBOOT" />
-    </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true"/>
         <option name="test-file-name" value="FlickerTests.apk"/>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 209d1aa..7076a07 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.server.wm.flicker.close
 
+import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -24,6 +25,8 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group4
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -79,6 +82,33 @@
     @Test
     override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
 
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
+
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun launcherLayerReplacesApp() {
+        // This test doesn't work in shell transitions because of b/206086894
+        assumeFalse(isShellTransitionsEnabled)
+        super.launcherLayerReplacesApp()
+    }
+
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun entireScreenCovered() {
+        // This test doesn't work in shell transitions because of b/206086894
+        assumeFalse(isShellTransitionsEnabled)
+        super.entireScreenCovered()
+    }
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index ac557cf..b5d01ef 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.server.wm.flicker.close
 
+import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -23,6 +24,8 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group4
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -78,6 +81,33 @@
     @Test
     override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
 
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
+
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun launcherLayerReplacesApp() {
+        // This test doesn't work in shell transitions because of b/206086894
+        assumeFalse(isShellTransitionsEnabled)
+        super.launcherLayerReplacesApp()
+    }
+
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun entireScreenCovered() {
+        // This test doesn't work in shell transitions because of b/206086894
+        assumeFalse(isShellTransitionsEnabled)
+        super.entireScreenCovered()
+    }
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index c7dfbf8..f12e4ae 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -30,6 +30,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
@@ -37,6 +38,7 @@
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -154,7 +156,11 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 5315da1d..b1bdb31 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -34,10 +34,12 @@
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -115,7 +117,11 @@
 
     @Presubmit
     @Test
-    fun entireScreenCovered() = testSpec.entireScreenCovered()
+    fun entireScreenCovered() {
+        // This test doesn't work in shell transitions because of b/206086894
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.entireScreenCovered()
+    }
 
     @Presubmit
     @Test
@@ -153,7 +159,11 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index d063b69..d975cf4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -32,10 +32,12 @@
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -119,20 +121,24 @@
     @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isLandscapeOrSeascapeAtStart)
+        assumeFalse(testSpec.isLandscapeOrSeascapeAtStart)
         testSpec.navBarLayerRotatesAndScales()
     }
 
     @FlakyTest
     @Test
     fun navBarLayerRotatesAndScales_Flaky() {
-        Assume.assumeTrue(testSpec.isLandscapeOrSeascapeAtStart)
+        assumeTrue(testSpec.isLandscapeOrSeascapeAtStart)
         testSpec.navBarLayerRotatesAndScales()
     }
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index b589969..ac90752 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -34,9 +34,11 @@
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -124,7 +126,11 @@
 
     @Presubmit
     @Test
-    fun entireScreenCovered() = testSpec.entireScreenCovered()
+    fun entireScreenCovered() {
+        // This test doesn't work in shell transitions because of b/206086894
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.entireScreenCovered()
+    }
 
     @Presubmit
     @Test
@@ -146,7 +152,11 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 7bf0186..cc5d9d2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -34,9 +34,11 @@
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -128,7 +130,11 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
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 5d8a382..84e78ec 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
@@ -17,10 +17,10 @@
 package com.android.server.wm.flicker.ime
 
 import android.app.Instrumentation
-import android.os.SystemProperties
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -37,11 +37,13 @@
 import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -60,8 +62,6 @@
 class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
-    private val isShellTransitionsEnabled =
-            SystemProperties.getBoolean("persist.debug.shell_transit", false)
 
     @FlickerBuilderProvider
     fun buildFlicker(): FlickerBuilder {
@@ -101,6 +101,8 @@
     @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+        // This test doesn't work in shell transitions because of b/204570898
+        assumeFalse(isShellTransitionsEnabled)
         val component = FlickerComponentName("", "RecentTaskScreenshotSurface")
         testSpec.assertWm {
             this.visibleWindowsShownMoreThanOneConsecutiveEntry(
@@ -114,6 +116,8 @@
     @Presubmit
     @Test
     fun launcherWindowBecomesInvisible() {
+        // This test doesn't work in shell transitions because of b/204574221
+        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertWm {
             this.isAppWindowVisible(LAUNCHER_COMPONENT)
                     .then()
@@ -123,12 +127,16 @@
 
     @Presubmit
     @Test
-    fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
+    fun imeWindowIsAlwaysVisible() {
+        // This test doesn't work in shell transitions because of b/204570898
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
+    }
 
     @Presubmit
     @Test
     fun imeAppWindowVisibilityLegacy() {
-        Assume.assumeFalse(isShellTransitionsEnabled)
+        assumeFalse(isShellTransitionsEnabled)
         // the app starts visible in live tile, and stays visible for the duration of entering
         // and exiting overview. However, legacy transitions seem to have a bug which causes
         // everything to restart during the test, so expect the app to disappear and come back.
@@ -143,10 +151,10 @@
         }
     }
 
-    @Presubmit
+    @FlakyTest(bugId = 204570898)
     @Test
     fun imeAppWindowVisibility() {
-        Assume.assumeTrue(isShellTransitionsEnabled)
+        assumeTrue(isShellTransitionsEnabled)
         // the app starts visible in live tile, and stays visible for the duration of entering
         // and exiting overview. Since we log 1x per frame, sometimes the activity visibility
         // and the app visibility are updated together, sometimes not, thus ignore activity
@@ -172,7 +180,7 @@
     @Presubmit
     @Test
     fun imeLayerIsBecomesVisibleLegacy() {
-        Assume.assumeFalse(isShellTransitionsEnabled)
+        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayers {
             this.isVisible(FlickerComponentName.IME)
                     .then()
@@ -182,10 +190,10 @@
         }
     }
 
-    @Presubmit
+    @FlakyTest(bugId = 204570898)
     @Test
     fun imeLayerIsBecomesVisible() {
-        Assume.assumeTrue(isShellTransitionsEnabled)
+        assumeTrue(isShellTransitionsEnabled)
         testSpec.assertLayers {
             this.isVisible(FlickerComponentName.IME)
         }
@@ -194,6 +202,8 @@
     @Presubmit
     @Test
     fun appLayerReplacesLauncher() {
+        // This test doesn't work in shell transitions because of b/204574221
+        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayers {
             this.isVisible(LAUNCHER_COMPONENT)
                 .then()
@@ -209,7 +219,11 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index c4fec7f..fe434268f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -25,7 +25,9 @@
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -79,6 +81,15 @@
         }
 
     /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
+
+    /** {@inheritDoc} */
     @FlakyTest
     @Test
     override fun navBarLayerRotatesAndScales() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index c572e8b..53b5354 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -28,7 +28,9 @@
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -96,14 +98,54 @@
         }
 
     /** {@inheritDoc} */
-    @FlakyTest
+    @Presubmit
     @Test
-    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
 
     /** {@inheritDoc} */
     @Presubmit
     @Test
-    override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+    override fun entireScreenCovered() {
+        // This test doesn't work in shell transitions because of b/204570898
+        assumeFalse(isShellTransitionsEnabled)
+        super.entireScreenCovered()
+    }
+
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun appWindowReplacesLauncherAsTopWindow() {
+        // This test doesn't work in shell transitions because of b/206085788
+        assumeFalse(isShellTransitionsEnabled)
+        super.appWindowReplacesLauncherAsTopWindow()
+    }
+
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun appLayerReplacesLauncher() {
+        // This test doesn't work in shell transitions because of b/206085788
+        assumeFalse(isShellTransitionsEnabled)
+        super.appLayerReplacesLauncher()
+    }
+
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        // This test doesn't work in shell transitions because of b/206090480
+        assumeFalse(isShellTransitionsEnabled)
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
+    /** {@inheritDoc} */
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
 
     /** {@inheritDoc} */
     @Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index bd9d7d7..429841b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -28,9 +28,11 @@
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.traces.common.FlickerComponentName
 import com.google.common.truth.Truth
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -188,15 +190,21 @@
     /** {@inheritDoc} */
     @FlakyTest(bugId = 202936526)
     @Test
-    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
 
     /** {@inheritDoc} */
     @Presubmit
     @Test
     fun statusBarLayerPositionAtEnd() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayersEnd {
             val display = this.entry.displays.minByOrNull { it.id }
-                ?: throw RuntimeException("There is no display!")
+                ?: error("There is no display!")
             this.visibleRegion(FlickerComponentName.STATUS_BAR)
                 .coversExactly(WindowUtils.getStatusBarPosition(display))
         }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index dc7df34..4db01bf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -25,6 +25,8 @@
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -83,6 +85,33 @@
         }
 
     /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        super.statusBarLayerRotatesScales()
+    }
+
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun appWindowReplacesLauncherAsTopWindow() {
+        // This test doesn't work in shell transitions because of b/206094140
+        assumeFalse(isShellTransitionsEnabled)
+        super.appWindowReplacesLauncherAsTopWindow()
+    }
+
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        // This test doesn't work in shell transitions because of b/206094140
+        assumeFalse(isShellTransitionsEnabled)
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
+    /** {@inheritDoc} */
     @FlakyTest
     @Test
     override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 4a90404..a97a48e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -26,11 +26,13 @@
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
 import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Rule
 import org.junit.Test
@@ -161,7 +163,11 @@
      */
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+    fun statusBarLayerRotatesScales() {
+        // This test doesn't work in shell transitions because of b/206753786
+        assumeFalse(isShellTransitionsEnabled)
+        testSpec.statusBarLayerRotatesScales()
+    }
 
     /** {@inheritDoc} */
     @FlakyTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index c55d7af..3ca60e3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -26,8 +26,10 @@
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.testapp.ActivityOptions
 import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume.assumeFalse
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -100,6 +102,8 @@
     @Presubmit
     @Test
     fun appWindowFullScreen() {
+        // This test doesn't work in shell transitions because of b/206101151
+        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertWm {
             this.invoke("isFullScreen") {
                 val appWindow = it.windowState(testApp.`package`)
@@ -135,17 +139,30 @@
     @Presubmit
     @Test
     fun appLayerAlwaysVisible() {
+        // This test doesn't work in shell transitions because of b/206101151
+        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayers {
             isVisible(testApp.component)
         }
     }
 
+    /** {@inheritDoc} */
+    @Presubmit
+    @Test
+    override fun focusDoesNotChange() {
+        // This test doesn't work in shell transitions because of b/206101151
+        assumeFalse(isShellTransitionsEnabled)
+        super.focusDoesNotChange()
+    }
+
     /**
      * Checks that [testApp] layer covers the entire screen during the whole transition
      */
     @Presubmit
     @Test
     fun appLayerRotates() {
+        // This test doesn't work in shell transitions because of b/206101151
+        assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayers {
             this.invoke("entireScreenCovered") { entry ->
                 entry.entry.displays.map { display ->