Merge "Remove hidden connectivity method usage" into sc-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index b4e167a..081163b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -121,54 +121,81 @@
     private final SparseArray<UidDefaultNetworkCallback> mCurrentDefaultNetworkCallbacks =
             new SparseArray<>();
     private final Comparator<UidStats> mUidStatsComparator = new Comparator<UidStats>() {
-        private int prioritizeExistence(int v1, int v2) {
-            if (v1 > 0 && v2 > 0) {
+        private int prioritizeExistenceOver(int threshold, int v1, int v2) {
+            // Check if they're both on the same side of the threshold.
+            if ((v1 > threshold && v2 > threshold) || (v1 <= threshold && v2 <= threshold)) {
                 return 0;
             }
-            return v2 - v1;
+            // They're on opposite sides of the threshold.
+            if (v1 > threshold) {
+                return -1;
+            }
+            return 1;
         }
 
         @Override
         public int compare(UidStats us1, UidStats us2) {
-            // TODO: build a better prioritization scheme
-            // Some things to use:
-            //   * Proc state
-            //   * IMPORTANT_WHILE_IN_FOREGROUND bit
-            final int runningPriority = prioritizeExistence(us1.numRunning, us2.numRunning);
+            // Prioritize a UID ahead of another based on:
+            //   1. Already running connectivity jobs (so we don't drop the listener)
+            //   2. Waiting connectivity jobs would be ready with connectivity
+            //   3. An existing network satisfies a waiting connectivity job's requirements
+            //   4. TOP proc state
+            //   5. Existence of treat-as-EJ EJs (not just requested EJs)
+            //   6. FGS proc state
+            //   7. EJ enqueue time
+            //   8. Any other important job priorities/proc states
+            //   9. Enqueue time
+            // TODO: maybe consider number of jobs
+            // TODO: consider IMPORTANT_WHILE_FOREGROUND bit
+            final int runningPriority = prioritizeExistenceOver(0, us1.numRunning, us2.numRunning);
             if (runningPriority != 0) {
                 return runningPriority;
             }
             // Prioritize any UIDs that have jobs that would be ready ahead of UIDs that don't.
-            final int readyWithConnPriority =
-                    prioritizeExistence(us1.numReadyWithConnectivity, us2.numReadyWithConnectivity);
+            final int readyWithConnPriority = prioritizeExistenceOver(0,
+                    us1.numReadyWithConnectivity, us2.numReadyWithConnectivity);
             if (readyWithConnPriority != 0) {
                 return readyWithConnPriority;
             }
             // They both have jobs that would be ready. Prioritize the UIDs whose requested
             // network is available ahead of UIDs that don't have their requested network available.
-            final int reqAvailPriority = prioritizeExistence(
+            final int reqAvailPriority = prioritizeExistenceOver(0,
                     us1.numRequestedNetworkAvailable, us2.numRequestedNetworkAvailable);
             if (reqAvailPriority != 0) {
                 return reqAvailPriority;
             }
-            // They both have jobs with available networks. Prioritize based on:
-            //   1. (eventually) proc state
-            //   2. Existence of runnable EJs (not just requested)
-            //   3. Enqueue time
-            // TODO: maybe consider number of jobs
-            final int ejPriority = prioritizeExistence(us1.numEJs, us2.numEJs);
+            // Prioritize the top app. If neither are top apps, then use a later prioritization
+            // check.
+            final int topPriority = prioritizeExistenceOver(JobInfo.PRIORITY_TOP_APP - 1,
+                    us1.basePriority, us2.basePriority);
+            if (topPriority != 0) {
+                return topPriority;
+            }
+            // They're either both TOP or both not TOP. Prioritize the app that has runnable EJs
+            // pending.
+            final int ejPriority = prioritizeExistenceOver(0, us1.numEJs, us2.numEJs);
             if (ejPriority != 0) {
                 return ejPriority;
             }
-            // They both have EJs. Order them by EJ enqueue time to help provide low EJ latency.
+            // They both have runnable EJs.
+            // Prioritize an FGS+ app. If neither are FGS+ apps, then use a later prioritization
+            // check.
+            final int fgsPriority = prioritizeExistenceOver(JobInfo.PRIORITY_FOREGROUND_SERVICE - 1,
+                    us1.basePriority, us2.basePriority);
+            if (fgsPriority != 0) {
+                return fgsPriority;
+            }
+            // Order them by EJ enqueue time to help provide low EJ latency.
             if (us1.earliestEJEnqueueTime < us2.earliestEJEnqueueTime) {
                 return -1;
             } else if (us1.earliestEJEnqueueTime > us2.earliestEJEnqueueTime) {
                 return 1;
             }
+            // Order by any latent important proc states.
             if (us1.basePriority != us2.basePriority) {
                 return us2.basePriority - us1.basePriority;
             }
+            // Order by enqueue time.
             if (us1.earliestEnqueueTime < us2.earliestEnqueueTime) {
                 return -1;
             }
@@ -480,7 +507,7 @@
         UidStats uidStats = mUidStats.get(uid);
         if (uidStats != null && uidStats.basePriority != newPriority) {
             uidStats.basePriority = newPriority;
-            maybeAdjustRegisteredCallbacksLocked();
+            postAdjustCallbacks();
         }
     }
 
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
index 7df0b4b..ce68447 100644
--- a/apex/media/framework/api/system-current.txt
+++ b/apex/media/framework/api/system-current.txt
@@ -25,7 +25,7 @@
   }
 
   public static final class MediaTranscodeManager.TranscodingSession {
-    method public void addClientUid(int);
+    method public boolean addClientUid(int);
     method public void cancel();
     method @NonNull public java.util.List<java.lang.Integer> getClientUids();
     method public int getErrorCode();
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index a7de602..d7e9609 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -1361,8 +1361,6 @@
         private @TranscodingSessionErrorCode int mErrorCode = ERROR_NONE;
         @GuardedBy("mLock")
         private boolean mHasRetried = false;
-        @GuardedBy("mLock")
-        private @NonNull List<Integer> mClientUidList = new ArrayList<>();
         // The original request that associated with this session.
         private final TranscodingRequest mRequest;
 
@@ -1381,7 +1379,6 @@
             mListenerExecutor = executor;
             mListener = listener;
             mRequest = request;
-            mClientUidList.add(request.getClientUid());
         }
 
         /**
@@ -1532,17 +1529,31 @@
          * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could add the
          * uid. Note that the permission check happens on the service side upon starting the
          * transcoding. If the client does not have the permission, the transcoding will fail.
+         * @param uid  the additional client uid to be added.
+         * @return true if successfully added, false otherwise.
          */
-        public void addClientUid(int uid) {
+        public boolean addClientUid(int uid) {
             if (uid < 0) {
                 throw new IllegalArgumentException("Invalid Uid");
             }
-            synchronized (mLock) {
-                if (!mClientUidList.contains(uid)) {
-                    // see ag/14023202 for implementation
-                    mClientUidList.add(uid);
-                }
+
+            // Get the client interface.
+            ITranscodingClient client = mManager.getTranscodingClient();
+            if (client == null) {
+                Log.e(TAG, "Service is dead...");
+                return false;
             }
+
+            try {
+                if (!client.addClientUid(mSessionId, uid)) {
+                    Log.e(TAG, "Failed to add client uid");
+                    return false;
+                }
+            } catch (Exception ex) {
+                Log.e(TAG, "Failed to get client uids due to " + ex);
+                return false;
+            }
+            return true;
         }
 
         /**
@@ -1551,9 +1562,25 @@
          */
         @NonNull
         public List<Integer> getClientUids() {
-            synchronized (mLock) {
-                return mClientUidList;
+            List<Integer> uidList = new ArrayList<Integer>();
+
+            // Get the client interface.
+            ITranscodingClient client = mManager.getTranscodingClient();
+            if (client == null) {
+                Log.e(TAG, "Service is dead...");
+                return uidList;
             }
+
+            try {
+                int[] clientUids  = client.getClientUids(mSessionId);
+                for (int i : clientUids) {
+                    uidList.add(i);
+                }
+            } catch (Exception ex) {
+                Log.e(TAG, "Failed to get client uids due to " + ex);
+            }
+
+            return uidList;
         }
 
         /**
diff --git a/api/dump_api_shas.sh b/api/dump_api_shas.sh
new file mode 100755
index 0000000..c023b31
--- /dev/null
+++ b/api/dump_api_shas.sh
@@ -0,0 +1,56 @@
+#!/bin/bash -e
+# This script dumps the git SHAs of all commits inside api tracking directories.
+# It can used by tools wanting to track API changes, and the primary original
+# purpose is to verify verify all API change SHAs have been tracked by the
+# server-side API-council tools.
+#
+# The only argument is used to specify a git commit range to filter by.
+#
+# Example invocation (API changes between O and P):
+# frameworks/base/api/dump_api_shas.sh origin/oreo-dev..origin/pie-dev
+
+set -o pipefail
+
+eecho() { echo $@ >&2 ; }
+
+if [[ $1 == *..* ]]; then
+    exclude=${1/..*}
+    include=${1/*..}
+else
+    eecho No range or invalid range specified, defaulting to all commits from HEAD.
+    exclude=
+    include=HEAD
+fi
+
+eecho -n building queryview...
+{ source build/envsetup.sh && lunch aosp_arm && m queryview; } >/dev/null 2>&1 \
+  || { eecho failed; exit 1; }
+eecho "done"
+
+# This finds the directories where the dependant java_sdk_libs are defined
+bpdirs=$(
+  bazel query --config=queryview --output=package \
+    'kind(java_sdk_library, deps(//frameworks/base/api/..., 1))' 2>/dev/null
+  echo frameworks/base/core/api # Not a java_sdk_library.
+  echo frameworks/base/services/api # Not a java_sdk_library.
+)
+
+# Find relevant api subdirectories
+apidirs=$(
+  find $bpdirs -type f -name '*current.txt' -path '*/api/*' \
+    | xargs realpath --relative-to=$(pwd) | xargs dirname | sort | uniq
+)
+
+# Dump sorted SHAs of commits in these directories
+{ for d in $apidirs; do
+    ( cd $d
+      eecho inspecting $d
+      exclude_arg=$(test -n "$exclude" && {
+        git rev-parse -q --verify $exclude > /dev/null && echo "--not $exclude" \
+          || eecho "$d has no revision $exclude, including all commits"; } || true)
+      for f in $(find . -name '*current.txt'); do
+        git --no-pager log --pretty=format:%H --no-merges --follow $include $exclude_arg -- $f
+        echo # No trailing newline with --no-pager
+      done
+    )
+done; } | sort | uniq
diff --git a/config/OWNERS b/config/OWNERS
index 001038d..0691dbc 100644
--- a/config/OWNERS
+++ b/config/OWNERS
@@ -1,14 +1,8 @@
 include /ZYGOTE_OWNERS
 
-# compat-team@ for changes to hiddenapi files
-
-per-file hiddenapi-* = andreionea@google.com, mathewi@google.com, satayev@google.com
-
 # art-team@ manages the boot image profiles
 per-file boot-* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
 per-file dirty-image-objects = calin@google.com, mathieuc@google.com, ngeoffray@google.com
 per-file generate-preloaded-classes.sh = calin@google.com, mathieuc@google.com, ngeoffray@google.com
 per-file preloaded-classes* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
 
-# Escalations:
-per-file hiddenapi-* = bdc@google.com, narayan@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index 10c4a4f..88080f0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1182,7 +1182,7 @@
     field public static final int reqNavigation = 16843306; // 0x101022a
     field public static final int reqTouchScreen = 16843303; // 0x1010227
     field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
-    field public static final int requestOptimizedExternalStorageAccess;
+    field public static final int requestRawExternalStorageAccess;
     field public static final int requireDeviceScreenOn;
     field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
     field public static final int required = 16843406; // 0x101028e
@@ -8552,8 +8552,8 @@
     ctor public AppWidgetProviderInfo(android.os.Parcel);
     method public android.appwidget.AppWidgetProviderInfo clone();
     method public int describeContents();
+    method @NonNull public android.content.pm.ActivityInfo getActivityInfo();
     method public final android.os.UserHandle getProfile();
-    method @NonNull public android.content.pm.ActivityInfo getProviderInfo();
     method @Nullable public final CharSequence loadDescription(@NonNull android.content.Context);
     method public final android.graphics.drawable.Drawable loadIcon(@NonNull android.content.Context, int);
     method public final String loadLabel(android.content.pm.PackageManager);
@@ -12441,7 +12441,7 @@
     method public void setAppIcon(@Nullable android.graphics.Bitmap);
     method public void setAppLabel(@Nullable CharSequence);
     method public void setAppPackageName(@Nullable String);
-    method public void setAutoRevokePermissionsMode(boolean);
+    method @Deprecated public void setAutoRevokePermissionsMode(boolean);
     method public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams);
     method public void setInstallLocation(int);
     method public void setInstallReason(int);
@@ -20332,6 +20332,7 @@
     field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
     field public static final int ENCODING_DTS = 7; // 0x7
     field public static final int ENCODING_DTS_HD = 8; // 0x8
+    field public static final int ENCODING_DTS_UHD = 27; // 0x1b
     field public static final int ENCODING_E_AC3 = 6; // 0x6
     field public static final int ENCODING_E_AC3_JOC = 18; // 0x12
     field public static final int ENCODING_IEC61937 = 13; // 0xd
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c126dbd..0772478 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -69,6 +69,7 @@
     field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
     field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS";
     field @Deprecated public static final String BROADCAST_NETWORK_PRIVILEGED = "android.permission.BROADCAST_NETWORK_PRIVILEGED";
+    field public static final String BYPASS_ROLE_QUALIFICATION = "android.permission.BYPASS_ROLE_QUALIFICATION";
     field public static final String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED";
     field public static final String CAMERA_OPEN_CLOSE_LISTENER = "android.permission.CAMERA_OPEN_CLOSE_LISTENER";
     field public static final String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD";
@@ -2508,7 +2509,7 @@
 package android.content.pm {
 
   public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
-    method @Nullable public Boolean hasRequestOptimizedExternalStorageAccess();
+    method @Nullable public Boolean hasRequestRawExternalStorageAccess();
     method public boolean isEncryptionAware();
     method public boolean isInstantApp();
     method public boolean isOem();
@@ -9439,7 +9440,7 @@
   }
 
   public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
-    method @Nullable public int[] getAttestationIds();
+    method @NonNull public int[] getAttestationIds();
     method public int getNamespace();
   }
 
@@ -14240,7 +14241,7 @@
     method public void onStartFailed(int, @NonNull android.os.PersistableBundle);
     method public void onStarted(@NonNull android.os.PersistableBundle);
     method public void onStopFailed(int, @NonNull android.os.PersistableBundle);
-    method public void onStopped();
+    method public void onStopped(int, @NonNull android.os.PersistableBundle);
     field public static final int REASON_BAD_PARAMETERS = 3; // 0x3
     field public static final int REASON_GENERIC_ERROR = 4; // 0x4
     field public static final int REASON_LOCAL_REQUEST = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 9931bf9..906524e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -233,6 +233,8 @@
     field public static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time";
     field public static final String KEY_FG_SERVICE_STATE_SETTLE_TIME = "fg_service_state_settle_time";
     field public static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time";
+    field public static final String OPSTR_ACTIVITY_RECOGNITION = "android:activity_recognition";
+    field public static final String OPSTR_ACTIVITY_RECOGNITION_SOURCE = "android:activity_recognition_source";
     field public static final String OPSTR_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls";
     field public static final String OPSTR_PHONE_CALL_CAMERA = "android:phone_call_camera";
     field public static final String OPSTR_PHONE_CALL_MICROPHONE = "android:phone_call_microphone";
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e50432e..35890c8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4335,17 +4335,10 @@
 
     private String getBackupAgentName(CreateBackupAgentData data) {
         String agentName = data.appInfo.backupAgentName;
-        if (!UserHandle.isCore(data.appInfo.uid)
-                && data.operationType == BackupManager.OperationType.MIGRATION) {
-            // If this is a migration, use the default backup agent regardless of the app's
-            // preferences.
+        // full backup operation but no app-supplied agent?  use the default implementation
+        if (agentName == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
+                || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) {
             agentName = DEFAULT_FULL_BACKUP_AGENT;
-        } else {
-            // full backup operation but no app-supplied agent?  use the default implementation
-            if (agentName == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
-                    || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) {
-                agentName = DEFAULT_FULL_BACKUP_AGENT;
-            }
         }
         return agentName;
     }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 436007ca..8e2626a 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1225,9 +1225,19 @@
     /** @hide */
     public static final int OP_UWB_RANGING = AppProtoEnums.APP_OP_UWB_RANGING;
 
+    /**
+     * Activity recognition being accessed by an activity recognition source, which
+     * is a component that already has access since it is the one that detects
+     * activity recognition.
+     *
+     * @hide
+     */
+    public static final int OP_ACTIVITY_RECOGNITION_SOURCE =
+            AppProtoEnums.APP_OP_ACTIVITY_RECOGNITION_SOURCE;
+
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int _NUM_OP = 113;
+    public static final int _NUM_OP = 114;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1478,6 +1488,7 @@
     public static final String OPSTR_USE_BIOMETRIC = "android:use_biometric";
 
     /** @hide Recognize physical activity. */
+    @TestApi
     public static final String OPSTR_ACTIVITY_RECOGNITION = "android:activity_recognition";
 
     /** @hide Financial app read sms. */
@@ -1643,6 +1654,17 @@
     /** @hide */
     public static final String OPSTR_UWB_RANGING = "android:uwb_ranging";
 
+    /**
+     * Activity recognition being accessed by an activity recognition source, which
+     * is a component that already has access since it is the one that detects
+     * activity recognition.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String OPSTR_ACTIVITY_RECOGNITION_SOURCE =
+            "android:activity_recognition_source";
+
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
     /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -1853,6 +1875,7 @@
             OP_MANAGE_MEDIA,                    // MANAGE_MEDIA
             OP_BLUETOOTH_CONNECT,               // OP_BLUETOOTH_CONNECT
             OP_UWB_RANGING,                     // OP_UWB_RANGING
+            OP_ACTIVITY_RECOGNITION_SOURCE      // OP_ACTIVITY_RECOGNITION_SOURCE
     };
 
     /**
@@ -1972,6 +1995,7 @@
             OPSTR_MANAGE_MEDIA,
             OPSTR_BLUETOOTH_CONNECT,
             OPSTR_UWB_RANGING,
+            OPSTR_ACTIVITY_RECOGNITION_SOURCE
     };
 
     /**
@@ -2091,7 +2115,8 @@
             "COARSE_LOCATION_SOURCE",
             "MANAGE_MEDIA",
             "BLUETOOTH_CONNECT",
-            "UWB_RANGING"
+            "UWB_RANGING",
+            "ACTIVITY_RECOGNITION_SOURCE"
     };
 
     /**
@@ -2213,6 +2238,7 @@
             Manifest.permission.MANAGE_MEDIA,
             Manifest.permission.BLUETOOTH_CONNECT,
             Manifest.permission.UWB_RANGING,
+            null, // no permission for OP_ACTIVITY_RECOGNITION_SOURCE,
     };
 
     /**
@@ -2334,6 +2360,7 @@
             null, // MANAGE_MEDIA
             null, // BLUETOOTH_CONNECT
             null, // UWB_RANGING
+            null, // ACTIVITY_RECOGNITION_SOURCE
     };
 
     /**
@@ -2454,6 +2481,7 @@
             null, // MANAGE_MEDIA
             null, // BLUETOOTH_CONNECT
             null, // UWB_RANGING
+            null  // ACTIVITY_RECOGNITION_SOURCE
     };
 
     /**
@@ -2573,6 +2601,7 @@
             AppOpsManager.MODE_DEFAULT, // MANAGE_MEDIA
             AppOpsManager.MODE_ALLOWED, // BLUETOOTH_CONNECT
             AppOpsManager.MODE_ALLOWED, // UWB_RANGING
+            AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION_SOURCE
     };
 
     /**
@@ -2696,6 +2725,7 @@
             false, // MANAGE_MEDIA
             false, // BLUETOOTH_CONNECT
             false, // UWB_RANGING
+            false, // ACTIVITY_RECOGNITION_SOURCE
     };
 
     /**
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl
index 7713e25..74018a8 100644
--- a/core/java/android/app/IUidObserver.aidl
+++ b/core/java/android/app/IUidObserver.aidl
@@ -24,7 +24,7 @@
     // below block of transactions.
 
     // Since these transactions are also called from native code, these must be kept in sync with
-    // the ones in frameworks/native/include/binder/IActivityManager.h
+    // the ones in frameworks/native/include_activitymanager/binder/IActivityManager.h
     // =============== Beginning of transactions used on native side as well ======================
 
     /**
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index f0d580f..da03a3d 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -419,6 +419,14 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface Importance {}
 
+    /** @hide */
+    @IntDef(prefix = { "BUBBLE_PREFERENCE_" }, value = {
+            BUBBLE_PREFERENCE_NONE, BUBBLE_PREFERENCE_SELECTED,
+            BUBBLE_PREFERENCE_ALL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BubblePreference {}
+
     /**
      * Activity Action: Launch an Automatic Zen Rule configuration screen
      * <p>
@@ -1379,7 +1387,7 @@
      * @see Notification#getBubbleMetadata()
      * @return the users' bubble preference for the app.
      */
-    public int getBubblePreference() {
+    public @BubblePreference int getBubblePreference() {
         INotificationManager service = getService();
         try {
             return service.getBubblePreferenceForPackage(mContext.getPackageName(),
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 94a4fde0..0841910 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -544,11 +544,6 @@
     }
 
     private Set<String> getExtraExcludeDirsIfAny(Context context) throws IOException {
-        if (isDeviceToDeviceMigration()) {
-            return Collections.emptySet();
-        }
-
-        // If this is not a migration, also exclude no-backup and cache dirs.
         Set<String> excludedDirs = new HashSet<>();
         excludedDirs.add(context.getCacheDir().getCanonicalPath());
         excludedDirs.add(context.getCodeCacheDir().getCanonicalPath());
@@ -556,10 +551,6 @@
         return Collections.unmodifiableSet(excludedDirs);
     }
 
-    private boolean isDeviceToDeviceMigration() {
-        return mOperationType == OperationType.MIGRATION;
-    }
-
     /** @hide */
     @VisibleForTesting
     public IncludeExcludeRules getIncludeExcludeRules(FullBackup.BackupScheme backupScheme)
@@ -905,11 +896,6 @@
     }
 
     private boolean isFileEligibleForRestore(File destination) throws IOException {
-        if (isDeviceToDeviceMigration()) {
-            // Everything is eligible for device-to-device migration.
-            return true;
-        }
-
         FullBackup.BackupScheme bs = FullBackup.getBackupScheme(this, mOperationType);
         if (!bs.isFullRestoreEnabled()) {
             if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
diff --git a/core/java/android/app/time/LocationTimeZoneManager.java b/core/java/android/app/time/LocationTimeZoneManager.java
index 066aada..f506f12 100644
--- a/core/java/android/app/time/LocationTimeZoneManager.java
+++ b/core/java/android/app/time/LocationTimeZoneManager.java
@@ -50,31 +50,6 @@
     public static final String SHELL_COMMAND_STOP = "stop";
 
     /**
-     * A shell command that can put providers into different modes. Takes effect next time the
-     * service is started.
-     */
-    public static final String SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE =
-            "set_provider_mode_override";
-
-    /**
-     * The default provider mode.
-     * For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}.
-     */
-    public static final String PROVIDER_MODE_OVERRIDE_NONE = "none";
-
-    /**
-     * The "simulated" provider mode.
-     * For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}.
-     */
-    public static final String PROVIDER_MODE_OVERRIDE_SIMULATED = "simulated";
-
-    /**
-     * The "disabled" provider mode (equivalent to there being no provider configured).
-     * For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}.
-     */
-    public static final String PROVIDER_MODE_OVERRIDE_DISABLED = "disabled";
-
-    /**
      * A shell command that tells the service to record state information during tests. The next
      * argument value is "true" or "false".
      */
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 1cbb2fb..3db1885 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -479,7 +479,7 @@
      * Returns the broadcast receiver that is providing this widget.
      */
     @NonNull
-    public ActivityInfo getProviderInfo() {
+    public ActivityInfo getActivityInfo() {
         return providerInfo;
     }
 
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index b07d6d5..7956be3 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -67,6 +67,9 @@
      * Using it requires declaring uses-permission
      * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_WATCH} in the manifest.
      *
+     * <a href="{@docRoot}about/versions/12/features#cdm-profiles">Learn more</a>
+     * about device profiles.
+     *
      * @see AssociationRequest.Builder#setDeviceProfile
      */
     public static final String DEVICE_PROFILE_WATCH = "android.app.role.COMPANION_DEVICE_WATCH";
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 2ce7156..0116db0 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -375,6 +375,14 @@
      * Calling app must check for feature presence of
      * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} before calling this API.
      *
+     * For Bluetooth LE devices this is based on scanning for device with the given address.
+     * For Bluetooth classic devices this is triggered when the device connects/disconnects.
+     * WiFi devices are not supported.
+     *
+     * If a Bluetooth LE device wants to use a rotating mac address, it is recommended to use
+     * Resolvable Private Address, and ensure the device is bonded to the phone so that android OS
+     * is able to resolve the address.
+     *
      * @param deviceAddress a previously-associated companion device's address
      *
      * @throws DeviceNotAssociatedException if the given device was not previously associated
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index cadbd60..11adfa3 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -51,6 +51,25 @@
  */
 @SystemService(Context.CLIPBOARD_SERVICE)
 public class ClipboardManager extends android.text.ClipboardManager {
+
+    /**
+     * DeviceConfig property, within the clipboard namespace, that determines whether notifications
+     * are shown when an app accesses clipboard. This may be overridden by a user-controlled
+     * setting.
+     *
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_SHOW_ACCESS_NOTIFICATIONS =
+            "show_access_notifications";
+
+    /**
+     * Default value for the DeviceConfig property that determines whether notifications are shown
+     * when an app accesses clipboard.
+     *
+     * @hide
+     */
+    public static final boolean DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS = true;
+
     private final Context mContext;
     private final Handler mHandler;
     private final IClipboard mService;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6badf0e0..6ad204e 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1436,11 +1436,11 @@
     private @NativeHeapZeroInitialized int nativeHeapZeroInitialized = ZEROINIT_DEFAULT;
 
     /**
-     * If {@code true} this app requests optimized external storage access.
+     * If {@code true} this app requests raw external storage access.
      * The request may not be honored due to policy or other reasons.
      */
     @Nullable
-    private Boolean requestOptimizedExternalStorageAccess;
+    private Boolean requestRawExternalStorageAccess;
 
     /**
      * Represents the default policy. The actual policy used will depend on other properties of
@@ -1598,9 +1598,9 @@
             if (nativeHeapZeroInitialized != ZEROINIT_DEFAULT) {
                 pw.println(prefix + "nativeHeapZeroInitialized=" + nativeHeapZeroInitialized);
             }
-            if (requestOptimizedExternalStorageAccess != null) {
-                pw.println(prefix + "requestOptimizedExternalStorageAccess="
-                        + requestOptimizedExternalStorageAccess);
+            if (requestRawExternalStorageAccess != null) {
+                pw.println(prefix + "requestRawExternalStorageAccess="
+                        + requestRawExternalStorageAccess);
             }
         }
         super.dumpBack(pw, prefix);
@@ -1829,7 +1829,7 @@
         gwpAsanMode = orig.gwpAsanMode;
         memtagMode = orig.memtagMode;
         nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
-        requestOptimizedExternalStorageAccess = orig.requestOptimizedExternalStorageAccess;
+        requestRawExternalStorageAccess = orig.requestRawExternalStorageAccess;
     }
 
     public String toString() {
@@ -1918,7 +1918,7 @@
         dest.writeInt(gwpAsanMode);
         dest.writeInt(memtagMode);
         dest.writeInt(nativeHeapZeroInitialized);
-        sForBoolean.parcel(requestOptimizedExternalStorageAccess, dest, parcelableFlags);
+        sForBoolean.parcel(requestRawExternalStorageAccess, dest, parcelableFlags);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -2004,7 +2004,7 @@
         gwpAsanMode = source.readInt();
         memtagMode = source.readInt();
         nativeHeapZeroInitialized = source.readInt();
-        requestOptimizedExternalStorageAccess = sForBoolean.unparcel(source);
+        requestRawExternalStorageAccess = sForBoolean.unparcel(source);
     }
 
     /**
@@ -2121,10 +2121,10 @@
     /**
      * @return
      * <ul>
-     * <li>{@code true} if this app requested optimized external storage access
-     * <li>{@code false} if this app requests to disable optimized external storage access.
+     * <li>{@code true} if this app requested raw external storage access
+     * <li>{@code false} if this app requests to disable raw external storage access.
      * <li>{@code null} if the app didn't specify
-     * {@link android.R.styleable#AndroidManifestApplication_requestOptimizedExternalStorageAccess}
+     * {@link android.R.styleable#AndroidManifestApplication_requestRawExternalStorageAccess}
      * in its manifest file.
      * </ul>
      *
@@ -2132,8 +2132,8 @@
      */
     @SystemApi
     @Nullable
-    public Boolean hasRequestOptimizedExternalStorageAccess() {
-        return requestOptimizedExternalStorageAccess;
+    public Boolean hasRequestRawExternalStorageAccess() {
+        return requestRawExternalStorageAccess;
     }
 
     /**
@@ -2421,8 +2421,8 @@
         nativeHeapZeroInitialized = value;
     }
     /** {@hide} */
-    public void setRequestOptimizedExternalStorageAccess(@Nullable Boolean value) {
-        requestOptimizedExternalStorageAccess = value;
+    public void setRequestRawExternalStorageAccess(@Nullable Boolean value) {
+        requestRawExternalStorageAccess = value;
     }
 
     /** {@hide} */
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 3be7f74..0be77e0 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1808,7 +1808,10 @@
          * If user explicitly enabled or disabled it via settings, this call is ignored.
          *
          * @param shouldAutoRevoke whether permissions should be auto-revoked.
+         *
+         * @deprecated No longer used
          */
+        @Deprecated
         public void setAutoRevokePermissionsMode(boolean shouldAutoRevoke) {
             autoRevokePermissionsMode = shouldAutoRevoke ? MODE_ALLOWED : MODE_IGNORED;
         }
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 1c65e00..8dcba7f 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -259,8 +259,8 @@
     ParsingPackage setNativeHeapZeroInitialized(
             @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized);
 
-    ParsingPackage setRequestOptimizedExternalStorageAccess(
-            @Nullable Boolean requestOptimizedExternalStorageAccess);
+    ParsingPackage setRequestRawExternalStorageAccess(
+            @Nullable Boolean requestRawExternalStorageAccess);
 
     ParsingPackage setCrossProfile(boolean crossProfile);
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 97e1b54..ea7135e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -400,7 +400,7 @@
 
     @Nullable
     @DataClass.ParcelWith(ForBoolean.class)
-    private Boolean requestOptimizedExternalStorageAccess;
+    private Boolean requestRawExternalStorageAccess;
 
     // TODO(chiuwinson): Non-null
     @Nullable
@@ -1086,7 +1086,7 @@
         appInfo.setGwpAsanMode(gwpAsanMode);
         appInfo.setMemtagMode(memtagMode);
         appInfo.setNativeHeapZeroInitialized(nativeHeapZeroInitialized);
-        appInfo.setRequestOptimizedExternalStorageAccess(requestOptimizedExternalStorageAccess);
+        appInfo.setRequestRawExternalStorageAccess(requestRawExternalStorageAccess);
         appInfo.setBaseCodePath(mBaseApkPath);
         appInfo.setBaseResourcePath(mBaseApkPath);
         appInfo.setCodePath(mPath);
@@ -1223,7 +1223,7 @@
         dest.writeMap(this.mProperties);
         dest.writeInt(this.memtagMode);
         dest.writeInt(this.nativeHeapZeroInitialized);
-        sForBoolean.parcel(this.requestOptimizedExternalStorageAccess, dest, flags);
+        sForBoolean.parcel(this.requestRawExternalStorageAccess, dest, flags);
     }
 
     public ParsingPackageImpl(Parcel in) {
@@ -1348,7 +1348,7 @@
         this.mProperties = in.readHashMap(boot);
         this.memtagMode = in.readInt();
         this.nativeHeapZeroInitialized = in.readInt();
-        this.requestOptimizedExternalStorageAccess = sForBoolean.unparcel(in);
+        this.requestRawExternalStorageAccess = sForBoolean.unparcel(in);
         assignDerivedFields();
     }
 
@@ -2131,8 +2131,8 @@
 
     @Nullable
     @Override
-    public Boolean hasRequestOptimizedExternalStorageAccess() {
-        return requestOptimizedExternalStorageAccess;
+    public Boolean hasRequestRawExternalStorageAccess() {
+        return requestRawExternalStorageAccess;
     }
 
     @Override
@@ -2586,8 +2586,8 @@
     }
 
     @Override
-    public ParsingPackageImpl setRequestOptimizedExternalStorageAccess(@Nullable Boolean value) {
-        requestOptimizedExternalStorageAccess = value;
+    public ParsingPackageImpl setRequestRawExternalStorageAccess(@Nullable Boolean value) {
+        requestRawExternalStorageAccess = value;
         return this;
     }
     @Override
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index cfd828e..4d4cc1a 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -905,7 +905,7 @@
     int getNativeHeapZeroInitialized();
 
     @Nullable
-    Boolean hasRequestOptimizedExternalStorageAccess();
+    Boolean hasRequestRawExternalStorageAccess();
 
     // TODO(b/135203078): Hide and enforce going through PackageInfoUtils
     ApplicationInfo toAppInfoWithoutState();
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 4e7bd70..a1ffc0c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -2019,9 +2019,9 @@
                         v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
             }
             if (sa.hasValue(
-                    R.styleable.AndroidManifestApplication_requestOptimizedExternalStorageAccess)) {
-                pkg.setRequestOptimizedExternalStorageAccess(sa.getBoolean(R.styleable
-                                .AndroidManifestApplication_requestOptimizedExternalStorageAccess,
+                    R.styleable.AndroidManifestApplication_requestRawExternalStorageAccess)) {
+                pkg.setRequestRawExternalStorageAccess(sa.getBoolean(R.styleable
+                                .AndroidManifestApplication_requestRawExternalStorageAccess,
                         false));
             }
         } finally {
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index d7bb226..ba6fc6e 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -53,6 +53,11 @@
                          in PerUidReadTimeouts[] perUidReadTimeouts);
 
     /**
+     * PM/system is done with this storage, ok to increase timeouts.
+     */
+    void onInstallationComplete(int storageId);
+
+    /**
      * Bind-mounts a path under a storage to a full path. Can be permanent or temporary.
      */
     const int BIND_TEMPORARY = 0;
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 2a42b98..6e25968 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -205,16 +205,26 @@
     /**
      * Resets the states and unbinds storage instances for an installation session.
      */
-    public void cleanUp() {
-        if (mDefaultStorage == null) {
-            return;
+    public void cleanUpAndMarkComplete() {
+        IncrementalStorage defaultStorage = cleanUp();
+        if (defaultStorage != null) {
+            defaultStorage.onInstallationComplete();
+        }
+    }
+
+    private IncrementalStorage cleanUp() {
+        IncrementalStorage defaultStorage = mDefaultStorage;
+        mInheritedStorage = null;
+        mDefaultStorage = null;
+        if (defaultStorage == null) {
+            return null;
         }
 
         try {
             mIncrementalManager.unregisterLoadingProgressCallbacks(mStageDir.getAbsolutePath());
-            mDefaultStorage.unBind(mStageDir.getAbsolutePath());
+            defaultStorage.unBind(mStageDir.getAbsolutePath());
         } catch (IOException ignored) {
         }
-        mDefaultStorage = null;
+        return defaultStorage;
     }
 }
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index 7cf0144..c19e29f 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -398,7 +398,7 @@
     }
 
     /**
-     * Iinitializes and starts the DataLoader.
+     * Initializes and starts the DataLoader.
      * This makes sure all install-time parameters are applied.
      * Does not affect persistent DataLoader params.
      * @return True if start request was successfully queued.
@@ -419,6 +419,18 @@
         }
     }
 
+    /**
+     * Marks the completion of installation.
+     */
+    public void onInstallationComplete() {
+        try {
+            mService.onInstallationComplete(mId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+
     private static final int UUID_BYTE_SIZE = 16;
 
     /**
diff --git a/core/java/android/uwb/IUwbRangingCallbacks.aidl b/core/java/android/uwb/IUwbRangingCallbacks.aidl
index f71f3ff..f15debb 100644
--- a/core/java/android/uwb/IUwbRangingCallbacks.aidl
+++ b/core/java/android/uwb/IUwbRangingCallbacks.aidl
@@ -92,9 +92,13 @@
    * Called when the ranging session has been stopped
    *
    * @param sessionHandle the session the callback is being invoked for
+   * @param reason the reason the session was stopped
+   * @param parameters protocol specific parameters
    */
 
-  void onRangingStopped(in SessionHandle sessionHandle);
+  void onRangingStopped(in SessionHandle sessionHandle,
+                        RangingChangeReason reason,
+                        in PersistableBundle parameters);
 
   /**
    * Called when a ranging session fails to stop
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index 5c7f0f5..ff8b912 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -171,7 +171,8 @@
     }
 
     @Override
-    public void onRangingStopped(SessionHandle sessionHandle) {
+    public void onRangingStopped(SessionHandle sessionHandle, @RangingChangeReason int reason,
+            PersistableBundle params) {
         synchronized (this) {
             if (!hasSession(sessionHandle)) {
                 Log.w(TAG, "onRangingStopped - received unexpected SessionHandle: "
@@ -180,7 +181,7 @@
             }
 
             RangingSession session = mRangingSessionTable.get(sessionHandle);
-            session.onRangingStopped();
+            session.onRangingStopped(convertToReason(reason), params);
         }
     }
 
diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java
index 52ec5bd..345b69d 100644
--- a/core/java/android/uwb/RangingSession.java
+++ b/core/java/android/uwb/RangingSession.java
@@ -191,8 +191,11 @@
 
         /**
          * Invoked when a request to stop the session succeeds
+         *
+         * @param reason reason for the session stop
+         * @param parameters protocol specific parameters related to the stop reason
          */
-        void onStopped();
+        void onStopped(@Reason int reason, @NonNull PersistableBundle parameters);
 
         /**
          * Invoked when a request to stop the session fails
@@ -434,14 +437,15 @@
     /**
      * @hide
      */
-    public void onRangingStopped() {
+    public void onRangingStopped(@Callback.Reason int reason,
+            @NonNull PersistableBundle params) {
         if (mState == State.CLOSED) {
             Log.w(TAG, "onRangingStopped invoked for a closed session");
             return;
         }
 
         mState = State.IDLE;
-        executeCallback(() -> mCallback.onStopped());
+        executeCallback(() -> mCallback.onStopped(reason, params));
     }
 
     /**
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index b207ad3..04528e9 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -16,6 +16,8 @@
 
 #define ATRACE_TAG ATRACE_TAG_RESOURCES
 
+#include "signal.h"
+
 #include "android-base/logging.h"
 #include "android-base/macros.h"
 #include "android-base/stringprintf.h"
@@ -353,8 +355,59 @@
   return reinterpret_cast<jlong>(apk_assets.release());
 }
 
+// STOPSHIP (b/159041693): Revert signal handler when reason for issue is found.
+static thread_local std::stringstream destroy_info;
+static struct sigaction old_handler_action;
+
+static void DestroyErrorHandler(int sig, siginfo_t* info, void* ucontext) {
+  if (sig != SIGSEGV) {
+    return;
+  }
+
+  LOG(ERROR) << "(b/159041693) - Failed to destroy ApkAssets " << destroy_info.str();
+  if (old_handler_action.sa_handler == SIG_DFL) {
+      // reset the action to default and re-raise the signal. It will kill the process
+      signal(sig, SIG_DFL);
+      raise(sig);
+      return;
+  }
+  if (old_handler_action.sa_handler == SIG_IGN) {
+      // ignoring SIGBUS won't help us much, as we'll get back right here after retrying.
+      return;
+  }
+  if (old_handler_action.sa_flags & SA_SIGINFO) {
+      old_handler_action.sa_sigaction(sig, info, ucontext);
+  } else {
+      old_handler_action.sa_handler(sig);
+  }
+}
+
 static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
-  delete reinterpret_cast<ApkAssets*>(ptr);
+  auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+  destroy_info << "{ptr=" << apk_assets;
+  if (apk_assets != nullptr) {
+    destroy_info << ", name='" << apk_assets->GetDebugName() << "'"
+                 << ", idmap=" << apk_assets->GetLoadedIdmap()
+                 << ", {arsc=" << apk_assets->GetLoadedArsc();
+    if (auto arsc = apk_assets->GetLoadedArsc()) {
+      destroy_info << ", strings=" << arsc->GetStringPool()
+                   << ", packages=" << &arsc->GetPackages()
+                   << " [";
+      for (auto& package : arsc->GetPackages()) {
+        destroy_info << "{unique_ptr=" << &package
+                     << ", package=" << package.get() << "},";
+      }
+      destroy_info << "]";
+    }
+    destroy_info << "}";
+  }
+  destroy_info << "}";
+
+  delete apk_assets;
+
+  // Deleting the apk assets did not lead to a crash.
+  destroy_info.str("");
+  destroy_info.clear();
 }
 
 static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
@@ -507,6 +560,17 @@
   jclass parcelFd = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
   gParcelFileDescriptorOffsets.detachFd = GetMethodIDOrDie(env, parcelFd, "detachFd", "()I");
 
+  // STOPSHIP (b/159041693): Revert signal handler when reason for issue is found.
+  sigset_t allowed;
+  sigemptyset(&allowed);
+  sigaddset(&allowed, SIGSEGV);
+  pthread_sigmask(SIG_UNBLOCK, &allowed, nullptr);
+  struct sigaction action = {
+          .sa_flags = SA_SIGINFO,
+          .sa_sigaction = &DestroyErrorHandler,
+  };
+  sigaction(SIGSEGV, &action, &old_handler_action);
+
   return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods,
                               arraysize(gApkAssetsMethods));
 }
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 5630a1e..7fde92c 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -45,6 +45,7 @@
 #define ENCODING_MPEGH_BL_L4 24
 #define ENCODING_MPEGH_LC_L3 25
 #define ENCODING_MPEGH_LC_L4 26
+#define ENCODING_DTS_UHD 27
 
 #define ENCODING_INVALID    0
 #define ENCODING_DEFAULT    1
@@ -110,6 +111,8 @@
         return AUDIO_FORMAT_MPEGH_LC_L3;
     case ENCODING_MPEGH_LC_L4:
         return AUDIO_FORMAT_MPEGH_LC_L4;
+    case ENCODING_DTS_UHD:
+        return AUDIO_FORMAT_DTS_UHD;
     default:
         return AUDIO_FORMAT_INVALID;
     }
@@ -179,6 +182,8 @@
         return ENCODING_MPEGH_LC_L3;
     case AUDIO_FORMAT_MPEGH_LC_L4:
         return ENCODING_MPEGH_LC_L4;
+    case AUDIO_FORMAT_DTS_UHD:
+        return ENCODING_DTS_UHD;
     case AUDIO_FORMAT_DEFAULT:
         return ENCODING_DEFAULT;
     default:
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f5cc24d..58abfeb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1390,7 +1390,7 @@
     <!-- Required to be able to discover and connect to nearby Bluetooth devices.
          <p>Protection level: dangerous -->
     <permission-group android:name="android.permission-group.NEARBY_DEVICES"
-        android:icon="@drawable/ic_qs_bluetooth"
+        android:icon="@drawable/perm_group_nearby_devices"
         android:label="@string/permgrouplab_nearby_devices"
         android:description="@string/permgroupdesc_nearby_devices"
         android:priority="750" />
@@ -4240,6 +4240,15 @@
     <permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
                 android:protectionLevel="signature|installer" />
 
+    <!-- @SystemApi Allows an application to bypass role qualification. This allows switching role
+         holders to otherwise non eligible holders. Only the shell is allowed to do this, the
+         qualification for the shell role itself cannot be bypassed, and each role needs to
+         explicitly allow bypassing qualification in its definition. The bypass state will not be
+         persisted across reboot.
+     @hide -->
+    <permission android:name="android.permission.BYPASS_ROLE_QUALIFICATION"
+                android:protectionLevel="internal|role" />
+
     <!-- @SystemApi Allows an application to observe role holder changes.
          @hide -->
     <permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
diff --git a/core/res/res/drawable/perm_group_nearby_devices.xml b/core/res/res/drawable/perm_group_nearby_devices.xml
new file mode 100644
index 0000000..84cc432
--- /dev/null
+++ b/core/res/res/drawable/perm_group_nearby_devices.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M12,16.427L7.574,12 12,7.574 16.426,12zM10.58,2.59l-8,8c-0.78,0.78 -0.78,2.05 0,2.83l8,8c0.78,0.78 2.05,0.78 2.83,0l8,-8c0.78,-0.78 0.78,-2.05 0,-2.83l-8,-8c-0.78,-0.79 -2.04,-0.79 -2.83,0zM13.39,17.81L12,19.2l-1.39,-1.39 -4.42,-4.42L4.8,12l1.39,-1.39 4.42,-4.42L12,4.8l1.39,1.39 4.42,4.42L19.2,12l-1.39,1.39 -4.42,4.42z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 986bb82..c51b2d8 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1867,16 +1867,21 @@
              -->
         <attr name="preserveLegacyExternalStorage" format="boolean" />
 
-        <!-- If {@code true} this app would like optimized external storage access.
+        <!-- If {@code true} this app would like raw external storage access.
 
         <p> This flag can only be used by apps holding
         <ul>
         <li>{@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission or
         <li>{@link android.app.role}#SYSTEM_GALLERY role.
         </ul>
-        When the flag is set, bulk file path operations will be optimized.
+        <p> When the flag is set, all file path access on external storage will bypass database
+        operations that update MediaStore collection. Raw external storage access as a side effect
+        can improve performance of bulk file path operations but can cause unexpected behavior in
+        apps due to inconsistencies in MediaStore collection and lower file system.
+        When the flag is set, app should scan the file after file path operations to ensure
+        consistency of MediaStore collection.
 
-        The default value is {@code true} if
+        <p> The default value is {@code true} if
         <ul>
         <li>app has {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission and
         targets targetSDK<=30.
@@ -1884,7 +1889,7 @@
         </ul>
         {@code false} otherwise.
         -->
-        <attr name="requestOptimizedExternalStorageAccess" format="boolean" />
+        <attr name="requestRawExternalStorageAccess" format="boolean" />
 
         <!-- If {@code true} this app declares that it should be visible to all other apps on
              device, regardless of what they declare via the {@code queries} tags in their
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 0b41769..cb7f69b 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -34,7 +34,7 @@
     <color name="quaternary_device_default_settings">@color/quaternary_material_settings</color>
 
     <color name="accent_device_default_light">@color/system_accent1_600</color>
-    <color name="accent_device_default_dark">@color/system_accent1_200</color>
+    <color name="accent_device_default_dark">@color/system_accent1_100</color>
     <color name="accent_device_default">@color/accent_device_default_light</color>
     <color name="accent_primary_device_default">@color/system_accent1_100</color>
     <color name="accent_secondary_device_default">@color/system_accent2_100</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc2bdf3..49bd853 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1637,32 +1637,22 @@
          config_timeZoneRulesUpdateTrackingEnabled are true.] -->
     <integer name="config_timeZoneRulesCheckRetryCount">5</integer>
 
-    <!-- Whether the geolocation time zone detection feature is enabled. -->
+    <!-- Whether the geolocation time zone detection feature is enabled. Setting this to false means
+         the feature cannot be used. Setting this to true means it may be used if other
+         configuration allows (see provider configuration below, also compile time overlays). -->
     <bool name="config_enableGeolocationTimeZoneDetection" translatable="false">true</bool>
 
-    <!-- Whether the primary LocationTimeZoneProvider is enabled device.
+    <!-- Whether the primary LocationTimeZoneProvider is enabled.
          Ignored if config_enableGeolocationTimeZoneDetection is false -->
     <bool name="config_enablePrimaryLocationTimeZoneProvider" translatable="false">false</bool>
-    <!-- Used when enablePrimaryLocationTimeZoneProvider is true. Controls whether to enable primary
-         location time zone provider overlay which allows the primary location time zone provider to
-         be replaced by an app at run-time. When disabled, only the
-         config_primaryLocationTimeZoneProviderPackageName package will be searched for the primary
-         location time zone provider, otherwise any system package is eligible. Anyone who wants to
-         disable the runtime overlay mechanism can set it to false. -->
-    <bool name="config_enablePrimaryLocationTimeZoneOverlay" translatable="false">false</bool>
-    <!-- Package name providing the primary location time zone provider. Used only when
-         config_enablePrimaryLocationTimeZoneOverlay is false. -->
+    <!-- The package name providing the primary location time zone provider.
+         Only used when config_enableGeolocationTimeZoneDetection and
+         enablePrimaryLocationTimeZoneProvider are true. -->
     <string name="config_primaryLocationTimeZoneProviderPackageName" translatable="false">@null</string>
 
-    <!-- Whether the secondary LocationTimeZoneProvider is enabled device.
+    <!-- Whether the secondary LocationTimeZoneProvider is enabled.
          Ignored if config_enableGeolocationTimeZoneDetection is false -->
     <bool name="config_enableSecondaryLocationTimeZoneProvider" translatable="false">true</bool>
-    <!-- Used when enableSecondaryLocationTimeZoneProvider is true. Controls whether to enable
-         secondary location time zone provider overlay which allows the primary location time zone
-         provider to config_secondaryLocationTimeZoneProviderPackageName package will be searched
-         for the secondary location time zone provider, otherwise any system package is eligible.
-         Anyone who wants to disable the runtime overlay mechanism can set it to false. -->
-    <bool name="config_enableSecondaryLocationTimeZoneOverlay" translatable="false">false</bool>
     <!-- Package name providing the secondary location time zone provider. Used only when
          config_enableSecondaryLocationTimeZoneOverlay is false.
 
@@ -1942,11 +1932,13 @@
     <!-- The name of the package that will hold the speech recognizer role by default. -->
     <string name="config_systemSpeechRecognizer" translatable="false"></string>
     <!-- The name of the package that will hold the system Wi-Fi coex manager role. -->
-    <string name="config_systemWifiCoexManager" translateable="false"></string>
+    <string name="config_systemWifiCoexManager" translatable="false"></string>
     <!-- The name of the package that will hold the wellbeing role. -->
     <string name="config_systemWellbeing" translatable="false"></string>
     <!-- The name of the package that will hold the television notification handler role -->
     <string name="config_systemTelevisionNotificationHandler" translatable="false"></string>
+    <!-- The name of the package that will hold the system activity recognizer role. -->
+    <string name="config_systemActivityRecognizer" translatable="false"></string>
 
     <!-- The name of the package that will be allowed to change its components' label/icon. -->
     <string name="config_overrideComponentUiPackage" translatable="false"></string>
@@ -2580,10 +2572,6 @@
     <!-- Set to true if after a provisioning apn the radio should be restarted -->
     <bool name="config_restartRadioAfterProvisioning">false</bool>
 
-    <!-- Boolean indicating if RADIO POWER OFF is required on receiving SIM REFRESH with RESET.
-         This will be handled by modem if it is false. -->
-    <bool name="config_requireRadioPowerOffOnSimRefreshReset">false</bool>
-
     <!-- Vibrator pattern to be used as the default for notifications
          that specify DEFAULT_VIBRATE.
      -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b402c95..3a5621b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3092,7 +3092,7 @@
     <public name="attributionTags"/>
     <public name="suppressesSpellChecker" />
     <public name="usesPermissionFlags" />
-    <public name="requestOptimizedExternalStorageAccess" />
+    <public name="requestRawExternalStorageAccess" />
     <!-- @hide @SystemApi -->
     <public name="playHomeTransitionSound" />
     <public name="lStar" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4c421ed..90e9c09 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -823,9 +823,9 @@
     <string name="permgroupdesc_camera">take pictures and record video</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]-->
-    <string name="permgrouplab_nearby_devices">Nearby Bluetooth Devices</string>
+    <string name="permgrouplab_nearby_devices">Nearby devices</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]-->
-    <string name="permgroupdesc_nearby_devices">discover and connect to nearby Bluetooth devices</string>
+    <string name="permgroupdesc_nearby_devices">discover and connect to nearby devices</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_calllog">Call logs</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index eb1fe1d..dbfb030 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -350,7 +350,6 @@
   <java-symbol type="bool" name="config_camera_sound_forced" />
   <java-symbol type="bool" name="config_dontPreferApn" />
   <java-symbol type="bool" name="config_restartRadioAfterProvisioning" />
-  <java-symbol type="bool" name="config_requireRadioPowerOffOnSimRefreshReset" />
   <java-symbol type="bool" name="config_speed_up_audio_on_mt_calls" />
   <java-symbol type="bool" name="config_useFixedVolume" />
   <java-symbol type="bool" name="config_enableMultiUserUI"/>
@@ -2180,10 +2179,8 @@
   <java-symbol type="bool" name="config_enableGnssTimeUpdateService" />
   <java-symbol type="bool" name="config_enableGeolocationTimeZoneDetection" />
   <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneProvider" />
-  <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneOverlay" />
   <java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" />
   <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneProvider" />
-  <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneOverlay" />
   <java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" />
 
   <java-symbol type="layout" name="resolver_list" />
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
index dd60dd4..1a63660 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
@@ -44,8 +44,6 @@
 import com.android.bandwidthtest.NetworkState.StateTransitionDirection;
 import com.android.internal.util.AsyncChannel;
 
-import junit.framework.Assert;
-
 import java.io.IOException;
 import java.net.UnknownHostException;
 import java.util.List;
@@ -76,7 +74,11 @@
     private WifiManager mWifiManager;
     private Context mContext;
     // Verify connectivity state
-    private static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE + 1;
+    // ConnectivityManager.TYPE_* is deprecated and no longer extended, so use the max public
+    // network type - TYPE_VPN should be enough.
+    // TODO: Replace registering CONNECTIVITY_ACTION with registering NetworkCallback and check
+    //  network by NetworkCapabilities.TRANSPORT_* and NetworkCapabilities.hasTransport() instead.
+    private static final int NUM_NETWORK_TYPES = ConnectivityManager.TYPE_VPN + 1;
     private NetworkState[] mConnectivityState = new NetworkState[NUM_NETWORK_TYPES];
 
     public ConnectionUtil(Context context) {
diff --git a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
index 8271bed..5de6d42 100644
--- a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
+++ b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
@@ -128,8 +128,8 @@
         rangingManager.onRangingReconfigureFailed(handle, REASON, PARAMS);
         verify(callback, times(1)).onReconfigureFailed(eq(REASON), eq(PARAMS));
 
-        rangingManager.onRangingStopped(handle);
-        verify(callback, times(1)).onStopped();
+        rangingManager.onRangingStopped(handle, REASON, PARAMS);
+        verify(callback, times(1)).onStopped(eq(REASON), eq(PARAMS));
 
         rangingManager.onRangingStopFailed(handle, REASON, PARAMS);
         verify(callback, times(1)).onStopFailed(eq(REASON), eq(PARAMS));
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 0987f77..489da16 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -429,6 +429,7 @@
         <permission name="android.permission.LOG_COMPAT_CHANGE" />
         <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
         <permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" />
+        <permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD" />
         <!-- Permissions required to test ambient display. -->
         <permission name="android.permission.READ_DREAM_STATE" />
         <permission name="android.permission.WRITE_DREAM_STATE" />
diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
index 9ee1ef1..c317831 100644
--- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java
+++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
@@ -89,7 +89,7 @@
     }
 
     public boolean shouldAnimateSparkle() {
-        return mAnimateSparkle;
+        return mAnimateSparkle && ValueAnimator.getDurationScale() > 0;
     }
 
     public float getSparklePhase() {
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 8b8cbbc..0865332 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -180,6 +180,7 @@
     private Matrix mMaskMatrix;
     private PorterDuffColorFilter mMaskColorFilter;
     private boolean mHasValidMask;
+    private int mComputedRadius = -1;
 
     /** The current ripple. May be actively animating or pending entry. */
     private RippleForeground mRipple;
@@ -385,9 +386,7 @@
             mRipple.onBoundsChange();
         }
 
-        mState.mMaxRadius = mState.mMaxRadius <= 0 && mState.mRippleStyle != STYLE_SOLID
-                ? (int) computeRadius()
-                : mState.mMaxRadius;
+        mComputedRadius = Math.round(computeRadius());
         invalidateSelf();
     }
 
@@ -918,7 +917,7 @@
             ColorFilter origFilter = p.getColorFilter();
             p.setColorFilter(mMaskColorFilter);
             p.setAlpha(alpha);
-            c.drawCircle(cx, cy, mState.mMaxRadius, p);
+            c.drawCircle(cx, cy, getComputedRadius(), p);
             p.setAlpha(origAlpha);
             p.setColorFilter(origFilter);
         }
@@ -930,11 +929,17 @@
         return radius;
     }
 
+    private int getComputedRadius() {
+        if (mState.mMaxRadius >= 0) return mState.mMaxRadius;
+        if (mComputedRadius >= 0) return mComputedRadius;
+        return (int) computeRadius();
+    }
+
     @NonNull
     private RippleAnimationSession.AnimationProperties<Float, Paint> createAnimationProperties(
             float x, float y, float cx, float cy, float w, float h) {
         Paint p = new Paint(mRipplePaint);
-        float radius = mState.mMaxRadius;
+        float radius = getComputedRadius();
         RippleAnimationSession.AnimationProperties<Float, Paint> properties;
         RippleShader shader = new RippleShader();
         int color = mMaskColorFilter == null
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index a9b2447..4608d02 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -71,7 +71,8 @@
             + "  float thickness = 0.3 * radius;\n"
             + "  float currentRadius = radius * progress;\n"
             + "  float circle_outer = softCircle(uv, xy, currentRadius + thickness, blur);\n"
-            + "  float circle_inner = softCircle(uv, xy, currentRadius - thickness, blur);\n"
+            + "  float circle_inner = softCircle(uv, xy, max(currentRadius - thickness, 0.), "
+            + "    blur);\n"
             + "  return saturate(circle_outer - circle_inner);\n"
             + "}\n"
             + "float subProgress(float start, float end, float progress) {\n"
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 1f9022b..a6aa4f2 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -353,7 +353,7 @@
             boolean userPresenceRequired,
             byte[] attestationChallenge,
             boolean devicePropertiesAttestationIncluded,
-            int[] attestationIds,
+            @NonNull int[] attestationIds,
             boolean uniqueIdIncluded,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
@@ -779,9 +779,8 @@
      * @return integer array representing the requested device IDs to attest.
      */
     @SystemApi
-    @Nullable
-    public int[] getAttestationIds() {
-        return Utils.cloneIfNotNull(mAttestationIds);
+    public @NonNull int[] getAttestationIds() {
+        return mAttestationIds.clone();
     }
 
     /**
@@ -911,7 +910,7 @@
         private boolean mUserPresenceRequired = false;
         private byte[] mAttestationChallenge = null;
         private boolean mDevicePropertiesAttestationIncluded = false;
-        private int[] mAttestationIds = null;
+        private int[] mAttestationIds = new int[0];
         private boolean mUniqueIdIncluded = false;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index c26d9f583..dc7f3dd 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -655,7 +655,7 @@
             }
 
             int[] idTypes = mSpec.getAttestationIds();
-            if (idTypes == null) {
+            if (idTypes.length == 0) {
                 return;
             }
             final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAccessibilityUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAccessibilityUtil.java
new file mode 100644
index 0000000..1302461
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAccessibilityUtil.java
@@ -0,0 +1,91 @@
+/*
+ * 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.onehanded;
+
+import android.content.Context;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.R;
+
+import java.io.PrintWriter;
+
+/**
+ * The util for handling A11y events.
+ */
+public final class OneHandedAccessibilityUtil {
+    private static final String TAG = "OneHandedAccessibilityUtil";
+
+    private final AccessibilityManager mAccessibilityManager;
+    private final String mStartOneHandedDescription;
+    private final String mStopOneHandedDescription;
+    private final String mPackageName;
+
+    private String mDescription;
+
+    public OneHandedAccessibilityUtil(Context context) {
+        mAccessibilityManager = AccessibilityManager.getInstance(context);
+        mPackageName = context.getPackageName();
+        mStartOneHandedDescription = context.getResources().getString(
+                R.string.accessibility_action_start_one_handed);
+        mStopOneHandedDescription = context.getResources().getString(
+                R.string.accessibility_action_stop_one_handed);
+    }
+
+    /**
+     * Gets One-Handed start description.
+     * @return text of start description.
+     */
+    public String getOneHandedStartDescription() {
+        return mStartOneHandedDescription;
+    }
+
+    /**
+     * Gets One-Handed stop description.
+     * @return text of stop description.
+     */
+    public String getOneHandedStopDescription() {
+        return mStopOneHandedDescription;
+    }
+
+    /**
+     * Announcement of A11y Events
+     * @param description for accessibility announcement text
+     */
+    public void announcementForScreenReader(String description) {
+        if (!mAccessibilityManager.isTouchExplorationEnabled()) {
+            return;
+        }
+        mDescription = description;
+        final AccessibilityEvent event = AccessibilityEvent.obtain();
+        event.setPackageName(mPackageName);
+        event.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT);
+        event.getText().add(mDescription);
+        mAccessibilityManager.sendAccessibilityEvent(event);
+    }
+
+    public void dump(@NonNull PrintWriter pw) {
+        final String innerPrefix = "  ";
+        pw.println(TAG + "States: ");
+        pw.print(innerPrefix + "mPackageName=");
+        pw.println(mPackageName);
+        pw.print(innerPrefix + "mDescription=");
+        pw.println(mDescription);
+    }
+}
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 38cf9e6..19098fd 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
@@ -83,6 +83,7 @@
     private final AccessibilityManager mAccessibilityManager;
     private final DisplayController mDisplayController;
     private final OneHandedSettingsUtil mOneHandedSettingsUtil;
+    private final OneHandedAccessibilityUtil mOneHandedAccessibilityUtil;
     private final OneHandedTimeoutHandler mTimeoutHandler;
     private final OneHandedTouchHandler mTouchHandler;
     private final OneHandedTutorialHandler mTutorialHandler;
@@ -193,6 +194,8 @@
             return null;
         }
 
+        OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
+        OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context);
         OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
         OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
                 windowManager, mainExecutor);
@@ -205,16 +208,16 @@
         OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
                 new OneHandedBackgroundPanelOrganizer(context, displayLayout, mainExecutor);
         OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
-                context, displayLayout, animationController, tutorialHandler,
+                context, displayLayout, settingsUtil, animationController, tutorialHandler,
                 oneHandedBackgroundPanelOrganizer, mainExecutor);
-        OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
         OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
         IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
                 ServiceManager.getService(Context.OVERLAY_SERVICE));
         return new OneHandedController(context, displayController,
                 oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
-                gestureHandler, settingsUtil, timeoutHandler, oneHandedUiEventsLogger,
-                overlayManager, taskStackListener, mainExecutor, mainHandler);
+                gestureHandler, settingsUtil, accessibilityUtil, timeoutHandler,
+                oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor,
+                mainHandler);
     }
 
     @VisibleForTesting
@@ -226,6 +229,7 @@
             OneHandedTutorialHandler tutorialHandler,
             OneHandedGestureHandler gestureHandler,
             OneHandedSettingsUtil settingsUtil,
+            OneHandedAccessibilityUtil oneHandedAccessibilityUtil,
             OneHandedTimeoutHandler timeoutHandler,
             OneHandedUiEventLogger uiEventsLogger,
             IOverlayManager overlayManager,
@@ -234,6 +238,7 @@
             Handler mainHandler) {
         mContext = context;
         mOneHandedSettingsUtil = settingsUtil;
+        mOneHandedAccessibilityUtil = oneHandedAccessibilityUtil;
         mBackgroundPanelOrganizer = backgroundPanelOrganizer;
         mDisplayAreaOrganizer = displayAreaOrganizer;
         mDisplayController = displayController;
@@ -334,6 +339,8 @@
         if (!mDisplayAreaOrganizer.isInOneHanded()) {
             final int yOffSet = Math.round(
                     mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
+            mOneHandedAccessibilityUtil.announcementForScreenReader(
+                    mOneHandedAccessibilityUtil.getOneHandedStartDescription());
             mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
             mTimeoutHandler.resetTimer();
 
@@ -345,6 +352,8 @@
     @VisibleForTesting
     void stopOneHanded() {
         if (mDisplayAreaOrganizer.isInOneHanded()) {
+            mOneHandedAccessibilityUtil.announcementForScreenReader(
+                    mOneHandedAccessibilityUtil.getOneHandedStopDescription());
             mDisplayAreaOrganizer.scheduleOffset(0, 0);
             mTimeoutHandler.removeTimer();
         }
@@ -352,6 +361,8 @@
 
     private void stopOneHanded(int uiEvent) {
         if (mDisplayAreaOrganizer.isInOneHanded()) {
+            mOneHandedAccessibilityUtil.announcementForScreenReader(
+                    mOneHandedAccessibilityUtil.getOneHandedStopDescription());
             mDisplayAreaOrganizer.scheduleOffset(0, 0);
             mTimeoutHandler.removeTimer();
             mOneHandedUiEventLogger.writeEvent(uiEvent);
@@ -629,6 +640,10 @@
             mTutorialHandler.dump(pw);
         }
 
+        if (mOneHandedAccessibilityUtil != null) {
+            mOneHandedAccessibilityUtil.dump(pw);
+        }
+
         mOneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver(), mUserId);
 
         if (mOverlayManager != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 682c9a3f..d1b3f1a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.onehanded;
 
+import static android.os.UserHandle.myUserId;
+
 import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT;
 import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
 
@@ -61,6 +63,7 @@
 
     private final Rect mLastVisualDisplayBounds = new Rect();
     private final Rect mDefaultDisplayBounds = new Rect();
+    private final OneHandedSettingsUtil mOneHandedSettingsUtil;
 
     private boolean mIsInOneHanded;
     private int mEnterExitAnimationDurationMs;
@@ -109,12 +112,14 @@
      */
     public OneHandedDisplayAreaOrganizer(Context context,
             DisplayLayout displayLayout,
+            OneHandedSettingsUtil oneHandedSettingsUtil,
             OneHandedAnimationController animationController,
             OneHandedTutorialHandler tutorialHandler,
             OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
             ShellExecutor mainExecutor) {
         super(mainExecutor);
         mDisplayLayout.set(displayLayout);
+        mOneHandedSettingsUtil = oneHandedSettingsUtil;
         updateDisplayBounds();
         mAnimationController = animationController;
         final int animationDurationConfig = context.getResources().getInteger(
@@ -168,6 +173,11 @@
         if (mDisplayLayout.rotation() == toRotation) {
             return;
         }
+
+        if (!mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(context.getContentResolver(),
+                myUserId())) {
+            return;
+        }
         mDisplayLayout.rotateTo(context.getResources(), toRotation);
         resetWindowsOffset(wct);
         updateDisplayBounds();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index d539835..b445917 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -28,8 +28,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
@@ -51,7 +49,6 @@
             "persist.debug.one_handed_offset_percentage";
     private static final int MAX_TUTORIAL_SHOW_COUNT = 2;
     private final WindowManager mWindowManager;
-    private final AccessibilityManager mAccessibilityManager;
     private final String mPackageName;
     private final Rect mDisplaySize;
 
@@ -59,8 +56,6 @@
     private View mTutorialView;
     private ContentResolver mContentResolver;
     private boolean mCanShowTutorial;
-    private String mStartOneHandedDescription;
-    private String mStopOneHandedDescription;
     private boolean mIsOneHandedMode;
 
     private enum ONE_HANDED_TRIGGER_STATE {
@@ -106,11 +101,6 @@
         mDisplaySize = windowManager.getCurrentWindowMetrics().getBounds();
         mPackageName = context.getPackageName();
         mContentResolver = context.getContentResolver();
-        mAccessibilityManager = AccessibilityManager.getInstance(context);
-        mStartOneHandedDescription = context.getResources().getString(
-                R.string.accessibility_action_start_one_handed);
-        mStopOneHandedDescription = context.getResources().getString(
-                R.string.accessibility_action_stop_one_handed);
         mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
                 Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
                 ? false : true;
@@ -131,14 +121,12 @@
     public void onStartFinished(Rect bounds) {
         updateFinished(View.VISIBLE, 0f);
         updateTutorialCount();
-        announcementForScreenReader(true);
         mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
     }
 
     @Override
     public void onStopFinished(Rect bounds) {
         updateFinished(View.INVISIBLE, -mTargetViewContainer.getHeight());
-        announcementForScreenReader(false);
         removeTutorialFromWindowManager();
         mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
     }
@@ -170,17 +158,6 @@
                 Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, showCount);
     }
 
-    private void announcementForScreenReader(boolean isStartOneHanded) {
-        if (mAccessibilityManager.isTouchExplorationEnabled()) {
-            final AccessibilityEvent event = AccessibilityEvent.obtain();
-            event.setPackageName(mPackageName);
-            event.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT);
-            event.getText().add(isStartOneHanded
-                    ? mStartOneHandedDescription : mStopOneHandedDescription);
-            mAccessibilityManager.sendAccessibilityEvent(event);
-        }
-    }
-
     /**
      * Adds the tutorial target view to the WindowManager and update its layout, so it's ready
      * to be animated in.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index e309f96..105bd82 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -58,6 +58,7 @@
 
     Display mDisplay;
     DisplayLayout mDisplayLayout;
+    OneHandedAccessibilityUtil mOneHandedAccessibilityUtil;
     OneHandedController mSpiedOneHandedController;
     OneHandedTimeoutHandler mSpiedTimeoutHandler;
 
@@ -116,6 +117,7 @@
                 new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height()));
         when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(mDisplayLayout);
 
+        mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext);
         mSpiedOneHandedController = spy(new OneHandedController(
                 mContext,
                 mMockDisplayController,
@@ -125,6 +127,7 @@
                 mMockTutorialHandler,
                 mMockGestureHandler,
                 mMockSettingsUitl,
+                mOneHandedAccessibilityUtil,
                 mSpiedTimeoutHandler,
                 mMockUiEventLogger,
                 mMockOverlayManager,
@@ -139,8 +142,8 @@
         final OneHandedAnimationController animationController = new OneHandedAnimationController(
                 mContext);
         OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
-                mContext, mDisplayLayout, animationController, mMockTutorialHandler,
-                mMockBackgroundOrganizer, mMockShellMainExecutor);
+                mContext, mDisplayLayout, mMockSettingsUitl, animationController,
+                mMockTutorialHandler, mMockBackgroundOrganizer, mMockShellMainExecutor);
 
         assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index f654bb5..eb731d2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -97,9 +97,13 @@
     OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
     @Mock
     ShellExecutor mMockShellMainExecutor;
+    @Mock
+    OneHandedSettingsUtil mMockSettingsUitl;
 
     List<DisplayAreaAppearedInfo> mDisplayAreaAppearedInfoList = new ArrayList<>();
 
+    final boolean mDefaultEnabled = true;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -126,9 +130,12 @@
         when(mMockAnimator.setTransitionDirection(anyInt())).thenReturn(mFakeAnimator);
         when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH);
         when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT);
+        when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
+                mDefaultEnabled);
 
         mSpiedDisplayAreaOrganizer = spy(new OneHandedDisplayAreaOrganizer(mContext,
                 mDisplayLayout,
+                mMockSettingsUitl,
                 mMockAnimationController,
                 mTutorialHandler,
                 mMockBackgroundOrganizer,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index 2886bb1..06a6671 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -66,6 +66,8 @@
     OneHandedUiEventLogger mMockUiEventLogger;
     @Mock
     OneHandedSettingsUtil mMockSettingsUtil;
+    @Mock
+    OneHandedAccessibilityUtil mMockAccessibilityUtil;
 
     @Before
     public void setUp() {
@@ -82,6 +84,7 @@
                 mMockTutorialHandler,
                 mMockGestureHandler,
                 mMockSettingsUtil,
+                mMockAccessibilityUtil,
                 mTimeoutHandler,
                 mMockUiEventLogger,
                 mMockOverlayManager,
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index eedb996..24f553f 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -324,6 +324,8 @@
     public static final int ENCODING_MPEGH_LC_L3 = 25;
     /** Audio data format: MPEG-H low complexity profile, level 4 */
     public static final int ENCODING_MPEGH_LC_L4 = 26;
+    /** Audio data format: DTS UHD compressed */
+    public static final int ENCODING_DTS_UHD = 27;
 
     /** @hide */
     public static String toLogFriendlyEncoding(int enc) {
@@ -380,6 +382,8 @@
                 return "ENCODING_MPEGH_LC_L3";
             case ENCODING_MPEGH_LC_L4:
                 return "ENCODING_MPEGH_LC_L4";
+            case ENCODING_DTS_UHD:
+                return "ENCODING_DTS_UHD";
             default :
                 return "invalid encoding " + enc;
         }
@@ -615,6 +619,7 @@
             case ENCODING_MPEGH_BL_L4:
             case ENCODING_MPEGH_LC_L3:
             case ENCODING_MPEGH_LC_L4:
+            case ENCODING_DTS_UHD:
                 return true;
             default:
                 return false;
@@ -650,6 +655,7 @@
             case ENCODING_MPEGH_BL_L4:
             case ENCODING_MPEGH_LC_L3:
             case ENCODING_MPEGH_LC_L4:
+            case ENCODING_DTS_UHD:
                 return true;
             default:
                 return false;
@@ -688,6 +694,7 @@
             case ENCODING_MPEGH_BL_L4:
             case ENCODING_MPEGH_LC_L3:
             case ENCODING_MPEGH_LC_L4:
+            case ENCODING_DTS_UHD:
                 return false;
             case ENCODING_INVALID:
             default:
@@ -726,6 +733,7 @@
             case ENCODING_MPEGH_BL_L4:
             case ENCODING_MPEGH_LC_L3:
             case ENCODING_MPEGH_LC_L4:
+            case ENCODING_DTS_UHD:
                 return false;
             case ENCODING_INVALID:
             default:
@@ -1012,6 +1020,7 @@
                 case ENCODING_MPEGH_BL_L4:
                 case ENCODING_MPEGH_LC_L3:
                 case ENCODING_MPEGH_LC_L4:
+                case ENCODING_DTS_UHD:
                     mEncoding = encoding;
                     break;
                 case ENCODING_INVALID:
@@ -1238,7 +1247,8 @@
         ENCODING_MPEGH_BL_L3,
         ENCODING_MPEGH_BL_L4,
         ENCODING_MPEGH_LC_L3,
-        ENCODING_MPEGH_LC_L4 }
+        ENCODING_MPEGH_LC_L4,
+        ENCODING_DTS_UHD }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface Encoding {}
@@ -1258,6 +1268,7 @@
             ENCODING_MPEGH_BL_L4,
             ENCODING_MPEGH_LC_L3,
             ENCODING_MPEGH_LC_L4,
+            ENCODING_DTS_UHD
     };
 
     /** @hide */
@@ -1274,7 +1285,8 @@
             ENCODING_MPEGH_BL_L3,
             ENCODING_MPEGH_BL_L4,
             ENCODING_MPEGH_LC_L3,
-            ENCODING_MPEGH_LC_L4 }
+            ENCODING_MPEGH_LC_L4,
+            ENCODING_DTS_UHD }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface SurroundSoundEncoding {}
@@ -1316,6 +1328,8 @@
                 return "MPEG-H 3D Audio low complexity profile level 3";
             case ENCODING_MPEGH_LC_L4:
                 return "MPEG-H 3D Audio low complexity profile level 4";
+            case ENCODING_DTS_UHD:
+                return "DTS UHD";
             default:
                 return "Unknown surround sound format";
         }
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 7220379..5f60fb6 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -428,6 +428,8 @@
                 return "AUDIO_FORMAT_MAT_2_0"; // (MAT | MAT_SUB_2_0)
             case /* AUDIO_FORMAT_MAT_2_1           */ 0x24000003:
                 return "AUDIO_FORMAT_MAT_2_1"; // (MAT | MAT_SUB_2_1)
+            case /* AUDIO_FORMAT_DTS_UHD */           0x2E000000:
+                return "AUDIO_FORMAT_DTS_UHD";
             default:
                 return "AUDIO_FORMAT_(" + audioFormat + ")";
         }
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 9566b92..5bb4fbc 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -79,6 +79,7 @@
         "libgui",
         "libharfbuzz_ng",  // Only for including hb.h via minikin
         "libsensor",
+        "libactivitymanager_aidl",
         "libandroid_runtime",
         "libminikin",
         "libnetd_client",
diff --git a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
index 5a76cd6..2bb006d 100644
--- a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
+++ b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
@@ -40,6 +40,23 @@
  */
 @SystemApi
 public final class OemNetworkPreferences implements Parcelable {
+    // Valid production preferences must be > 0, negative values reserved for testing
+    /**
+     * This preference is only to be used for testing and nothing else.
+     * Use only TRANSPORT_TEST transport networks.
+     * @hide
+     */
+    public static final int OEM_NETWORK_PREFERENCE_TEST_ONLY = -2;
+
+    /**
+     * This preference is only to be used for testing and nothing else.
+     * If an unmetered network is available, use it.
+     * Otherwise, if a network with the TRANSPORT_TEST transport is available, use it.
+     * Otherwise, use the general default network.
+     * @hide
+     */
+    public static final int OEM_NETWORK_PREFERENCE_TEST = -1;
+
     /**
      * Default in case this value is not set. Using it will result in an error.
      */
@@ -69,6 +86,12 @@
      */
     public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4;
 
+    /**
+     * The max allowed value for an OEM network preference.
+     * @hide
+     */
+    public static final int OEM_NETWORK_PREFERENCE_MAX = OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+
     @NonNull
     private final Bundle mNetworkMappings;
 
@@ -96,7 +119,7 @@
 
     @Override
     public String toString() {
-        return "OemNetworkPreferences{" + "mNetworkMappings=" + mNetworkMappings + '}';
+        return "OemNetworkPreferences{" + "mNetworkMappings=" + getNetworkPreferences() + '}';
     }
 
     @Override
@@ -185,6 +208,8 @@
 
     /** @hide */
     @IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = {
+            OEM_NETWORK_PREFERENCE_TEST_ONLY,
+            OEM_NETWORK_PREFERENCE_TEST,
             OEM_NETWORK_PREFERENCE_UNINITIALIZED,
             OEM_NETWORK_PREFERENCE_OEM_PAID,
             OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK,
@@ -205,6 +230,10 @@
     @NonNull
     public static String oemNetworkPreferenceToString(@OemNetworkPreference int value) {
         switch (value) {
+            case OEM_NETWORK_PREFERENCE_TEST_ONLY:
+                return "OEM_NETWORK_PREFERENCE_TEST_ONLY";
+            case OEM_NETWORK_PREFERENCE_TEST:
+                return "OEM_NETWORK_PREFERENCE_TEST";
             case OEM_NETWORK_PREFERENCE_UNINITIALIZED:
                 return "OEM_NETWORK_PREFERENCE_UNINITIALIZED";
             case OEM_NETWORK_PREFERENCE_OEM_PAID:
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index 518f198..20ccf06 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -113,7 +113,6 @@
     ],
     jarjar_rules: "jarjar-rules.txt",
     apex_available: [
-        "//apex_available:platform", // For arc-services
         "com.android.tethering",
     ],
 }
diff --git a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
index b0a9b95..397b0f9 100644
--- a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
+++ b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
@@ -165,9 +165,8 @@
     }
 
     private List<EmergencyNumber> getPromotedEmergencyNumbers(int categories) {
-        // TODO(b/171542607): Use platform API when its bug is fixed.
-        Map<Integer, List<EmergencyNumber>> allLists = filterEmergencyNumbersByCategories(
-                mTelephonyManager.getEmergencyNumberList(), categories);
+        Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList(
+                categories);
         if (allLists == null || allLists.isEmpty()) {
             Log.w(TAG, "Unable to retrieve emergency number lists!");
             return new ArrayList<>();
@@ -212,28 +211,4 @@
         }
         return promotedEmergencyNumberLists.get(SubscriptionManager.getDefaultSubscriptionId());
     }
-
-    /**
-     * Filter emergency numbers with categories.
-     */
-    private Map<Integer, List<EmergencyNumber>> filterEmergencyNumbersByCategories(
-            Map<Integer, List<EmergencyNumber>> emergencyNumberList, int categories) {
-        Map<Integer, List<EmergencyNumber>> filteredMap = new ArrayMap<>();
-        if (emergencyNumberList == null) {
-            return filteredMap;
-        }
-        for (Integer subscriptionId : emergencyNumberList.keySet()) {
-            List<EmergencyNumber> allNumbersForSub = emergencyNumberList.get(
-                    subscriptionId);
-            List<EmergencyNumber> numbersForCategoriesPerSub = new ArrayList<>();
-            for (EmergencyNumber number : allNumbersForSub) {
-                if (number.isInEmergencyServiceCategories(categories)) {
-                    numbersForCategoriesPerSub.add(number);
-                }
-            }
-            filteredMap.put(
-                    subscriptionId, numbersForCategoriesPerSub);
-        }
-        return filteredMap;
-    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
index 695b5b6..40339de 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
@@ -23,6 +23,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.mock;
@@ -126,7 +127,7 @@
         List<EmergencyNumber> numbersForSubId = new ArrayList<>();
         numbersForSubId.add(emergencyNumber);
         numbers.put(subId, numbersForSubId);
-        when(mTelephonyManager.getEmergencyNumberList()).thenReturn(numbers);
+        when(mTelephonyManager.getEmergencyNumberList(anyInt())).thenReturn(numbers);
         when(emergencyNumber.getNumber()).thenReturn(TELEPHONY_EMERGENCY_NUMBER);
     }
 }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5a3298d..16937b7 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -176,6 +176,7 @@
     <uses-permission android:name="android.permission.SET_TIME_ZONE" />
     <uses-permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS" />
     <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
+    <uses-permission android:name="android.permission.BYPASS_ROLE_QUALIFICATION" />
     <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" />
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
     <uses-permission android:name="android.permission.STATUS_BAR" />
@@ -265,10 +266,11 @@
     <!-- permissions required for CTS test - PhoneStateListenerTest -->
     <uses-permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" />
 
-    <!-- Permissions required for ganting and logging -->
+    <!-- Permissions required for granting and logging -->
     <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
     <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
     <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/>
+    <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
 
     <!-- Permission required for CTS test - BatterySaverTest -->
     <uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 88d7710..85ecb1c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -275,8 +275,6 @@
     <!-- Permission to make accessibility service access Bubbles -->
     <uses-permission android:name="android.permission.ADD_TRUSTED_DISPLAY" />
 
-    <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" />
-
 
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 35423a9..f8a9a045 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -16,9 +16,7 @@
 
 package com.android.systemui.plugins;
 
-import android.app.smartspace.SmartspaceTarget;
 import android.os.Parcelable;
-import android.view.ViewGroup;
 
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
@@ -38,25 +36,9 @@
     /** Unregister a listener. */
     void unregisterListener(SmartspaceTargetListener listener);
 
-    /**
-     * Create a view to be shown within the parent. Do not add the view, as the parent
-     * will be responsible for correctly setting the LayoutParams
-     */
-    default SmartspaceView getView(ViewGroup parent) {
-        return null;
-    }
-
-    /** Updates Smartspace data and propagates it to any listeners. */
-    void onTargetsAvailable(List<SmartspaceTarget> targets);
-
     /** Provides Smartspace data to registered listeners. */
     interface SmartspaceTargetListener {
         /** Each Parcelable is a SmartspaceTarget that represents a card. */
         void onSmartspaceTargetsUpdated(List<? extends Parcelable> targets);
     }
-
-    /** View to which this plugin can be registered, in order to get updates. */
-    interface SmartspaceView {
-        void registerDataProvider(BcSmartspaceDataPlugin plugin);
-    }
 }
diff --git a/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml b/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
new file mode 100644
index 0000000..bdd6270
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ 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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="?android:attr/colorAccent" />
+    <corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml
new file mode 100644
index 0000000..c90fc31
--- /dev/null
+++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml
@@ -0,0 +1,50 @@
+<!--
+  ~ 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.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/ongoing_call_chip"
+    android:layout_width="wrap_content"
+    android:layout_height="@dimen/ongoing_appops_chip_height"
+    android:layout_gravity="center_vertical|start"
+    android:gravity="center_vertical"
+    android:background="@drawable/ongoing_call_chip_bg"
+    android:paddingStart="@dimen/ongoing_call_chip_side_padding"
+    android:paddingEnd="@dimen/ongoing_call_chip_side_padding"
+    android:visibility="gone"
+>
+
+    <ImageView
+        android:src="@*android:drawable/ic_phone"
+        android:layout_width="@dimen/ongoing_call_chip_icon_size"
+        android:layout_height="@dimen/ongoing_call_chip_icon_size"
+        android:paddingEnd="@dimen/ongoing_call_chip_icon_text_padding"
+        android:tint="?android:attr/colorPrimary"
+    />
+
+    <!-- TODO(b/183229367): The text in this view isn't quite centered within the chip. -->
+    <!-- TODO(b/183229367): This text view's width shouldn't change as the time increases. -->
+    <Chronometer
+        android:id="@+id/ongoing_call_chip_time"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:gravity="center"
+        android:textAppearance="@android:style/TextAppearance.Material.Small"
+        android:fontFamily="@*android:string/config_headlineFontFamily"
+        android:textColor="?android:attr/colorPrimary"
+    />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index f8db97d..8b244c7 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -82,6 +82,8 @@
                     android:gravity="center_vertical|start"
                 />
 
+                <include layout="@layout/ongoing_call_chip" />
+
                 <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
                     android:id="@+id/notification_icon_area"
                     android:layout_width="0dp"
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 4fdeb6f..cd2395e 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -29,4 +29,10 @@
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
+    <!-- Screenshots -->
+    <style name="LongScreenshotActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
+        <item name="android:windowLightStatusBar">false</item>
+        <item name="android:windowLightNavigationBar">false</item>
+    </style>
+
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 237edf8..e834f3f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1442,4 +1442,11 @@
     <dimen name="wallet_empty_state_corner_radius">24dp</dimen>
     <dimen name="wallet_tile_card_view_height">32dp</dimen>
     <dimen name="wallet_tile_card_view_width">50dp</dimen>
+
+    <!-- Ongoing call chip -->
+    <dimen name="ongoing_call_chip_side_padding">12dp</dimen>
+    <dimen name="ongoing_call_chip_icon_size">16dp</dimen>
+    <!-- The padding between the icon and the text. -->
+    <dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
+    <dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index c39db94..834b482 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -49,7 +49,5 @@
 
     <bool name="flag_charging_ripple">false</bool>
 
-    <bool name="flag_ongoing_call_status_bar_chip">false</bool>
-
-    <bool name="flag_smartspace">false</bool>
+    <bool name="flag_ongoing_call_status_bar_chip">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index de14dbd..ba07829 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -623,6 +623,8 @@
     <!-- Screenshots -->
     <style name="LongScreenshotActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
         <item name="android:windowNoTitle">true</item>
+        <item name="android:windowLightStatusBar">true</item>
+        <item name="android:windowLightNavigationBar">true</item>
     </style>
 
     <!-- Privacy dialog -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 24b7cd1..0675200 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -16,15 +16,8 @@
 
 package com.android.keyguard;
 
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
 import android.app.WallpaperManager;
-import android.app.smartspace.SmartspaceConfig;
-import android.app.smartspace.SmartspaceManager;
-import android.app.smartspace.SmartspaceSession;
 import android.content.ContentResolver;
-import android.content.Context;
 import android.content.res.Resources;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -32,7 +25,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
-import android.widget.RelativeLayout;
 
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.clock.ClockManager;
@@ -40,12 +32,8 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin;
 import com.android.systemui.plugins.ClockPlugin;
-import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -55,7 +43,6 @@
 
 import java.util.Locale;
 import java.util.TimeZone;
-import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -81,13 +68,6 @@
     private AnimatableClockController mNewLockScreenLargeClockViewController;
     private FrameLayout mNewLockScreenLargeClockFrame;
 
-    private PluginManager mPluginManager;
-    private boolean mIsSmartspaceEnabled;
-    PluginListener mPluginListener;
-    private Executor mUiExecutor;
-    private SmartspaceSession mSmartspaceSession;
-    private SmartspaceSession.Callback mSmartspaceCallback;
-
     private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
     private final StatusBarStateController.StateListener mStateListener =
@@ -116,9 +96,6 @@
     private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
     private String mTimeFormat;
 
-    // If set, will replace keyguard_status_area
-    private BcSmartspaceDataPlugin.SmartspaceView mSmartspaceView;
-
     @Inject
     public KeyguardClockSwitchController(
             KeyguardClockSwitch keyguardClockSwitch,
@@ -128,10 +105,7 @@
             KeyguardSliceViewController keyguardSliceViewController,
             NotificationIconAreaController notificationIconAreaController,
             ContentResolver contentResolver,
-            BroadcastDispatcher broadcastDispatcher,
-            PluginManager pluginManager,
-            FeatureFlags featureFlags,
-            @Main Executor uiExecutor) {
+            BroadcastDispatcher broadcastDispatcher) {
         super(keyguardClockSwitch);
         mResources = resources;
         mStatusBarStateController = statusBarStateController;
@@ -141,9 +115,6 @@
         mNotificationIconAreaController = notificationIconAreaController;
         mBroadcastDispatcher = broadcastDispatcher;
         mTimeFormat = Settings.System.getString(contentResolver, Settings.System.TIME_12_24);
-        mPluginManager = pluginManager;
-        mIsSmartspaceEnabled = featureFlags.isSmartspaceEnabled();
-        mUiExecutor = uiExecutor;
     }
 
     /**
@@ -166,63 +137,6 @@
         updateAodIcons();
         mNewLockScreenClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view);
         mNewLockScreenLargeClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view_large);
-
-        // If a smartspace plugin is detected, replace the existing smartspace
-        // (keyguard_status_area), and initialize a new session
-        mPluginListener = new PluginListener<BcSmartspaceDataPlugin>() {
-
-            @Override
-            public void onPluginConnected(BcSmartspaceDataPlugin plugin, Context pluginContext) {
-                if (!mIsSmartspaceEnabled) return;
-
-                View ksa = mView.findViewById(R.id.keyguard_status_area);
-                int ksaIndex = mView.indexOfChild(ksa);
-                ksa.setVisibility(View.GONE);
-
-                mSmartspaceView = plugin.getView(mView);
-                mSmartspaceView.registerDataProvider(plugin);
-
-                RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
-                        MATCH_PARENT, WRAP_CONTENT);
-                lp.addRule(RelativeLayout.BELOW, R.id.new_lockscreen_clock_view);
-                mView.addView((View) mSmartspaceView, ksaIndex, lp);
-
-                View nic = mView.findViewById(
-                        com.android.systemui.R.id.left_aligned_notification_icon_container);
-                lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
-                lp.addRule(RelativeLayout.BELOW, ((View) mSmartspaceView).getId());
-                nic.setLayoutParams(lp);
-
-                createSmartspaceSession(plugin);
-            }
-
-            @Override
-            public void onPluginDisconnected(BcSmartspaceDataPlugin plugin) {
-                if (!mIsSmartspaceEnabled) return;
-
-                mView.removeView((View) mSmartspaceView);
-                mView.findViewById(R.id.keyguard_status_area).setVisibility(View.VISIBLE);
-
-                View nic = mView.findViewById(
-                        com.android.systemui.R.id.left_aligned_notification_icon_container);
-                RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)
-                        nic.getLayoutParams();
-                lp.addRule(RelativeLayout.BELOW, R.id.keyguard_status_area);
-                nic.setLayoutParams(lp);
-
-                mSmartspaceView = null;
-            }
-
-            private void createSmartspaceSession(BcSmartspaceDataPlugin plugin) {
-                mSmartspaceSession = getContext().getSystemService(SmartspaceManager.class)
-                        .createSmartspaceSession(
-                                new SmartspaceConfig.Builder(getContext(), "lockscreen").build());
-                mSmartspaceCallback = targets -> plugin.onTargetsAvailable(targets);
-                mSmartspaceSession.registerSmartspaceUpdates(mUiExecutor, mSmartspaceCallback);
-                mSmartspaceSession.requestSmartspaceUpdate();
-            }
-        };
-        mPluginManager.addPluginListener(mPluginListener, BcSmartspaceDataPlugin.class, false);
     }
 
     @Override
@@ -233,13 +147,6 @@
         mStatusBarStateController.removeCallback(mStateListener);
         mColorExtractor.removeOnColorsChangedListener(mColorsListener);
         mView.setClockPlugin(null, mStatusBarStateController.getState());
-
-        if (mSmartspaceSession != null) {
-            mSmartspaceSession.unregisterSmartspaceUpdates(mSmartspaceCallback);
-            mSmartspaceSession.destroy();
-            mSmartspaceSession = null;
-        }
-        mPluginManager.removePluginListener(mPluginListener);
     }
 
     /**
@@ -315,11 +222,6 @@
             PropertyAnimator.setProperty(mNewLockScreenLargeClockFrame, AnimatableProperty.SCALE_Y,
                     scale, props, animate);
         }
-
-        if (mSmartspaceView != null) {
-            PropertyAnimator.setProperty((View) mSmartspaceView, AnimatableProperty.TRANSLATION_X,
-                    x, props, animate);
-        }
         mKeyguardSliceViewController.updatePosition(x, props, animate);
         mNotificationIconAreaController.updatePosition(x, props, animate);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index 2eba952..c23bcb8 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -40,6 +40,7 @@
             new PathInterpolator(0.8f, 0f, 0.6f, 1f);
     public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
     public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
+    public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f);
     public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
     public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
     public static final Interpolator LINEAR = new LinearInterpolator();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index cc167b9..9d00262 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -46,7 +46,6 @@
     private GlobalRootComponent mRootComponent;
     private WMComponent mWMComponent;
     private SysUIComponent mSysUIComponent;
-    private boolean mInitializeComponents;
 
     public static <T extends SystemUIFactory> T getInstance() {
         return (T) mFactory;
@@ -89,13 +88,13 @@
     public void init(Context context, boolean fromTest)
             throws ExecutionException, InterruptedException {
         // Only initialize components for the main system ui process running as the primary user
-        mInitializeComponents = !fromTest
+        final boolean initializeComponents = !fromTest
                 && android.os.Process.myUserHandle().isSystem()
                 && ActivityThread.currentProcessName().equals(ActivityThread.currentPackageName());
         mRootComponent = buildGlobalRootComponent(context);
         // Stand up WMComponent
         mWMComponent = mRootComponent.getWMComponentBuilder().build();
-        if (mInitializeComponents) {
+        if (initializeComponents) {
             // Only initialize when not starting from tests since this currently initializes some
             // components that shouldn't be run in the test environment
             mWMComponent.init();
@@ -103,7 +102,7 @@
 
         // And finally, retrieve whatever SysUI needs from WMShell and build SysUI.
         SysUIComponent.Builder builder = mRootComponent.getSysUIComponent();
-        if (mInitializeComponents) {
+        if (initializeComponents) {
             // Only initialize when not starting from tests since this currently initializes some
             // components that shouldn't be run in the test environment
             builder = prepareSysUIComponentBuilder(builder, mWMComponent)
@@ -135,7 +134,7 @@
                     .setStartingSurface(Optional.ofNullable(null));
         }
         mSysUIComponent = builder.build();
-        if (mInitializeComponents) {
+        if (initializeComponents) {
             mSysUIComponent.init();
         }
 
@@ -161,9 +160,6 @@
                 .build();
     }
 
-    protected boolean shouldInitializeComponents() {
-        return mInitializeComponents;
-    }
 
     public GlobalRootComponent getRootComponent() {
         return mRootComponent;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index 4dd8780..6a012eb 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -38,8 +38,10 @@
     public static final int BOUNCER_UNLOCK = 8;
     public static final int PULSE_EXPAND = 9;
     public static final int BRIGHTNESS_SLIDER = 10;
-    public static final int UDFPS_AUTHENTICATION = 11;
-    public static final int DISABLED_UDFPS_AFFORDANCE = 12;
+    public static final int SHADE_DRAG = 11;
+    public static final int QS_COLLAPSE = 12;
+    public static final int UDFPS_AUTHENTICATION = 13;
+    public static final int DISABLED_UDFPS_AFFORDANCE = 14;
 
     @IntDef({
             QUICK_SETTINGS,
@@ -53,6 +55,9 @@
             BOUNCER_UNLOCK,
             PULSE_EXPAND,
             BRIGHTNESS_SLIDER,
+            SHADE_DRAG,
+            QS_COLLAPSE,
+            BRIGHTNESS_SLIDER,
             UDFPS_AUTHENTICATION,
             DISABLED_UDFPS_AFFORDANCE
     })
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index 6a70622..a4e1637 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -22,6 +22,9 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
+import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
 
 import android.provider.DeviceConfig;
 import android.view.MotionEvent;
@@ -148,7 +151,9 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
-        if (interactionType == Classifier.BRIGHTNESS_SLIDER
+        if (interactionType == BRIGHTNESS_SLIDER
+                || interactionType == SHADE_DRAG
+                || interactionType == QS_COLLAPSE
                 || interactionType == Classifier.UDFPS_AUTHENTICATION
                 || interactionType == Classifier.DISABLED_UDFPS_AFFORDANCE) {
             return Result.passed(0);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
index 6f80010..3bc24c7 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
 import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 
 import android.provider.DeviceConfig;
@@ -116,7 +117,8 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
-        if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER) {
+        if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER
+                || interactionType == QS_COLLAPSE) {
             return Result.passed(0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index 50e94b3..1042516 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -23,8 +23,10 @@
 import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
 import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
 import static com.android.systemui.classifier.Classifier.PULSE_EXPAND;
+import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
+import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
 import static com.android.systemui.classifier.Classifier.UNLOCK;
 
 import javax.inject.Inject;
@@ -77,6 +79,12 @@
             case RIGHT_AFFORDANCE:  // Swiping from the bottom right corner for camera or similar.
                 wrongDirection = right || !up;
                 break;
+            case SHADE_DRAG:
+                wrongDirection = !vertical;
+                break;
+            case QS_COLLAPSE:
+                wrongDirection = !vertical || !up;
+                break;
             default:
                 wrongDirection = true;
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index d9197ef..e1349f2 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -21,6 +21,7 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
 import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
 
 import android.graphics.Point;
 import android.provider.DeviceConfig;
@@ -88,7 +89,7 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
-        if (interactionType == BRIGHTNESS_SLIDER) {
+        if (interactionType == BRIGHTNESS_SLIDER || interactionType == SHADE_DRAG) {
             return Result.passed(0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index 7a8b2c6..b953323 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -118,7 +118,11 @@
     override fun setClickable(clickable: Boolean) {
         super.setClickable(clickable)
         background = if (clickable && mShowRippleEffect) {
-            mTileBackground
+            mRipple?.also {
+                // In case that the colorBackgroundDrawable was used as the background, make sure
+                // it has the correct callback instead of null
+                colorBackgroundDrawable?.callback = it
+            }
         } else {
             colorBackgroundDrawable
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 7ef88bb..5cebf7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -142,9 +142,9 @@
                 }
                 return true;
             case MotionEvent.ACTION_UP:
-                if (!mFalsingManager.isUnlockingDisabled() && !isFalseTouch()
-                        && mDragDownCallback.onDraggedDown(mStartingChild,
-                        (int) (y - mInitialTouchY))) {
+                if (!mFalsingManager.isUnlockingDisabled() && mDragDownCallback.canDragDown()
+                        && !isFalseTouch()) {
+                    mDragDownCallback.onDraggedDown(mStartingChild, (int) (y - mInitialTouchY));
                     if (mStartingChild == null) {
                         cancelExpansion();
                     } else {
@@ -263,7 +263,10 @@
         /**
          * @return true if the interaction is accepted, false if it should be cancelled
          */
-        boolean onDraggedDown(View startingChild, int dragLengthY);
+        boolean canDragDown();
+
+        /** Call when a view has been dragged. */
+        void onDraggedDown(View startingChild, int dragLengthY);
         void onDragDownReset();
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index ec3a857..f51fbed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -97,8 +97,4 @@
     public boolean isOngoingCallStatusBarChipEnabled() {
         return mFlagReader.isEnabled(R.bool.flag_ongoing_call_status_bar_chip);
     }
-
-    public boolean isSmartspaceEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_smartspace);
-    }
 }
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 14c73b5..20383fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -63,6 +63,7 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.SystemClock;
 import com.android.wm.shell.bubbles.Bubbles;
 
 import java.util.Optional;
@@ -234,9 +235,11 @@
     @Provides
     @SysUISingleton
     static OngoingCallController provideOngoingCallController(
-            CommonNotifCollection notifCollection, FeatureFlags featureFlags) {
+            CommonNotifCollection notifCollection,
+            FeatureFlags featureFlags,
+            SystemClock systemClock) {
         OngoingCallController ongoingCallController =
-                new OngoingCallController(notifCollection, featureFlags);
+                new OngoingCallController(notifCollection, featureFlags, systemClock);
         ongoingCallController.init();
         return ongoingCallController;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 18e5ead..17f70cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -720,6 +720,10 @@
             mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
             mCurrentAlphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
             targetValue = 1.0f;
+            if (!mIsHeadsUpAnimation && isChildInGroup()) {
+                // slower fade in of children to avoid visibly overlapping with other children
+                mCurrentAlphaInterpolator = Interpolators.SLOW_OUT_LINEAR_IN;
+            }
         } else {
             mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
             mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
@@ -819,6 +823,10 @@
         if (mIsHeadsUpAnimation && !mIsAppearing) {
             startWidthFraction = 0;
         }
+        if (mIsAppearing && !mIsHeadsUpAnimation && isChildInGroup()) {
+            // Children in a group (when not heads up) should simply fade in.
+            startWidthFraction = 1;
+        }
         float width = MathUtils.lerp(startWidthFraction, 1.0f, 1.0f - widthFraction)
                         * getWidth();
         float left;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index ad06e7d..b28cc14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -5499,9 +5499,16 @@
     @ShadeViewRefactor(RefactorComponent.INPUT)
     private final DragDownCallback mDragDownCallback = new DragDownCallback() {
 
+        @Override
+        public boolean canDragDown() {
+            return mStatusBarState == StatusBarState.KEYGUARD
+                    && (mController.hasActiveNotifications() || mKeyguardMediaControllorVisible)
+                    || mController.isInLockedDownShade();
+        }
+
         /* Only ever called as a consequence of a lockscreen expansion gesture. */
         @Override
-        public boolean onDraggedDown(View startingChild, int dragLengthY) {
+        public void onDraggedDown(View startingChild, int dragLengthY) {
             boolean canDragDown =
                     mController.hasActiveNotifications() || mKeyguardMediaControllorVisible;
             if (mStatusBarState == StatusBarState.KEYGUARD && canDragDown) {
@@ -5519,16 +5526,10 @@
                         row.onExpandedByGesture(true /* drag down is always an open */);
                     }
                 }
-
-                return true;
             } else if (mController.isInLockedDownShade()) {
                 mStatusbarStateController.setLeaveOpenOnKeyguardHide(true);
                 mStatusBar.dismissKeyguardThenExecute(() -> false /* dismissAction */,
                         null /* cancelRunnable */, false /* afterKeyguardGone */);
-                return true;
-            } else {
-                // abort gesture.
-                return false;
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index ce7b397..7776e69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -59,10 +59,12 @@
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.SwipeHelper;
+import com.android.systemui.classifier.Classifier;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.media.KeyguardMediaController;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -143,6 +145,7 @@
     private final ZenModeController mZenModeController;
     private final MetricsLogger mMetricsLogger;
     private final FalsingCollector mFalsingCollector;
+    private final FalsingManager mFalsingManager;
     private final Resources mResources;
     private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
     private final ScrimController mScrimController;
@@ -556,6 +559,7 @@
             NotificationLockscreenUserManager lockscreenUserManager,
             MetricsLogger metricsLogger,
             FalsingCollector falsingCollector,
+            FalsingManager falsingManager,
             @Main Resources resources,
             NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
             StatusBar statusBar,
@@ -589,6 +593,7 @@
         mLockscreenUserManager = lockscreenUserManager;
         mMetricsLogger = metricsLogger;
         mFalsingCollector = falsingCollector;
+        mFalsingManager = falsingManager;
         mResources = resources;
         mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder;
         mStatusBar = statusBar;
@@ -1614,6 +1619,9 @@
                 }
             }
             if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+                // Ensure the falsing manager records the touch. we don't do anything with it
+                // at the moment.
+                mFalsingManager.isFalseTouch(Classifier.SHADE_DRAG);
                 mView.setCheckForLeaveBehind(true);
             }
             traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index f64a0e0..9d1c609 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
 import com.android.systemui.statusbar.policy.EncryptionHelper;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.NetworkController;
@@ -67,6 +68,7 @@
     private NetworkController mNetworkController;
     private LinearLayout mSystemIconArea;
     private View mClockView;
+    private ViewGroup mOngoingCallChip;
     private View mNotificationIconAreaInner;
     private View mCenteredIconArea;
     private int mDisabled1;
@@ -86,6 +88,20 @@
         }
     };
 
+    private final OngoingCallListener mOngoingCallListener = new OngoingCallListener() {
+        @Override
+        public void onOngoingCallStarted(boolean animate) {
+            disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate);
+            animateShow(mOngoingCallChip, animate);
+        }
+
+        @Override
+        public void onOngoingCallEnded(boolean animate) {
+            disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate);
+            animateHiddenState(mOngoingCallChip, View.GONE, animate);
+        }
+    };
+
     @Inject
     public CollapsedStatusBarFragment(OngoingCallController ongoingCallController) {
         mOngoingCallController = ongoingCallController;
@@ -142,6 +158,7 @@
         super.onResume();
         mCommandQueue.addCallback(this);
         mStatusBarStateController.addCallback(this);
+        mOngoingCallController.addCallback(mOngoingCallListener);
     }
 
     @Override
@@ -149,6 +166,7 @@
         super.onPause();
         mCommandQueue.removeCallback(this);
         mStatusBarStateController.removeCallback(this);
+        mOngoingCallController.removeCallback(mOngoingCallListener);
     }
 
     @Override
@@ -179,6 +197,8 @@
         }
         statusBarCenteredIconArea.addView(mCenteredIconArea);
 
+        initOngoingCallChip();
+
         // Default to showing until we know otherwise.
         showNotificationIconArea(false);
     }
@@ -228,6 +248,10 @@
             state |= DISABLE_CLOCK;
         }
 
+        if (mOngoingCallController.getHasOngoingCall()) {
+            state |= DISABLE_NOTIFICATION_ICONS;
+        }
+
         if (!mKeyguardStateController.isLaunchTransitionFadingAway()
                 && !mKeyguardStateController.isKeyguardFadingAway()
                 && shouldHideNotificationIcons()
@@ -395,6 +419,11 @@
         }
     }
 
+    private void initOngoingCallChip() {
+        mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
+        mOngoingCallController.setChipView(mOngoingCallChip);
+    }
+
     @Override
     public void onStateChanged(int newState) {
 
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 364b532..610369b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -24,6 +24,7 @@
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
@@ -1450,11 +1451,7 @@
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
                 trackMovement(event);
-                if (mQsTracking) {
-                    flingQsWithCurrentVelocity(y,
-                            event.getActionMasked() == MotionEvent.ACTION_CANCEL);
-                    mQsTracking = false;
-                }
+                mQsTracking = false;
                 break;
         }
         return false;
@@ -1526,10 +1523,17 @@
 
     private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
         float vel = getCurrentQSVelocity();
-        final boolean expandsQs = flingExpandsQs(vel);
+        boolean expandsQs = flingExpandsQs(vel);
         if (expandsQs) {
-            logQsSwipeDown(y);
+            if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) {
+                expandsQs = false;
+            } else {
+                logQsSwipeDown(y);
+            }
+        } else if (vel < 0) {
+            mFalsingManager.isFalseTouch(QS_COLLAPSE);
         }
+
         flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE);
     }
 
@@ -1545,9 +1549,6 @@
     }
 
     private boolean flingExpandsQs(float vel) {
-        if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) {
-            return false;
-        }
         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
             return getQsExpansionFraction() > 0.5f;
         } else {
@@ -1556,9 +1557,6 @@
     }
 
     private boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
-        if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) {
-            return false;
-        }
         if (mFalsingManager.isClassifierEnabled()) {
             return mFalsingManager.isFalseTouch(interactionType);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 77abe79..936e289 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.GENERIC;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.classifier.Classifier.UNLOCK;
 
@@ -430,9 +431,9 @@
                 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
                 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK);
             }
-            @Classifier.InteractionType int interactionType = vel > 0
-                    ? QUICK_SETTINGS : (
-                            mKeyguardStateController.canDismissLockScreen()
+            @Classifier.InteractionType int interactionType = vel == 0 ? GENERIC
+                    : vel > 0 ? QUICK_SETTINGS
+                            : (mKeyguardStateController.canDismissLockScreen()
                                     ? UNLOCK : BOUNCER_UNLOCK);
 
             fling(vel, expand, isFalseTouch(x, y, interactionType));
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 7e433e8..c37ddd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1825,7 +1825,7 @@
     }
 
     public boolean isFalsingThresholdNeeded() {
-        return mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+        return true;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 60d3ea3..b55db6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -19,11 +19,16 @@
 import android.app.Notification
 import android.app.Notification.CallStyle.CALL_TYPE_ONGOING
 import android.util.Log
+import android.view.ViewGroup
+import android.widget.Chronometer
+import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.FeatureFlags
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 
 /**
@@ -32,19 +37,39 @@
 @SysUISingleton
 class OngoingCallController @Inject constructor(
     private val notifCollection: CommonNotifCollection,
-    private val featureFlags: FeatureFlags
-) {
+    private val featureFlags: FeatureFlags,
+    private val systemClock: SystemClock
+) : CallbackController<OngoingCallListener> {
+
+    var hasOngoingCall = false
+        private set
+    private var chipView: ViewGroup? = null
+
+    private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
 
     private val notifListener = object : NotifCollectionListener {
         override fun onEntryUpdated(entry: NotificationEntry) {
-            if (isOngoingCallNotification(entry) && DEBUG) {
-                Log.d(TAG, "Ongoing call notification updated")
+            if (isOngoingCallNotification(entry)) {
+                val timeView = chipView?.findViewById<Chronometer>(R.id.ongoing_call_chip_time)
+                if (timeView != null) {
+                    hasOngoingCall = true
+                    val callStartTime = entry.sbn.notification.`when`
+                    timeView.base = callStartTime -
+                            System.currentTimeMillis() +
+                            systemClock.elapsedRealtime()
+                    timeView.start()
+                    mListeners.forEach { l -> l.onOngoingCallStarted(animate = true) }
+                } else if (DEBUG) {
+                    Log.w(TAG, "Ongoing call chip view could not be found; " +
+                            "Not displaying chip in status bar")
+                }
             }
         }
 
         override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
-            if (isOngoingCallNotification(entry) && DEBUG) {
-                Log.d(TAG, "Ongoing call notification removed")
+            if (isOngoingCallNotification(entry)) {
+                hasOngoingCall = false
+                mListeners.forEach { l -> l.onOngoingCallEnded(animate = true) }
             }
         }
     }
@@ -54,6 +79,24 @@
             notifCollection.addCollectionListener(notifListener)
         }
     }
+
+    fun setChipView(chipView: ViewGroup?) {
+        this.chipView = chipView
+    }
+
+    override fun addCallback(listener: OngoingCallListener) {
+        synchronized(mListeners) {
+            if (!mListeners.contains(listener)) {
+                mListeners.add(listener)
+            }
+        }
+    }
+
+    override fun removeCallback(listener: OngoingCallListener) {
+        synchronized(mListeners) {
+            mListeners.remove(listener)
+        }
+    }
 }
 
 private fun isOngoingCallNotification(entry: NotificationEntry): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt
new file mode 100644
index 0000000..7c583a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.ongoingcall
+
+/** A listener that's notified when an ongoing call is started or ended. */
+interface OngoingCallListener {
+    /** Called when an ongoing call is started. */
+    fun onOngoingCallStarted(animate: Boolean)
+
+    /** Called when an ongoing call is ended. */
+    fun onOngoingCallEnded(animate: Boolean)
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 0fcd79b..70a7b7a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -18,34 +18,26 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
-import android.content.Context;
 import android.content.res.Resources;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
-import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
-import android.widget.RelativeLayout;
 
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.clock.ClockManager;
-import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
@@ -58,8 +50,6 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.verification.VerificationMode;
 
-import java.util.concurrent.Executor;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@@ -88,12 +78,6 @@
     ContentResolver mContentResolver;
     @Mock
     BroadcastDispatcher mBroadcastDispatcher;
-    @Mock
-    private PluginManager mPluginManager;
-    @Mock
-    private FeatureFlags mFeatureFlags;
-    @Mock
-    private Executor mExecutor;
 
     private KeyguardClockSwitchController mController;
 
@@ -103,8 +87,6 @@
 
         when(mView.findViewById(com.android.systemui.R.id.left_aligned_notification_icon_container))
                 .thenReturn(mNotificationIcons);
-        when(mView.getContext()).thenReturn(getContext());
-        when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
         when(mView.isAttachedToWindow()).thenReturn(true);
         when(mResources.getString(anyInt())).thenReturn("h:mm");
         mController = new KeyguardClockSwitchController(
@@ -116,10 +98,7 @@
                 mKeyguardSliceViewController,
                 mNotificationIconAreaController,
                 mContentResolver,
-                mBroadcastDispatcher,
-                mPluginManager,
-                mFeatureFlags,
-                mExecutor);
+                mBroadcastDispatcher);
 
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
         when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
@@ -203,45 +182,6 @@
         verify(mView).setClockPlugin(mClockPlugin, StatusBarState.SHADE);
     }
 
-    @Test
-    public void testSmartspacePluginConnectedRemovesKeyguardStatusArea() {
-        mController.init();
-
-        View statusArea = mock(View.class);
-        when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(statusArea);
-
-        View nic = mock(View.class);
-        when(mView.findViewById(R.id.left_aligned_notification_icon_container)).thenReturn(nic);
-        when(nic.getLayoutParams()).thenReturn(mock(RelativeLayout.LayoutParams.class));
-
-        BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class);
-        TestView view = mock(TestView.class);
-        when(plugin.getView(any())).thenReturn(view);
-
-        mController.mPluginListener.onPluginConnected(plugin, mContext);
-        verify(statusArea).setVisibility(View.GONE);
-    }
-
-    @Test
-    public void testSmartspacePluginDisconnectedShowsKeyguardStatusArea() {
-        mController.init();
-
-        View statusArea = mock(View.class);
-        when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(statusArea);
-
-        View nic = mock(View.class);
-        when(mView.findViewById(R.id.left_aligned_notification_icon_container)).thenReturn(nic);
-        when(nic.getLayoutParams()).thenReturn(mock(RelativeLayout.LayoutParams.class));
-
-        BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class);
-        TestView view = mock(TestView.class);
-        when(plugin.getView(any())).thenReturn(view);
-
-        mController.mPluginListener.onPluginConnected(plugin, mContext);
-        mController.mPluginListener.onPluginDisconnected(plugin);
-        verify(statusArea).setVisibility(View.VISIBLE);
-    }
-
     private void verifyAttachment(VerificationMode times) {
         verify(mClockManager, times).addOnClockChangedListener(
                 any(ClockManager.ClockChangedListener.class));
@@ -251,12 +191,4 @@
                 any(ColorExtractor.OnColorsChangedListener.class));
         verify(mView, times).updateColors(mGradientColors);
     }
-
-    private static class TestView extends View implements BcSmartspaceDataPlugin.SmartspaceView {
-        TestView(Context context, AttributeSet attrs) {
-            super(context, attrs);
-        }
-
-        public void registerDataProvider(BcSmartspaceDataPlugin plugin) { }
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
index 3c4fde8..895339f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
@@ -43,6 +43,7 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingCollectorFake;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -160,6 +161,7 @@
                 mNotificationLockscreenUserManager,
                 mMetricsLogger,
                 new FalsingCollectorFake(),
+                new FalsingManagerFake(),
                 mResources,
                 mNotificationSwipeHelperBuilder,
                 mStatusBar,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 96cdaf9..67fd5eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -38,12 +38,16 @@
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
+import java.util.Objects;
+
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
@@ -53,6 +57,7 @@
     private View mNotificationAreaInner;
     private View mCenteredNotificationAreaView;
     private StatusBarStateController mStatusBarStateController;
+    private OngoingCallController mOngoingCallController;
 
     public CollapsedStatusBarFragmentTest() {
         super(CollapsedStatusBarFragment.class);
@@ -162,8 +167,51 @@
         Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
     }
 
+    @Test
+    public void onOngoingCallStarted_notificationsHiddenAndOngoingCallChipDisplayed() {
+        mFragments.dispatchResume();
+        processAllMessages();
+
+        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+        fragment.initNotificationIconArea(mMockNotificiationAreaController);
+
+        ArgumentCaptor<OngoingCallListener> ongoingCallListenerCaptor = ArgumentCaptor.forClass(
+                OngoingCallListener.class);
+        Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture());
+        OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue());
+
+        when(mOngoingCallController.getHasOngoingCall()).thenReturn(true);
+        listener.onOngoingCallStarted(/* animate= */ false);
+
+        assertEquals(View.VISIBLE,
+                mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+        Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+    }
+
+    @Test
+    public void onOngoingCallEnded_notificationsDisplayedAndOngoingCallChipHidden() {
+        mFragments.dispatchResume();
+        processAllMessages();
+
+        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+        fragment.initNotificationIconArea(mMockNotificiationAreaController);
+
+        ArgumentCaptor<OngoingCallListener> ongoingCallListenerCaptor = ArgumentCaptor.forClass(
+                OngoingCallListener.class);
+        Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture());
+        OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue());
+
+        when(mOngoingCallController.getHasOngoingCall()).thenReturn(false);
+        listener.onOngoingCallEnded(/* animate= */ false);
+
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+        Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+    }
+
     @Override
     protected Fragment instantiate(Context context, String className, Bundle arguments) {
-        return new CollapsedStatusBarFragment(mock(OngoingCallController.class));
+        mOngoingCallController = mock(OngoingCallController.class);
+        return new CollapsedStatusBarFragment(mOngoingCallController);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
new file mode 100644
index 0000000..d87d1d1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.ongoingcall
+
+import android.app.Notification
+import android.app.PendingIntent
+import android.app.Person
+import android.service.notification.NotificationListenerService.REASON_USER_STOPPED
+import androidx.test.filters.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.LayoutInflater
+import android.widget.LinearLayout
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.never
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class OngoingCallControllerTest : SysuiTestCase() {
+
+    private lateinit var controller: OngoingCallController
+    private lateinit var notifCollectionListener: NotifCollectionListener
+
+    @Mock private lateinit var mockOngoingCallListener: OngoingCallListener
+
+    private lateinit var chipView: LinearLayout
+
+    @Before
+    fun setUp() {
+        allowTestableLooperAsMainThread()
+        TestableLooper.get(this).runWithLooper {
+            chipView = LayoutInflater.from(mContext)
+                    .inflate(R.layout.ongoing_call_chip, null) as LinearLayout
+        }
+
+        MockitoAnnotations.initMocks(this)
+        val featureFlags = mock(FeatureFlags::class.java)
+        `when`(featureFlags.isOngoingCallStatusBarChipEnabled).thenReturn(true)
+        val notificationCollection = mock(CommonNotifCollection::class.java)
+
+        controller = OngoingCallController(notificationCollection, featureFlags, FakeSystemClock())
+        controller.init()
+        controller.addCallback(mockOngoingCallListener)
+        controller.setChipView(chipView)
+
+        val collectionListenerCaptor = ArgumentCaptor.forClass(NotifCollectionListener::class.java)
+        verify(notificationCollection).addCollectionListener(collectionListenerCaptor.capture())
+        notifCollectionListener = collectionListenerCaptor.value!!
+    }
+
+    @Test
+    fun onEntryUpdated_isOngoingCallNotif_listenerNotifiedWithRightCallTime() {
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+        verify(mockOngoingCallListener).onOngoingCallStarted(anyBoolean())
+    }
+
+    @Test
+    fun onEntryUpdated_notOngoingCallNotif_listenerNotNotified() {
+        notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
+
+        verify(mockOngoingCallListener, never()).onOngoingCallStarted(anyBoolean())
+    }
+
+    @Test
+    fun onEntryRemoved_ongoingCallNotif_listenerNotified() {
+        notifCollectionListener.onEntryRemoved(createOngoingCallNotifEntry(), REASON_USER_STOPPED)
+
+        verify(mockOngoingCallListener).onOngoingCallEnded(anyBoolean())
+    }
+
+    @Test
+    fun onEntryRemoved_notOngoingCallNotif_listenerNotNotified() {
+        notifCollectionListener.onEntryRemoved(createNotCallNotifEntry(), REASON_USER_STOPPED)
+
+        verify(mockOngoingCallListener, never()).onOngoingCallEnded(anyBoolean())
+    }
+
+    @Test
+    fun hasOngoingCall_noOngoingCallNotifSent_returnsFalse() {
+        assertThat(controller.hasOngoingCall).isFalse()
+    }
+
+    @Test
+    fun hasOngoingCall_ongoingCallNotifSentAndChipViewSet_returnsTrue() {
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+        assertThat(controller.hasOngoingCall).isTrue()
+    }
+
+    @Test
+    fun hasOngoingCall_ongoingCallNotifSentButNoChipView_returnsFalse() {
+        controller.setChipView(null)
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+        assertThat(controller.hasOngoingCall).isFalse()
+    }
+
+    @Test
+    fun hasOngoingCall_ongoingCallNotifSentThenRemoved_returnsFalse() {
+        val ongoingCallNotifEntry = createOngoingCallNotifEntry()
+
+        notifCollectionListener.onEntryUpdated(ongoingCallNotifEntry)
+        notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
+
+        assertThat(controller.hasOngoingCall).isFalse()
+    }
+
+    private fun createOngoingCallNotifEntry(): NotificationEntry {
+        val notificationEntryBuilder = NotificationEntryBuilder()
+        notificationEntryBuilder.modifyNotification(context).style = ongoingCallStyle
+        return notificationEntryBuilder.build()
+    }
+
+    private fun createNotCallNotifEntry() = NotificationEntryBuilder().build()
+}
+
+private val ongoingCallStyle = Notification.CallStyle.forOngoingCall(
+        Person.Builder().setName("name").build(),
+        /* hangUpIntent= */ mock(PendingIntent::class.java))
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index ed77147..65a2a7b 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.DUMP;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
@@ -69,6 +70,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.net.module.util.LocationPermissionChecker;
 import com.android.server.vcn.TelephonySubscriptionTracker;
 import com.android.server.vcn.Vcn;
@@ -76,7 +78,9 @@
 import com.android.server.vcn.VcnNetworkProvider;
 import com.android.server.vcn.util.PersistableBundleUtils;
 
+import java.io.FileDescriptor;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -928,6 +932,33 @@
         }
     }
 
+    /**
+     * Dumps the state of the VcnManagementService for logging and debugging purposes.
+     *
+     * <p>PII and credentials MUST NEVER be dumped here.
+     */
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        mContext.enforceCallingOrSelfPermission(DUMP, TAG);
+
+        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
+
+        pw.println("VcnManagementService dump:");
+        pw.increaseIndent();
+
+        mNetworkProvider.dump(pw);
+
+        synchronized (mLock) {
+            pw.println("mVcns:");
+            for (Vcn vcn : mVcns.values()) {
+                vcn.dump(pw);
+            }
+            pw.println();
+        }
+
+        pw.decreaseIndent();
+    }
+
     // TODO(b/180452282): Make name more generic and implement directly with VcnManagementService
     /** Callback for Vcn signals sent up to VcnManagementService. */
     public interface VcnCallback {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 23dda78..a737ea7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15656,9 +15656,11 @@
             final ActivityServiceConnectionsHolder holder =
                     (ActivityServiceConnectionsHolder) connectionHolder;
             synchronized (ActivityManagerService.this) {
-                holder.forEachConnection(cr -> mServices.removeConnectionLocked(
-                        (ConnectionRecord) cr, null /* skipApp */, holder /* skipAct */,
-                        false /* enqueueOomAdj */));
+                synchronized (mProcLock) {
+                    holder.forEachConnection(cr -> mServices.removeConnectionLocked(
+                            (ConnectionRecord) cr, null /* skipApp */, holder /* skipAct */,
+                            false /* enqueueOomAdj */));
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 2bc81cb..9c10814 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -31,6 +31,7 @@
 import android.app.UriGrantsManager;
 import android.content.ClipData;
 import android.content.ClipDescription;
+import android.content.ClipboardManager;
 import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -57,10 +58,6 @@
 import android.os.UserManager;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.VmSocketAddress;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -82,128 +79,9 @@
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
-import java.io.FileDescriptor;
-import java.io.InterruptedIOException;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
-
-// The following class is Android Emulator specific. It is used to read and
-// write contents of the host system's clipboard.
-class HostClipboardMonitor implements Runnable {
-    public interface HostClipboardCallback {
-        void onHostClipboardUpdated(String contents);
-    }
-
-    private FileDescriptor mPipe = null;
-    private HostClipboardCallback mHostClipboardCallback;
-    private static final String PIPE_NAME = "pipe:clipboard";
-    private static final int HOST_PORT = 5000;
-
-    private static byte[] createOpenHandshake() {
-        // String.getBytes doesn't include the null terminator,
-        // but the QEMU pipe device requires the pipe service name
-        // to be null-terminated.
-
-        final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1);
-        bits[PIPE_NAME.length()] = 0;
-        return bits;
-    }
-
-    private boolean openPipe() {
-        try {
-            final FileDescriptor fd = Os.socket(OsConstants.AF_VSOCK, OsConstants.SOCK_STREAM, 0);
-
-            try {
-                Os.connect(fd, new VmSocketAddress(HOST_PORT, OsConstants.VMADDR_CID_HOST));
-
-                final byte[] handshake = createOpenHandshake();
-                Os.write(fd, handshake, 0, handshake.length);
-                mPipe = fd;
-                return true;
-            } catch (ErrnoException | SocketException | InterruptedIOException e) {
-                Os.close(fd);
-            }
-        } catch (ErrnoException e) {
-        }
-
-        return false;
-    }
-
-    private void closePipe() {
-        try {
-            final FileDescriptor fd = mPipe;
-            mPipe = null;
-            if (fd != null) {
-                Os.close(fd);
-            }
-        } catch (ErrnoException ignore) {
-        }
-    }
-
-    private byte[] receiveMessage() throws ErrnoException, InterruptedIOException {
-        final byte[] lengthBits = new byte[4];
-        Os.read(mPipe, lengthBits, 0, lengthBits.length);
-
-        final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
-        bb.order(ByteOrder.LITTLE_ENDIAN);
-        final int msgLen = bb.getInt();
-
-        final byte[] msg = new byte[msgLen];
-        Os.read(mPipe, msg, 0, msg.length);
-
-        return msg;
-    }
-
-    private void sendMessage(byte[] msg) throws ErrnoException, InterruptedIOException {
-        final byte[] lengthBits = new byte[4];
-        final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
-        bb.order(ByteOrder.LITTLE_ENDIAN);
-        bb.putInt(msg.length);
-
-        Os.write(mPipe, lengthBits, 0, lengthBits.length);
-        Os.write(mPipe, msg, 0, msg.length);
-    }
-
-    public HostClipboardMonitor(HostClipboardCallback cb) {
-        mHostClipboardCallback = cb;
-    }
-
-    @Override
-    public void run() {
-        while (!Thread.interrupted()) {
-            try {
-                // There's no guarantee that QEMU pipes will be ready at the moment
-                // this method is invoked. We simply try to get the pipe open and
-                // retry on failure indefinitely.
-                while ((mPipe == null) && !openPipe()) {
-                    Thread.sleep(100);
-                }
-
-                final byte[] receivedData = receiveMessage();
-                mHostClipboardCallback.onHostClipboardUpdated(
-                    new String(receivedData));
-            } catch (ErrnoException | InterruptedIOException e) {
-                closePipe();
-            } catch (InterruptedException e) {
-            }
-        }
-    }
-
-    public void setHostClipboard(String content) {
-        try {
-            if (mPipe != null) {
-                sendMessage(content.getBytes());
-            }
-        } catch (ErrnoException | InterruptedIOException e) {
-            Slog.e("HostClipboardMonitor",
-                   "Failed to set host clipboard " + e.getMessage());
-        }
-    }
-}
+import java.util.function.Consumer;
 
 /**
  * Implementation of the clipboard for copy and paste.
@@ -220,8 +98,6 @@
             SystemProperties.getBoolean("ro.boot.qemu", false);
 
     // DeviceConfig properties
-    private static final String PROPERTY_SHOW_ACCESS_NOTIFICATIONS = "show_access_notifications";
-    private static final boolean DEFAULT_SHOW_ACCESS_NOTIFICATIONS = true;
     private static final String PROPERTY_MAX_CLASSIFICATION_LENGTH = "max_classification_length";
     private static final int DEFAULT_MAX_CLASSIFICATION_LENGTH = 400;
 
@@ -235,14 +111,15 @@
     private final ContentCaptureManagerInternal mContentCaptureInternal;
     private final AutofillManagerInternal mAutofillInternal;
     private final IBinder mPermissionOwner;
-    private final HostClipboardMonitor mHostClipboardMonitor;
+    private final Consumer<ClipData> mEmulatorClipboardMonitor;
     private final Handler mWorkerHandler;
 
     @GuardedBy("mLock")
     private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
 
     @GuardedBy("mLock")
-    private boolean mShowAccessNotifications = DEFAULT_SHOW_ACCESS_NOTIFICATIONS;
+    private boolean mShowAccessNotifications =
+            ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS;
 
     @GuardedBy("mLock")
     private int mMaxClassificationLength = DEFAULT_MAX_CLASSIFICATION_LENGTH;
@@ -267,24 +144,14 @@
         final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
         mPermissionOwner = permOwner;
         if (IS_EMULATOR) {
-            mHostClipboardMonitor = new HostClipboardMonitor(
-                new HostClipboardMonitor.HostClipboardCallback() {
-                    @Override
-                    public void onHostClipboardUpdated(String contents){
-                        ClipData clip =
-                            new ClipData("host clipboard",
-                                         new String[]{"text/plain"},
-                                         new ClipData.Item(contents));
-                        synchronized (mLock) {
-                            setPrimaryClipInternalLocked(getClipboardLocked(0), clip,
-                                    android.os.Process.SYSTEM_UID, null);
-                        }
-                    }
-                });
-            Thread hostMonitorThread = new Thread(mHostClipboardMonitor);
-            hostMonitorThread.start();
+            mEmulatorClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {
+                synchronized (mLock) {
+                    setPrimaryClipInternalLocked(getClipboardLocked(0), clip,
+                            android.os.Process.SYSTEM_UID, null);
+                }
+            });
         } else {
-            mHostClipboardMonitor = null;
+            mEmulatorClipboardMonitor = (clip) -> {};
         }
 
         updateConfig();
@@ -310,8 +177,10 @@
 
     private void updateConfig() {
         synchronized (mLock) {
-            mShowAccessNotifications = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
-                    PROPERTY_SHOW_ACCESS_NOTIFICATIONS, DEFAULT_SHOW_ACCESS_NOTIFICATIONS);
+            mShowAccessNotifications = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_CLIPBOARD,
+                    ClipboardManager.DEVICE_CONFIG_SHOW_ACCESS_NOTIFICATIONS,
+                    ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS);
             mMaxClassificationLength = DeviceConfig.getInt(DeviceConfig.NAMESPACE_CLIPBOARD,
                     PROPERTY_MAX_CLASSIFICATION_LENGTH, DEFAULT_MAX_CLASSIFICATION_LENGTH);
         }
@@ -643,18 +512,7 @@
     @GuardedBy("mLock")
     private void setPrimaryClipInternalLocked(
             @Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
-        // Push clipboard to host, if any
-        if (mHostClipboardMonitor != null) {
-            if (clip == null) {
-                // Someone really wants the clipboard cleared, so push empty
-                mHostClipboardMonitor.setHostClipboard("");
-            } else if (clip.getItemCount() > 0) {
-                final CharSequence text = clip.getItemAt(0).getText();
-                if (text != null) {
-                    mHostClipboardMonitor.setHostClipboard(text.toString());
-                }
-            }
-        }
+        mEmulatorClipboardMonitor.accept(clip);
 
         final int userId = UserHandle.getUserId(uid);
         if (clip != null) {
@@ -1056,11 +914,9 @@
         if (clipboard.primaryClip == null) {
             return;
         }
-        if (!mShowAccessNotifications) {
-            return;
-        }
         if (Settings.Secure.getInt(getContext().getContentResolver(),
-                Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, 1) == 0) {
+                Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS,
+                (mShowAccessNotifications ? 1 : 0)) == 0) {
             return;
         }
         // Don't notify if the app accessing the clipboard is the same as the current owner.
diff --git a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
new file mode 100644
index 0000000..62b701a
--- /dev/null
+++ b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.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 com.android.server.clipboard;
+
+import android.annotation.Nullable;
+import android.content.ClipData;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.VmSocketAddress;
+import android.util.Slog;
+
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+// The following class is Android Emulator specific. It is used to read and
+// write contents of the host system's clipboard.
+class EmulatorClipboardMonitor implements Consumer<ClipData> {
+    private static final String TAG = "EmulatorClipboardMonitor";
+    private static final String PIPE_NAME = "pipe:clipboard";
+    private static final int HOST_PORT = 5000;
+    private final Thread mHostMonitorThread;
+    private FileDescriptor mPipe = null;
+
+    private static byte[] createOpenHandshake() {
+        // String.getBytes doesn't include the null terminator,
+        // but the QEMU pipe device requires the pipe service name
+        // to be null-terminated.
+
+        final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1);
+        bits[PIPE_NAME.length()] = 0;
+        return bits;
+    }
+
+    private boolean isPipeOpened() {
+        return mPipe != null;
+    }
+
+    private synchronized boolean openPipe() {
+        if (mPipe != null) {
+            return true;
+        }
+
+        try {
+            final FileDescriptor fd = Os.socket(OsConstants.AF_VSOCK, OsConstants.SOCK_STREAM, 0);
+
+            try {
+                Os.connect(fd, new VmSocketAddress(HOST_PORT, OsConstants.VMADDR_CID_HOST));
+
+                final byte[] handshake = createOpenHandshake();
+                Os.write(fd, handshake, 0, handshake.length);
+                mPipe = fd;
+                return true;
+            } catch (ErrnoException | SocketException | InterruptedIOException e) {
+                Os.close(fd);
+            }
+        } catch (ErrnoException e) {
+        }
+
+        return false;
+    }
+
+    private synchronized void closePipe() {
+        try {
+            final FileDescriptor fd = mPipe;
+            mPipe = null;
+            if (fd != null) {
+                Os.close(fd);
+            }
+        } catch (ErrnoException ignore) {
+        }
+    }
+
+    private byte[] receiveMessage() throws ErrnoException, InterruptedIOException {
+        final byte[] lengthBits = new byte[4];
+        Os.read(mPipe, lengthBits, 0, lengthBits.length);
+
+        final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
+        bb.order(ByteOrder.LITTLE_ENDIAN);
+        final int msgLen = bb.getInt();
+
+        final byte[] msg = new byte[msgLen];
+        Os.read(mPipe, msg, 0, msg.length);
+
+        return msg;
+    }
+
+    private void sendMessage(final byte[] msg) throws ErrnoException, InterruptedIOException {
+        final byte[] lengthBits = new byte[4];
+        final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
+        bb.order(ByteOrder.LITTLE_ENDIAN);
+        bb.putInt(msg.length);
+
+        Os.write(mPipe, lengthBits, 0, lengthBits.length);
+        Os.write(mPipe, msg, 0, msg.length);
+    }
+
+    EmulatorClipboardMonitor(final Consumer<ClipData> setAndroidClipboard) {
+        this.mHostMonitorThread = new Thread(() -> {
+            while (!Thread.interrupted()) {
+                try {
+                    // There's no guarantee that QEMU pipes will be ready at the moment
+                    // this method is invoked. We simply try to get the pipe open and
+                    // retry on failure indefinitely.
+                    while (!openPipe()) {
+                        Thread.sleep(100);
+                    }
+
+                    final byte[] receivedData = receiveMessage();
+
+                    final String str = new String(receivedData);
+                    final ClipData clip = new ClipData("host clipboard",
+                                                       new String[]{"text/plain"},
+                                                       new ClipData.Item(str));
+
+                    setAndroidClipboard.accept(clip);
+                } catch (ErrnoException | InterruptedIOException e) {
+                    closePipe();
+                } catch (InterruptedException | IllegalArgumentException e) {
+                }
+            }
+        });
+
+        this.mHostMonitorThread.start();
+    }
+
+    @Override
+    public void accept(final @Nullable ClipData clip) {
+        if (clip == null) {
+            setHostClipboardImpl("");
+        } else if (clip.getItemCount() > 0) {
+            final CharSequence text = clip.getItemAt(0).getText();
+            if (text != null) {
+                setHostClipboardImpl(text.toString());
+            }
+        }
+    }
+
+    private void setHostClipboardImpl(final String value) {
+        try {
+            if (isPipeOpened()) {
+                sendMessage(value.getBytes());
+            }
+        } catch (ErrnoException | InterruptedIOException e) {
+            Slog.e(TAG, "Failed to set host clipboard " + e.getMessage());
+        } catch (IllegalArgumentException e) {
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/clipboard/OWNERS b/services/core/java/com/android/server/clipboard/OWNERS
new file mode 100644
index 0000000..5449df9
--- /dev/null
+++ b/services/core/java/com/android/server/clipboard/OWNERS
@@ -0,0 +1 @@
+per-file EmulatorClipboardMonitor.java = bohu@google.com,lfy@google.com,rkir@google.com
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 03fb3a4..acfeb6c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -713,7 +713,9 @@
         assertRunOnServiceThread();
         if (!mService.isPowerStandbyOrTransient()) {
             addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress()));
-            if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
+            if (!isDirectConnectAddress(avr.getPhysicalAddress())) {
+                startArcAction(false);
+            } else if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
                     && !hasAction(SetArcTransmissionStateAction.class)) {
                 startArcAction(true);
             }
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index f5d6489..1050835 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -27,6 +27,7 @@
 import android.service.notification.IConditionProvider;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.ZenModeConfig;
+import android.util.Log;
 import android.util.Slog;
 
 import java.io.PrintWriter;
@@ -122,8 +123,11 @@
 
     public static void traceSetNotificationPolicy(String pkg, int targetSdk,
             NotificationManager.Policy policy) {
-        append(TYPE_SET_NOTIFICATION_POLICY, "pkg=" + pkg + " targetSdk=" + targetSdk
-                + " NotificationPolicy=" + policy.toString());
+        String policyLog = "pkg=" + pkg + " targetSdk=" + targetSdk
+                + " NotificationPolicy=" + policy.toString();
+        append(TYPE_SET_NOTIFICATION_POLICY, policyLog);
+        // TODO(b/180205791): remove when we can better surface apps that are changing policy
+        Log.d(TAG, "Zen Policy Changed: " + policyLog);
     }
 
     public static void traceSubscribe(Uri uri, IConditionProvider provider, RemoteException e) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a6d4ed9..e532790 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -4157,7 +4157,7 @@
         if (stageDir != null && !params.isStaged) {
             try {
                 if (incrementalFileStorages != null) {
-                    incrementalFileStorages.cleanUp();
+                    incrementalFileStorages.cleanUpAndMarkComplete();
                 }
                 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
             } catch (InstallerException ignored) {
@@ -4183,7 +4183,7 @@
         }
         try {
             if (incrementalFileStorages != null) {
-                incrementalFileStorages.cleanUp();
+                incrementalFileStorages.cleanUpAndMarkComplete();
             }
             mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
         } catch (InstallerException ignored) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 30ce57f..9477464 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5966,7 +5966,8 @@
                 backgroundHandler,
                 SYSTEM_PARTITIONS,
                 (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
-                (i, pm) -> PermissionManagerService.create(context),
+                (i, pm) -> PermissionManagerService.create(context,
+                        i.getSystemConfig().getAvailableFeatures()),
                 (i, pm) -> new UserManagerService(context, pm,
                         new UserDataPreparer(installer, installLock, context, onlyCore),
                         lock),
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 310ef23..2d1178a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -69,8 +69,10 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
+import android.content.AttributionSource;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.PermissionGroupInfoFlags;
 import android.content.pm.PackageManager.PermissionInfoFlags;
@@ -85,7 +87,6 @@
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.metrics.LogMaker;
 import android.os.AsyncTask;
-import android.content.AttributionSource;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Debug;
@@ -177,6 +178,10 @@
 
     private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60);
 
+    // For automotive products, CarService enforces allow-listing of the privileged permissions
+    // com.android.car is the package name which declares auto specific permissions
+    private static final String CAR_PACKAGE_NAME = "com.android.car";
+
     /** Cap the size of permission trees that 3rd party apps can define; in characters of text */
     private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768;
     /** Empty array to avoid allocations */
@@ -210,6 +215,10 @@
         STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
     }
 
+    /** Set of source package names for Privileged Permission Allowlist */
+    private final ArraySet<String> mPrivilegedPermissionAllowlistSourcePackageNames =
+            new ArraySet<>();
+
     /** Lock to protect internal data access */
     private final Object mLock = new Object();
 
@@ -356,7 +365,8 @@
         }
     };
 
-    PermissionManagerService(@NonNull Context context) {
+    PermissionManagerService(@NonNull Context context,
+            @NonNull ArrayMap<String, FeatureInfo> availableFeatures) {
         // The package info cache is the cache for package and permission information.
         // Disable the package info and package permission caches locally but leave the
         // checkPermission cache active.
@@ -368,6 +378,13 @@
         mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
 
+        mPrivilegedPermissionAllowlistSourcePackageNames.add(PLATFORM_PACKAGE_NAME);
+        // PackageManager.hasSystemFeature() is not used here because PackageManagerService
+        // isn't ready yet.
+        if (availableFeatures.containsKey(PackageManager.FEATURE_AUTOMOTIVE)) {
+            mPrivilegedPermissionAllowlistSourcePackageNames.add(CAR_PACKAGE_NAME);
+        }
+
         mHandlerThread = new ServiceThread(TAG,
                 Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
         mHandlerThread.start();
@@ -422,7 +439,8 @@
      * lock created by the permission manager itself.
      */
     @NonNull
-    public static PermissionManagerServiceInternal create(@NonNull Context context) {
+    public static PermissionManagerServiceInternal create(@NonNull Context context,
+            ArrayMap<String, FeatureInfo> availableFeatures) {
         final PermissionManagerServiceInternal permMgrInt =
                 LocalServices.getService(PermissionManagerServiceInternal.class);
         if (permMgrInt != null) {
@@ -431,7 +449,7 @@
         PermissionManagerService permissionService =
                 (PermissionManagerService) ServiceManager.getService("permissionmgr");
         if (permissionService == null) {
-            permissionService = new PermissionManagerService(context);
+            permissionService = new PermissionManagerService(context, availableFeatures);
             ServiceManager.addService("permissionmgr", permissionService);
         }
         return LocalServices.getService(PermissionManagerServiceInternal.class);
@@ -3318,7 +3336,8 @@
         if (!pkg.isPrivileged()) {
             return true;
         }
-        if (!Objects.equals(permission.getPackageName(), PLATFORM_PACKAGE_NAME)) {
+        if (!mPrivilegedPermissionAllowlistSourcePackageNames
+                .contains(permission.getPackageName())) {
             return true;
         }
         final String permissionName = permission.getName();
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index fa27b4b4..25709d4 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -21,11 +21,22 @@
 import android.app.AppOpsManager;
 import android.app.AppOpsManagerInternal;
 import android.app.SyncNotedAppOp;
+import android.app.role.RoleManager;
 import android.content.AttributionSource;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.location.LocationManagerInternal;
+import android.net.Uri;
 import android.os.IBinder;
+import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.function.HeptFunction;
@@ -35,6 +46,8 @@
 import com.android.internal.util.function.TriFunction;
 import com.android.server.LocalServices;
 
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -42,9 +55,21 @@
  * This class defines policy for special behaviors around app ops.
  */
 public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegate {
+    private static final String LOG_TAG = AppOpsPolicy.class.getName();
+
+    private static final String ACTIVITY_RECOGNITION_TAGS =
+            "android:activity_recognition_allow_listed_tags";
+    private static final String ACTIVITY_RECOGNITION_TAGS_SEPARATOR = ";";
+
     @NonNull
     private final Object mLock = new Object();
 
+    @NonNull
+    private final Context mContext;
+
+    @NonNull
+    private final RoleManager mRoleManager;
+
     /**
      * The locking policy around the location tags is a bit special. Since we want to
      * avoid grabbing the lock on every op note we are taking the approach where the
@@ -60,48 +85,57 @@
     private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags =
             new ConcurrentHashMap<>();
 
-    public AppOpsPolicy() {
+    @GuardedBy("mLock - writes only - see above")
+    @NonNull
+    private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>>
+            mActivityRecognitionTags = new ConcurrentHashMap<>();
+
+    public AppOpsPolicy(@NonNull Context context) {
+        mContext = context;
+        mRoleManager = mContext.getSystemService(RoleManager.class);
+
         final LocationManagerInternal locationManagerInternal = LocalServices.getService(
                 LocationManagerInternal.class);
         locationManagerInternal.setOnProviderLocationTagsChangeListener((providerTagInfo) -> {
             synchronized (mLock) {
-                final int uid = providerTagInfo.getUid();
-                // We make a copy of the per UID state to limit our mutation to one
-                // operation in the underlying concurrent data structure.
-                ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid);
-                if (uidTags != null) {
-                    uidTags = new ArrayMap<>(uidTags);
-                }
-
-                final String packageName = providerTagInfo.getPackageName();
-                ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null;
-                if (packageTags != null) {
-                    packageTags = new ArraySet<>(packageTags);
-                }
-
-                final Set<String> providerTags = providerTagInfo.getTags();
-                if (providerTags != null && !providerTags.isEmpty()) {
-                    if (packageTags != null) {
-                        packageTags.clear();
-                        packageTags.addAll(providerTags);
-                    } else {
-                        packageTags = new ArraySet<>(providerTags);
-                    }
-                    if (uidTags == null) {
-                        uidTags = new ArrayMap<>();
-                    }
-                    uidTags.put(packageName, packageTags);
-                    mLocationTags.put(uid, uidTags);
-                } else if (uidTags != null) {
-                    uidTags.remove(packageName);
-                    if (!uidTags.isEmpty()) {
-                        mLocationTags.put(uid, uidTags);
-                    } else {
-                        mLocationTags.remove(uid);
-                    }
-                }
+                updateAllowListedTagsForPackageLocked(providerTagInfo.getUid(),
+                        providerTagInfo.getPackageName(), providerTagInfo.getTags(),
+                        mLocationTags);
             }
         });
+
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        intentFilter.addDataScheme("package");
+
+        context.registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final Uri uri = intent.getData();
+                if (uri == null) {
+                    return;
+                }
+                final String packageName = uri.getSchemeSpecificPart();
+                if (TextUtils.isEmpty(packageName)) {
+                    return;
+                }
+                final List<String> activityRecognizers = mRoleManager.getRoleHolders(
+                        RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER);
+                if (activityRecognizers.contains(packageName)) {
+                    updateActivityRecognizerTags(packageName);
+                }
+            }
+        }, UserHandle.SYSTEM, intentFilter, null, null);
+
+        mRoleManager.addOnRoleHoldersChangedListenerAsUser(context.getMainExecutor(),
+                (String roleName, UserHandle user) -> {
+            if (RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER.equals(roleName)) {
+                initializeActivityRecognizersTags();
+            }
+        }, UserHandle.SYSTEM);
+
+        initializeActivityRecognizersTags();
     }
 
     @Override
@@ -121,7 +155,7 @@
             @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable
             String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer,
                     String, String, Boolean, String, Boolean, SyncNotedAppOp> superImpl) {
-        return superImpl.apply(resolveOpCode(code, uid, packageName, attributionTag), uid,
+        return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag), uid,
                 packageName, attributionTag, shouldCollectAsyncNotedOp,
                 message, shouldCollectMessage);
     }
@@ -132,7 +166,7 @@
             boolean shouldCollectMessage, boolean skipProxyOperation, @NonNull HexFunction<Integer,
                     AttributionSource, Boolean, String, Boolean, Boolean,
             SyncNotedAppOp> superImpl) {
-        return superImpl.apply(resolveOpCode(code, attributionSource.getUid(),
+        return superImpl.apply(resolveDatasourceOp(code, attributionSource.getUid(),
                 attributionSource.getPackageName(), attributionSource.getAttributionTag()),
                 attributionSource, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
                 skipProxyOperation);
@@ -144,7 +178,7 @@
             boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
             boolean skipProxyOperation, @NonNull OctFunction<IBinder, Integer, AttributionSource,
                     Boolean, Boolean, String, Boolean, Boolean, SyncNotedAppOp> superImpl) {
-        return superImpl.apply(token, resolveOpCode(code, attributionSource.getUid(),
+        return superImpl.apply(token, resolveDatasourceOp(code, attributionSource.getUid(),
                 attributionSource.getPackageName(), attributionSource.getAttributionTag()),
                 attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message,
                 shouldCollectMessage, skipProxyOperation);
@@ -154,36 +188,129 @@
     public void finishProxyOperation(IBinder clientId, int code,
             @NonNull AttributionSource attributionSource,
             @NonNull TriFunction<IBinder, Integer, AttributionSource, Void> superImpl) {
-        superImpl.apply(clientId, resolveOpCode(code, attributionSource.getUid(),
+        superImpl.apply(clientId, resolveDatasourceOp(code, attributionSource.getUid(),
                 attributionSource.getPackageName(), attributionSource.getAttributionTag()),
                 attributionSource);
     }
 
-    private int resolveOpCode(int code, int uid, @NonNull String packageName,
+    private int resolveDatasourceOp(int code, int uid, @NonNull String packageName,
             @Nullable String attributionTag) {
-        if (isHandledOp(code) && attributionTag != null) {
-            // Only a single lookup from the underlying concurrent data structure
-            final ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid);
-            if (uidTags != null) {
-                final ArraySet<String> packageTags = uidTags.get(packageName);
-                if (packageTags != null && packageTags.contains(attributionTag)) {
-                    return resolveHandledOp(code);
+        if (attributionTag == null) {
+            return code;
+        }
+        int resolvedCode = resolveLocationOp(code);
+        if (resolvedCode != code) {
+            if (isDatasourceAttributionTag(uid, packageName, attributionTag,
+                    mLocationTags)) {
+                return resolvedCode;
+            }
+        } else {
+            resolvedCode = resolveArOp(code);
+            if (resolvedCode != code) {
+                if (isDatasourceAttributionTag(uid, packageName, attributionTag,
+                        mActivityRecognitionTags)) {
+                    return resolvedCode;
                 }
             }
         }
         return code;
     }
 
-    private static boolean isHandledOp(int code) {
-        switch (code) {
-            case AppOpsManager.OP_FINE_LOCATION:
-            case AppOpsManager.OP_COARSE_LOCATION:
+    private void initializeActivityRecognizersTags() {
+        final List<String> activityRecognizers = mRoleManager.getRoleHolders(
+                RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER);
+        final int recognizerCount = activityRecognizers.size();
+        if (recognizerCount > 0) {
+            for (int i = 0; i < recognizerCount; i++) {
+                final String activityRecognizer = activityRecognizers.get(i);
+                updateActivityRecognizerTags(activityRecognizer);
+            }
+        } else {
+            clearActivityRecognitionTags();
+        }
+    }
+
+    private void clearActivityRecognitionTags() {
+        synchronized (mLock) {
+            mActivityRecognitionTags.clear();
+        }
+    }
+
+    private void updateActivityRecognizerTags(@NonNull String activityRecognizer) {
+        try {
+            final ApplicationInfo recognizerAppInfo = mContext.getPackageManager()
+                    .getApplicationInfoAsUser(activityRecognizer, PackageManager.GET_META_DATA,
+                            UserHandle.USER_SYSTEM);
+            if (recognizerAppInfo.metaData == null) {
+                return;
+            }
+            final String tagsList = recognizerAppInfo.metaData.getString(ACTIVITY_RECOGNITION_TAGS);
+            if (tagsList != null) {
+                final String[] tags = tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR);
+                synchronized (mLock) {
+                    updateAllowListedTagsForPackageLocked(recognizerAppInfo.uid,
+                            recognizerAppInfo.packageName, new ArraySet<>(tags),
+                            mActivityRecognitionTags);
+                }
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.wtf(LOG_TAG, "Missing " + RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER
+                    + " role holder package " + activityRecognizer);
+        }
+    }
+
+    private static void updateAllowListedTagsForPackageLocked(int uid, String packageName,
+            Set<String> allowListedTags, ConcurrentHashMap<Integer, ArrayMap<String,
+            ArraySet<String>>> datastore) {
+        // We make a copy of the per UID state to limit our mutation to one
+        // operation in the underlying concurrent data structure.
+        ArrayMap<String, ArraySet<String>> uidTags = datastore.get(uid);
+        if (uidTags != null) {
+            uidTags = new ArrayMap<>(uidTags);
+        }
+
+        ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null;
+        if (packageTags != null) {
+            packageTags = new ArraySet<>(packageTags);
+        }
+
+        if (allowListedTags != null && !allowListedTags.isEmpty()) {
+            if (packageTags != null) {
+                packageTags.clear();
+                packageTags.addAll(allowListedTags);
+            } else {
+                packageTags = new ArraySet<>(allowListedTags);
+            }
+            if (uidTags == null) {
+                uidTags = new ArrayMap<>();
+            }
+            uidTags.put(packageName, packageTags);
+            datastore.put(uid, uidTags);
+        } else if (uidTags != null) {
+            uidTags.remove(packageName);
+            if (!uidTags.isEmpty()) {
+                datastore.put(uid, uidTags);
+            } else {
+                datastore.remove(uid);
+            }
+        }
+    }
+
+    private static boolean isDatasourceAttributionTag(int uid, @NonNull String packageName,
+            @NonNull String attributionTag, @NonNull Map<Integer, ArrayMap<String,
+            ArraySet<String>>> mappedOps) {
+        // Only a single lookup from the underlying concurrent data structure
+        final ArrayMap<String, ArraySet<String>> uidTags = mappedOps.get(uid);
+        if (uidTags != null) {
+            final ArraySet<String> packageTags = uidTags.get(packageName);
+            if (packageTags != null && packageTags.contains(attributionTag)) {
                 return true;
+            }
         }
         return false;
     }
 
-    private static int resolveHandledOp(int code) {
+    private static int resolveLocationOp(int code) {
         switch (code) {
             case AppOpsManager.OP_FINE_LOCATION:
                 return AppOpsManager.OP_FINE_LOCATION_SOURCE;
@@ -192,4 +319,11 @@
         }
         return code;
     }
+
+    private static int resolveArOp(int code) {
+        if (code == AppOpsManager.OP_ACTIVITY_RECOGNITION) {
+            return AppOpsManager.OP_ACTIVITY_RECOGNITION_SOURCE;
+        }
+        return code;
+    }
 }
diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java
index 52ff48b..d91e9c2 100644
--- a/services/core/java/com/android/server/timedetector/ServerFlags.java
+++ b/services/core/java/com/android/server/timedetector/ServerFlags.java
@@ -28,6 +28,8 @@
 import com.android.server.timezonedetector.ConfigurationChangeListener;
 import com.android.server.timezonedetector.ServiceConfigAccessor;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.time.Duration;
 import java.util.Map;
 import java.util.Objects;
@@ -53,14 +55,15 @@
      */
     @StringDef(prefix = "KEY_", value = {
             KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
-            KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
-            KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+            KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
+            KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
             KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
             KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
             KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
             KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
             KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
     })
+    @Retention(RetentionPolicy.SOURCE)
     @interface DeviceConfigKey {}
 
     /**
@@ -75,19 +78,19 @@
 
     /**
      * The key for the server flag that can override the device config for whether the primary
-     * location time zone provider is enabled or disabled.
+     * location time zone provider is enabled, disabled, or (for testing) in simulation mode.
      */
     @DeviceConfigKey
-    public static final String KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE =
-            "primary_location_time_zone_provider_enabled_override";
+    public static final String KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE =
+            "primary_location_time_zone_provider_mode_override";
 
     /**
      * The key for the server flag that can override the device config for whether the secondary
-     * location time zone provider is enabled or disabled.
+     * location time zone provider is enabled or disabled, or (for testing) in simulation mode.
      */
     @DeviceConfigKey
-    public static final String KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE =
-            "secondary_location_time_zone_provider_enabled_override";
+    public static final String KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE =
+            "secondary_location_time_zone_provider_mode_override";
 
     /**
      * The key for the minimum delay after location time zone detection has been enabled before the
@@ -196,6 +199,16 @@
     }
 
     /**
+     * Returns an optional string value from {@link DeviceConfig} from the system_time
+     * namespace, returns {@link Optional#empty()} if there is no explicit value set.
+     */
+    @NonNull
+    public Optional<String> getOptionalString(@DeviceConfigKey String key) {
+        String value = DeviceConfig.getProperty(NAMESPACE_SYSTEM_TIME, key);
+        return Optional.ofNullable(value);
+    }
+
+    /**
      * Returns an optional boolean value from {@link DeviceConfig} from the system_time
      * namespace, returns {@link Optional#empty()} if there is no explicit value set.
      */
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
index 222e852..50d37f4 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringDef;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
@@ -27,6 +28,10 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.timedetector.ServerFlags;
 
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.time.Duration;
 import java.util.Collections;
 import java.util.Objects;
@@ -40,13 +45,38 @@
  */
 public final class ServiceConfigAccessor {
 
+    @StringDef(prefix = "PROVIDER_MODE_",
+            value = { PROVIDER_MODE_SIMULATED, PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED})
+    @Retention(RetentionPolicy.SOURCE)
+    @Target(ElementType.TYPE_USE)
+    @interface ProviderMode {}
+
+    /**
+     * The "simulated" provider mode.
+     * For use with {@link #getPrimaryLocationTimeZoneProviderMode()} and {@link
+     * #getSecondaryLocationTimeZoneProviderMode()}.
+     */
+    public static final @ProviderMode String PROVIDER_MODE_SIMULATED = "simulated";
+
+    /**
+     * The "disabled" provider mode. For use with {@link #getPrimaryLocationTimeZoneProviderMode()}
+     * and {@link #getSecondaryLocationTimeZoneProviderMode()}.
+     */
+    public static final @ProviderMode String PROVIDER_MODE_DISABLED = "disabled";
+
+    /**
+     * The "enabled" provider mode. For use with {@link #getPrimaryLocationTimeZoneProviderMode()}
+     * and {@link #getSecondaryLocationTimeZoneProviderMode()}.
+     */
+    public static final @ProviderMode String PROVIDER_MODE_ENABLED = "enabled";
+
     private static final Set<String> 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,
-                    ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
-                    ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+                    ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
+                    ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
                     ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
                     ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
                     ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS
@@ -139,14 +169,28 @@
 
     /**
      * Returns {@code true} if the location-based time zone detection feature is supported on the
-     * device. This can be used during feature testing on builds that are capable of location time
-     * zone detection to enable / disable the feature for some users.
+     * device.
      */
     public boolean isGeoTimeZoneDetectionFeatureSupported() {
+        // For the feature to be enabled it must:
+        // 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
-                && isGeoTimeZoneDetectionFeatureSupportedInternal();
+                && isGeoTimeZoneDetectionFeatureSupportedInternal()
+                && atLeastOneProviderIsEnabled();
     }
 
+    private boolean atLeastOneProviderIsEnabled() {
+        return !(Objects.equals(getPrimaryLocationTimeZoneProviderMode(), PROVIDER_MODE_DISABLED)
+                && Objects.equals(getSecondaryLocationTimeZoneProviderMode(),
+                PROVIDER_MODE_DISABLED));
+    }
+
+    /**
+     * Returns {@code true} if the location-based time zone detection feature is not explicitly
+     * disabled by a server flag.
+     */
     private boolean isGeoTimeZoneDetectionFeatureSupportedInternal() {
         final boolean defaultEnabled = true;
         return mServerFlags.getBoolean(
@@ -154,42 +198,49 @@
                 defaultEnabled);
     }
 
+    @NonNull
+    public String getPrimaryLocationTimeZoneProviderPackageName() {
+        return mContext.getResources().getString(
+                R.string.config_primaryLocationTimeZoneProviderPackageName);
+    }
+
+    @NonNull
+    public String getSecondaryLocationTimeZoneProviderPackageName() {
+        return mContext.getResources().getString(
+                R.string.config_secondaryLocationTimeZoneProviderPackageName);
+    }
+
     /**
      * Returns {@code true} if the primary location time zone provider can be used.
      */
-    public boolean isPrimaryLocationTimeZoneProviderEnabled() {
-        return getPrimaryLocationTimeZoneProviderEnabledOverride()
-                .orElse(isPrimaryLocationTimeZoneProviderEnabledInConfig());
-    }
-
-    private boolean isPrimaryLocationTimeZoneProviderEnabledInConfig() {
-        int providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider;
-        return getConfigBoolean(providerEnabledConfigId);
+    @NonNull
+    public @ProviderMode String getPrimaryLocationTimeZoneProviderMode() {
+        return mServerFlags.getOptionalString(
+                ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE)
+                .orElse(getPrimaryLocationTimeZoneProviderModeFromConfig());
     }
 
     @NonNull
-    private Optional<Boolean> getPrimaryLocationTimeZoneProviderEnabledOverride() {
-        return mServerFlags.getOptionalBoolean(
-                ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE);
+    private @ProviderMode String getPrimaryLocationTimeZoneProviderModeFromConfig() {
+        int providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider;
+        return getConfigBoolean(providerEnabledConfigId)
+                ? PROVIDER_MODE_ENABLED : PROVIDER_MODE_DISABLED;
     }
 
     /**
-     * Returns {@code true} if the secondary location time zone provider can be used.
+     * Returns the mode for the secondary location time zone provider can be used.
      */
-    public boolean isSecondaryLocationTimeZoneProviderEnabled() {
-        return getSecondaryLocationTimeZoneProviderEnabledOverride()
-                .orElse(isSecondaryLocationTimeZoneProviderEnabledInConfig());
-    }
-
-    private boolean isSecondaryLocationTimeZoneProviderEnabledInConfig() {
-        int providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider;
-        return getConfigBoolean(providerEnabledConfigId);
+    public @ProviderMode String getSecondaryLocationTimeZoneProviderMode() {
+        return mServerFlags.getOptionalString(
+                ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE)
+                .orElse(getSecondaryLocationTimeZoneProviderModeFromConfig());
     }
 
     @NonNull
-    private Optional<Boolean> getSecondaryLocationTimeZoneProviderEnabledOverride() {
-        return mServerFlags.getOptionalBoolean(
-                ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE);
+    private @ProviderMode String getSecondaryLocationTimeZoneProviderModeFromConfig() {
+        int providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider;
+        return getConfigBoolean(providerEnabledConfigId)
+                ? PROVIDER_MODE_ENABLED : PROVIDER_MODE_DISABLED;
     }
 
     /**
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
index fb2a184..d2190fd 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
@@ -30,6 +30,7 @@
 import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
 
 import android.annotation.DurationMillisLong;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.RemoteCallback;
@@ -604,14 +605,14 @@
      * known provider, then the command is logged and discarded.
      */
     void handleProviderTestCommand(
-            @NonNull String providerName, @NonNull TestCommand testCommand,
+            @IntRange(from = 0, to = 1) int providerIndex, @NonNull TestCommand testCommand,
             @Nullable RemoteCallback callback) {
         mThreadingDomain.assertCurrentThread();
 
-        LocationTimeZoneProvider targetProvider = getLocationTimeZoneProvider(providerName);
+        LocationTimeZoneProvider targetProvider = getLocationTimeZoneProvider(providerIndex);
         if (targetProvider == null) {
             warnLog("Unable to process test command:"
-                    + " providerName=" + providerName + ", testCommand=" + testCommand);
+                    + " providerIndex=" + providerIndex + ", testCommand=" + testCommand);
             return;
         }
 
@@ -620,7 +621,7 @@
                 targetProvider.handleTestCommand(testCommand, callback);
             } catch (Exception e) {
                 warnLog("Unable to process test command:"
-                        + " providerName=" + providerName + ", testCommand=" + testCommand, e);
+                        + " providerIndex=" + providerIndex + ", testCommand=" + testCommand, e);
             }
         }
     }
@@ -658,14 +659,15 @@
     }
 
     @Nullable
-    private LocationTimeZoneProvider getLocationTimeZoneProvider(@NonNull String providerName) {
+    private LocationTimeZoneProvider getLocationTimeZoneProvider(
+            @IntRange(from = 0, to = 1) int providerIndex) {
         LocationTimeZoneProvider targetProvider;
-        if (Objects.equals(mPrimaryProvider.getName(), providerName)) {
+        if (providerIndex == 0) {
             targetProvider = mPrimaryProvider;
-        } else if (Objects.equals(mSecondaryProvider.getName(), providerName)) {
+        } else if (providerIndex == 1) {
             targetProvider = mSecondaryProvider;
         } else {
-            warnLog("Bad providerName=" + providerName);
+            warnLog("Bad providerIndex=" + providerIndex);
             targetProvider = null;
         }
         return targetProvider;
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 326cfe7..d8d44d4 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -16,13 +16,12 @@
 
 package com.android.server.timezonedetector.location;
 
-import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
-import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
 import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
 
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_DISABLED;
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_SIMULATED;
+
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -37,12 +36,12 @@
 import android.util.Log;
 import android.util.Slog;
 
-import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
+import com.android.server.timezonedetector.Dumpable;
 import com.android.server.timezonedetector.ServiceConfigAccessor;
 import com.android.server.timezonedetector.TimeZoneDetectorInternal;
 import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger;
@@ -131,11 +130,15 @@
 
     private static final String ATTRIBUTION_TAG = "LocationTimeZoneService";
 
-    private static final String PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION =
-            TimeZoneProviderService.PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE;
-    private static final String SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION =
-            TimeZoneProviderService.SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE;
+    @GuardedBy("mSharedLock")
+    private final ProviderConfig mPrimaryProviderConfig = new ProviderConfig(
+            0 /* index */, "primary",
+            TimeZoneProviderService.PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE);
 
+    @GuardedBy("mSharedLock")
+    private final ProviderConfig mSecondaryProviderConfig = new ProviderConfig(
+            1 /* index */, "secondary",
+            TimeZoneProviderService.SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE);
 
     @NonNull private final Context mContext;
 
@@ -165,14 +168,6 @@
     @GuardedBy("mSharedLock")
     private ControllerEnvironmentImpl mEnvironment;
 
-    @GuardedBy("mSharedLock")
-    @NonNull
-    private String mPrimaryProviderModeOverride = PROVIDER_MODE_OVERRIDE_NONE;
-
-    @GuardedBy("mSharedLock")
-    @NonNull
-    private String mSecondaryProviderModeOverride = PROVIDER_MODE_OVERRIDE_NONE;
-
     LocationTimeZoneManagerService(Context context) {
         mContext = context.createAttributionContext(ATTRIBUTION_TAG);
         mHandler = FgThread.getHandler();
@@ -200,9 +195,14 @@
         mThreadingDomain.assertCurrentThread();
 
         synchronized (mSharedLock) {
-            // Stop and start the service, waiting until completion.
-            stopOnDomainThread();
-            startOnDomainThread();
+            // Avoid starting the service if it is currently stopped. This is required because
+            // server flags are used by tests to set behavior with the service stopped, and we don't
+            // want the service being restarted after each flag is set.
+            if (mLocationTimeZoneDetectorController != null) {
+                // Stop and start the service, waiting until completion.
+                stopOnDomainThread();
+                startOnDomainThread();
+            }
         }
     }
 
@@ -257,8 +257,8 @@
             }
 
             if (mLocationTimeZoneDetectorController == null) {
-                LocationTimeZoneProvider primary = createPrimaryProvider();
-                LocationTimeZoneProvider secondary = createSecondaryProvider();
+                LocationTimeZoneProvider primary = mPrimaryProviderConfig.createProvider();
+                LocationTimeZoneProvider secondary = mSecondaryProviderConfig.createProvider();
 
                 ControllerImpl controller =
                         new ControllerImpl(mThreadingDomain, primary, secondary);
@@ -273,88 +273,6 @@
         }
     }
 
-    @NonNull
-    private LocationTimeZoneProvider createPrimaryProvider() {
-        LocationTimeZoneProviderProxy proxy;
-        if (isProviderInSimulationMode(PRIMARY_PROVIDER_NAME)) {
-            proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
-        } else if (!isProviderEnabled(PRIMARY_PROVIDER_NAME)) {
-            proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
-        } else {
-            proxy = new RealLocationTimeZoneProviderProxy(
-                    mContext,
-                    mHandler,
-                    mThreadingDomain,
-                    PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION,
-                    R.bool.config_enablePrimaryLocationTimeZoneOverlay,
-                    R.string.config_primaryLocationTimeZoneProviderPackageName
-            );
-        }
-        ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(0);
-        return new BinderLocationTimeZoneProvider(
-                providerMetricsLogger, mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
-    }
-
-    @NonNull
-    private LocationTimeZoneProvider createSecondaryProvider() {
-        LocationTimeZoneProviderProxy proxy;
-        if (isProviderInSimulationMode(SECONDARY_PROVIDER_NAME)) {
-            proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
-        } else if (!isProviderEnabled(SECONDARY_PROVIDER_NAME)) {
-            proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
-        } else {
-            proxy = new RealLocationTimeZoneProviderProxy(
-                    mContext,
-                    mHandler,
-                    mThreadingDomain,
-                    SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION,
-                    R.bool.config_enableSecondaryLocationTimeZoneOverlay,
-                    R.string.config_secondaryLocationTimeZoneProviderPackageName
-            );
-        }
-        ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(1);
-        return new BinderLocationTimeZoneProvider(
-                providerMetricsLogger, mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy);
-    }
-
-    /** Used for bug triage and in tests to simulate provider events. */
-    private boolean isProviderInSimulationMode(@NonNull String providerName) {
-        return isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_SIMULATED);
-    }
-
-    /** Used for bug triage, and by tests and experiments to remove a provider. */
-    private boolean isProviderEnabled(@NonNull String providerName) {
-        if (isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED)) {
-            return false;
-        }
-
-        switch (providerName) {
-            case PRIMARY_PROVIDER_NAME: {
-                return mServiceConfigAccessor.isPrimaryLocationTimeZoneProviderEnabled();
-            }
-            case SECONDARY_PROVIDER_NAME: {
-                return mServiceConfigAccessor.isSecondaryLocationTimeZoneProviderEnabled();
-            }
-            default: {
-                throw new IllegalArgumentException(providerName);
-            }
-        }
-    }
-
-    private boolean isProviderModeOverrideSet(@NonNull String providerName, @NonNull String mode) {
-        switch (providerName) {
-            case PRIMARY_PROVIDER_NAME: {
-                return Objects.equals(mPrimaryProviderModeOverride, mode);
-            }
-            case SECONDARY_PROVIDER_NAME: {
-                return Objects.equals(mSecondaryProviderModeOverride, mode);
-            }
-            default: {
-                throw new IllegalArgumentException(providerName);
-            }
-        }
-    }
-
     /**
      * Stops the service for tests and other rare cases. To avoid tests needing to sleep, this
      * method will not return until all the system server components have stopped.
@@ -390,33 +308,6 @@
     }
 
     /** Sets this service into provider state recording mode for tests. */
-    void setProviderModeOverride(@NonNull String providerName, @NonNull String mode) {
-        enforceManageTimeZoneDetectorPermission();
-
-        Preconditions.checkArgument(
-                PRIMARY_PROVIDER_NAME.equals(providerName)
-                        || SECONDARY_PROVIDER_NAME.equals(providerName));
-        Preconditions.checkArgument(PROVIDER_MODE_OVERRIDE_DISABLED.equals(mode)
-                || PROVIDER_MODE_OVERRIDE_SIMULATED.equals(mode)
-                || PROVIDER_MODE_OVERRIDE_NONE.equals(mode));
-
-        mThreadingDomain.postAndWait(() -> {
-            synchronized (mSharedLock) {
-                switch (providerName) {
-                    case PRIMARY_PROVIDER_NAME: {
-                        mPrimaryProviderModeOverride = mode;
-                        break;
-                    }
-                    case SECONDARY_PROVIDER_NAME: {
-                        mSecondaryProviderModeOverride = mode;
-                        break;
-                    }
-                }
-            }
-        }, BLOCKING_OP_WAIT_DURATION_MILLIS);
-    }
-
-    /** Sets this service into provider state recording mode for tests. */
     void setProviderStateRecordingEnabled(boolean enabled) {
         enforceManageTimeZoneDetectorPermission();
 
@@ -457,8 +348,8 @@
      * Passes a {@link TestCommand} to the specified provider and waits for the response.
      */
     @NonNull
-    Bundle handleProviderTestCommand(
-            @NonNull String providerName, @NonNull TestCommand testCommand) {
+    Bundle handleProviderTestCommand(@IntRange(from = 0, to = 1) int providerIndex,
+            @NonNull TestCommand testCommand) {
         enforceManageTimeZoneDetectorPermission();
 
         // Because this method blocks and posts work to the threading domain thread, it would cause
@@ -479,7 +370,7 @@
                     return;
                 }
                 mLocationTimeZoneDetectorController.handleProviderTestCommand(
-                        providerName, testCommand, remoteCallback);
+                        providerIndex, testCommand, remoteCallback);
             }
         });
 
@@ -505,6 +396,17 @@
         synchronized (mSharedLock) {
             ipw.println("LocationTimeZoneManagerService:");
             ipw.increaseIndent();
+
+            ipw.println("Primary provider config:");
+            ipw.increaseIndent();
+            mPrimaryProviderConfig.dump(ipw, args);
+            ipw.decreaseIndent();
+
+            ipw.println("Secondary provider config:");
+            ipw.increaseIndent();
+            mSecondaryProviderConfig.dump(ipw, args);
+            ipw.decreaseIndent();
+
             if (mLocationTimeZoneDetectorController == null) {
                 ipw.println("{Stopped}");
             } else {
@@ -541,4 +443,75 @@
                 android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION,
                 "manage time and time zone detection");
     }
+
+    /** An inner class for managing a provider's config. */
+    private final class ProviderConfig implements Dumpable {
+        @IntRange(from = 0, to = 1) private final int mIndex;
+        @NonNull private final String mName;
+        @NonNull private final String mServiceAction;
+
+        ProviderConfig(@IntRange(from = 0, to = 1) int index, @NonNull String name,
+                @NonNull String serviceAction) {
+            Preconditions.checkArgument(index >= 0 && index <= 1);
+            mIndex = index;
+            mName = Objects.requireNonNull(name);
+            mServiceAction = Objects.requireNonNull(serviceAction);
+        }
+
+        @NonNull
+        LocationTimeZoneProvider createProvider() {
+            LocationTimeZoneProviderProxy proxy = createProxy();
+            ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(mIndex);
+            return new BinderLocationTimeZoneProvider(
+                    providerMetricsLogger, mThreadingDomain, mName, proxy);
+        }
+
+        @GuardedBy("mSharedLock")
+        @Override
+        public void dump(IndentingPrintWriter ipw, String[] args) {
+            ipw.printf("getMode()=%s\n", getMode());
+            ipw.printf("getPackageName()=%s\n", getPackageName());
+        }
+
+        @NonNull
+        private LocationTimeZoneProviderProxy createProxy() {
+            String mode = getMode();
+            if (Objects.equals(mode, PROVIDER_MODE_SIMULATED)) {
+                return new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
+            } else if (Objects.equals(mode, PROVIDER_MODE_DISABLED)) {
+                return new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
+            } else {
+                // mode == PROVIDER_MODE_OVERRIDE_ENABLED (or unknown).
+                return createRealProxy();
+            }
+        }
+
+        /** Returns the mode of the provider. */
+        @NonNull
+        private String getMode() {
+            if (mIndex == 0) {
+                return mServiceConfigAccessor.getPrimaryLocationTimeZoneProviderMode();
+            } else {
+                return mServiceConfigAccessor.getSecondaryLocationTimeZoneProviderMode();
+            }
+        }
+
+        @NonNull
+        private RealLocationTimeZoneProviderProxy createRealProxy() {
+            String providerServiceAction = mServiceAction;
+            String providerPackageName = getPackageName();
+            return new RealLocationTimeZoneProviderProxy(
+                    mContext, mHandler, mThreadingDomain, providerServiceAction,
+                    providerPackageName);
+        }
+
+        @NonNull
+        private String getPackageName() {
+            if (mIndex == 0) {
+                return mServiceConfigAccessor.getPrimaryLocationTimeZoneProviderPackageName();
+            } else {
+                return mServiceConfigAccessor.getSecondaryLocationTimeZoneProviderPackageName();
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
index 40638080..c6df624 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
@@ -16,19 +16,25 @@
 package com.android.server.timezonedetector.location;
 
 import static android.app.time.LocationTimeZoneManager.DUMP_STATE_OPTION_PROTO;
-import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
-import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
 import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_DUMP_STATE;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_RECORD_PROVIDER_STATES;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND;
-import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_START;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_STOP;
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
 
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS;
+import static com.android.server.timedetector.ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
+import static com.android.server.timedetector.ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_DISABLED;
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_ENABLED;
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_SIMULATED;
 import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
 import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
 import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
@@ -53,16 +59,12 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
 /** Implements the shell command interface for {@link LocationTimeZoneManagerService}. */
 class LocationTimeZoneManagerShellCommand extends ShellCommand {
 
-    private static final List<String> VALID_PROVIDER_NAMES =
-            Arrays.asList(PRIMARY_PROVIDER_NAME, SECONDARY_PROVIDER_NAME);
-
     private final LocationTimeZoneManagerService mService;
 
     LocationTimeZoneManagerShellCommand(LocationTimeZoneManagerService service) {
@@ -82,9 +84,6 @@
             case SHELL_COMMAND_STOP: {
                 return runStop();
             }
-            case SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE: {
-                return runSetProviderModeOverride();
-            }
             case SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND: {
                 return runSendProviderTestCommand();
             }
@@ -110,10 +109,6 @@
         pw.println("    Starts the location_time_zone_manager, creating time zone providers.");
         pw.printf("  %s\n", SHELL_COMMAND_STOP);
         pw.println("    Stops the location_time_zone_manager, destroying time zone providers.");
-        pw.printf("  %s <provider name> <mode>\n", SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE);
-        pw.println("    Sets a provider into a test mode next time the service started.");
-        pw.printf("    Values: %s|%s|%s\n", PROVIDER_MODE_OVERRIDE_NONE,
-                PROVIDER_MODE_OVERRIDE_DISABLED, PROVIDER_MODE_OVERRIDE_SIMULATED);
         pw.printf("  %s (true|false)\n", SHELL_COMMAND_RECORD_PROVIDER_STATES);
         pw.printf("    Enables / disables provider state recording mode. See also %s. The default"
                 + " state is always \"false\".\n", SHELL_COMMAND_DUMP_STATE);
@@ -126,11 +121,11 @@
         pw.println("    Dumps Location Time Zone Manager state for tests as text or binary proto"
                 + " form.");
         pw.println("    See the LocationTimeZoneManagerServiceStateProto definition for details.");
-        pw.printf("  %s <provider name> <test command>\n",
+        pw.printf("  %s <provider index> <test command>\n",
                 SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
         pw.println("    Passes a test command to the named provider.");
         pw.println();
-        pw.printf("<provider name> = One of %s\n", VALID_PROVIDER_NAMES);
+        pw.println("<provider index> = 0 (primary), 1 (secondary)");
         pw.println();
         pw.printf("%s details:\n", SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
         pw.println();
@@ -146,6 +141,47 @@
         pw.println();
         pw.println("Test commands cannot currently be passed to real provider implementations.");
         pw.println();
+        pw.printf("This service is also affected by the following device_config flags in the"
+                + " %s namespace:\n", NAMESPACE_SYSTEM_TIME);
+        pw.printf("    %s - [default=true], only observed if the feature is enabled in config,"
+                        + "set this to false to disable the feature\n",
+                KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED);
+        pw.printf("    %s - [default=false]. Only used if the device does not have an explicit"
+                        + " 'location time zone detection enabled' setting configured [*].\n",
+                KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT);
+        pw.printf("    %s - [default=<unset>]. Used to override the device's 'location time zone"
+                        + " detection enabled' setting [*]\n",
+                KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE);
+        pw.printf("    %s - Overrides the mode of the primary provider. Values=%s|%s|%s\n",
+                KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
+                PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED, PROVIDER_MODE_SIMULATED);
+        pw.printf("    %s - Overrides the mode of the secondary provider. Values=%s|%s|%s\n",
+                KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
+                PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED, PROVIDER_MODE_SIMULATED);
+        pw.printf("    %s - \n",
+                KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE);
+        pw.printf("    %s - Sets the amount of time the service waits when uncertain before making"
+                        + " an 'uncertain' suggestion to the time zone detector.\n",
+                KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS);
+        pw.printf("    %s - Sets the initialization time passed to the location time zone providers"
+                        + "\n",
+                KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS);
+        pw.printf("    %s - Sets the amount of extra time added to the location time zone providers"
+                        + " initialization time\n",
+                KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS);
+        pw.println();
+        pw.println("[*] The user must still have location = on / auto time zone detection = on");
+        pw.println();
+        pw.printf("Typically, use '%s' to stop the service before setting individual"
+                + " flags and '%s' after to restart it.\n",
+                SHELL_COMMAND_STOP, SHELL_COMMAND_START);
+        pw.println();
+        pw.println("Example:");
+        pw.printf("    $ adb shell cmd device_config put %s %s %s\n",
+                NAMESPACE_SYSTEM_TIME, KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
+                "true");
+        pw.println("See adb shell cmd device_config for more information.");
+        pw.println();
     }
 
     private int runStart() {
@@ -172,21 +208,6 @@
         return 0;
     }
 
-    private int runSetProviderModeOverride() {
-        PrintWriter outPrintWriter = getOutPrintWriter();
-        try {
-            String providerName = getNextArgRequired();
-            String modeOverride = getNextArgRequired();
-            outPrintWriter.println("Setting provider mode override for " + providerName
-                    + " to " + modeOverride);
-            mService.setProviderModeOverride(providerName, modeOverride);
-        } catch (RuntimeException e) {
-            reportError(e);
-            return 1;
-        }
-        return 0;
-    }
-
     private int runRecordProviderStates() {
         PrintWriter outPrintWriter = getOutPrintWriter();
         boolean enabled;
@@ -293,10 +314,10 @@
     private int runSendProviderTestCommand() {
         PrintWriter outPrintWriter = getOutPrintWriter();
 
-        String providerName;
+        int providerIndex;
         TestCommand testCommand;
         try {
-            providerName = validateProviderName(getNextArgRequired());
+            providerIndex = parseProviderIndex(getNextArgRequired());
             testCommand = createTestCommandFromNextShellArg();
         } catch (RuntimeException e) {
             reportError(e);
@@ -304,9 +325,9 @@
         }
 
         outPrintWriter.println("Injecting testCommand=" + testCommand
-                + " to providerName=" + providerName);
+                + " to providerIndex=" + providerIndex);
         try {
-            Bundle result = mService.handleProviderTestCommand(providerName, testCommand);
+            Bundle result = mService.handleProviderTestCommand(providerIndex, testCommand);
             outPrintWriter.println(result);
         } catch (RuntimeException e) {
             reportError(e);
@@ -326,11 +347,11 @@
         e.printStackTrace(errPrintWriter);
     }
 
-    @NonNull
-    static String validateProviderName(@NonNull String value) {
-        if (!VALID_PROVIDER_NAMES.contains(value)) {
-            throw new IllegalArgumentException("Unknown provider name=" + value);
+    private static int parseProviderIndex(@NonNull String providerIndexString) {
+        int providerIndex = Integer.parseInt(providerIndexString);
+        if (providerIndex < 0 || providerIndex > 1) {
+            throw new IllegalArgumentException(providerIndexString);
         }
-        return value;
+        return providerIndex;
     }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
index 6c3f016..b5ac712 100644
--- a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
@@ -62,15 +62,17 @@
     RealLocationTimeZoneProviderProxy(
             @NonNull Context context, @NonNull Handler handler,
             @NonNull ThreadingDomain threadingDomain, @NonNull String action,
-            int enableOverlayResId, int nonOverlayPackageResId) {
+            @NonNull String providerPackageName) {
         super(context, threadingDomain);
         mManagerProxy = null;
         mRequest = TimeZoneProviderRequest.createStopUpdatesRequest();
+
+        Objects.requireNonNull(providerPackageName);
         mServiceWatcher = ServiceWatcher.create(context,
                 handler,
                 "RealLocationTimeZoneProviderProxy",
-                new CurrentUserServiceSupplier(context, action, enableOverlayResId,
-                        nonOverlayPackageResId, BIND_TIME_ZONE_PROVIDER_SERVICE,
+                new CurrentUserServiceSupplier(context, action,
+                        providerPackageName, BIND_TIME_ZONE_PROVIDER_SERVICE,
                         INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE),
                 this);
     }
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index ae806aa..7bc6056 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -38,6 +38,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.VcnManagementService.VcnCallback;
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 
@@ -328,6 +329,8 @@
 
     private void handleNetworkRequested(
             @NonNull NetworkRequest request, int score, int providerId) {
+        Slog.v(getLogTag(), "Received request " + request);
+
         if (score > getNetworkScore()) {
             if (VDBG) {
                 Slog.v(
@@ -409,6 +412,26 @@
         return TAG + " [" + mSubscriptionGroup.hashCode() + "]";
     }
 
+    /**
+     * Dumps the state of this Vcn for logging and debugging purposes.
+     *
+     * <p>PII and credentials MUST NEVER be dumped here.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("Vcn (" + mSubscriptionGroup + "):");
+        pw.increaseIndent();
+
+        pw.println("mCurrentStatus: " + mCurrentStatus);
+
+        pw.println("mVcnGatewayConnections:");
+        for (VcnGatewayConnection gw : mVcnGatewayConnections.values()) {
+            gw.dump(pw);
+        }
+        pw.println();
+
+        pw.decreaseIndent();
+    }
+
     /** Retrieves the network score for a VCN Network */
     // Package visibility for use in VcnGatewayConnection
     static int getNetworkScore() {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 20c08eb..83ac36f 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -77,6 +77,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.internal.util.WakeupMessage;
@@ -84,6 +85,7 @@
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
 import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.util.MtuUtils;
 
 import java.io.IOException;
 import java.net.Inet4Address;
@@ -448,6 +450,44 @@
      */
     private static final int EVENT_SAFE_MODE_TIMEOUT_EXCEEDED = 10;
 
+    /**
+     * Sent when an IKE has completed migration, and created updated transforms for application.
+     *
+     * <p>Only relevant in the Connected state.
+     *
+     * @param arg1 The session token for the IKE Session that completed migration, used to prevent
+     *     out-of-date signals from propagating.
+     * @param obj @NonNull An EventMigrationCompletedInfo instance with relevant data.
+     */
+    private static final int EVENT_MIGRATION_COMPLETED = 11;
+
+    private static class EventMigrationCompletedInfo implements EventInfo {
+        @NonNull public final IpSecTransform inTransform;
+        @NonNull public final IpSecTransform outTransform;
+
+        EventMigrationCompletedInfo(
+                @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform) {
+            this.inTransform = Objects.requireNonNull(inTransform);
+            this.outTransform = Objects.requireNonNull(outTransform);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(inTransform, outTransform);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (!(other instanceof EventMigrationCompletedInfo)) {
+                return false;
+            }
+
+            final EventMigrationCompletedInfo rhs = (EventMigrationCompletedInfo) other;
+            return Objects.equals(inTransform, rhs.inTransform)
+                    && Objects.equals(outTransform, rhs.outTransform);
+        }
+    }
+
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     @NonNull
     final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -574,7 +614,7 @@
      * <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable
      * otherwise.
      */
-    private NetworkAgent mNetworkAgent;
+    private VcnNetworkAgent mNetworkAgent;
 
     @Nullable private WakeupMessage mTeardownTimeoutAlarm;
     @Nullable private WakeupMessage mDisconnectRequestAlarm;
@@ -1053,6 +1093,14 @@
         sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, token);
     }
 
+    private void migrationCompleted(
+            int token, @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform) {
+        sendMessageAndAcquireWakeLock(
+                EVENT_MIGRATION_COMPLETED,
+                token,
+                new EventMigrationCompletedInfo(inTransform, outTransform));
+    }
+
     private void childTransformCreated(
             int token, @NonNull IpSecTransform transform, int direction) {
         sendMessageAndAcquireWakeLock(
@@ -1148,7 +1196,9 @@
                 case EVENT_SETUP_COMPLETED: // Fallthrough
                 case EVENT_DISCONNECT_REQUESTED: // Fallthrough
                 case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough
-                case EVENT_SUBSCRIPTIONS_CHANGED:
+                case EVENT_SUBSCRIPTIONS_CHANGED: // Fallthrough
+                case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: // Fallthrough
+                case EVENT_MIGRATION_COMPLETED:
                     logUnexpectedEvent(msg.what);
                     break;
                 default:
@@ -1440,30 +1490,32 @@
     private abstract class ConnectedStateBase extends ActiveBaseState {
         protected void updateNetworkAgent(
                 @NonNull IpSecTunnelInterface tunnelIface,
-                @NonNull NetworkAgent agent,
+                @NonNull VcnNetworkAgent agent,
                 @NonNull VcnChildSessionConfiguration childConfig) {
             final NetworkCapabilities caps =
                     buildNetworkCapabilities(mConnectionConfig, mUnderlying);
             final LinkProperties lp =
-                    buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+                    buildConnectedLinkProperties(
+                            mConnectionConfig, tunnelIface, childConfig, mUnderlying);
 
             agent.sendNetworkCapabilities(caps);
             agent.sendLinkProperties(lp);
         }
 
-        protected NetworkAgent buildNetworkAgent(
+        protected VcnNetworkAgent buildNetworkAgent(
                 @NonNull IpSecTunnelInterface tunnelIface,
                 @NonNull VcnChildSessionConfiguration childConfig) {
             final NetworkCapabilities caps =
                     buildNetworkCapabilities(mConnectionConfig, mUnderlying);
             final LinkProperties lp =
-                    buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+                    buildConnectedLinkProperties(
+                            mConnectionConfig, tunnelIface, childConfig, mUnderlying);
             final NetworkAgentConfig nac =
                     new NetworkAgentConfig.Builder()
                             .setLegacyType(ConnectivityManager.TYPE_MOBILE)
                             .build();
 
-            final NetworkAgent agent =
+            final VcnNetworkAgent agent =
                     mDeps.newNetworkAgent(
                             mVcnContext,
                             TAG,
@@ -1472,15 +1524,21 @@
                             Vcn.getNetworkScore(),
                             nac,
                             mVcnContext.getVcnNetworkProvider(),
-                            () -> {
-                                Slog.d(TAG, "NetworkAgent was unwanted");
-                                // If network agent has already been torn down, skip sending the
-                                // disconnect. Unwanted() is always called, even when networkAgents
-                                // are unregistered in teardownNetwork(), so prevent duplicate
-                                // notifications.
-                                if (mNetworkAgent != null) {
-                                    teardownAsynchronously();
+                            (agentRef) -> {
+                                // Only trigger teardown if the NetworkAgent hasn't been replaced or
+                                // changed. This guards against two cases - the first where
+                                // unwanted() may be called as a result of the
+                                // NetworkAgent.unregister() call, which might trigger a teardown
+                                // instead of just a Network disconnect, as well as the case where a
+                                // new NetworkAgent replaces an old one before the unwanted() call
+                                // is processed.
+                                if (mNetworkAgent != agentRef) {
+                                    Slog.d(TAG, "unwanted() called on stale NetworkAgent");
+                                    return;
                                 }
+
+                                Slog.d(TAG, "NetworkAgent was unwanted");
+                                teardownAsynchronously();
                             } /* networkUnwantedCallback */,
                             (status) -> {
                                 if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
@@ -1620,12 +1678,36 @@
                 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
                     handleSafeModeTimeoutExceeded();
                     break;
+                case EVENT_MIGRATION_COMPLETED:
+                    final EventMigrationCompletedInfo migrationCompletedInfo =
+                            (EventMigrationCompletedInfo) msg.obj;
+
+                    handleMigrationCompleted(migrationCompletedInfo);
+                    break;
                 default:
                     logUnhandledMessage(msg);
                     break;
             }
         }
 
+        private void handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo) {
+            applyTransform(
+                    mCurrentToken,
+                    mTunnelIface,
+                    mUnderlying.network,
+                    migrationCompletedInfo.inTransform,
+                    IpSecManager.DIRECTION_IN);
+
+            applyTransform(
+                    mCurrentToken,
+                    mTunnelIface,
+                    mUnderlying.network,
+                    migrationCompletedInfo.outTransform,
+                    IpSecManager.DIRECTION_OUT);
+
+            updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+        }
+
         private void handleUnderlyingNetworkChanged(@NonNull Message msg) {
             final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
             mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
@@ -1815,7 +1897,10 @@
     private static LinkProperties buildConnectedLinkProperties(
             @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
             @NonNull IpSecTunnelInterface tunnelIface,
-            @NonNull VcnChildSessionConfiguration childConfig) {
+            @NonNull VcnChildSessionConfiguration childConfig,
+            @Nullable UnderlyingNetworkRecord underlying) {
+        final VcnControlPlaneIkeConfig controlPlaneConfig =
+                (VcnControlPlaneIkeConfig) gatewayConnectionConfig.getControlPlaneConfig();
         final LinkProperties lp = new LinkProperties();
 
         lp.setInterfaceName(tunnelIface.getInterfaceName());
@@ -1831,7 +1916,12 @@
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null /*gateway*/,
                 null /*iface*/, RouteInfo.RTN_UNICAST));
 
-        lp.setMtu(gatewayConnectionConfig.getMaxMtu());
+        final int underlyingMtu = (underlying == null) ? 0 : underlying.linkProperties.getMtu();
+        lp.setMtu(
+                MtuUtils.getMtu(
+                        controlPlaneConfig.getChildSessionParams().getSaProposals(),
+                        gatewayConnectionConfig.getMaxMtu(),
+                        underlyingMtu));
 
         return lp;
     }
@@ -1912,8 +2002,7 @@
                 @NonNull IpSecTransform inIpSecTransform,
                 @NonNull IpSecTransform outIpSecTransform) {
             Slog.v(TAG, "ChildTransformsMigrated; token " + mToken);
-            onIpSecTransformCreated(inIpSecTransform, IpSecManager.DIRECTION_IN);
-            onIpSecTransformCreated(outIpSecTransform, IpSecManager.DIRECTION_OUT);
+            migrationCompleted(mToken, inIpSecTransform, outIpSecTransform);
         }
 
         @Override
@@ -1924,6 +2013,27 @@
         }
     }
 
+    /**
+     * Dumps the state of this VcnGatewayConnection for logging and debugging purposes.
+     *
+     * <p>PII and credentials MUST NEVER be dumped here.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("VcnGatewayConnection (" + mConnectionConfig.getGatewayConnectionName() + "):");
+        pw.increaseIndent();
+
+        pw.println("Current state: " + getCurrentState().getClass().getSimpleName());
+        pw.println("mIsQuitting: " + mIsQuitting);
+        pw.println("mIsInSafeMode: " + mIsInSafeMode);
+        pw.println("mCurrentToken: " + mCurrentToken);
+        pw.println("mFailedAttempts: " + mFailedAttempts);
+        pw.println(
+                "mNetworkAgent.getNetwork(): "
+                        + (mNetworkAgent == null ? null : mNetworkAgent.getNetwork()));
+
+        pw.decreaseIndent();
+    }
+
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     void setTunnelInterface(IpSecTunnelInterface tunnelIface) {
         mTunnelIface = tunnelIface;
@@ -1965,12 +2075,12 @@
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
-    NetworkAgent getNetworkAgent() {
+    VcnNetworkAgent getNetworkAgent() {
         return mNetworkAgent;
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
-    void setNetworkAgent(@Nullable NetworkAgent networkAgent) {
+    void setNetworkAgent(@Nullable VcnNetworkAgent networkAgent) {
         mNetworkAgent = networkAgent;
     }
 
@@ -2058,8 +2168,8 @@
             return new WakeupMessage(vcnContext.getContext(), handler, tag, runnable);
         }
 
-        /** Builds a new NetworkAgent. */
-        public NetworkAgent newNetworkAgent(
+        /** Builds a new VcnNetworkAgent. */
+        public VcnNetworkAgent newNetworkAgent(
                 @NonNull VcnContext vcnContext,
                 @NonNull String tag,
                 @NonNull NetworkCapabilities caps,
@@ -2067,27 +2177,18 @@
                 @NonNull int score,
                 @NonNull NetworkAgentConfig nac,
                 @NonNull NetworkProvider provider,
-                @NonNull Runnable networkUnwantedCallback,
+                @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback,
                 @NonNull Consumer<Integer> validationStatusCallback) {
-            return new NetworkAgent(
-                    vcnContext.getContext(),
-                    vcnContext.getLooper(),
+            return new VcnNetworkAgent(
+                    vcnContext,
                     tag,
                     caps,
                     lp,
                     score,
                     nac,
-                    provider) {
-                @Override
-                public void onNetworkUnwanted() {
-                    networkUnwantedCallback.run();
-                }
-
-                @Override
-                public void onValidationStatus(int status, @Nullable Uri redirectUri) {
-                    validationStatusCallback.accept(status);
-                }
-            };
+                    provider,
+                    networkUnwantedCallback,
+                    validationStatusCallback);
         }
 
         /** Gets the elapsed real time since boot, in millis. */
@@ -2203,4 +2304,73 @@
             mImpl.release();
         }
     }
+
+    /** Proxy Implementation of NetworkAgent, used for testing. */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static class VcnNetworkAgent {
+        private final NetworkAgent mImpl;
+
+        public VcnNetworkAgent(
+                @NonNull VcnContext vcnContext,
+                @NonNull String tag,
+                @NonNull NetworkCapabilities caps,
+                @NonNull LinkProperties lp,
+                @NonNull int score,
+                @NonNull NetworkAgentConfig nac,
+                @NonNull NetworkProvider provider,
+                @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback,
+                @NonNull Consumer<Integer> validationStatusCallback) {
+            mImpl =
+                    new NetworkAgent(
+                            vcnContext.getContext(),
+                            vcnContext.getLooper(),
+                            tag,
+                            caps,
+                            lp,
+                            score,
+                            nac,
+                            provider) {
+                        @Override
+                        public void onNetworkUnwanted() {
+                            networkUnwantedCallback.accept(VcnNetworkAgent.this);
+                        }
+
+                        @Override
+                        public void onValidationStatus(int status, @Nullable Uri redirectUri) {
+                            validationStatusCallback.accept(status);
+                        }
+                    };
+        }
+
+        /** Registers the underlying NetworkAgent */
+        public void register() {
+            mImpl.register();
+        }
+
+        /** Marks the underlying NetworkAgent as connected */
+        public void markConnected() {
+            mImpl.markConnected();
+        }
+
+        /** Unregisters the underlying NetworkAgent */
+        public void unregister() {
+            mImpl.unregister();
+        }
+
+        /** Sends new NetworkCapabilities for the underlying NetworkAgent */
+        public void sendNetworkCapabilities(@NonNull NetworkCapabilities caps) {
+            mImpl.sendNetworkCapabilities(caps);
+        }
+
+        /** Sends new LinkProperties for the underlying NetworkAgent */
+        public void sendLinkProperties(@NonNull LinkProperties lp) {
+            mImpl.sendLinkProperties(lp);
+        }
+
+        /** Retrieves the Network for the underlying NetworkAgent */
+        @Nullable
+        public Network getNetwork() {
+            return mImpl.getNetwork();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index a909695..be0deb5 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -29,6 +29,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
 
 import java.util.Objects;
 import java.util.Set;
@@ -129,10 +130,50 @@
             mScore = score;
             mProviderId = providerId;
         }
+
+        /**
+         * Dumps the state of this NetworkRequestEntry for logging and debugging purposes.
+         *
+         * <p>PII and credentials MUST NEVER be dumped here.
+         */
+        public void dump(IndentingPrintWriter pw) {
+            pw.println("NetworkRequestEntry:");
+            pw.increaseIndent();
+
+            pw.println("mRequest: " + mRequest);
+            pw.println("mScore: " + mScore);
+            pw.println("mProviderId: " + mProviderId);
+
+            pw.decreaseIndent();
+        }
     }
 
     // package-private
     interface NetworkRequestListener {
         void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId);
     }
+
+    /**
+     * Dumps the state of this VcnNetworkProvider for logging and debugging purposes.
+     *
+     * <p>PII and credentials MUST NEVER be dumped here.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("VcnNetworkProvider:");
+        pw.increaseIndent();
+
+        pw.println("mListeners:");
+        for (NetworkRequestListener listener : mListeners) {
+            pw.println(listener);
+        }
+        pw.println();
+
+        pw.println("mRequests.values:");
+        for (NetworkRequestEntry entry : mRequests.values()) {
+            entry.dump(pw);
+        }
+        pw.println();
+
+        pw.decreaseIndent();
+    }
 }
diff --git a/services/core/java/com/android/server/vcn/util/MtuUtils.java b/services/core/java/com/android/server/vcn/util/MtuUtils.java
new file mode 100644
index 0000000..49c1a02
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/util/MtuUtils.java
@@ -0,0 +1,155 @@
+/*
+ * 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.server.vcn.util;
+
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_3DES;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CTR;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_CHACHA20_POLY1305;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_CMAC_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_NONE;
+
+import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
+
+import static java.lang.Math.max;
+import static java.util.Collections.unmodifiableMap;
+
+import android.annotation.NonNull;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.util.ArrayMap;
+import android.util.Pair;
+import android.util.Slog;
+
+import java.util.List;
+import java.util.Map;
+
+/** @hide */
+public class MtuUtils {
+    private static final String TAG = MtuUtils.class.getSimpleName();
+    /**
+     * Max ESP overhead possible
+     *
+     * <p>60 (Outer IPv4 + options) + 8 (UDP encap) + 4 (SPI) + 4 (Seq) + 2 (Pad + NextHeader)
+     */
+    private static final int GENERIC_ESP_OVERHEAD_MAX = 78;
+
+    /** Maximum overheads of authentication algorithms, keyed on IANA-defined constants */
+    private static final Map<Integer, Integer> AUTH_ALGORITHM_OVERHEAD;
+
+    static {
+        final Map<Integer, Integer> map = new ArrayMap<>();
+        map.put(INTEGRITY_ALGORITHM_NONE, 0);
+        map.put(INTEGRITY_ALGORITHM_HMAC_SHA1_96, 12);
+        map.put(INTEGRITY_ALGORITHM_AES_XCBC_96, 12);
+        map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, 32);
+        map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, 48);
+        map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, 64);
+        map.put(INTEGRITY_ALGORITHM_AES_CMAC_96, 12);
+
+        AUTH_ALGORITHM_OVERHEAD = unmodifiableMap(map);
+    }
+
+    /** Maximum overheads of encryption algorithms, keyed on IANA-defined constants */
+    private static final Map<Integer, Integer> CRYPT_ALGORITHM_OVERHEAD;
+
+    static {
+        final Map<Integer, Integer> map = new ArrayMap<>();
+        map.put(ENCRYPTION_ALGORITHM_3DES, 15); // 8 (IV) + 7 (Max pad)
+        map.put(ENCRYPTION_ALGORITHM_AES_CBC, 31); // 16 (IV) + 15 (Max pad)
+        map.put(ENCRYPTION_ALGORITHM_AES_CTR, 11); // 8 (IV) + 3 (Max pad)
+
+        CRYPT_ALGORITHM_OVERHEAD = unmodifiableMap(map);
+    }
+
+    /** Maximum overheads of combined mode algorithms, keyed on IANA-defined constants */
+    private static final Map<Integer, Integer> AUTHCRYPT_ALGORITHM_OVERHEAD;
+
+    static {
+        final Map<Integer, Integer> map = new ArrayMap<>();
+        map.put(ENCRYPTION_ALGORITHM_AES_GCM_8, 19); // 8 (IV) + 3 (Max pad) + 8 (ICV)
+        map.put(ENCRYPTION_ALGORITHM_AES_GCM_12, 23); // 8 (IV) + 3 (Max pad) + 12 (ICV)
+        map.put(ENCRYPTION_ALGORITHM_AES_GCM_16, 27); // 8 (IV) + 3 (Max pad) + 16 (ICV)
+        map.put(ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, 27); // 8 (IV) + 3 (Max pad) + 16 (ICV)
+
+        AUTHCRYPT_ALGORITHM_OVERHEAD = unmodifiableMap(map);
+    }
+
+    /**
+     * Calculates the MTU of the inner interface based on the parameters provided
+     *
+     * <p>The MTU of the inner interface will be the minimum of the following:
+     *
+     * <ul>
+     *   <li>The MTU of the outer interface, minus the greatest ESP overhead (based on proposed
+     *       algorithms).
+     *   <li>The maximum MTU as provided in the arguments.
+     * </ul>
+     */
+    public static int getMtu(
+            @NonNull List<ChildSaProposal> childProposals, int maxMtu, int underlyingMtu) {
+        if (underlyingMtu <= 0) {
+            return IPV6_MIN_MTU;
+        }
+
+        boolean hasUnknownAlgorithm = false;
+        int maxAuthOverhead = 0;
+        int maxCryptOverhead = 0;
+        int maxAuthCryptOverhead = 0;
+
+        for (ChildSaProposal proposal : childProposals) {
+            for (Pair<Integer, Integer> encryptionAlgoPair : proposal.getEncryptionAlgorithms()) {
+                final int algo = encryptionAlgoPair.first;
+
+                if (AUTHCRYPT_ALGORITHM_OVERHEAD.containsKey(algo)) {
+                    maxAuthCryptOverhead =
+                            max(maxAuthCryptOverhead, AUTHCRYPT_ALGORITHM_OVERHEAD.get(algo));
+                    continue;
+                } else if (CRYPT_ALGORITHM_OVERHEAD.containsKey(algo)) {
+                    maxCryptOverhead = max(maxCryptOverhead, CRYPT_ALGORITHM_OVERHEAD.get(algo));
+                    continue;
+                }
+
+                Slog.wtf(TAG, "Unknown encryption algorithm requested: " + algo);
+                return IPV6_MIN_MTU;
+            }
+
+            for (int algo : proposal.getIntegrityAlgorithms()) {
+                if (AUTH_ALGORITHM_OVERHEAD.containsKey(algo)) {
+                    maxAuthOverhead = max(maxAuthOverhead, AUTH_ALGORITHM_OVERHEAD.get(algo));
+                    continue;
+                }
+
+                Slog.wtf(TAG, "Unknown integrity algorithm requested: " + algo);
+                return IPV6_MIN_MTU;
+            }
+        }
+
+        // Return minimum of maxMtu, and the adjusted MTUs based on algorithms.
+        final int combinedModeMtu = underlyingMtu - maxAuthCryptOverhead - GENERIC_ESP_OVERHEAD_MAX;
+        final int normalModeMtu =
+                underlyingMtu - maxCryptOverhead - maxAuthOverhead - GENERIC_ESP_OVERHEAD_MAX;
+        return Math.min(Math.min(maxMtu, combinedModeMtu), normalModeMtu);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 01a46b3..376595b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1887,6 +1887,14 @@
         return selectedTheme;
     }
 
+    // Whether this activity launched from system or Home or SystemUI
+    private boolean launchedFromSystemSurface() {
+        return launchedFromUid == Process.SYSTEM_UID || launchedFromUid == Process.ROOT_UID
+                || launchedFromHomeProcess
+                || mAtmService.getSysUiServiceComponentLocked().getPackageName().equals(
+                        launchedFromPackage);
+    }
+
     private boolean validateStartingWindowTheme(String pkg, int theme) {
         // If this is a translucent window, then don't show a starting window -- the current
         // effect (a full-screen opaque starting window that fades away to the real contents
@@ -1913,7 +1921,9 @@
                     "Translucent=%s Floating=%s ShowWallpaper=%s Disable=%s",
                     windowIsTranslucent, windowIsFloating, windowShowWallpaper,
                     windowDisableStarting);
-            if (windowIsTranslucent || windowIsFloating || windowDisableStarting) {
+            // If this activity is launched from system surface, ignore windowDisableStarting
+            if (windowIsTranslucent || windowIsFloating
+                    || (windowDisableStarting && !launchedFromSystemSurface())) {
                 return false;
             }
             if (windowShowWallpaper
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index fe728ab..fb664ab 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -60,7 +60,7 @@
     const std::string instance = std::string() + IStats::descriptor + "/default";
     const binder_exception_t err =
             AServiceManager_addService(statsService->asBinder().get(), instance.c_str());
-    LOG_ALWAYS_FATAL_IF(err != EX_NONE, "Cannot register %s: %d", instance.c_str(), err);
+    LOG_ALWAYS_FATAL_IF(err != EX_NONE, "Cannot register AIDL %s: %d", instance.c_str(), err);
 }
 
 static void startStatsHidlService() {
@@ -69,13 +69,18 @@
 
     android::sp<IStats> statsHal = new StatsHal();
     const android::status_t err = statsHal->registerAsService();
-    LOG_ALWAYS_FATAL_IF(err != android::OK, "Cannot register %s: %d", IStats::descriptor, err);
+    LOG_ALWAYS_FATAL_IF(err != android::OK, "Cannot register HIDL %s: %d", IStats::descriptor, err);
 }
 
 } // namespace
 
 namespace android {
 
+static void android_server_SystemServer_startIStatsService(JNIEnv* /* env */, jobject /* clazz */) {
+    startStatsHidlService();
+    startStatsAidlService();
+}
+
 static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jobject /* clazz */) {
     char propBuf[PROPERTY_VALUE_MAX];
     property_get("system_init.startsensorservice", propBuf, "1");
@@ -129,9 +134,6 @@
     } else {
         ALOGW("%s is deprecated. Skipping registration.", ISchedulingPolicyService::descriptor);
     }
-
-    startStatsAidlService();
-    startStatsHidlService();
 }
 
 static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */,
@@ -160,6 +162,7 @@
  */
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
+        {"startIStatsService", "()V", (void*)android_server_SystemServer_startIStatsService},
         {"startSensorService", "()V", (void*)android_server_SystemServer_startSensorService},
         {"startMemtrackProxyService", "()V",
          (void*)android_server_SystemServer_startMemtrackProxyService},
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 63488f9..f843ea4 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -150,6 +150,11 @@
     return ok();
 }
 
+binder::Status BinderIncrementalService::onInstallationComplete(int32_t storageId) {
+    mImpl.onInstallationComplete(storageId);
+    return ok();
+}
+
 binder::Status BinderIncrementalService::makeBindMount(int32_t storageId,
                                                        const std::string& sourcePath,
                                                        const std::string& targetFullPath,
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index ebb23dc..5c8741b 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -52,6 +52,8 @@
             const ::android::sp<IStorageHealthListener>& healthListener,
             const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts,
             bool* _aidl_return) final;
+    binder::Status onInstallationComplete(int32_t storageId) final;
+
     binder::Status makeBindMount(int32_t storageId, const std::string& sourcePath,
                                  const std::string& targetFullPath, int32_t bindType,
                                  int32_t* _aidl_return) final;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 6695ba8..388f932 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -100,6 +100,12 @@
     return (s & (Constants::blockSize - 1)) == 0;
 }
 
+static bool getAlwaysEnableReadTimeoutsForSystemDataLoaders() {
+    return android::base::
+            GetBoolProperty("debug.incremental.always_enable_read_timeouts_for_system_dataloaders",
+                            true);
+}
+
 static bool getEnforceReadLogsMaxIntervalForSystemDataLoaders() {
     return android::base::GetBoolProperty("debug.incremental.enforce_readlogs_max_interval_for_"
                                           "system_dataloaders",
@@ -315,19 +321,11 @@
     ::rmdir(path::c_str(root));
 }
 
-void IncrementalService::IncFsMount::setReadLogsEnabled(bool value) {
+void IncrementalService::IncFsMount::setFlag(StorageFlags flag, bool value) {
     if (value) {
-        flags |= StorageFlags::ReadLogsEnabled;
+        flags |= flag;
     } else {
-        flags &= ~StorageFlags::ReadLogsEnabled;
-    }
-}
-
-void IncrementalService::IncFsMount::setReadLogsRequested(bool value) {
-    if (value) {
-        flags |= StorageFlags::ReadLogsRequested;
-    } else {
-        flags &= ~StorageFlags::ReadLogsRequested;
+        flags &= ~flag;
     }
 }
 
@@ -728,10 +726,17 @@
             LOG(INFO) << "Skipped data loader stub creation because it already exists";
             return false;
         }
+
         prepareDataLoaderLocked(*ifs, std::move(dataLoaderParams), std::move(statusListener),
                                 healthCheckParams, std::move(healthListener));
         CHECK(ifs->dataLoaderStub);
         dataLoaderStub = ifs->dataLoaderStub;
+
+        // Disable long read timeouts for non-system dataloaders.
+        // To be re-enabled after installation is complete.
+        ifs->setReadTimeoutsRequested(dataLoaderStub->isSystemDataLoader() &&
+                                      getAlwaysEnableReadTimeoutsForSystemDataLoaders());
+        applyStorageParamsLocked(*ifs);
     }
 
     if (dataLoaderStub->isSystemDataLoader() &&
@@ -765,6 +770,18 @@
     return dataLoaderStub->requestStart();
 }
 
+void IncrementalService::onInstallationComplete(StorageId storage) {
+    IfsMountPtr ifs = getIfs(storage);
+    if (!ifs) {
+        return;
+    }
+
+    // Always enable long read timeouts after installation is complete.
+    std::unique_lock l(ifs->lock);
+    ifs->setReadTimeoutsRequested(true);
+    applyStorageParamsLocked(*ifs);
+}
+
 IncrementalService::BindPathMap::const_iterator IncrementalService::findStorageLocked(
         std::string_view path) const {
     return findParentPath(mBindsByPath, path);
@@ -868,7 +885,7 @@
         if (!ifs->readLogsRequested()) {
             return 0;
         }
-        if (auto status = applyStorageParamsLocked(*ifs, /*enableReadLogs=*/true); status != 0) {
+        if (auto status = applyStorageParamsLocked(*ifs); status != 0) {
             return status;
         }
     }
@@ -880,10 +897,10 @@
 
 int IncrementalService::disableReadLogsLocked(IncFsMount& ifs) {
     ifs.setReadLogsRequested(false);
-    return applyStorageParamsLocked(ifs, /*enableReadLogs=*/false);
+    return applyStorageParamsLocked(ifs);
 }
 
-int IncrementalService::applyStorageParamsLocked(IncFsMount& ifs, bool enableReadLogs) {
+int IncrementalService::applyStorageParamsLocked(IncFsMount& ifs) {
     os::incremental::IncrementalFileSystemControlParcel control;
     control.cmd.reset(dup(ifs.control.cmd()));
     control.pendingReads.reset(dup(ifs.control.pendingReads()));
@@ -892,11 +909,15 @@
         control.log.reset(dup(logsFd));
     }
 
+    bool enableReadLogs = ifs.readLogsRequested();
+    bool enableReadTimeouts = ifs.readTimeoutsRequested();
+
     std::lock_guard l(mMountOperationLock);
-    auto status = mVold->setIncFsMountOptions(control, enableReadLogs);
+    auto status = mVold->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts);
     if (status.isOk()) {
-        // Store enabled state.
+        // Store states.
         ifs.setReadLogsEnabled(enableReadLogs);
+        ifs.setReadTimeoutsEnabled(enableReadTimeouts);
     } else {
         LOG(ERROR) << "applyStorageParams failed: " << status.toString8();
     }
@@ -1271,7 +1292,7 @@
         maxPendingTimeUs = std::max(maxPendingTimeUs, microseconds(timeouts.maxPendingTimeUs));
     }
     if (maxPendingTimeUs < Constants::minPerUidTimeout) {
-        LOG(ERROR) << "Skip setting  read timeouts (maxPendingTime < Constants::minPerUidTimeout): "
+        LOG(ERROR) << "Skip setting read timeouts (maxPendingTime < Constants::minPerUidTimeout): "
                    << duration_cast<milliseconds>(maxPendingTimeUs).count() << "ms < "
                    << Constants::minPerUidTimeout.count() << "ms";
         return;
@@ -2946,8 +2967,10 @@
         return result;
     }
 
-    LOG(DEBUG) << id() << ": pendingReads: " << control.pendingReads() << ", "
-               << mLastPendingReads.size() << ": " << mLastPendingReads.front().bootClockTsUs;
+    LOG(DEBUG) << id() << ": pendingReads: fd(" << control.pendingReads() << "), count("
+               << mLastPendingReads.size() << "), block: " << mLastPendingReads.front().block
+               << ", time: " << mLastPendingReads.front().bootClockTsUs
+               << ", uid: " << mLastPendingReads.front().uid;
 
     return getOldestTsFromLastPendingReads();
 }
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index fb6f56c..e3b1e6f 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -118,6 +118,9 @@
         ReadLogsAllowed = 1 << 0,
         ReadLogsEnabled = 1 << 1,
         ReadLogsRequested = 1 << 2,
+
+        ReadTimeoutsEnabled = 1 << 3,
+        ReadTimeoutsRequested = 1 << 4,
     };
 
     struct LoadingProgress {
@@ -160,6 +163,7 @@
                       const StorageHealthCheckParams& healthCheckParams,
                       StorageHealthListener healthListener,
                       std::vector<PerUidReadTimeouts> perUidReadTimeouts);
+    void onInstallationComplete(StorageId storage);
 
     int bind(StorageId storage, std::string_view source, std::string_view target, BindKind kind);
     int unbind(StorageId storage, std::string_view target);
@@ -316,7 +320,7 @@
         } mHealthBase = {TimePoint::max(), kMaxBootClockTsUs};
         StorageHealthCheckParams mHealthCheckParams;
         int mStreamStatus = content::pm::IDataLoaderStatusListener::STREAM_HEALTHY;
-        std::vector<incfs::ReadInfo> mLastPendingReads;
+        std::vector<incfs::ReadInfoWithUid> mLastPendingReads;
     };
     using DataLoaderStubPtr = sp<DataLoaderStub>;
 
@@ -364,13 +368,32 @@
         void disallowReadLogs() { flags &= ~StorageFlags::ReadLogsAllowed; }
         int32_t readLogsAllowed() const { return (flags & StorageFlags::ReadLogsAllowed); }
 
-        void setReadLogsEnabled(bool value);
+        void setReadLogsEnabled(bool value) {
+            return setFlag(StorageFlags::ReadLogsEnabled, value);
+        }
         int32_t readLogsEnabled() const { return (flags & StorageFlags::ReadLogsEnabled); }
 
-        void setReadLogsRequested(bool value);
+        void setReadLogsRequested(bool value) {
+            return setFlag(StorageFlags::ReadLogsRequested, value);
+        }
         int32_t readLogsRequested() const { return (flags & StorageFlags::ReadLogsRequested); }
 
+        void setReadTimeoutsEnabled(bool value) {
+            return setFlag(StorageFlags::ReadTimeoutsEnabled, value);
+        }
+        int32_t readTimeoutsEnabled() const { return (flags & StorageFlags::ReadTimeoutsEnabled); }
+
+        void setReadTimeoutsRequested(bool value) {
+            return setFlag(StorageFlags::ReadTimeoutsRequested, value);
+        }
+        int32_t readTimeoutsRequested() const {
+            return (flags & StorageFlags::ReadTimeoutsRequested);
+        }
+
         static void cleanupFilesystem(std::string_view root);
+
+    private:
+        void setFlag(StorageFlags flag, bool value);
     };
 
     using IfsMountPtr = std::shared_ptr<IncFsMount>;
@@ -422,7 +445,7 @@
     int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode);
 
     int disableReadLogsLocked(IncFsMount& ifs);
-    int applyStorageParamsLocked(IncFsMount& ifs, bool enableReadLogs);
+    int applyStorageParamsLocked(IncFsMount& ifs);
 
     LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
 
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 8e416f3..0755a22 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -55,8 +55,8 @@
     }
     binder::Status setIncFsMountOptions(
             const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
-            bool enableReadLogs) const final {
-        return mInterface->setIncFsMountOptions(control, enableReadLogs);
+            bool enableReadLogs, bool enableReadTimeouts) const final {
+        return mInterface->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts);
     }
 
 private:
@@ -233,8 +233,9 @@
     ErrorCode reserveSpace(const Control& control, FileId id, IncFsSize size) const final {
         return incfs::reserveSpace(control, id, size);
     }
-    WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
-                                   std::vector<incfs::ReadInfo>* pendingReadsBuffer) const final {
+    WaitResult waitForPendingReads(
+            const Control& control, std::chrono::milliseconds timeout,
+            std::vector<incfs::ReadInfoWithUid>* pendingReadsBuffer) const final {
         return incfs::waitForPendingReads(control, timeout, pendingReadsBuffer);
     }
     ErrorCode setUidReadTimeouts(const Control& control,
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index d4cdcbe..78e9589 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -56,8 +56,8 @@
     virtual binder::Status bindMount(const std::string& sourceDir,
                                      const std::string& targetDir) const = 0;
     virtual binder::Status setIncFsMountOptions(
-            const os::incremental::IncrementalFileSystemControlParcel& control,
-            bool enableReadLogs) const = 0;
+            const os::incremental::IncrementalFileSystemControlParcel& control, bool enableReadLogs,
+            bool enableReadTimeouts) const = 0;
 };
 
 class DataLoaderManagerWrapper {
@@ -117,7 +117,7 @@
     virtual ErrorCode reserveSpace(const Control& control, FileId id, IncFsSize size) const = 0;
     virtual WaitResult waitForPendingReads(
             const Control& control, std::chrono::milliseconds timeout,
-            std::vector<incfs::ReadInfo>* pendingReadsBuffer) const = 0;
+            std::vector<incfs::ReadInfoWithUid>* pendingReadsBuffer) const = 0;
     virtual ErrorCode setUidReadTimeouts(
             const Control& control,
             const std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts)
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 1ec446d..14bcd4e 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -56,10 +56,10 @@
     MOCK_CONST_METHOD1(unmountIncFs, binder::Status(const std::string& dir));
     MOCK_CONST_METHOD2(bindMount,
                        binder::Status(const std::string& sourceDir, const std::string& argetDir));
-    MOCK_CONST_METHOD2(
+    MOCK_CONST_METHOD3(
             setIncFsMountOptions,
             binder::Status(const ::android::os::incremental::IncrementalFileSystemControlParcel&,
-                           bool));
+                           bool, bool));
 
     void mountIncFsFails() {
         ON_CALL(*this, mountIncFs(_, _, _, _))
@@ -83,12 +83,13 @@
         ON_CALL(*this, bindMount(_, _)).WillByDefault(Return(binder::Status::ok()));
     }
     void setIncFsMountOptionsFails() const {
-        ON_CALL(*this, setIncFsMountOptions(_, _))
+        ON_CALL(*this, setIncFsMountOptions(_, _, _))
                 .WillByDefault(Return(
                         binder::Status::fromExceptionCode(1, String8("failed to set options"))));
     }
     void setIncFsMountOptionsSuccess() {
-        ON_CALL(*this, setIncFsMountOptions(_, _)).WillByDefault(Return(binder::Status::ok()));
+        ON_CALL(*this, setIncFsMountOptions(_, _, _))
+                .WillByDefault(Invoke(this, &MockVoldService::setIncFsMountOptionsOk));
     }
     binder::Status getInvalidControlParcel(const std::string& imagePath,
                                            const std::string& targetDir, int32_t flags,
@@ -103,10 +104,23 @@
         _aidl_return->log.reset(base::unique_fd(dup(STDIN_FILENO)));
         return binder::Status::ok();
     }
+    binder::Status setIncFsMountOptionsOk(
+            const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
+            bool enableReadLogs, bool enableReadTimeouts) {
+        mReadLogsEnabled = enableReadLogs;
+        mReadTimeoutsEnabled = enableReadTimeouts;
+        return binder::Status::ok();
+    }
+
+    bool readLogsEnabled() const { return mReadLogsEnabled; }
+    bool readTimeoutsEnabled() const { return mReadTimeoutsEnabled; }
 
 private:
     TemporaryFile cmdFile;
     TemporaryFile logFile;
+
+    bool mReadLogsEnabled = false;
+    bool mReadTimeoutsEnabled = true;
 };
 
 class MockDataLoader : public IDataLoader {
@@ -395,7 +409,7 @@
     MOCK_CONST_METHOD3(reserveSpace, ErrorCode(const Control& control, FileId id, IncFsSize size));
     MOCK_CONST_METHOD3(waitForPendingReads,
                        WaitResult(const Control& control, std::chrono::milliseconds timeout,
-                                  std::vector<incfs::ReadInfo>* pendingReadsBuffer));
+                                  std::vector<incfs::ReadInfoWithUid>* pendingReadsBuffer));
     MOCK_CONST_METHOD2(setUidReadTimeouts,
                        ErrorCode(const Control& control,
                                  const std::vector<PerUidReadTimeouts>& perUidReadTimeouts));
@@ -435,7 +449,7 @@
         ON_CALL(*this, waitForPendingReads(_, _, _))
                 .WillByDefault(
                         Invoke([ts](const Control& control, std::chrono::milliseconds timeout,
-                                    std::vector<incfs::ReadInfo>* pendingReadsBuffer) {
+                                    std::vector<incfs::ReadInfoWithUid>* pendingReadsBuffer) {
                             pendingReadsBuffer->push_back({.bootClockTsUs = ts});
                             return android::incfs::WaitResult::HaveData;
                         }));
@@ -1302,8 +1316,10 @@
 
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+    // on startLoading
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
     // We are calling setIncFsMountOptions(true).
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1325,8 +1341,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // Enabling and then disabling readlogs.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1353,8 +1369,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // Enabling and then disabling readlogs.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(2);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(2);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1394,8 +1410,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // Enabling and then disabling readlogs.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(3);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(0);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(3);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1435,8 +1451,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(2);
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // Enabling and then disabling readlogs.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(3);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(5);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(3);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1448,9 +1464,14 @@
                                                IncrementalService::CreateOptions::CreateNew);
     ASSERT_GE(storageId, 0);
 
+    // Before install - long timeouts.
+    ASSERT_TRUE(mVold->readTimeoutsEnabled());
+
     auto dataLoaderParcel = mDataLoaderParcel;
     ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(dataLoaderParcel), {}, {},
                                                   {}, {}));
+    // During install - short timeouts.
+    ASSERT_FALSE(mVold->readTimeoutsEnabled());
 
     // Disable readlogs callback present.
     ASSERT_EQ(storageId, mTimedQueue->mId);
@@ -1463,9 +1484,15 @@
     mClock->advance(90min);
     ASSERT_GE(mDataLoader->setStorageParams(true), 0);
 
+    mIncrementalService->onInstallationComplete(storageId);
+    // After install - long timeouts.
+    ASSERT_TRUE(mVold->readTimeoutsEnabled());
+
     // New installation.
     ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
                                                   {}, {}));
+    // New installation - short timeouts.
+    ASSERT_FALSE(mVold->readTimeoutsEnabled());
 
     // New callback present.
     ASSERT_EQ(storageId, mTimedQueue->mId);
@@ -1485,6 +1512,10 @@
     // And timeout.
     mClock->advance(90min);
     ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM);
+
+    mIncrementalService->onInstallationComplete(storageId);
+    // After install - long timeouts.
+    ASSERT_TRUE(mVold->readTimeoutsEnabled());
 }
 
 TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) {
@@ -1495,9 +1526,9 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // We are calling setIncFsMountOptions(true).
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
     // setIncFsMountOptions(false) is called on the callback.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // After callback is called, disable read logs and remove callback.
@@ -1520,7 +1551,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // checkPermission fails, no calls to set opitions,  start or stop WatchingMode.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(0);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(0);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
     TemporaryDir tempDir;
@@ -1539,7 +1571,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // checkPermission fails, no calls to set opitions,  start or stop WatchingMode.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(0);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(0);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
     TemporaryDir tempDir;
@@ -1559,7 +1592,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // We are calling setIncFsMountOptions.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
     // setIncFsMountOptions fails, no calls to start or stop WatchingMode.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9b2a1e7..1426579 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -442,15 +442,15 @@
 
     private final SystemServerDumper mDumper = new SystemServerDumper();
 
-
     /**
      * The pending WTF to be logged into dropbox.
      */
     private static LinkedList<Pair<String, ApplicationErrorReport.CrashInfo>> sPendingWtfs;
 
-    /**
-     * Start the sensor service. This is a blocking call and can take time.
-     */
+    /** Start the IStats services. This is a blocking call and can take time. */
+    private static native void startIStatsService();
+
+    /** Start the sensor service. This is a blocking call and can take time. */
     private static native void startSensorService();
 
     /**
@@ -1029,6 +1029,10 @@
         mSystemServiceManager.startService(PowerStatsService.class);
         t.traceEnd();
 
+        t.traceBegin("StartIStatsService");
+        startIStatsService();
+        t.traceEnd();
+
         // Start MemtrackProxyService before ActivityManager, so that early calls
         // to Memtrack::getMemory() don't fail.
         t.traceBegin("MemtrackProxyService");
@@ -2678,7 +2682,7 @@
 
             t.traceBegin("RegisterAppOpsPolicy");
             try {
-                mActivityManagerService.setAppOpsPolicy(new AppOpsPolicy());
+                mActivityManagerService.setAppOpsPolicy(new AppOpsPolicy(mSystemContext));
             } catch (Throwable e) {
                 reportWtf("registering app ops policy", e);
             }
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 75614d6..9f24d9a 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -63,6 +63,7 @@
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -105,6 +106,7 @@
 public class DataManager {
 
     private static final String TAG = "DataManager";
+    private static final boolean DEBUG = false;
 
     private static final long RECENT_NOTIFICATIONS_MAX_AGE_MS = 10 * DateUtils.DAY_IN_MILLIS;
     private static final long QUERY_EVENTS_MAX_AGE_MS = 5L * DateUtils.MINUTE_IN_MILLIS;
@@ -217,6 +219,7 @@
         List<ShortcutInfo> shortcuts = getShortcuts(packageName, userId,
                 Collections.singletonList(shortcutId));
         if (shortcuts != null && !shortcuts.isEmpty()) {
+            if (DEBUG) Log.d(TAG, "Found shortcut for " + shortcuts.get(0).getLabel());
             return shortcuts.get(0);
         }
         return null;
@@ -258,6 +261,7 @@
         }
         ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId);
         if (shortcutInfo == null) {
+            Slog.e(TAG, " Shortcut no longer found: " + shortcutId);
             return null;
         }
         int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
@@ -705,6 +709,7 @@
             }
         });
         for (String packageName : packagesToDelete) {
+            if (DEBUG) Log.d(TAG, "Deleting packages data for: " + packageName);
             userData.deletePackageData(packageName);
         }
     }
@@ -716,6 +721,7 @@
         @ShortcutQuery.QueryFlags int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC
                 | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
                 | ShortcutQuery.FLAG_MATCH_CACHED | ShortcutQuery.FLAG_GET_PERSONS_DATA;
+        if (DEBUG) Log.d(TAG, " Get shortcuts with IDs: " + shortcutIds);
         return mShortcutServiceInternal.getShortcuts(
                 UserHandle.USER_SYSTEM, mContext.getPackageName(),
                 /*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null,
@@ -742,7 +748,7 @@
         TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
         String defaultDialer = telecomManager != null
                 ? telecomManager.getDefaultDialerPackage(
-                        new UserHandle(userData.getUserId())) : null;
+                new UserHandle(userData.getUserId())) : null;
         userData.setDefaultDialer(defaultDialer);
     }
 
@@ -848,6 +854,9 @@
         ConversationStore conversationStore = packageData.getConversationStore();
         ConversationInfo oldConversationInfo =
                 conversationStore.getConversation(shortcutInfo.getId());
+        if (oldConversationInfo == null) {
+            if (DEBUG) Log.d(TAG, "Nothing previously stored about conversation.");
+        }
         ConversationInfo.Builder builder = oldConversationInfo != null
                 ? new ConversationInfo.Builder(oldConversationInfo)
                 : new ConversationInfo.Builder();
@@ -1083,6 +1092,7 @@
                 Set<String> shortcutIds = new HashSet<>();
                 for (ShortcutInfo shortcutInfo : shortcuts) {
                     if (packageData != null) {
+                        if (DEBUG) Log.d(TAG, "Deleting shortcut: " + shortcutInfo.getId());
                         packageData.deleteDataForConversation(shortcutInfo.getId());
                     }
                     shortcutIds.add(shortcutInfo.getId());
@@ -1309,6 +1319,7 @@
             int userId = getChangingUserId();
             UserData userData = getUnlockedUserData(userId);
             if (userData != null) {
+                if (DEBUG) Log.d(TAG, "Delete package data for: " + packageName);
                 userData.deletePackageData(packageName);
             }
         }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
index 557c14a..9937ec1 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -98,7 +97,7 @@
         when(nextClient.getTargetUserId()).thenReturn(nextUserId);
 
         mScheduler.scheduleClientMonitor(nextClient);
-        verify(nextClient, never()).start(any());
+
         assertEquals(0, mUserStoppedCallback.numInvocations);
         assertEquals(1, mUserStartedCallback.numInvocations);
 
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 5e50bea..d250297 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -571,7 +571,7 @@
      *
      * @throws SecurityException if the caller does not have the required permission/privileges
      */
-    public static void enforeceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
+    public static void enforceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
             Context context, int subId, String message) {
         if (context.checkCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE)
                 == PERMISSION_GRANTED) {
@@ -591,7 +591,7 @@
      *
      * @throws SecurityException if the caller does not have the required permission/privileges
      */
-    public static void enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+    public static void enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
             Context context, int subId, String message) {
         if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
                 == PERMISSION_GRANTED) {
@@ -613,7 +613,7 @@
      *
      * @throws SecurityException if the caller does not have the required permission/privileges
      */
-    public static void enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+    public static void enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
             Context context, int subId, String message) {
         if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
                 == PERMISSION_GRANTED) {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 34c0018..bb67593 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -26,6 +26,7 @@
 
 import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
 import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -46,15 +47,19 @@
 import android.net.LinkProperties;
 import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.ChildSaProposal;
 import android.net.ipsec.ike.exceptions.AuthenticationFailedException;
 import android.net.ipsec.ike.exceptions.IkeException;
 import android.net.ipsec.ike.exceptions.IkeInternalException;
 import android.net.ipsec.ike.exceptions.TemporaryFailureException;
+import android.net.vcn.VcnControlPlaneIkeConfig;
 import android.net.vcn.VcnManager.VcnErrorCode;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.vcn.util.MtuUtils;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -73,13 +78,13 @@
 @SmallTest
 public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
     private VcnIkeSession mIkeSession;
-    private NetworkAgent mNetworkAgent;
+    private VcnNetworkAgent mNetworkAgent;
 
     @Before
     public void setUp() throws Exception {
         super.setUp();
 
-        mNetworkAgent = mock(NetworkAgent.class);
+        mNetworkAgent = mock(VcnNetworkAgent.class);
         doReturn(mNetworkAgent)
                 .when(mDeps)
                 .newNetworkAgent(any(), any(), any(), any(), anyInt(), any(), any(), any(), any());
@@ -152,7 +157,9 @@
     }
 
     @Test
-    public void testMigratedTransformsAreApplied() throws Exception {
+    public void testMigration() throws Exception {
+        triggerChildOpened();
+
         getChildSessionCallback()
                 .onIpSecTransformsMigrated(makeDummyIpSecTransform(), makeDummyIpSecTransform());
         mTestLooper.dispatchAll();
@@ -170,6 +177,17 @@
         }
 
         assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+
+        final List<ChildSaProposal> saProposals =
+                ((VcnControlPlaneIkeConfig) mConfig.getControlPlaneConfig())
+                        .getChildSessionParams()
+                        .getSaProposals();
+        final int expectedMtu =
+                MtuUtils.getMtu(
+                        saProposals,
+                        mConfig.getMaxMtu(),
+                        TEST_UNDERLYING_NETWORK_RECORD_1.linkProperties.getMtu());
+        verify(mNetworkAgent).sendLinkProperties(argThat(lp -> expectedMtu == lp.getMtu()));
     }
 
     private void triggerChildOpened() {
@@ -299,8 +317,9 @@
                 .removeAddressFromTunnelInterface(
                         eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any());
 
-        // TODO(b/184579891): Also verify link properties updated and sent when sendLinkProperties
-        // is mockable
+        verify(mNetworkAgent).sendLinkProperties(argThat(
+                lp -> newInternalAddrs.equals(lp.getLinkAddresses())
+                        && Collections.singletonList(TEST_DNS_ADDR_2).equals(lp.getDnsServers())));
 
         // Verify that IpSecTunnelInterface only created once
         verify(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
@@ -323,6 +342,66 @@
         assertFalse(mGatewayConnection.isInSafeMode());
     }
 
+    private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
+        triggerChildOpened();
+        mTestLooper.dispatchAll();
+
+        final ArgumentCaptor<Consumer<VcnNetworkAgent>> unwantedCallbackCaptor =
+                ArgumentCaptor.forClass(Consumer.class);
+        verify(mDeps)
+                .newNetworkAgent(
+                        any(),
+                        any(),
+                        any(),
+                        any(),
+                        anyInt(),
+                        any(),
+                        any(),
+                        unwantedCallbackCaptor.capture(),
+                        any());
+
+        return unwantedCallbackCaptor.getValue();
+    }
+
+    @Test
+    public void testUnwantedNetworkAgentTriggersTeardown() throws Exception {
+        final Consumer<VcnNetworkAgent> unwantedCallback = setupNetworkAndGetUnwantedCallback();
+
+        unwantedCallback.accept(mNetworkAgent);
+        mTestLooper.dispatchAll();
+
+        assertTrue(mGatewayConnection.isQuitting());
+        assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testUnwantedNetworkAgentWithDisconnectedNetworkAgent() throws Exception {
+        final Consumer<VcnNetworkAgent> unwantedCallback = setupNetworkAndGetUnwantedCallback();
+
+        mGatewayConnection.setNetworkAgent(null);
+        unwantedCallback.accept(mNetworkAgent);
+        mTestLooper.dispatchAll();
+
+        // Verify that the call was ignored; the state machine is still running, and the state has
+        // not changed.
+        assertFalse(mGatewayConnection.isQuitting());
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+    }
+
+    @Test
+    public void testUnwantedNetworkAgentWithNewNetworkAgent() throws Exception {
+        final Consumer<VcnNetworkAgent> unwantedCallback = setupNetworkAndGetUnwantedCallback();
+        final VcnNetworkAgent testAgent = mock(VcnNetworkAgent.class);
+
+        mGatewayConnection.setNetworkAgent(testAgent);
+        unwantedCallback.accept(mNetworkAgent);
+        mTestLooper.dispatchAll();
+
+        assertFalse(mGatewayConnection.isQuitting());
+        assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+        assertEquals(testAgent, mGatewayConnection.getNetworkAgent());
+    }
+
     @Test
     public void testChildSessionClosedTriggersDisconnect() throws Exception {
         // Verify scheduled but not canceled when entering ConnectedState
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index c5ed8f6..dc73be2 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -18,6 +18,7 @@
 
 import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
 import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
 import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
 
 import static org.junit.Assert.assertEquals;
@@ -44,7 +45,6 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
-import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
 import android.net.ipsec.ike.ChildSessionCallback;
 import android.net.ipsec.ike.IkeSessionCallback;
@@ -90,12 +90,18 @@
     protected static final int TEST_SUB_ID = 5;
     protected static final long ELAPSED_REAL_TIME = 123456789L;
     protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
+
     protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
             new UnderlyingNetworkRecord(
                     new Network(0),
                     new NetworkCapabilities(),
                     new LinkProperties(),
                     false /* blocked */);
+
+    static {
+        TEST_UNDERLYING_NETWORK_RECORD_1.linkProperties.setMtu(1500);
+    }
+
     protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 =
             new UnderlyingNetworkRecord(
                     new Network(1),
@@ -103,6 +109,10 @@
                     new LinkProperties(),
                     false /* blocked */);
 
+    static {
+        TEST_UNDERLYING_NETWORK_RECORD_2.linkProperties.setMtu(1460);
+    }
+
     protected static final TelephonySubscriptionSnapshot TEST_SUBSCRIPTION_SNAPSHOT =
             new TelephonySubscriptionSnapshot(
                     Collections.singletonMap(TEST_SUB_ID, TEST_SUB_GRP), Collections.EMPTY_MAP);
@@ -278,8 +288,8 @@
 
     protected void verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
             @NonNull State expectedState) {
-        // Set a NetworkAgent, and expect it to be unregistered and cleared
-        final NetworkAgent mockNetworkAgent = mock(NetworkAgent.class);
+        // Set a VcnNetworkAgent, and expect it to be unregistered and cleared
+        final VcnNetworkAgent mockNetworkAgent = mock(VcnNetworkAgent.class);
         mGatewayConnection.setNetworkAgent(mockNetworkAgent);
 
         // SafeMode timer starts when VcnGatewayConnection exits DisconnectedState (the initial
diff --git a/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
new file mode 100644
index 0000000..29511f7
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn.util;
+
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256;
+
+import static com.android.net.module.util.NetworkStackConstants.ETHER_MTU;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
+import static com.android.server.vcn.util.MtuUtils.getMtu;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import static java.util.Collections.emptyList;
+
+import android.net.ipsec.ike.ChildSaProposal;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MtuUtilsTest {
+    @Test
+    public void testUnderlyingMtuZero() {
+        assertEquals(
+                IPV6_MIN_MTU, getMtu(emptyList(), ETHER_MTU /* maxMtu */, 0 /* underlyingMtu */));
+    }
+
+    @Test
+    public void testClampsToMaxMtu() {
+        assertEquals(0, getMtu(emptyList(), 0 /* maxMtu */, IPV6_MIN_MTU /* underlyingMtu */));
+    }
+
+    @Test
+    public void testNormalModeAlgorithmLessThanUnderlyingMtu() {
+        final List<ChildSaProposal> saProposals =
+                Arrays.asList(
+                        new ChildSaProposal.Builder()
+                                .addEncryptionAlgorithm(
+                                        ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256)
+                                .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
+                                .build());
+
+        final int actualMtu =
+                getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+        assertTrue(ETHER_MTU > actualMtu);
+    }
+
+    @Test
+    public void testCombinedModeAlgorithmLessThanUnderlyingMtu() {
+        final List<ChildSaProposal> saProposals =
+                Arrays.asList(
+                        new ChildSaProposal.Builder()
+                                .addEncryptionAlgorithm(
+                                        ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256)
+                                .addEncryptionAlgorithm(
+                                        ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256)
+                                .addEncryptionAlgorithm(
+                                        ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256)
+                                .build());
+
+        final int actualMtu =
+                getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+        assertTrue(ETHER_MTU > actualMtu);
+    }
+}
diff --git a/tools/hiddenapi/checksorted_sha.sh b/tools/hiddenapi/checksorted_sha.sh
index ceb705f..451fed6 100755
--- a/tools/hiddenapi/checksorted_sha.sh
+++ b/tools/hiddenapi/checksorted_sha.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 set -e
 LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
-git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do
+git show --name-only --pretty=format: $1 | grep "boot/hiddenapi/hiddenapi-.*txt" | while read file; do
     diff <(git show $1:$file) <(git show $1:$file | $LOCAL_DIR/sort_api.sh )  || {
       echo -e "\e[1m\e[31m$file $1 is not sorted or contains duplicates. To sort it correctly:\e[0m"
       echo -e "\e[33m${LOCAL_DIR}/sort_api.sh $2/frameworks/base/$file\e[0m"
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
index 2924e01..822aba4 100755
--- a/tools/hiddenapi/exclude.sh
+++ b/tools/hiddenapi/exclude.sh
@@ -48,7 +48,7 @@
 PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done)
 RE=$(echo ${PACKAGES} | sed "s/ /|/g")
 EXIT_CODE=0
-for file in $(git show --name-only --pretty=format: $SHA | grep "config/hiddenapi-.*txt"); do
+for file in $(git show --name-only --pretty=format: $SHA | grep "boot/hiddenapi/hiddenapi-.*txt"); do
     ENTRIES=$(grep -E "^\+L(${RE})/" <(git diff ${SHA}~1 ${SHA} $file) | sed "s|^\+||" || echo)
     if [[ -n "${ENTRIES}" ]]; then
       echo -e "\e[1m\e[31m$file $SHA contains the following entries\e[0m"