Merge "Satellite API changes"
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 1022490..bd8d27c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -27,6 +27,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.BackgroundStartPrivileges;
 import android.app.UserSwitchObserver;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
@@ -388,6 +389,12 @@
     private final ArrayList<ContextAssignment> mRecycledPreferredUidOnly = new ArrayList<>();
     private final ArrayList<ContextAssignment> mRecycledStoppable = new ArrayList<>();
     private final AssignmentInfo mRecycledAssignmentInfo = new AssignmentInfo();
+    private final SparseIntArray mRecycledPrivilegedState = new SparseIntArray();
+
+    private static final int PRIVILEGED_STATE_UNDEFINED = 0;
+    private static final int PRIVILEGED_STATE_NONE = 1;
+    private static final int PRIVILEGED_STATE_BAL = 2;
+    private static final int PRIVILEGED_STATE_TOP = 3;
 
     private final Pools.Pool<ContextAssignment> mContextAssignmentPool =
             new Pools.SimplePool<>(MAX_RETAINED_OBJECTS);
@@ -792,7 +799,7 @@
 
         cleanUpAfterAssignmentChangesLocked(
                 mRecycledChanged, mRecycledIdle, mRecycledPreferredUidOnly, mRecycledStoppable,
-                mRecycledAssignmentInfo);
+                mRecycledAssignmentInfo, mRecycledPrivilegedState);
 
         noteConcurrency();
     }
@@ -915,7 +922,8 @@
                 continue;
             }
 
-            final boolean hasImmediacyPrivilege = hasImmediacyPrivilegeLocked(nextPending);
+            final boolean hasImmediacyPrivilege =
+                    hasImmediacyPrivilegeLocked(nextPending, mRecycledPrivilegedState);
             if (DEBUG && isSimilarJobRunningLocked(nextPending)) {
                 Slog.w(TAG, "Already running similar job to: " + nextPending);
             }
@@ -1183,7 +1191,8 @@
             final ArraySet<ContextAssignment> idle,
             final List<ContextAssignment> preferredUidOnly,
             final List<ContextAssignment> stoppable,
-            final AssignmentInfo assignmentInfo) {
+            final AssignmentInfo assignmentInfo,
+            final SparseIntArray privilegedState) {
         for (int s = stoppable.size() - 1; s >= 0; --s) {
             final ContextAssignment assignment = stoppable.get(s);
             assignment.clear();
@@ -1205,20 +1214,58 @@
         stoppable.clear();
         preferredUidOnly.clear();
         assignmentInfo.clear();
+        privilegedState.clear();
         mWorkCountTracker.resetStagingCount();
         mActivePkgStats.forEach(mPackageStatsStagingCountClearer);
     }
 
     @VisibleForTesting
     @GuardedBy("mLock")
-    boolean hasImmediacyPrivilegeLocked(@NonNull JobStatus job) {
+    boolean hasImmediacyPrivilegeLocked(@NonNull JobStatus job,
+            @NonNull SparseIntArray cachedPrivilegedState) {
+        if (!job.shouldTreatAsExpeditedJob() && !job.shouldTreatAsUserInitiatedJob()) {
+            return false;
+        }
         // EJs & user-initiated jobs for the TOP app should run immediately.
         // However, even for user-initiated jobs, if the app has not recently been in TOP or BAL
         // state, we don't give the immediacy privilege so that we can try and maintain
         // reasonably concurrency behavior.
-        return job.lastEvaluatedBias == JobInfo.BIAS_TOP_APP
-                // TODO(): include BAL state for user-initiated jobs
-                && (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob());
+        if (job.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
+            return true;
+        }
+        final int uid = job.getSourceUid();
+        final int privilegedState = cachedPrivilegedState.get(uid, PRIVILEGED_STATE_UNDEFINED);
+        switch (privilegedState) {
+            case PRIVILEGED_STATE_TOP:
+                return true;
+            case PRIVILEGED_STATE_BAL:
+                return job.shouldTreatAsUserInitiatedJob();
+            case PRIVILEGED_STATE_NONE:
+                return false;
+            case PRIVILEGED_STATE_UNDEFINED:
+            default:
+                final ActivityManagerInternal activityManagerInternal =
+                        LocalServices.getService(ActivityManagerInternal.class);
+                final int procState = activityManagerInternal.getUidProcessState(uid);
+                if (procState == ActivityManager.PROCESS_STATE_TOP) {
+                    cachedPrivilegedState.put(uid, PRIVILEGED_STATE_TOP);
+                    return true;
+                }
+                if (job.shouldTreatAsExpeditedJob()) {
+                    // EJs only get the TOP privilege.
+                    return false;
+                }
+
+                final BackgroundStartPrivileges bsp =
+                        activityManagerInternal.getBackgroundStartPrivileges(uid);
+                final boolean balAllowed = bsp.allowsBackgroundActivityStarts();
+                if (DEBUG) {
+                    Slog.d(TAG, "Job " + job.toShortString() + " bal state: " + bsp);
+                }
+                cachedPrivilegedState.put(uid,
+                        balAllowed ? PRIVILEGED_STATE_BAL : PRIVILEGED_STATE_NONE);
+                return balAllowed;
+        }
     }
 
     @GuardedBy("mLock")
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 419111a..22fea7f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -31,6 +31,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
+import android.app.BackgroundStartPrivileges;
 import android.app.IUidObserver;
 import android.app.compat.CompatChanges;
 import android.app.job.IJobScheduler;
@@ -3841,6 +3842,25 @@
                         return callingResult;
                     }
                 }
+
+                final int uid = sourceUid != -1 ? sourceUid : callingUid;
+                final int procState = mActivityManagerInternal.getUidProcessState(uid);
+                if (DEBUG) {
+                    Slog.d(TAG, "Uid " + uid + " proc state="
+                            + ActivityManager.procStateToString(procState));
+                }
+                if (procState != ActivityManager.PROCESS_STATE_TOP) {
+                    final BackgroundStartPrivileges bsp =
+                            mActivityManagerInternal.getBackgroundStartPrivileges(uid);
+                    if (DEBUG) {
+                        Slog.d(TAG, "Uid " + uid + ": " + bsp);
+                    }
+                    if (!bsp.allowsBackgroundActivityStarts()) {
+                        Slog.e(TAG,
+                                "Uid " + uid + " not in a state to schedule user-initiated jobs");
+                        return JobScheduler.RESULT_FAILURE;
+                    }
+                }
             }
             if (jobWorkItem != null) {
                 jobWorkItem.enforceValidity(rejectNegativeNetworkEstimates);
diff --git a/core/api/current.txt b/core/api/current.txt
index d54290b..7ed066e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7782,6 +7782,7 @@
     method public int addOverrideApn(@NonNull android.content.ComponentName, @NonNull android.telephony.data.ApnSetting);
     method public void addPersistentPreferredActivity(@NonNull android.content.ComponentName, android.content.IntentFilter, @NonNull android.content.ComponentName);
     method public void addUserRestriction(@NonNull android.content.ComponentName, String);
+    method public void addUserRestrictionGlobally(@NonNull String);
     method public boolean bindDeviceAdminServiceAsUser(@NonNull android.content.ComponentName, android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
     method public boolean canAdminGrantSensorsPermissions();
     method public boolean canUsbDataSignalingBeDisabled();
@@ -7884,6 +7885,7 @@
     method @Nullable public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(@Nullable android.content.ComponentName, @NonNull android.content.ComponentName);
     method @NonNull public java.util.List<java.lang.String> getUserControlDisabledPackages(@NonNull android.content.ComponentName);
     method @NonNull public android.os.Bundle getUserRestrictions(@NonNull android.content.ComponentName);
+    method @NonNull public android.os.Bundle getUserRestrictionsGlobally();
     method @Nullable public String getWifiMacAddress(@NonNull android.content.ComponentName);
     method @Nullable public android.app.admin.WifiSsidPolicy getWifiSsidPolicy();
     method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 67aa074..6426cf0 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1559,6 +1559,13 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.UnknownAuthority> CREATOR;
   }
 
+  public final class UserRestrictionPolicyKey extends android.app.admin.PolicyKey {
+    method public int describeContents();
+    method @NonNull public String getRestriction();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.UserRestrictionPolicyKey> CREATOR;
+  }
+
 }
 
 package android.app.ambientcontext {
@@ -2276,7 +2283,6 @@
     method public void notifyEvent(@NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent);
     method @Nullable public void query(@NonNull android.app.search.Query, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.search.SearchTarget>>);
     method public void registerEmptyQueryResultUpdateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.app.search.SearchSession.Callback);
-    method public void requestEmptyQueryResultUpdate();
     method public void unregisterEmptyQueryResultUpdateCallback(@NonNull android.app.search.SearchSession.Callback);
   }
 
@@ -12684,7 +12690,6 @@
     method @MainThread public abstract void onDestroy(@NonNull android.app.search.SearchSessionId);
     method @MainThread public abstract void onNotifyEvent(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent);
     method @MainThread public abstract void onQuery(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull java.util.function.Consumer<java.util.List<android.app.search.SearchTarget>>);
-    method @MainThread public void onRequestEmptyQueryResultUpdate(@NonNull android.app.search.SearchSessionId);
     method public void onSearchSessionCreated(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
     method @MainThread public void onStartUpdateEmptyQueryResult();
     method @MainThread public void onStopUpdateEmptyQueryResult();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index bea2bfc..bb3ea7b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1497,10 +1497,13 @@
     method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void removeKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
     method public void removeUniqueIdAssociation(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void setCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@FloatRange(from=0, to=1) float);
     field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL
   }
 
+  public class InputSettings {
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, @FloatRange(from=0, to=1) float);
+  }
+
 }
 
 package android.hardware.lights {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 2ad5c20..aa9212f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -464,6 +464,11 @@
     public abstract boolean isActivityStartsLoggingEnabled();
     /** Returns true if the background activity starts is enabled. */
     public abstract boolean isBackgroundActivityStartsEnabled();
+    /**
+     * Returns The current {@link BackgroundStartPrivileges} of the UID.
+     */
+    @NonNull
+    public abstract BackgroundStartPrivileges getBackgroundStartPrivileges(int uid);
     public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing);
 
     /** @see com.android.server.am.ActivityManagerService#monitor */
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ac65a6b..4016e32 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11269,6 +11269,13 @@
      * See the constants in {@link android.os.UserManager} for the list of restrictions that can
      * be enforced device-wide.
      *
+     * <p>For callers targeting Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or
+     * above, calling this API will result in applying the restriction locally on the calling user,
+     * or locally on the parent profile if called from the
+     * {@link DevicePolicyManager} instance obtained from
+     * {@link #getParentProfileInstance(ComponentName)}. To set a restriction globally, call
+     * {@link #addUserRestrictionGlobally} instead.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param key   The key of the restriction.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
@@ -11277,7 +11284,38 @@
             @UserManager.UserRestrictionKey String key) {
         if (mService != null) {
             try {
-                mService.setUserRestriction(admin, key, true, mParentInstance);
+                mService.setUserRestriction(
+                        admin, mContext.getPackageName(), key, true, mParentInstance);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Called by a profile or device owner to set a user restriction specified by the provided
+     * {@code key} globally on all users. To clear the restriction use
+     * {@link #clearUserRestriction}.
+     *
+     * <p>For a given user, a restriction will be set if it was applied globally or locally by any
+     * admin.
+     *
+     * <p> The calling device admin must be a profile or device owner; if it is not, a security
+     * exception will be thrown.
+     *
+     * <p> See the constants in {@link android.os.UserManager} for the list of restrictions that can
+     * be enforced device-wide.
+     *
+     * @param key The key of the restriction.
+     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws IllegalStateException if caller is not targeting Android
+     * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or above.
+     */
+    public void addUserRestrictionGlobally(@NonNull @UserManager.UserRestrictionKey String key) {
+        throwIfParentInstance("addUserRestrictionGlobally");
+        if (mService != null) {
+            try {
+                mService.setUserRestrictionGlobally(mContext.getPackageName(), key);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -11296,6 +11334,10 @@
      * <p>
      * See the constants in {@link android.os.UserManager} for the list of restrictions.
      *
+     * <p>For callers targeting Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or
+     * above, calling this API will result in clearing any local and global restriction with the
+     * specified key that was previously set by the caller.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param key   The key of the restriction.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
@@ -11304,7 +11346,8 @@
             @UserManager.UserRestrictionKey String key) {
         if (mService != null) {
             try {
-                mService.setUserRestriction(admin, key, false, mParentInstance);
+                mService.setUserRestriction(
+                        admin, mContext.getPackageName(), key, false, mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -11324,6 +11367,18 @@
      * {@link #getParentProfileInstance(ComponentName)}, for retrieving device-wide restrictions
      * it previously set with {@link #addUserRestriction(ComponentName, String)}.
      *
+     * <p>For callers targeting Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or
+     * above, this API will return the local restrictions set on the calling user, or on the parent
+     * profile if called from the {@link DevicePolicyManager} instance obtained from
+     * {@link #getParentProfileInstance(ComponentName)}. To get global restrictions set by admin,
+     * call {@link #getUserRestrictionsGlobally()} instead.
+     *
+     * <p>Note that this is different that the returned restrictions for callers targeting pre
+     * Android {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, were this API returns
+     * all local/global restrictions set by the admin on the calling user using
+     * {@link #addUserRestriction(ComponentName, String)} or the parent user if called on the
+     * {@link DevicePolicyManager} instance it obtained from {@link #getParentProfileInstance}.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @return a {@link Bundle} whose keys are the user restrictions, and the values a
      * {@code boolean} indicating whether the restriction is set.
@@ -11333,7 +11388,33 @@
         Bundle ret = null;
         if (mService != null) {
             try {
-                ret = mService.getUserRestrictions(admin, mParentInstance);
+                ret = mService.getUserRestrictions(
+                        admin, mContext.getPackageName(), mParentInstance);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return ret == null ? new Bundle() : ret;
+    }
+
+    /**
+     * Called by a profile or device owner to get global user restrictions set with
+     * {@link #addUserRestrictionGlobally(String)}.
+     * <p>
+     * To get all the user restrictions currently set for a certain user, use
+     * {@link UserManager#getUserRestrictions()}.
+     * @return a {@link Bundle} whose keys are the user restrictions, and the values a
+     * {@code boolean} indicating whether the restriction is set.
+     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws IllegalStateException if caller is not targeting Android
+     * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or above.
+     */
+    public @NonNull Bundle getUserRestrictionsGlobally() {
+        throwIfParentInstance("createAdminSupportIntent");
+        Bundle ret = null;
+        if (mService != null) {
+            try {
+                ret = mService.getUserRestrictionsGlobally(mContext.getPackageName());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 3f66b45..5b9e948 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -247,8 +247,11 @@
     void setRestrictionsProvider(in ComponentName who, in ComponentName provider);
     ComponentName getRestrictionsProvider(int userHandle);
 
-    void setUserRestriction(in ComponentName who, in String key, boolean enable, boolean parent);
-    Bundle getUserRestrictions(in ComponentName who, boolean parent);
+    void setUserRestriction(in ComponentName who, in String callerPackage, in String key, boolean enable, boolean parent);
+    void setUserRestrictionGlobally(in String callerPackage, in String key);
+    Bundle getUserRestrictions(in ComponentName who, in String callerPackage, boolean parent);
+    Bundle getUserRestrictionsGlobally(in String callerPackage);
+
     void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
     void clearCrossProfileIntentFilters(in ComponentName admin);
 
diff --git a/core/java/android/app/admin/IntentFilterPolicyKey.java b/core/java/android/app/admin/IntentFilterPolicyKey.java
index f2f8aa4..a49152d 100644
--- a/core/java/android/app/admin/IntentFilterPolicyKey.java
+++ b/core/java/android/app/admin/IntentFilterPolicyKey.java
@@ -18,6 +18,7 @@
 
 import static android.app.admin.PolicyUpdatesReceiver.EXTRA_INTENT_FILTER;
 import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_BUNDLE_KEY;
+import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_KEY;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -100,7 +101,7 @@
      */
     @Override
     public void writeToBundle(Bundle bundle) {
-        super.writeToBundle(bundle);
+        bundle.putString(EXTRA_POLICY_KEY, getIdentifier());
         Bundle extraPolicyParams = new Bundle();
         extraPolicyParams.putParcelable(EXTRA_INTENT_FILTER, mFilter);
         bundle.putBundle(EXTRA_POLICY_BUNDLE_KEY, extraPolicyParams);
diff --git a/core/java/android/app/admin/NoArgsPolicyKey.java b/core/java/android/app/admin/NoArgsPolicyKey.java
index 57b67d5..2c9d2be 100644
--- a/core/java/android/app/admin/NoArgsPolicyKey.java
+++ b/core/java/android/app/admin/NoArgsPolicyKey.java
@@ -16,8 +16,11 @@
 
 package android.app.admin;
 
+import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_KEY;
+
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -41,6 +44,14 @@
         this(source.readString());
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public void writeToBundle(Bundle bundle) {
+        bundle.putString(EXTRA_POLICY_KEY, getIdentifier());
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/app/admin/PackagePermissionPolicyKey.java b/core/java/android/app/admin/PackagePermissionPolicyKey.java
index 4aa2e38..90ca052 100644
--- a/core/java/android/app/admin/PackagePermissionPolicyKey.java
+++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java
@@ -19,6 +19,7 @@
 import static android.app.admin.PolicyUpdatesReceiver.EXTRA_PACKAGE_NAME;
 import static android.app.admin.PolicyUpdatesReceiver.EXTRA_PERMISSION_NAME;
 import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_BUNDLE_KEY;
+import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_KEY;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -119,7 +120,7 @@
      */
     @Override
     public void writeToBundle(Bundle bundle) {
-        super.writeToBundle(bundle);
+        bundle.putString(EXTRA_POLICY_KEY, getIdentifier());
         Bundle extraPolicyParams = new Bundle();
         extraPolicyParams.putString(EXTRA_PACKAGE_NAME, mPackageName);
         extraPolicyParams.putString(EXTRA_PERMISSION_NAME, mPermissionName);
diff --git a/core/java/android/app/admin/PackagePolicyKey.java b/core/java/android/app/admin/PackagePolicyKey.java
index 3469970..08b6110 100644
--- a/core/java/android/app/admin/PackagePolicyKey.java
+++ b/core/java/android/app/admin/PackagePolicyKey.java
@@ -18,6 +18,7 @@
 
 import static android.app.admin.PolicyUpdatesReceiver.EXTRA_PACKAGE_NAME;
 import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_BUNDLE_KEY;
+import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_KEY;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -101,7 +102,7 @@
      */
     @Override
     public void writeToBundle(Bundle bundle) {
-        super.writeToBundle(bundle);
+        bundle.putString(EXTRA_POLICY_KEY, getIdentifier());
         Bundle extraPolicyParams = new Bundle();
         extraPolicyParams.putString(EXTRA_PACKAGE_NAME, mPackageName);
         bundle.putBundle(EXTRA_POLICY_BUNDLE_KEY, extraPolicyParams);
diff --git a/core/java/android/app/admin/PolicyKey.java b/core/java/android/app/admin/PolicyKey.java
index 84cc66b..3544c19 100644
--- a/core/java/android/app/admin/PolicyKey.java
+++ b/core/java/android/app/admin/PolicyKey.java
@@ -16,8 +16,6 @@
 
 package android.app.admin;
 
-import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_KEY;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -103,9 +101,7 @@
     /**
      * @hide
      */
-    public void writeToBundle(Bundle bundle) {
-        bundle.putString(EXTRA_POLICY_KEY, mIdentifier);
-    }
+    public abstract void writeToBundle(Bundle bundle);
 
     @Override
     public boolean equals(@Nullable Object o) {
diff --git a/core/java/android/app/admin/UserRestrictionPolicyKey.java b/core/java/android/app/admin/UserRestrictionPolicyKey.java
new file mode 100644
index 0000000..92014763
--- /dev/null
+++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import static android.app.admin.PolicyUpdatesReceiver.EXTRA_POLICY_KEY;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * Class used to identify a policy that relates to a certain user restriction
+ * (See {@link DevicePolicyManager#addUserRestriction} and
+ * {@link DevicePolicyManager#addUserRestrictionGlobally}).
+ *
+ * @hide
+ */
+@SystemApi
+public final class UserRestrictionPolicyKey extends PolicyKey {
+
+    private final String mRestriction;
+
+    /**
+     * @hide
+     */
+    public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) {
+        super(identifier);
+        mRestriction = Objects.requireNonNull(restriction);
+    }
+
+    private UserRestrictionPolicyKey(Parcel source) {
+        this(source.readString(), source.readString());
+    }
+
+    /**
+     * Returns the user restriction associated with this policy.
+     */
+    @NonNull
+    public String getRestriction() {
+        return mRestriction;
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void writeToBundle(Bundle bundle) {
+        bundle.putString(EXTRA_POLICY_KEY, getIdentifier());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(getIdentifier());
+        dest.writeString(mRestriction);
+    }
+
+    @NonNull
+    public static final Creator<UserRestrictionPolicyKey> CREATOR =
+            new Creator<UserRestrictionPolicyKey>() {
+                @Override
+                public UserRestrictionPolicyKey createFromParcel(Parcel source) {
+                    return new UserRestrictionPolicyKey(source);
+                }
+
+                @Override
+                public UserRestrictionPolicyKey[] newArray(int size) {
+                    return new UserRestrictionPolicyKey[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return "UserRestrictionPolicyKey " + getIdentifier();
+    }
+}
diff --git a/core/java/android/app/search/ISearchUiManager.aidl b/core/java/android/app/search/ISearchUiManager.aidl
index fefbd5a..4cf0b81 100644
--- a/core/java/android/app/search/ISearchUiManager.aidl
+++ b/core/java/android/app/search/ISearchUiManager.aidl
@@ -38,8 +38,6 @@
 
     void registerEmptyQueryResultUpdateCallback(in SearchSessionId sessionId, in ISearchCallback callback);
 
-    void requestEmptyQueryResultUpdate(in SearchSessionId sessionId);
-
     void unregisterEmptyQueryResultUpdateCallback(in SearchSessionId sessionId, in ISearchCallback callback);
 
     void destroySearchSession(in SearchSessionId sessionId);
diff --git a/core/java/android/app/search/SearchSession.java b/core/java/android/app/search/SearchSession.java
index 9e0a1d0..0dbd81e 100644
--- a/core/java/android/app/search/SearchSession.java
+++ b/core/java/android/app/search/SearchSession.java
@@ -225,32 +225,6 @@
     }
 
     /**
-     * Requests the search ui service to dispatch a new set of search targets to the pre-registered
-     * callback for zero state. Zero state means when user entered search ui but not issued any
-     * query yet. This method can be used for client to sync up with server data if they think data
-     * might be out of sync, for example, after restart.
-     * Pre-register a callback with
-     * {@link SearchSession#registerEmptyQueryResultUpdateCallback(Executor, Callback)}
-     * is required before calling this method. Otherwise this is no-op.
-     *
-     * @see {@link SearchSession#registerEmptyQueryResultUpdateCallback(Executor, Callback)}
-     * @see {@link SearchSession.Callback#onTargetsAvailable(List)}.
-     */
-    public void requestEmptyQueryResultUpdate() {
-        if (mIsClosed.get()) {
-            throw new IllegalStateException("This client has already been destroyed.");
-        }
-
-        try {
-            mInterface.requestEmptyQueryResultUpdate(mSessionId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to request empty query result update", e);
-            e.rethrowAsRuntimeException();
-        }
-    }
-
-
-    /**
      * Destroys the client and unregisters the callback. Any method on this class after this call
      * will throw {@link IllegalStateException}.
      *
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 709fa60..41c406d 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -614,8 +614,15 @@
             boolean status = true;
             synchronized (mInterfaceLock) {
                 try {
-                    mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
-                    mInitialized = true;
+                    if (mSessionProcessor != null) {
+                        mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
+                        mInitialized = true;
+                    } else {
+                        Log.v(TAG, "Failed to start capture session, session released before " +
+                                "extension start!");
+                        status = false;
+                        mCaptureSession.close();
+                    }
                 } catch (RemoteException e) {
                     Log.e(TAG, "Failed to start capture session,"
                             + " extension service does not respond!");
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 657541c..2ea9ea0 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -51,11 +51,9 @@
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.os.VibratorManager;
-import android.provider.Settings;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Display;
@@ -215,30 +213,6 @@
             "android.hardware.input.metadata.KEYBOARD_LAYOUTS";
 
     /**
-     * Pointer Speed: The minimum (slowest) pointer speed (-7).
-     * @hide
-     */
-    public static final int MIN_POINTER_SPEED = -7;
-
-    /**
-     * Pointer Speed: The maximum (fastest) pointer speed (7).
-     * @hide
-     */
-    public static final int MAX_POINTER_SPEED = 7;
-
-    /**
-     * Pointer Speed: The default pointer speed (0).
-     * @hide
-     */
-    public static final int DEFAULT_POINTER_SPEED = 0;
-
-    /**
-     * The maximum allowed obscuring opacity by UID to propagate touches (0 <= x <= 1).
-     * @hide
-     */
-    public static final float DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = .8f;
-
-    /**
      * Prevent touches from being consumed by apps if these touches passed through a non-trusted
      * window from a different UID and are considered unsafe.
      *
@@ -1135,58 +1109,18 @@
     }
 
     /**
-     * Gets the mouse pointer speed.
-     * <p>
-     * Only returns the permanent mouse pointer speed.  Ignores any temporary pointer
-     * speed set by {@link #tryPointerSpeed}.
-     * </p>
-     *
-     * @param context The application context.
-     * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
-     * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
-     *
-     * @hide
-     */
-    public int getPointerSpeed(Context context) {
-        return Settings.System.getInt(context.getContentResolver(),
-                Settings.System.POINTER_SPEED, DEFAULT_POINTER_SPEED);
-    }
-
-    /**
-     * Sets the mouse pointer speed.
-     * <p>
-     * Requires {@link android.Manifest.permission#WRITE_SETTINGS}.
-     * </p>
-     *
-     * @param context The application context.
-     * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
-     * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
-     *
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
-    public void setPointerSpeed(Context context, int speed) {
-        if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
-            throw new IllegalArgumentException("speed out of range");
-        }
-
-        Settings.System.putInt(context.getContentResolver(),
-                Settings.System.POINTER_SPEED, speed);
-    }
-
-    /**
      * Changes the mouse pointer speed temporarily, but does not save the setting.
      * <p>
      * Requires {@link android.Manifest.permission#SET_POINTER_SPEED}.
      * </p>
      *
-     * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
-     * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
+     * @param speed The pointer speed as a value between {@link InputSettings#MIN_POINTER_SPEED} and
+     * {@link InputSettings#MAX_POINTER_SPEED}, or the default value {@link InputSettings#DEFAULT_POINTER_SPEED}.
      *
      * @hide
      */
     public void tryPointerSpeed(int speed) {
-        if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
+        if (speed < InputSettings.MIN_POINTER_SPEED || speed > InputSettings.MAX_POINTER_SPEED) {
             throw new IllegalArgumentException("speed out of range");
         }
 
@@ -1211,44 +1145,8 @@
      */
     @FloatRange(from = 0, to = 1)
     public float getMaximumObscuringOpacityForTouch() {
-        return Settings.Global.getFloat(getContext().getContentResolver(),
-                Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
-                DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
-    }
-
-    /**
-     * Sets the maximum allowed obscuring opacity by UID to propagate touches.
-     *
-     * <p>For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams
-     * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
-     * above the touch-consuming window.
-     *
-     * <p>For a certain UID:
-     * <ul>
-     *     <li>If it's the same as the UID of the touch-consuming window, allow it to propagate
-     *     the touch.
-     *     <li>Otherwise take all its windows of eligible window types above the touch-consuming
-     *     window, compute their combined obscuring opacity considering that {@code
-     *     opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is
-     *     lesser than or equal to this setting and there are no other windows preventing the
-     *     touch, allow the UID to propagate the touch.
-     * </ul>
-     *
-     * <p>This value should be between 0 (inclusive) and 1 (inclusive).
-     *
-     * @see #getMaximumObscuringOpacityForTouch()
-     *
-     * @hide
-     */
-    @TestApi
-    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
-    public void setMaximumObscuringOpacityForTouch(@FloatRange(from = 0, to = 1) float opacity) {
-        if (opacity < 0 || opacity > 1) {
-            throw new IllegalArgumentException(
-                    "Maximum obscuring opacity for touch should be >= 0 and <= 1");
-        }
-        Settings.Global.putFloat(getContext().getContentResolver(),
-                Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity);
+        Context context = ActivityThread.currentApplication();
+        return InputSettings.getMaximumObscuringOpacityForTouch(context);
     }
 
     /**
@@ -2145,26 +2043,6 @@
     }
 
     /**
-     * Whether stylus has ever been used on device (false by default).
-     * @hide
-     */
-    public boolean isStylusEverUsed(@NonNull Context context) {
-        return Settings.Global.getInt(context.getContentResolver(),
-                        Settings.Global.STYLUS_EVER_USED, 0) == 1;
-    }
-
-    /**
-     * Set whether stylus has ever been used on device.
-     * Should only ever be set to true once after stylus first usage.
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
-    public void setStylusEverUsed(@NonNull Context context, boolean stylusEverUsed) {
-        Settings.Global.putInt(context.getContentResolver(),
-                Settings.Global.STYLUS_EVER_USED, stylusEverUsed ? 1 : 0);
-    }
-
-    /**
      * Whether there is a gesture-compatible touchpad connected to the device.
      * @hide
      */
@@ -2174,200 +2052,6 @@
     }
 
     /**
-     * Gets the touchpad pointer speed.
-     *
-     * The returned value only applies to gesture-compatible touchpads.
-     *
-     * @param context The application context.
-     * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
-     * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
-     *
-     * @hide
-     */
-    public int getTouchpadPointerSpeed(@NonNull Context context) {
-        return Settings.System.getIntForUser(context.getContentResolver(),
-                Settings.System.TOUCHPAD_POINTER_SPEED, DEFAULT_POINTER_SPEED,
-                UserHandle.USER_CURRENT);
-    }
-
-    /**
-     * Sets the touchpad pointer speed, and saves it in the settings.
-     *
-     * The new speed will only apply to gesture-compatible touchpads.
-     *
-     * @param context The application context.
-     * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
-     * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
-     *
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
-    public void setTouchpadPointerSpeed(@NonNull Context context, int speed) {
-        if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
-            throw new IllegalArgumentException("speed out of range");
-        }
-
-        Settings.System.putIntForUser(context.getContentResolver(),
-                Settings.System.TOUCHPAD_POINTER_SPEED, speed, UserHandle.USER_CURRENT);
-    }
-
-    /**
-     * Returns true if the touchpad should use pointer acceleration.
-     *
-     * The returned value only applies to gesture-compatible touchpads.
-     *
-     * @param context The application context.
-     * @return Whether the touchpad should use pointer acceleration.
-     *
-     * @hide
-     */
-    public boolean useTouchpadPointerAcceleration(@NonNull Context context) {
-        // TODO: obtain the actual behavior from the settings
-        return true;
-    }
-
-    /**
-     * Sets the pointer acceleration behavior for the touchpad.
-     *
-     * The new behavior is only applied to gesture-compatible touchpads.
-     *
-     * @param context The application context.
-     * @param enabled Will enable pointer acceleration if true, disable it if false
-     *
-     * @hide
-     */
-    public void setTouchpadPointerAcceleration(@NonNull Context context, boolean enabled) {
-        // TODO: set the right setting
-    }
-
-    /**
-     * Returns true if moving two fingers upwards on the touchpad should
-     * scroll down, which is known as natural scrolling.
-     *
-     * The returned value only applies to gesture-compatible touchpads.
-     *
-     * @param context The application context.
-     * @return Whether the touchpad should use natural scrolling.
-     *
-     * @hide
-     */
-    public boolean useTouchpadNaturalScrolling(@NonNull Context context) {
-        return Settings.System.getIntForUser(context.getContentResolver(),
-                Settings.System.TOUCHPAD_NATURAL_SCROLLING, 0, UserHandle.USER_CURRENT) == 1;
-    }
-
-    /**
-     * Sets the natural scroll behavior for the touchpad.
-     *
-     * If natural scrolling is enabled, moving two fingers upwards on the
-     * touchpad will scroll down.
-     *
-     * @param context The application context.
-     * @param enabled Will enable natural scroll if true, disable it if false
-     *
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
-    public void setTouchpadNaturalScrolling(@NonNull Context context, boolean enabled) {
-        Settings.System.putIntForUser(context.getContentResolver(),
-                Settings.System.TOUCHPAD_NATURAL_SCROLLING, enabled ? 1 : 0,
-                UserHandle.USER_CURRENT);
-    }
-
-    /**
-     * Returns true if the touchpad should use tap to click.
-     *
-     * The returned value only applies to gesture-compatible touchpads.
-     *
-     * @param context The application context.
-     * @return Whether the touchpad should use tap to click.
-     *
-     * @hide
-     */
-    public boolean useTouchpadTapToClick(@NonNull Context context) {
-        return Settings.System.getIntForUser(context.getContentResolver(),
-                Settings.System.TOUCHPAD_TAP_TO_CLICK, 0, UserHandle.USER_CURRENT) == 1;
-    }
-
-    /**
-     * Sets the tap to click behavior for the touchpad.
-     *
-     * The new behavior is only applied to gesture-compatible touchpads.
-     *
-     * @param context The application context.
-     * @param enabled Will enable tap to click if true, disable it if false
-     *
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
-    public void setTouchpadTapToClick(@NonNull Context context, boolean enabled) {
-        Settings.System.putIntForUser(context.getContentResolver(),
-                Settings.System.TOUCHPAD_TAP_TO_CLICK, enabled ? 1 : 0,
-                UserHandle.USER_CURRENT);
-    }
-
-    /**
-     * Returns true if the touchpad should use tap dragging.
-     *
-     * The returned value only applies to gesture-compatible touchpads.
-     *
-     * @param context The application context.
-     * @return Whether the touchpad should use tap dragging.
-     *
-     * @hide
-     */
-    public boolean useTouchpadTapDragging(@NonNull Context context) {
-        // TODO: obtain the actual behavior from the settings
-        return true;
-    }
-
-    /**
-     * Sets the tap dragging behavior for the touchpad.
-     *
-     * The new behavior is only applied to gesture-compatible touchpads.
-     *
-     * @param context The application context.
-     * @param enabled Will enable tap dragging if true, disable it if false
-     *
-     * @hide
-     */
-    public void setTouchpadTapDragging(@NonNull Context context, boolean enabled) {
-        // TODO: set the right setting
-    }
-
-    /**
-     * Returns true if the touchpad should use the right click zone.
-     *
-     * The returned value only applies to gesture-compatible touchpads.
-     *
-     * @param context The application context.
-     * @return Whether the touchpad should use the right click zone.
-     *
-     * @hide
-     */
-    public boolean useTouchpadRightClickZone(@NonNull Context context) {
-        return Settings.System.getIntForUser(context.getContentResolver(),
-                Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, 0, UserHandle.USER_CURRENT) == 1;
-    }
-
-    /**
-     * Sets the right click zone behavior for the touchpad.
-     *
-     * The new behavior is only applied to gesture-compatible touchpads.
-     *
-     * @param context The application context.
-     * @param enabled Will enable the right click zone if true, disable it if false
-     *
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
-    public void setTouchpadRightClickZone(@NonNull Context context, boolean enabled) {
-        Settings.System.putIntForUser(context.getContentResolver(),
-                Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, enabled ? 1 : 0,
-                UserHandle.USER_CURRENT);
-    }
-
-    /**
      * Registers a Keyboard backlight change listener to be notified about {@link
      * KeyboardBacklightState} changes for connected keyboard devices.
      *
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
new file mode 100644
index 0000000..cdf9ea5
--- /dev/null
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.Manifest;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+/**
+ * InputSettings encapsulates reading and writing settings related to input
+ *
+ * @hide
+ */
+@TestApi
+public class InputSettings {
+    /**
+     * Pointer Speed: The minimum (slowest) pointer speed (-7).
+     * @hide
+     */
+    public static final int MIN_POINTER_SPEED = -7;
+
+    /**
+     * Pointer Speed: The maximum (fastest) pointer speed (7).
+     * @hide
+     */
+    public static final int MAX_POINTER_SPEED = 7;
+
+    /**
+     * Pointer Speed: The default pointer speed (0).
+     * @hide
+     */
+    public static final int DEFAULT_POINTER_SPEED = 0;
+
+    /**
+     * The maximum allowed obscuring opacity by UID to propagate touches (0 <= x <= 1).
+     * @hide
+     */
+    public static final float DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = .8f;
+
+
+    private InputSettings() {
+    }
+
+    /**
+     * Gets the mouse pointer speed.
+     * <p>
+     * Only returns the permanent mouse pointer speed.  Ignores any temporary pointer
+     * speed set by {@link InputManager#tryPointerSpeed}.
+     * </p>
+     *
+     * @param context The application context.
+     * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
+     * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
+     *
+     * @hide
+     */
+    @SuppressLint("NonUserGetterCalled")
+    public static int getPointerSpeed(Context context) {
+        return Settings.System.getInt(context.getContentResolver(),
+                Settings.System.POINTER_SPEED, DEFAULT_POINTER_SPEED);
+    }
+
+    /**
+     * Sets the mouse pointer speed.
+     * <p>
+     * Requires {@link android.Manifest.permission#WRITE_SETTINGS}.
+     * </p>
+     *
+     * @param context The application context.
+     * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
+     * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+    public static void setPointerSpeed(Context context, int speed) {
+        if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
+            throw new IllegalArgumentException("speed out of range");
+        }
+
+        Settings.System.putInt(context.getContentResolver(),
+                Settings.System.POINTER_SPEED, speed);
+    }
+
+    /**
+     * Returns the maximum allowed obscuring opacity per UID to propagate touches.
+     *
+     * <p>For certain window types (e.g. {@link LayoutParams#TYPE_APPLICATION_OVERLAY}),
+     * the decision of honoring {@link LayoutParams#FLAG_NOT_TOUCHABLE} or not depends on
+     * the combined obscuring opacity of the windows above the touch-consuming window, per
+     * UID. Check documentation of {@link LayoutParams#FLAG_NOT_TOUCHABLE} for more details.
+     *
+     * <p>The value returned is between 0 (inclusive) and 1 (inclusive).
+     *
+     * @see LayoutParams#FLAG_NOT_TOUCHABLE
+     *
+     * @hide
+     */
+    @FloatRange(from = 0, to = 1)
+    public static float getMaximumObscuringOpacityForTouch(Context context) {
+        return Settings.Global.getFloat(context.getContentResolver(),
+                Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
+                DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
+    }
+
+    /**
+     * Sets the maximum allowed obscuring opacity by UID to propagate touches.
+     *
+     * <p>For certain window types (e.g. SAWs), the decision of honoring {@link LayoutParams
+     * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
+     * above the touch-consuming window.
+     *
+     * <p>For a certain UID:
+     * <ul>
+     *     <li>If it's the same as the UID of the touch-consuming window, allow it to propagate
+     *     the touch.
+     *     <li>Otherwise take all its windows of eligible window types above the touch-consuming
+     *     window, compute their combined obscuring opacity considering that {@code
+     *     opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is
+     *     less than or equal to this setting and there are no other windows preventing the
+     *     touch, allow the UID to propagate the touch.
+     * </ul>
+     *
+     * <p>This value should be between 0 (inclusive) and 1 (inclusive).
+     *
+     * @see #getMaximumObscuringOpacityForTouch(Context)
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    public static void setMaximumObscuringOpacityForTouch(
+            @NonNull Context context,
+            @FloatRange(from = 0, to = 1) float opacity) {
+        if (opacity < 0 || opacity > 1) {
+            throw new IllegalArgumentException(
+                    "Maximum obscuring opacity for touch should be >= 0 and <= 1");
+        }
+        Settings.Global.putFloat(context.getContentResolver(),
+                Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity);
+    }
+
+    /**
+     * Whether stylus has ever been used on device (false by default).
+     * @hide
+     */
+    public static boolean isStylusEverUsed(@NonNull Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                        Settings.Global.STYLUS_EVER_USED, 0) == 1;
+    }
+
+    /**
+     * Set whether stylus has ever been used on device.
+     * Should only ever be set to true once after stylus first usage.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    public static void setStylusEverUsed(@NonNull Context context, boolean stylusEverUsed) {
+        Settings.Global.putInt(context.getContentResolver(),
+                Settings.Global.STYLUS_EVER_USED, stylusEverUsed ? 1 : 0);
+    }
+
+
+    /**
+     * Gets the touchpad pointer speed.
+     *
+     * The returned value only applies to gesture-compatible touchpads.
+     *
+     * @param context The application context.
+     * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
+     * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
+     *
+     * @hide
+     */
+    public static int getTouchpadPointerSpeed(@NonNull Context context) {
+        return Settings.System.getIntForUser(context.getContentResolver(),
+                Settings.System.TOUCHPAD_POINTER_SPEED, DEFAULT_POINTER_SPEED,
+                UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Sets the touchpad pointer speed, and saves it in the settings.
+     *
+     * The new speed will only apply to gesture-compatible touchpads.
+     *
+     * @param context The application context.
+     * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and
+     * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+    public static void setTouchpadPointerSpeed(@NonNull Context context, int speed) {
+        if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) {
+            throw new IllegalArgumentException("speed out of range");
+        }
+
+        Settings.System.putIntForUser(context.getContentResolver(),
+                Settings.System.TOUCHPAD_POINTER_SPEED, speed, UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Returns true if moving two fingers upwards on the touchpad should
+     * scroll down, which is known as natural scrolling.
+     *
+     * The returned value only applies to gesture-compatible touchpads.
+     *
+     * @param context The application context.
+     * @return Whether the touchpad should use natural scrolling.
+     *
+     * @hide
+     */
+    public static boolean useTouchpadNaturalScrolling(@NonNull Context context) {
+        return Settings.System.getIntForUser(context.getContentResolver(),
+                Settings.System.TOUCHPAD_NATURAL_SCROLLING, 0, UserHandle.USER_CURRENT) == 1;
+    }
+
+    /**
+     * Sets the natural scroll behavior for the touchpad.
+     *
+     * If natural scrolling is enabled, moving two fingers upwards on the
+     * touchpad will scroll down.
+     *
+     * @param context The application context.
+     * @param enabled Will enable natural scroll if true, disable it if false
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+    public static void setTouchpadNaturalScrolling(@NonNull Context context, boolean enabled) {
+        Settings.System.putIntForUser(context.getContentResolver(),
+                Settings.System.TOUCHPAD_NATURAL_SCROLLING, enabled ? 1 : 0,
+                UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Returns true if the touchpad should use tap to click.
+     *
+     * The returned value only applies to gesture-compatible touchpads.
+     *
+     * @param context The application context.
+     * @return Whether the touchpad should use tap to click.
+     *
+     * @hide
+     */
+    public static boolean useTouchpadTapToClick(@NonNull Context context) {
+        return Settings.System.getIntForUser(context.getContentResolver(),
+                Settings.System.TOUCHPAD_TAP_TO_CLICK, 0, UserHandle.USER_CURRENT) == 1;
+    }
+
+    /**
+     * Sets the tap to click behavior for the touchpad.
+     *
+     * The new behavior is only applied to gesture-compatible touchpads.
+     *
+     * @param context The application context.
+     * @param enabled Will enable tap to click if true, disable it if false
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+    public static void setTouchpadTapToClick(@NonNull Context context, boolean enabled) {
+        Settings.System.putIntForUser(context.getContentResolver(),
+                Settings.System.TOUCHPAD_TAP_TO_CLICK, enabled ? 1 : 0,
+                UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Returns true if the touchpad should use the right click zone.
+     *
+     * The returned value only applies to gesture-compatible touchpads.
+     *
+     * @param context The application context.
+     * @return Whether the touchpad should use the right click zone.
+     *
+     * @hide
+     */
+    public static boolean useTouchpadRightClickZone(@NonNull Context context) {
+        return Settings.System.getIntForUser(context.getContentResolver(),
+                Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, 0, UserHandle.USER_CURRENT) == 1;
+    }
+
+    /**
+     * Sets the right click zone behavior for the touchpad.
+     *
+     * The new behavior is only applied to gesture-compatible touchpads.
+     *
+     * @param context The application context.
+     * @param enabled Will enable the right click zone if true, disable it if false
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+    public static void setTouchpadRightClickZone(@NonNull Context context, boolean enabled) {
+        Settings.System.putIntForUser(context.getContentResolver(),
+                Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, enabled ? 1 : 0,
+                UserHandle.USER_CURRENT);
+    }
+}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index c021cad..03c32d70 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -2982,6 +2982,12 @@
     /**
      * Signals that wake-on-lan/wake-on-wlan is allowed in Low Power Standby.
      *
+     * <p>If Low Power Standby is enabled ({@link #isLowPowerStandbyEnabled()}),
+     * wake-on-lan/wake-on-wlan may not be available while in standby.
+     * Use {@link #isAllowedInLowPowerStandby(String)} to determine whether the device allows this
+     * feature to be used during Low Power Standby with the currently active Low Power Standby
+     * policy.
+     *
      * @see #isAllowedInLowPowerStandby(String)
      */
     public static final String LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN =
diff --git a/core/java/android/service/search/ISearchUiService.aidl b/core/java/android/service/search/ISearchUiService.aidl
index bc6d421..f59347f 100644
--- a/core/java/android/service/search/ISearchUiService.aidl
+++ b/core/java/android/service/search/ISearchUiService.aidl
@@ -39,8 +39,6 @@
 
     void onRegisterEmptyQueryResultUpdateCallback (in SearchSessionId sessionId, in ISearchCallback callback);
 
-    void onRequestEmptyQueryResultUpdate(in SearchSessionId sessionId);
-
     void onUnregisterEmptyQueryResultUpdateCallback(in SearchSessionId sessionId, in ISearchCallback callback);
 
     void onDestroy(in SearchSessionId sessionId);
diff --git a/core/java/android/service/search/SearchUiService.java b/core/java/android/service/search/SearchUiService.java
index 55a96fa..8d05b80 100644
--- a/core/java/android/service/search/SearchUiService.java
+++ b/core/java/android/service/search/SearchUiService.java
@@ -112,12 +112,6 @@
         }
 
         @Override
-        public void onRequestEmptyQueryResultUpdate(SearchSessionId sessionId) {
-            mHandler.sendMessage(obtainMessage(SearchUiService::doRequestEmptyQueryResultUpdate,
-                    SearchUiService.this, sessionId));
-        }
-
-        @Override
         public void onUnregisterEmptyQueryResultUpdateCallback(SearchSessionId sessionId,
                 ISearchCallback callback) {
             mHandler.sendMessage(
@@ -220,24 +214,6 @@
     @MainThread
     public void onStartUpdateEmptyQueryResult() {}
 
-    private void doRequestEmptyQueryResultUpdate(@NonNull SearchSessionId sessionId) {
-        // Just an optimization, if there are no callbacks, then don't bother notifying the service
-        final ArrayList<CallbackWrapper> callbacks = mSessionEmptyQueryResultCallbacks.get(
-                sessionId);
-        if (callbacks != null && !callbacks.isEmpty()) {
-            onRequestEmptyQueryResultUpdate(sessionId);
-        }
-    }
-
-    /**
-     * Called by a client to request empty query search target result for zero state. This method
-     * is only called if there are one or more empty query result update callbacks registered.
-     *
-     * @see #updateEmptyQueryResult(SearchSessionId, List)
-     */
-    @MainThread
-    public void onRequestEmptyQueryResultUpdate(@NonNull SearchSessionId sessionId) {}
-
     private void doUnregisterEmptyQueryResultUpdateCallback(@NonNull SearchSessionId sessionId,
             @NonNull ISearchCallback callback) {
         final ArrayList<CallbackWrapper> callbacks = mSessionEmptyQueryResultCallbacks.get(
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index d0acedb..4a83bbe 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -1173,7 +1173,8 @@
          *
          * @param data The payload which includes frame information. Divide nanosecond values by
          *             {@code 1000000} to convert it to the {@link SystemClock#uptimeMillis()}
-         *             time base.
+         *             time base. {@code data} is not valid outside of {@code onVsync} and should
+         *             not be accessed outside the callback.
          * @see FrameCallback#doFrame
          **/
         void onVsync(@NonNull FrameData data);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index b4599c8..fc26766 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -334,8 +334,6 @@
                 "libtimeinstate",
                 "server_configurable_flags",
                 "libimage_io",
-                "libjpegdecoder",
-                "libjpegencoder",
                 "libjpegrecoverymap",
             ],
             export_shared_lib_headers: [
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 111cfd8..f11836e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -650,7 +650,6 @@
         }
 
         mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
-        mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);
 
         // If the displayId of the task is different than what PipBoundsHandler has, then update
         // it. This is possible if we entered PiP on an external display.
@@ -659,6 +658,17 @@
             mOnDisplayIdChangeCallback.accept(info.displayId);
         }
 
+        // UiEvent logging.
+        final PipUiEventLogger.PipUiEventEnum uiEventEnum;
+        if (isLaunchIntoPipTask()) {
+            uiEventEnum = PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER_CONTENT_PIP;
+        } else if (mPipTransitionState.getInSwipePipToHomeTransition()) {
+            uiEventEnum = PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_AUTO_ENTER;
+        } else {
+            uiEventEnum = PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER;
+        }
+        mPipUiEventLoggerLogger.log(uiEventEnum);
+
         if (mPipTransitionState.getInSwipePipToHomeTransition()) {
             if (!mWaitForFixedRotation) {
                 onEndOfSwipePipToHomeTransition();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
index 513ebba..3e5a19b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
@@ -78,6 +78,12 @@
         @UiEvent(doc = "Activity enters picture-in-picture mode")
         PICTURE_IN_PICTURE_ENTER(603),
 
+        @UiEvent(doc = "Activity enters picture-in-picture mode with auto-enter-pip API")
+        PICTURE_IN_PICTURE_AUTO_ENTER(1313),
+
+        @UiEvent(doc = "Activity enters picture-in-picture mode from content-pip API")
+        PICTURE_IN_PICTURE_ENTER_CONTENT_PIP(1314),
+
         @UiEvent(doc = "Expands from picture-in-picture to fullscreen")
         PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN(604),
 
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 9e3f115..03d89cc 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -396,8 +396,6 @@
         "libharfbuzz_ng",
         "libimage_io",
         "libjpeg",
-        "libjpegdecoder",
-        "libjpegencoder",
         "libjpegrecoverymap",
         "liblog",
         "libminikin",
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index 5bf53d0..8266beb 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -17,13 +17,25 @@
 #include "ImageDecoder.h"
 
 #include <Gainmap.h>
+#include <SkAlphaType.h>
 #include <SkAndroidCodec.h>
 #include <SkBitmap.h>
 #include <SkBlendMode.h>
 #include <SkCanvas.h>
+#include <SkCodec.h>
+#include <SkCodecAnimation.h>
+#include <SkColorSpace.h>
+#include <SkColorType.h>
 #include <SkEncodedOrigin.h>
+#include <SkImageInfo.h>
 #include <SkGainmapInfo.h>
+#include <SkMatrix.h>
 #include <SkPaint.h>
+#include <SkPngChunkReader.h>
+#include <SkRect.h>
+#include <SkRefCnt.h>
+#include <SkSamplingOptions.h>
+#include <SkSize.h>
 #include <SkStream.h>
 #include <hwui/Bitmap.h>
 #include <log/log.h>
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
index add62b1..fda7080 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -18,11 +18,16 @@
 
 #include <FrontBufferedStream.h>
 #include <HardwareBitmapUploader.h>
+#include <SkAlphaType.h>
 #include <SkAndroidCodec.h>
 #include <SkBitmap.h>
+#include <SkCodec.h>
+#include <SkCodecAnimation.h>
 #include <SkColorSpace.h>
+#include <SkColorType.h>
 #include <SkImageInfo.h>
 #include <SkRect.h>
+#include <SkSize.h>
 #include <SkStream.h>
 #include <SkString.h>
 #include <androidfw/Asset.h>
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index cd6ed23..e18b4a9 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -25,10 +25,14 @@
 #include <hwui/ImageDecoder.h>
 #include <log/log.h>
 #include <SkAndroidCodec.h>
+#include <SkAlphaType.h>
 #include <SkCodec.h>
+#include <SkCodecAnimation.h>
 #include <SkColorSpace.h>
+#include <SkColorType.h>
 #include <SkImageInfo.h>
 #include <SkRect.h>
+#include <SkRefCnt.h>
 #include <SkSize.h>
 #include <SkStream.h>
 #include <utils/Color.h>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 435a866..754433b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3029,4 +3029,22 @@
     [CHAR LIMIT=32]
     -->
     <string name="lock_screen_settings">Lock screen settings</string>
+
+    <!-- Content description for Wi-Fi not available icon on dream [CHAR LIMIT=NONE]-->
+    <string name="wifi_unavailable_dream_overlay_content_description">Wi-Fi not available</string>
+
+    <!-- Content description for camera blocked icon on dream [CHAR LIMIT=NONE] -->
+    <string name="camera_blocked_dream_overlay_content_description">Camera blocked</string>
+
+    <!-- Content description for camera and microphone blocked icon on dream [CHAR LIMIT=NONE] -->
+    <string name="camera_and_microphone_blocked_dream_overlay_content_description">Camera and microphone blocked</string>
+
+    <!-- Content description for camera and microphone disabled icon on dream [CHAR LIMIT=NONE] -->
+    <string name="microphone_blocked_dream_overlay_content_description">Microphone blocked</string>
+
+    <!-- Content description for priority mode icon on dream [CHAR LIMIT=NONE] -->
+    <string name="priority_mode_dream_overlay_content_description">Priority mode on</string>
+
+    <!-- Content description for when assistant attention is active [CHAR LIMIT=NONE] -->
+    <string name="assistant_attention_content_description">Assistant attention on</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index 54ae84f9..ead1a10 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -303,9 +303,18 @@
         pw.println("   requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup")
         pw.println("   requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent")
         pw.println("   requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail")
-        pw.println("   requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=${
-            onUnlockIntentWhenBiometricEnrolled.map { BiometricType.values()[it] }
-        }")
+
+        val onUnlockIntentWhenBiometricEnrolledString =
+            onUnlockIntentWhenBiometricEnrolled.map {
+                for (biometricType in BiometricType.values()) {
+                    if (biometricType.intValue == it) {
+                        return@map biometricType.name
+                    }
+                }
+                return@map "UNKNOWN"
+            }
+        pw.println("   requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=" +
+                "$onUnlockIntentWhenBiometricEnrolledString")
         pw.println("   requestActiveUnlockOnFaceError=$faceErrorsToTriggerBiometricFailOn")
         pw.println("   requestActiveUnlockOnFaceAcquireInfo=" +
                 "$faceAcquireInfoToTriggerBiometricFailOn")
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 27641fe..a14aa52 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -269,6 +269,8 @@
      */
     private void addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) {
         mWindow = new PhoneWindow(mContext);
+        // Default to SystemUI name for TalkBack.
+        mWindow.setTitle("");
         mWindow.setAttributes(layoutParams);
         mWindow.setWindowManager(null, layoutParams.token, "DreamOverlay", true);
 
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 2221a04..74a49a8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -259,7 +259,8 @@
                         mConnectivityManager.getActiveNetwork());
         final boolean available = capabilities != null
                 && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
-        showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available);
+        showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available,
+                R.string.wifi_unavailable_dream_overlay_content_description);
     }
 
     private void updateAlarmStatusIcon() {
@@ -274,7 +275,8 @@
 
     private void updateAssistantAttentionIcon() {
         showIcon(DreamOverlayStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE,
-                mDreamOverlayStateController.hasAssistantAttention());
+                mDreamOverlayStateController.hasAssistantAttention(),
+                R.string.assistant_attention_content_description);
     }
 
     private void updateVisibility() {
@@ -304,13 +306,16 @@
         @DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
         showIcon(
                 DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED,
-                !micBlocked && cameraBlocked);
+                !micBlocked && cameraBlocked,
+                R.string.camera_blocked_dream_overlay_content_description);
         showIcon(
                 DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED,
-                micBlocked && !cameraBlocked);
+                micBlocked && !cameraBlocked,
+                R.string.microphone_blocked_dream_overlay_content_description);
         showIcon(
                 DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
-                micBlocked && cameraBlocked);
+                micBlocked && cameraBlocked,
+                R.string.camera_and_microphone_blocked_dream_overlay_content_description);
     }
 
     private String buildNotificationsContentDescription(int notificationCount) {
@@ -323,11 +328,13 @@
     private void updatePriorityModeStatusIcon() {
         showIcon(
                 DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON,
-                mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF);
+                mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF,
+                R.string.priority_mode_dream_overlay_content_description);
     }
 
-    private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show) {
-        showIcon(iconType, show, null);
+    private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show,
+            int contentDescriptionResId) {
+        showIcon(iconType, show, mResources.getString(contentDescriptionResId));
     }
 
     private void showIcon(
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b75b6d8..85e050c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -566,6 +566,12 @@
     val CONTROLS_MANAGEMENT_NEW_FLOWS =
         releasedFlag(2002, "controls_management_new_flows", teamfood = true)
 
+    // Enables removing app from Home control panel as a part of a new flow
+    // TODO(b/269132640): Tracking Bug
+    @JvmField
+    val APP_PANELS_REMOVE_APPS_ALLOWED =
+        unreleasedFlag(2003, "app_panels_remove_apps_allowed", teamfood = false)
+
     // 2100 - Falsing Manager
     @JvmField val FALSING_FOR_LONG_TAPS = releasedFlag(2100, "falsing_for_long_taps")
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index 5a9f775..c9f645d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.keyguard.data.quickaffordance
 
 import android.app.StatusBarManager
+import android.app.admin.DevicePolicyManager
 import android.content.Context
 import android.content.pm.PackageManager
 import com.android.systemui.R
@@ -27,10 +28,14 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.UserTracker
 import dagger.Lazy
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.withContext
 
 @SysUISingleton
 class CameraQuickAffordanceConfig
@@ -39,6 +44,9 @@
     @Application private val context: Context,
     private val packageManager: PackageManager,
     private val cameraGestureHelper: Lazy<CameraGestureHelper>,
+    private val userTracker: UserTracker,
+    private val devicePolicyManager: DevicePolicyManager,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) : KeyguardQuickAffordanceConfig {
 
     override val key: String
@@ -79,7 +87,12 @@
         return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
     }
 
-    private fun isLaunchable(): Boolean {
-        return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
+    private suspend fun isLaunchable(): Boolean {
+        return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) &&
+            withContext(backgroundDispatcher) {
+                !devicePolicyManager.getCameraDisabled(null, userTracker.userId) &&
+                    devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId) and
+                        DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA == 0
+            }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
index d9ec3b1..6f821a2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.keyguard.data.quickaffordance
 
 import android.app.StatusBarManager
+import android.app.admin.DevicePolicyManager
 import android.content.Context
 import android.content.Intent
 import com.android.systemui.ActivityIntentHelper
@@ -29,10 +30,13 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.settings.UserTracker
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.withContext
 
 @SysUISingleton
 class VideoCameraQuickAffordanceConfig
@@ -42,6 +46,8 @@
     private val cameraIntents: CameraIntentsWrapper,
     private val activityIntentHelper: ActivityIntentHelper,
     private val userTracker: UserTracker,
+    private val devicePolicyManager: DevicePolicyManager,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) : KeyguardQuickAffordanceConfig {
 
     private val intent: Intent by lazy {
@@ -63,8 +69,8 @@
         get() = R.drawable.ic_videocam
 
     override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
-        get() =
-            flowOf(
+        get() = flow {
+            emit(
                 if (isLaunchable()) {
                     KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                         icon =
@@ -77,6 +83,7 @@
                     KeyguardQuickAffordanceConfig.LockScreenState.Hidden
                 }
             )
+        }
 
     override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
         return if (isLaunchable()) {
@@ -95,11 +102,14 @@
         )
     }
 
-    private fun isLaunchable(): Boolean {
+    private suspend fun isLaunchable(): Boolean {
         return activityIntentHelper.getTargetActivityInfo(
             intent,
             userTracker.userId,
             true,
-        ) != null
+        ) != null &&
+            withContext(backgroundDispatcher) {
+                !devicePolicyManager.getCameraDisabled(null, userTracker.userId)
+            }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index b72923a..68d2c5c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -164,13 +164,13 @@
                 mediaCarouselScrollHandler.scrollToStart()
             }
         }
-    private var currentlyExpanded = true
+
+    @VisibleForTesting
+    var currentlyExpanded = true
         set(value) {
             if (field != value) {
                 field = value
-                for (player in MediaPlayerData.players()) {
-                    player.setListening(field)
-                }
+                updateSeekbarListening(mediaCarouselScrollHandler.visibleToUser)
             }
         }
 
@@ -259,6 +259,7 @@
                 executor,
                 this::onSwipeToDismiss,
                 this::updatePageIndicatorLocation,
+                this::updateSeekbarListening,
                 this::closeGuts,
                 falsingCollector,
                 falsingManager,
@@ -590,6 +591,17 @@
                     ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex)
             }
         }
+        // Check postcondition: mediaContent should have the same number of children as there
+        // are
+        // elements in mediaPlayers.
+        if (MediaPlayerData.players().size != mediaContent.childCount) {
+            Log.e(
+                TAG,
+                "Size of players list and number of views in carousel are out of sync. " +
+                    "Players size is ${MediaPlayerData.players().size}. " +
+                    "View count is ${mediaContent.childCount}."
+            )
+        }
     }
 
     // Returns true if new player is added
@@ -618,7 +630,9 @@
                     )
                 newPlayer.mediaViewHolder?.player?.setLayoutParams(lp)
                 newPlayer.bindPlayer(data, key)
-                newPlayer.setListening(currentlyExpanded)
+                newPlayer.setListening(
+                    mediaCarouselScrollHandler.visibleToUser && currentlyExpanded
+                )
                 MediaPlayerData.addMediaPlayer(
                     key,
                     data,
@@ -665,17 +679,6 @@
             updatePageIndicator()
             mediaCarouselScrollHandler.onPlayersChanged()
             mediaFrame.requiresRemeasuring = true
-            // Check postcondition: mediaContent should have the same number of children as there
-            // are
-            // elements in mediaPlayers.
-            if (MediaPlayerData.players().size != mediaContent.childCount) {
-                Log.e(
-                    TAG,
-                    "Size of players list and number of views in carousel are out of sync. " +
-                        "Players size is ${MediaPlayerData.players().size}. " +
-                        "View count is ${mediaContent.childCount}."
-                )
-            }
             return existingPlayer == null
         }
 
@@ -914,6 +917,13 @@
                 .toFloat()
     }
 
+    /** Update listening to seekbar. */
+    private fun updateSeekbarListening(visibleToUser: Boolean) {
+        for (player in MediaPlayerData.players()) {
+            player.setListening(visibleToUser && currentlyExpanded)
+        }
+    }
+
     /** Update the dimension of this carousel. */
     private fun updateCarouselDimensions() {
         var width = 0
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
index 36b2eda..1ace316 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
@@ -57,6 +57,7 @@
     private val mainExecutor: DelayableExecutor,
     val dismissCallback: () -> Unit,
     private var translationChangedListener: () -> Unit,
+    private var seekBarUpdateListener: (visibleToUser: Boolean) -> Unit,
     private val closeGuts: (immediate: Boolean) -> Unit,
     private val falsingCollector: FalsingCollector,
     private val falsingManager: FalsingManager,
@@ -177,6 +178,12 @@
 
     /** Whether the media card is visible to user if any */
     var visibleToUser: Boolean = false
+        set(value) {
+            if (field != value) {
+                field = value
+                seekBarUpdateListener.invoke(field)
+            }
+        }
 
     /** Whether the quick setting is expanded or not */
     var qsExpanded: Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 6076e58..288266a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -347,6 +347,11 @@
         mSeekBarViewModel.setListening(listening);
     }
 
+    @VisibleForTesting
+    public boolean getListening() {
+        return mSeekBarViewModel.getListening();
+    }
+
     /** Sets whether the user is touching the seek bar to change the track position. */
     private void setIsScrubbing(boolean isScrubbing) {
         if (mMediaData == null || mMediaData.getSemanticActions() == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index 7a62bae..fc94aed 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -93,7 +93,13 @@
     @UiEvent(doc = "User has discarded the result of a long screenshot")
     SCREENSHOT_LONG_SCREENSHOT_EXIT(911),
     @UiEvent(doc = "A screenshot has been taken and saved to work profile")
-    SCREENSHOT_SAVED_TO_WORK_PROFILE(1240);
+    SCREENSHOT_SAVED_TO_WORK_PROFILE(1240),
+    @UiEvent(doc = "Notes application triggered the screenshot for notes")
+    SCREENSHOT_FOR_NOTE_TRIGGERED(1308),
+    @UiEvent(doc = "User accepted the screenshot to be sent to the notes app")
+    SCREENSHOT_FOR_NOTE_ACCEPTED(1309),
+    @UiEvent(doc = "User cancelled the screenshot for notes app flow")
+    SCREENSHOT_FOR_NOTE_CANCELLED(1310);
 
     private final int mId;
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
index f8d86a0..3133924 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
@@ -17,15 +17,21 @@
 package com.android.systemui.screenshot;
 
 import static com.android.systemui.screenshot.AppClipsTrampolineActivity.ACTION_FINISH_FROM_TRAMPOLINE;
+import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_CALLING_PACKAGE_NAME;
 import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_RESULT_RECEIVER;
 import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI;
 import static com.android.systemui.screenshot.AppClipsTrampolineActivity.PERMISSION_SELF;
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
 
 import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
@@ -33,15 +39,20 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ResultReceiver;
+import android.util.Log;
 import android.view.View;
 import android.widget.Button;
 import android.widget.ImageView;
 
 import androidx.activity.ComponentActivity;
+import androidx.annotation.Nullable;
 import androidx.lifecycle.ViewModelProvider;
 
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLogger.UiEventEnum;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
+import com.android.systemui.settings.UserTracker;
 
 import javax.inject.Inject;
 
@@ -64,9 +75,15 @@
  *
  * TODO(b/267309532): Polish UI and animations.
  */
-public final class AppClipsActivity extends ComponentActivity {
+public class AppClipsActivity extends ComponentActivity {
+
+    private static final String TAG = AppClipsActivity.class.getSimpleName();
+    private static final ApplicationInfoFlags APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0);
 
     private final AppClipsViewModel.Factory mViewModelFactory;
+    private final PackageManager mPackageManager;
+    private final UserTracker mUserTracker;
+    private final UiEventLogger mUiEventLogger;
     private final BroadcastReceiver mBroadcastReceiver;
     private final IntentFilter mIntentFilter;
 
@@ -80,10 +97,17 @@
     private AppClipsViewModel mViewModel;
 
     private ResultReceiver mResultReceiver;
+    @Nullable
+    private String mCallingPackageName;
+    private int mCallingPackageUid;
 
     @Inject
-    public AppClipsActivity(AppClipsViewModel.Factory viewModelFactory) {
+    public AppClipsActivity(AppClipsViewModel.Factory viewModelFactory,
+            PackageManager packageManager, UserTracker userTracker, UiEventLogger uiEventLogger) {
         mViewModelFactory = viewModelFactory;
+        mPackageManager = packageManager;
+        mUserTracker = userTracker;
+        mUiEventLogger = uiEventLogger;
 
         mBroadcastReceiver = new BroadcastReceiver() {
             @Override
@@ -113,6 +137,7 @@
                 RECEIVER_NOT_EXPORTED);
 
         Intent intent = getIntent();
+        setUpUiLogging(intent);
         mResultReceiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER, ResultReceiver.class);
         if (mResultReceiver == null) {
             setErrorThenFinish(Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED);
@@ -169,6 +194,17 @@
         }
     }
 
+    private void setUpUiLogging(Intent intent) {
+        mCallingPackageName = intent.getStringExtra(EXTRA_CALLING_PACKAGE_NAME);
+        mCallingPackageUid = 0;
+        try {
+            mCallingPackageUid = mPackageManager.getApplicationInfoAsUser(mCallingPackageName,
+                    APPLICATION_INFO_FLAGS, mUserTracker.getUserId()).uid;
+        } catch (NameNotFoundException e) {
+            Log.d(TAG, "Couldn't find notes app UID " + e);
+        }
+    }
+
     private void setScreenshot(Bitmap screenshot) {
         // Set background, status and navigation bar colors as the activity is no longer
         // translucent.
@@ -228,6 +264,7 @@
         data.putParcelable(EXTRA_SCREENSHOT_URI, uri);
         try {
             mResultReceiver.send(Activity.RESULT_OK, data);
+            logUiEvent(SCREENSHOT_FOR_NOTE_ACCEPTED);
         } catch (Exception e) {
             // Do nothing.
         }
@@ -251,6 +288,9 @@
         data.putInt(Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE, errorCode);
         try {
             mResultReceiver.send(RESULT_OK, data);
+            if (errorCode == Intent.CAPTURE_CONTENT_FOR_NOTE_USER_CANCELED) {
+                logUiEvent(SCREENSHOT_FOR_NOTE_CANCELLED);
+            }
         } catch (Exception e) {
             // Do nothing.
         }
@@ -259,6 +299,10 @@
         mResultReceiver = null;
     }
 
+    private void logUiEvent(UiEventEnum uiEvent) {
+        mUiEventLogger.log(uiEvent, mCallingPackageUid, mCallingPackageName);
+    }
+
     private void updateImageDimensions() {
         Drawable drawable = mPreview.getDrawable();
         if (drawable == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
index 4759cc6..1946b8e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
@@ -24,26 +24,33 @@
 import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
 
 import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS;
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_TRIGGERED;
 
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.ResultReceiver;
+import android.util.Log;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.notetask.NoteTaskController;
+import com.android.systemui.settings.UserTracker;
 import com.android.wm.shell.bubbles.Bubbles;
 
 import java.util.Optional;
@@ -70,15 +77,23 @@
 public class AppClipsTrampolineActivity extends Activity {
 
     private static final String TAG = AppClipsTrampolineActivity.class.getSimpleName();
-    public static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+    static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
     public static final String EXTRA_SCREENSHOT_URI = TAG + "SCREENSHOT_URI";
-    public static final String ACTION_FINISH_FROM_TRAMPOLINE = TAG + "FINISH_FROM_TRAMPOLINE";
-    static final String EXTRA_RESULT_RECEIVER = TAG + "RESULT_RECEIVER";
+    static final String ACTION_FINISH_FROM_TRAMPOLINE = TAG + "FINISH_FROM_TRAMPOLINE";
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    public static final String EXTRA_RESULT_RECEIVER = TAG + "RESULT_RECEIVER";
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    public static final String EXTRA_CALLING_PACKAGE_NAME = TAG + "CALLING_PACKAGE_NAME";
+    private static final ApplicationInfoFlags APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0);
 
     private final DevicePolicyManager mDevicePolicyManager;
     private final FeatureFlags mFeatureFlags;
     private final Optional<Bubbles> mOptionalBubbles;
     private final NoteTaskController mNoteTaskController;
+    private final PackageManager mPackageManager;
+    private final UserTracker mUserTracker;
+    private final UiEventLogger mUiEventLogger;
     private final ResultReceiver mResultReceiver;
 
     private Intent mKillAppClipsBroadcastIntent;
@@ -86,11 +101,15 @@
     @Inject
     public AppClipsTrampolineActivity(DevicePolicyManager devicePolicyManager, FeatureFlags flags,
             Optional<Bubbles> optionalBubbles, NoteTaskController noteTaskController,
+            PackageManager packageManager, UserTracker userTracker, UiEventLogger uiEventLogger,
             @Main Handler mainHandler) {
         mDevicePolicyManager = devicePolicyManager;
         mFeatureFlags = flags;
         mOptionalBubbles = optionalBubbles;
         mNoteTaskController = noteTaskController;
+        mPackageManager = packageManager;
+        mUserTracker = userTracker;
+        mUiEventLogger = uiEventLogger;
 
         mResultReceiver = createResultReceiver(mainHandler);
     }
@@ -138,8 +157,12 @@
             return;
         }
 
-        Intent intent = new Intent().setComponent(componentName).addFlags(
-                Intent.FLAG_ACTIVITY_NEW_TASK).putExtra(EXTRA_RESULT_RECEIVER, mResultReceiver);
+        String callingPackageName = getCallingPackage();
+        Intent intent = new Intent().setComponent(componentName)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                .putExtra(EXTRA_RESULT_RECEIVER, mResultReceiver)
+                .putExtra(EXTRA_CALLING_PACKAGE_NAME, callingPackageName);
+
         try {
             // Start the App Clips activity.
             startActivity(intent);
@@ -150,6 +173,9 @@
                     new Intent(ACTION_FINISH_FROM_TRAMPOLINE)
                             .setComponent(componentName)
                             .setPackage(componentName.getPackageName());
+
+            // Log successful triggering of screenshot for notes.
+            logScreenshotTriggeredUiEvent(callingPackageName);
         } catch (ActivityNotFoundException e) {
             setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_FAILED);
         }
@@ -170,6 +196,18 @@
         finish();
     }
 
+    private void logScreenshotTriggeredUiEvent(@Nullable String callingPackageName) {
+        int callingPackageUid = 0;
+        try {
+            callingPackageUid = mPackageManager.getApplicationInfoAsUser(callingPackageName,
+                    APPLICATION_INFO_FLAGS, mUserTracker.getUserId()).uid;
+        } catch (NameNotFoundException e) {
+            Log.d(TAG, "Couldn't find notes app UID " + e);
+        }
+
+        mUiEventLogger.log(SCREENSHOT_FOR_NOTE_TRIGGERED, callingPackageUid, callingPackageName);
+    }
+
     private class AppClipsResultReceiver extends ResultReceiver {
 
         AppClipsResultReceiver(Handler handler) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
index 5a7b5f9..b2910fd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
@@ -18,6 +18,8 @@
 
 import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED;
 
+import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
+
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.HardwareRenderer;
@@ -29,6 +31,7 @@
 import android.os.Process;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.ViewModel;
@@ -49,7 +52,8 @@
 import javax.inject.Inject;
 
 /** A {@link ViewModel} to help with the App Clips screenshot flow. */
-final class AppClipsViewModel extends ViewModel {
+@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+public final class AppClipsViewModel extends ViewModel {
 
     private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
     private final ImageExporter mImageExporter;
@@ -76,7 +80,8 @@
     }
 
     /** Grabs a screenshot and updates the {@link Bitmap} set in screenshot {@link LiveData}. */
-    void performScreenshot() {
+    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+    public void performScreenshot() {
         mBgExecutor.execute(() -> {
             Bitmap screenshot = mAppClipsCrossProcessHelper.takeScreenshot();
             mMainExecutor.execute(() -> {
@@ -90,12 +95,14 @@
     }
 
     /** Returns a {@link LiveData} that holds the captured screenshot. */
-    LiveData<Bitmap> getScreenshot() {
+    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+    public LiveData<Bitmap> getScreenshot() {
         return mScreenshotLiveData;
     }
 
     /** Returns a {@link LiveData} that holds the {@link Uri} where screenshot is saved. */
-    LiveData<Uri> getResultLiveData() {
+    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+    public LiveData<Uri> getResultLiveData() {
         return mResultLiveData;
     }
 
@@ -103,7 +110,8 @@
      * Returns a {@link LiveData} that holds the error codes for
      * {@link Intent#EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE}.
      */
-    LiveData<Integer> getErrorLiveData() {
+    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+    public LiveData<Integer> getErrorLiveData() {
         return mErrorLiveData;
     }
 
@@ -111,7 +119,8 @@
      * Saves the provided {@link Drawable} to storage then informs the result {@link Uri} to
      * {@link LiveData}.
      */
-    void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds) {
+    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+    public void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds) {
         mBgExecutor.execute(() -> {
             // Render the screenshot bitmap in background.
             Bitmap screenshotBitmap = renderBitmap(screenshotDrawable, bounds);
@@ -151,7 +160,8 @@
     }
 
     /** Helper factory to help with injecting {@link AppClipsViewModel}. */
-    static final class Factory implements ViewModelProvider.Factory {
+    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
+    public static final class Factory implements ViewModelProvider.Factory {
 
         private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
         private final ImageExporter mImageExporter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
index 2f34516..16c4027 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.data.model
 
+import android.os.ParcelUuid
+
 /**
  * SystemUI representation of [SubscriptionInfo]. Currently we only use two fields on the
  * subscriptions themselves: subscriptionId and isOpportunistic. Any new fields that we need can be
@@ -29,4 +31,7 @@
      * filtering in certain cases. See [MobileIconsInteractor] for the filtering logic
      */
     val isOpportunistic: Boolean = false,
+
+    /** Subscriptions in the same group may be filtered or treated as a single subscription */
+    val groupUuid: ParcelUuid? = null,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index c9049d8..e77266f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -376,6 +376,7 @@
         SubscriptionModel(
             subscriptionId = subscriptionId,
             isOpportunistic = isOpportunistic,
+            groupUuid = groupUuid,
         )
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 72d5113..5a2e11e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -150,6 +150,12 @@
 
                 val info1 = unfilteredSubs[0]
                 val info2 = unfilteredSubs[1]
+
+                // Filtering only applies to subscriptions in the same group
+                if (info1.groupUuid == null || info1.groupUuid != info2.groupUuid) {
+                    return@combine unfilteredSubs
+                }
+
                 // If both subscriptions are primary, show both
                 if (!info1.isOpportunistic && !info2.isOpportunistic) {
                     return@combine unfilteredSubs
@@ -186,7 +192,7 @@
      * validated bit from the old active network (A) while data is changing to the new one (B).
      *
      * This condition only applies if
-     * 1. A and B are in the same subscription group (e.c. for CBRS data switching) and
+     * 1. A and B are in the same subscription group (e.g. for CBRS data switching) and
      * 2. A was validated before the switch
      *
      * The goal of this is to minimize the flickering in the UI of the cellular indicator
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
index dee4a6f..c0cbd62 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
@@ -21,6 +21,7 @@
 import android.content.Context
 import android.hardware.BatteryState
 import android.hardware.input.InputManager
+import android.hardware.input.InputSettings
 import android.os.Handler
 import android.util.ArrayMap
 import android.util.Log
@@ -235,9 +236,9 @@
      */
     private fun onStylusUsed() {
         if (!featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)) return
-        if (inputManager.isStylusEverUsed(context)) return
+        if (InputSettings.isStylusEverUsed(context)) return
 
-        inputManager.setStylusEverUsed(context, true)
+        InputSettings.setStylusEverUsed(context, true)
         executeStylusCallbacks { cb -> cb.onStylusFirstUsed() }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java b/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java
index b41bca0..8d32a48 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java
@@ -43,11 +43,6 @@
 
     @Override
     public final void start() {
-        if (mConditionSet == null || mConditionSet.isEmpty()) {
-            onStart();
-            return;
-        }
-
         mStartToken = mMonitor.addSubscription(
                 new Monitor.Subscription.Builder(allConditionsMet -> {
                     if (allConditionsMet) {
@@ -63,11 +58,6 @@
 
     @Override
     public final void onBootCompleted() {
-        if (mConditionSet == null || mConditionSet.isEmpty()) {
-            bootCompleted();
-            return;
-        }
-
         mBootCompletedToken = mMonitor.addSubscription(
                 new Monitor.Subscription.Builder(allConditionsMet -> {
                     if (allConditionsMet) {
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index ed2772a..2ef3511 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -165,6 +165,12 @@
             android:exported="false"
             android:permission="com.android.systemui.permission.SELF"
             android:excludeFromRecents="true" />
+
+        <activity
+            android:name="com.android.systemui.screenshot.appclips.AppClipsActivityTest$AppClipsActivityTestable"
+            android:exported="false"
+            android:permission="com.android.systemui.permission.SELF"
+            android:excludeFromRecents="true" />
     </application>
 
     <instrumentation android:name="android.testing.TestableInstrumentation"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index e8d50ca..badeb27 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -24,13 +24,19 @@
 import android.os.PowerManager
 import android.os.PowerManager.WAKE_REASON_BIOMETRIC
 import android.os.UserHandle
-import android.provider.Settings
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.FakeSettings
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
@@ -41,20 +47,11 @@
 import org.mockito.Mockito.`when`
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import java.io.PrintWriter
 
 @SmallTest
 class ActiveUnlockConfigTest : SysuiTestCase() {
-    private val fakeWakeUri = Uri.Builder().appendPath("wake").build()
-    private val fakeUnlockIntentUri = Uri.Builder().appendPath("unlock-intent").build()
-    private val fakeBioFailUri = Uri.Builder().appendPath("bio-fail").build()
-    private val fakeFaceErrorsUri = Uri.Builder().appendPath("face-errors").build()
-    private val fakeFaceAcquiredUri = Uri.Builder().appendPath("face-acquired").build()
-    private val fakeUnlockIntentBioEnroll = Uri.Builder().appendPath("unlock-intent-bio").build()
-    private val fakeWakeupsConsideredUnlockIntents =
-        Uri.Builder().appendPath("wakeups-considered-unlock-intent").build()
-
-    @Mock
-    private lateinit var secureSettings: SecureSettings
+    private lateinit var secureSettings: FakeSettings
     @Mock
     private lateinit var contentResolver: ContentResolver
     @Mock
@@ -63,33 +60,20 @@
     private lateinit var dumpManager: DumpManager
     @Mock
     private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock private lateinit var mockPrintWriter: PrintWriter
 
     @Captor
     private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
 
     private lateinit var activeUnlockConfig: ActiveUnlockConfig
+    private var currentUser: Int = 0
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_WAKE))
-                .thenReturn(fakeWakeUri)
-        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
-                .thenReturn(fakeUnlockIntentUri)
-        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
-                .thenReturn(fakeBioFailUri)
-        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS))
-                .thenReturn(fakeFaceErrorsUri)
-        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
-                .thenReturn(fakeFaceAcquiredUri)
-        `when`(secureSettings.getUriFor(
-                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED))
-                .thenReturn(fakeUnlockIntentBioEnroll)
-        `when`(secureSettings.getUriFor(
-            Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
-            .thenReturn(fakeWakeupsConsideredUnlockIntents)
-
+        currentUser = KeyguardUpdateMonitor.getCurrentUser()
+        secureSettings = FakeSettings()
         activeUnlockConfig = ActiveUnlockConfig(
                 handler,
                 secureSettings,
@@ -105,8 +89,6 @@
 
     @Test
     fun onWakeupSettingChanged() {
-        verifyRegisterSettingObserver()
-
         // GIVEN no active unlock settings enabled
         assertFalse(
                 activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -114,9 +96,8 @@
         )
 
         // WHEN unlock on wake is allowed
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeWakeUri)
+        secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_WAKE, 1, currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE))
 
         // THEN active unlock triggers allowed on: wake, unlock-intent, and biometric failure
         assertTrue(
@@ -135,8 +116,6 @@
 
     @Test
     fun onUnlockIntentSettingChanged() {
-        verifyRegisterSettingObserver()
-
         // GIVEN no active unlock settings enabled
         assertFalse(
                 activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -144,9 +123,8 @@
         )
 
         // WHEN unlock on biometric failed is allowed
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeUnlockIntentUri)
+        secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 1, currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
 
         // THEN active unlock triggers allowed on: biometric failure ONLY
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -159,21 +137,19 @@
 
     @Test
     fun onBioFailSettingChanged() {
-        verifyRegisterSettingObserver()
-
         // GIVEN no active unlock settings enabled and triggering unlock intent on biometric
         // enrollment setting is disabled (empty string is disabled, null would use the default)
-        `when`(secureSettings.getStringForUser(
-                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
-                0)).thenReturn("")
-        updateSetting(fakeUnlockIntentBioEnroll)
+        secureSettings.putStringForUser(
+                ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, "", currentUser)
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+        ))
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
                 ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
 
         // WHEN unlock on biometric failed is allowed
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeBioFailUri)
+        secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
 
         // THEN active unlock triggers allowed on: biometric failure ONLY
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -186,17 +162,14 @@
 
     @Test
     fun faceErrorSettingsChanged() {
-        verifyRegisterSettingObserver()
-
         // GIVEN unlock on biometric fail
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeBioFailUri)
+        secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
 
         // WHEN face error timeout (3), allow trigger active unlock
-        `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
-                0)).thenReturn("3")
-        updateSetting(fakeFaceAcquiredUri)
+        secureSettings.putStringForUser(
+            ACTIVE_UNLOCK_ON_FACE_ERRORS, "3", currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS))
 
         // THEN active unlock triggers allowed on error TIMEOUT
         assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
@@ -208,19 +181,17 @@
 
     @Test
     fun faceAcquiredSettingsChanged() {
-        verifyRegisterSettingObserver()
-
         // GIVEN unlock on biometric fail
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeBioFailUri)
+        secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, "1", currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
 
         // WHEN face acquiredMsg DARK_GLASSESand MOUTH_COVERING are allowed to trigger
-        `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
-                0)).thenReturn(
+        secureSettings.putStringForUser(
+            ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
                 "${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
-                        "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}")
-        updateSetting(fakeFaceAcquiredUri)
+                        "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}",
+            currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
 
         // THEN active unlock triggers allowed on acquired messages DARK_GLASSES & MOUTH_COVERING
         assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
@@ -236,23 +207,23 @@
 
     @Test
     fun triggerOnUnlockIntentWhenBiometricEnrolledNone() {
-        verifyRegisterSettingObserver()
-
         // GIVEN unlock on biometric fail
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeBioFailUri)
+        secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
 
         // GIVEN fingerprint and face are NOT enrolled
         activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
-        `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false)
+        `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false)
         `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
 
         // WHEN unlock intent is allowed when NO biometrics are enrolled (0)
-        `when`(secureSettings.getStringForUser(
-                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
-                0)).thenReturn("${ActiveUnlockConfig.BiometricType.NONE.intValue}")
-        updateSetting(fakeUnlockIntentBioEnroll)
+
+        secureSettings.putStringForUser(
+            ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+            "${ActiveUnlockConfig.BiometricType.NONE.intValue}", currentUser)
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+        ))
 
         // THEN active unlock triggers allowed on unlock intent
         assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -261,12 +232,9 @@
 
     @Test
     fun triggerOnUnlockIntentWhenBiometricEnrolledFingerprintOrFaceOnly() {
-        verifyRegisterSettingObserver()
-
         // GIVEN unlock on biometric fail
-        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
-                0, 0)).thenReturn(1)
-        updateSetting(fakeBioFailUri)
+        secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
 
         // GIVEN fingerprint and face are both enrolled
         activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
@@ -275,12 +243,14 @@
 
         // WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs
         // are enrolled
-        `when`(secureSettings.getStringForUser(
-                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
-                0)).thenReturn(
+        secureSettings.putStringForUser(
+                ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
                 "${ActiveUnlockConfig.BiometricType.ANY_FACE.intValue}" +
-                        "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}")
-        updateSetting(fakeUnlockIntentBioEnroll)
+                        "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}",
+            currentUser)
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+        ))
 
         // THEN active unlock triggers NOT allowed on unlock intent
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -305,13 +275,12 @@
 
     @Test
     fun isWakeupConsideredUnlockIntent_singleValue() {
-        verifyRegisterSettingObserver()
-
         // GIVEN lift is considered an unlock intent
-        `when`(secureSettings.getStringForUser(
-            Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
-            0)).thenReturn(PowerManager.WAKE_REASON_LIFT.toString())
-        updateSetting(fakeWakeupsConsideredUnlockIntents)
+        secureSettings.putIntForUser(
+            ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
+            PowerManager.WAKE_REASON_LIFT,
+            currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
 
         // THEN only WAKE_REASON_LIFT is considered an unlock intent
         for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
@@ -325,17 +294,15 @@
 
     @Test
     fun isWakeupConsideredUnlockIntent_multiValue() {
-        verifyRegisterSettingObserver()
-
         // GIVEN lift and tap are considered an unlock intent
-        `when`(secureSettings.getStringForUser(
-            Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
-            0)).thenReturn(
+        secureSettings.putStringForUser(
+            ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
             PowerManager.WAKE_REASON_LIFT.toString() +
                     "|" +
-                    PowerManager.WAKE_REASON_TAP.toString()
+                    PowerManager.WAKE_REASON_TAP.toString(),
+            currentUser
         )
-        updateSetting(fakeWakeupsConsideredUnlockIntents)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
 
         // THEN WAKE_REASON_LIFT and WAKE_REASON TAP are considered an unlock intent
         for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
@@ -354,13 +321,10 @@
 
     @Test
     fun isWakeupConsideredUnlockIntent_emptyValues() {
-        verifyRegisterSettingObserver()
-
         // GIVEN lift and tap are considered an unlock intent
-        `when`(secureSettings.getStringForUser(
-            Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
-            0)).thenReturn(" ")
-        updateSetting(fakeWakeupsConsideredUnlockIntents)
+        secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS, " ",
+            currentUser)
+        updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
 
         // THEN no wake up gestures are considered an unlock intent
         for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
@@ -373,7 +337,23 @@
             PowerManager.WAKE_REASON_UNFOLD_DEVICE))
     }
 
+    @Test
+    fun dump_onUnlockIntentWhenBiometricEnrolled_invalidNum_noArrayOutOfBoundsException() {
+        // GIVEN an invalid input (-1)
+        secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+            "-1", currentUser)
+
+        // WHEN the setting updates
+        updateSetting(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+        ))
+
+        // THEN no exception thrown
+        activeUnlockConfig.dump(mockPrintWriter, emptyArray())
+    }
+
     private fun updateSetting(uri: Uri) {
+        verifyRegisterSettingObserver()
         settingsObserverCaptor.value.onChange(
                 false,
                 listOf(uri),
@@ -383,13 +363,17 @@
     }
 
     private fun verifyRegisterSettingObserver() {
-        verifyRegisterSettingObserver(fakeWakeUri)
-        verifyRegisterSettingObserver(fakeUnlockIntentUri)
-        verifyRegisterSettingObserver(fakeBioFailUri)
-        verifyRegisterSettingObserver(fakeFaceErrorsUri)
-        verifyRegisterSettingObserver(fakeFaceAcquiredUri)
-        verifyRegisterSettingObserver(fakeUnlockIntentBioEnroll)
-        verifyRegisterSettingObserver(fakeWakeupsConsideredUnlockIntents)
+        verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE))
+        verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
+        verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
+        verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS))
+        verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
+        verifyRegisterSettingObserver(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+        ))
+        verifyRegisterSettingObserver(secureSettings.getUriFor(
+            ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
+        ))
     }
 
     private fun verifyRegisterSettingObserver(uri: Uri) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
index b9db9c4..b3329eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
@@ -32,6 +32,7 @@
 
 import com.android.settingslib.dream.DreamBackend;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.condition.SelfExecutingMonitor;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.shared.condition.Monitor;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -67,7 +68,6 @@
 
     private ComplicationTypesUpdater mController;
 
-    @Mock
     private Monitor mMonitor;
 
     @Before
@@ -75,6 +75,7 @@
         MockitoAnnotations.initMocks(this);
         when(mDreamBackend.getEnabledComplications()).thenReturn(new HashSet<>());
 
+        mMonitor = SelfExecutingMonitor.createInstance();
         mController = new ComplicationTypesUpdater(mDreamBackend, mExecutor,
                 mSecureSettings, mDreamOverlayStateController, mMonitor);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
index 52aaea1..f6662d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
@@ -29,6 +29,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.condition.SelfExecutingMonitor;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.shared.condition.Monitor;
 
@@ -70,13 +71,13 @@
     @Mock
     private ComplicationLayoutParams mLayoutParams;
 
-    @Mock
     private Monitor mMonitor;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         when(mDreamClockTimeViewHolderProvider.get()).thenReturn(mDreamClockTimeViewHolder);
+        mMonitor = SelfExecutingMonitor.createInstance();
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
index 8534d4f..3312c43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
@@ -38,6 +38,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.view.LaunchableImageView;
+import com.android.systemui.condition.SelfExecutingMonitor;
 import com.android.systemui.controls.ControlsServiceInfo;
 import com.android.systemui.controls.controller.ControlsController;
 import com.android.systemui.controls.controller.StructureInfo;
@@ -102,7 +103,6 @@
     @Captor
     private ArgumentCaptor<DreamOverlayStateController.Callback> mStateCallbackCaptor;
 
-    @Mock
     private Monitor mMonitor;
 
     @Before
@@ -116,6 +116,8 @@
                 Optional.of(mControlsListingController));
         when(mControlsComponent.getVisibility()).thenReturn(AVAILABLE);
         when(mView.findViewById(R.id.home_controls_chip)).thenReturn(mHomeControlsView);
+
+        mMonitor = SelfExecutingMonitor.createInstance();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
index 77ca958..ef62abf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
@@ -30,6 +30,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.condition.SelfExecutingMonitor;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
 import com.android.systemui.plugins.BcSmartspaceDataPlugin;
@@ -64,7 +65,6 @@
     @Mock
     private View mBcSmartspaceView;
 
-    @Mock
     private Monitor mMonitor;
 
     private final Set<Condition> mPreconditions = new HashSet<>();
@@ -72,6 +72,7 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
+        mMonitor = SelfExecutingMonitor.createInstance();
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index db18ba6..5bb8367 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -18,15 +18,19 @@
 package com.android.systemui.keyguard.data.quickaffordance
 
 import android.app.StatusBarManager
+import android.app.admin.DevicePolicyManager
 import android.content.Context
 import android.content.pm.PackageManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.camera.CameraGestureHelper
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
 import org.junit.Before
@@ -44,21 +48,28 @@
     @Mock private lateinit var cameraGestureHelper: CameraGestureHelper
     @Mock private lateinit var context: Context
     @Mock private lateinit var packageManager: PackageManager
+    @Mock private lateinit var userTracker: UserTracker
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private lateinit var underTest: CameraQuickAffordanceConfig
+    private lateinit var testScope: TestScope
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        setLaunchable(true)
+        setLaunchable()
 
+        val testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
         underTest =
             CameraQuickAffordanceConfig(
                 context,
                 packageManager,
-            ) {
-                cameraGestureHelper
-            }
+                { cameraGestureHelper },
+                userTracker,
+                devicePolicyManager,
+                testDispatcher,
+            )
     }
 
     @Test
@@ -73,23 +84,57 @@
     }
 
     @Test
-    fun `getPickerScreenState - default when launchable`() = runTest {
-        setLaunchable(true)
+    fun `getPickerScreenState - default when launchable`() =
+        testScope.runTest {
+            setLaunchable(true)
 
-        Truth.assertThat(underTest.getPickerScreenState())
-            .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
-    }
+            Truth.assertThat(underTest.getPickerScreenState())
+                .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
+        }
 
     @Test
-    fun `getPickerScreenState - unavailable when not launchable`() = runTest {
-        setLaunchable(false)
+    fun `getPickerScreenState - unavailable when camera app not installed`() =
+        testScope.runTest {
+            setLaunchable(isCameraAppInstalled = false)
 
-        Truth.assertThat(underTest.getPickerScreenState())
-            .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
-    }
+            Truth.assertThat(underTest.getPickerScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+        }
 
-    private fun setLaunchable(isLaunchable: Boolean) {
+    @Test
+    fun `getPickerScreenState - unavailable when camera disabled by admin`() =
+        testScope.runTest {
+            setLaunchable(isCameraDisabledByDeviceAdmin = true)
+
+            Truth.assertThat(underTest.getPickerScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+        }
+
+    @Test
+    fun `getPickerScreenState - unavailable when secure camera disabled by admin`() =
+        testScope.runTest {
+            setLaunchable(isSecureCameraDisabledByDeviceAdmin = true)
+
+            Truth.assertThat(underTest.getPickerScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+        }
+
+    private fun setLaunchable(
+        isCameraAppInstalled: Boolean = true,
+        isCameraDisabledByDeviceAdmin: Boolean = false,
+        isSecureCameraDisabledByDeviceAdmin: Boolean = false,
+    ) {
         whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY))
-            .thenReturn(isLaunchable)
+            .thenReturn(isCameraAppInstalled)
+        whenever(devicePolicyManager.getCameraDisabled(null, userTracker.userId))
+            .thenReturn(isCameraDisabledByDeviceAdmin)
+        whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
+            .thenReturn(
+                if (isSecureCameraDisabledByDeviceAdmin) {
+                    DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA
+                } else {
+                    0
+                }
+            )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
index 5bd86bd..f1b9c5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.data.quickaffordance
 
+import android.app.admin.DevicePolicyManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.ActivityIntentHelper
@@ -24,11 +25,14 @@
 import com.android.systemui.camera.CameraIntentsWrapper
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -44,59 +48,94 @@
 class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() {
 
     @Mock private lateinit var activityIntentHelper: ActivityIntentHelper
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private lateinit var underTest: VideoCameraQuickAffordanceConfig
+    private lateinit var userTracker: UserTracker
+    private lateinit var testScope: TestScope
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
+        val testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
+        userTracker = FakeUserTracker()
         underTest =
             VideoCameraQuickAffordanceConfig(
                 context = context,
                 cameraIntents = CameraIntentsWrapper(context),
                 activityIntentHelper = activityIntentHelper,
-                userTracker = FakeUserTracker(),
+                userTracker = userTracker,
+                devicePolicyManager = devicePolicyManager,
+                backgroundDispatcher = testDispatcher,
             )
     }
 
     @Test
-    fun `lockScreenState - visible when launchable`() = runTest {
-        setLaunchable(true)
+    fun `lockScreenState - visible when launchable`() =
+        testScope.runTest {
+            setLaunchable()
 
-        val lockScreenState = collectLastValue(underTest.lockScreenState)
+            val lockScreenState = collectLastValue(underTest.lockScreenState)
 
-        assertThat(lockScreenState())
-            .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
-    }
+            assertThat(lockScreenState())
+                .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
+        }
 
     @Test
-    fun `lockScreenState - hidden when not launchable`() = runTest {
-        setLaunchable(false)
+    fun `lockScreenState - hidden when app not installed on device`() =
+        testScope.runTest {
+            setLaunchable(isVideoCameraAppInstalled = false)
 
-        val lockScreenState = collectLastValue(underTest.lockScreenState)
+            val lockScreenState = collectLastValue(underTest.lockScreenState)
 
-        assertThat(lockScreenState())
-            .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-    }
+            assertThat(lockScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+        }
 
     @Test
-    fun `getPickerScreenState - default when launchable`() = runTest {
-        setLaunchable(true)
+    fun `lockScreenState - hidden when camera disabled by admin`() =
+        testScope.runTest {
+            setLaunchable(isCameraDisabledByAdmin = true)
 
-        assertThat(underTest.getPickerScreenState())
-            .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
-    }
+            val lockScreenState = collectLastValue(underTest.lockScreenState)
+
+            assertThat(lockScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+        }
 
     @Test
-    fun `getPickerScreenState - unavailable when not launchable`() = runTest {
-        setLaunchable(false)
+    fun `getPickerScreenState - default when launchable`() =
+        testScope.runTest {
+            setLaunchable()
 
-        assertThat(underTest.getPickerScreenState())
-            .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
-    }
+            assertThat(underTest.getPickerScreenState())
+                .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
+        }
 
-    private fun setLaunchable(isLaunchable: Boolean) {
+    @Test
+    fun `getPickerScreenState - unavailable when app not installed on device`() =
+        testScope.runTest {
+            setLaunchable(isVideoCameraAppInstalled = false)
+
+            assertThat(underTest.getPickerScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+        }
+
+    @Test
+    fun `getPickerScreenState - unavailable when camera disabled by admin`() =
+        testScope.runTest {
+            setLaunchable(isCameraDisabledByAdmin = true)
+
+            assertThat(underTest.getPickerScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+        }
+
+    private fun setLaunchable(
+        isVideoCameraAppInstalled: Boolean = true,
+        isCameraDisabledByAdmin: Boolean = false,
+    ) {
         whenever(
                 activityIntentHelper.getTargetActivityInfo(
                     any(),
@@ -105,11 +144,13 @@
                 )
             )
             .thenReturn(
-                if (isLaunchable) {
+                if (isVideoCameraAppInstalled) {
                     mock()
                 } else {
                     null
                 }
             )
+        whenever(devicePolicyManager.getCameraDisabled(null, userTracker.userId))
+            .thenReturn(isCameraDisabledByAdmin)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index 997198e..a72634b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -61,7 +61,6 @@
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -69,6 +68,8 @@
 import org.mockito.Mock
 import org.mockito.Mockito.floatThat
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
@@ -107,7 +108,6 @@
     @Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
     @Captor
     lateinit var configListener: ArgumentCaptor<ConfigurationController.ConfigurationListener>
-    @Captor lateinit var newConfig: ArgumentCaptor<Configuration>
     @Captor lateinit var visualStabilityCallback: ArgumentCaptor<OnReorderingAllowedListener>
     @Captor lateinit var keyguardCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
 
@@ -150,7 +150,6 @@
         MediaPlayerData.clear()
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testPlayerOrdering() {
         // Test values: key, data, last active time
@@ -327,7 +326,6 @@
         }
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testOrderWithSmartspace_prioritized() {
         testPlayerOrdering()
@@ -335,7 +333,7 @@
         // If smartspace is prioritized
         MediaPlayerData.addMediaRecommendation(
             SMARTSPACE_KEY,
-            EMPTY_SMARTSPACE_MEDIA_DATA,
+            EMPTY_SMARTSPACE_MEDIA_DATA.copy(isActive = true),
             panel,
             true,
             clock
@@ -345,7 +343,6 @@
         assertTrue(MediaPlayerData.playerKeys().elementAt(2).isSsMediaRec)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testOrderWithSmartspace_prioritized_updatingVisibleMediaPlayers() {
         testPlayerOrdering()
@@ -362,7 +359,6 @@
         assertTrue(MediaPlayerData.visiblePlayerKeys().elementAt(2).isSsMediaRec)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testOrderWithSmartspace_notPrioritized() {
         testPlayerOrdering()
@@ -370,7 +366,7 @@
         // If smartspace is not prioritized
         MediaPlayerData.addMediaRecommendation(
             SMARTSPACE_KEY,
-            EMPTY_SMARTSPACE_MEDIA_DATA,
+            EMPTY_SMARTSPACE_MEDIA_DATA.copy(isActive = true),
             panel,
             false,
             clock
@@ -381,7 +377,6 @@
         assertTrue(MediaPlayerData.playerKeys().elementAt(idx).isSsMediaRec)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testPlayingExistingMediaPlayerFromCarousel_visibleMediaPlayersNotUpdated() {
         testPlayerOrdering()
@@ -419,7 +414,6 @@
         )
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testSwipeDismiss_logged() {
         mediaCarouselController.mediaCarouselScrollHandler.dismissCallback.invoke()
@@ -427,7 +421,6 @@
         verify(logger).logSwipeDismiss()
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testSettingsButton_logged() {
         mediaCarouselController.settingsButton.callOnClick()
@@ -435,18 +428,16 @@
         verify(logger).logCarouselSettings()
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testLocationChangeQs_logged() {
         mediaCarouselController.onDesiredLocationChanged(
-            MediaHierarchyManager.LOCATION_QS,
+            LOCATION_QS,
             mediaHostState,
             animate = false
         )
-        verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_QS)
+        verify(logger).logCarouselPosition(LOCATION_QS)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testLocationChangeQqs_logged() {
         mediaCarouselController.onDesiredLocationChanged(
@@ -457,7 +448,6 @@
         verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_QQS)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testLocationChangeLockscreen_logged() {
         mediaCarouselController.onDesiredLocationChanged(
@@ -468,7 +458,6 @@
         verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_LOCKSCREEN)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testLocationChangeDream_logged() {
         mediaCarouselController.onDesiredLocationChanged(
@@ -479,7 +468,6 @@
         verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_DREAM_OVERLAY)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testRecommendationRemoved_logged() {
         val packageName = "smartspace package"
@@ -493,7 +481,6 @@
         verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!))
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testMediaLoaded_ScrollToActivePlayer() {
         listener.value.onMediaDataLoaded(
@@ -551,7 +538,6 @@
         )
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testMediaLoadedFromRecommendationCard_ScrollToActivePlayer() {
         listener.value.onSmartspaceMediaDataLoaded(
@@ -595,7 +581,6 @@
         assertEquals(playerIndex, 0)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testRecommendationRemovedWhileNotVisible_updateHostVisibility() {
         var result = false
@@ -607,7 +592,6 @@
         assertEquals(true, result)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testRecommendationRemovedWhileVisible_thenReorders_updateHostVisibility() {
         var result = false
@@ -621,7 +605,6 @@
         assertEquals(true, result)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testGetCurrentVisibleMediaContentIntent() {
         val clickIntent1 = mock(PendingIntent::class.java)
@@ -668,7 +651,6 @@
         assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent2)
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testSetCurrentState_UpdatePageIndicatorAlphaWhenSquish() {
         val delta = 0.0001F
@@ -690,7 +672,6 @@
         verify(pageIndicator).alpha = floatThat { abs(it - 1.0F) < delta }
     }
 
-    @Ignore("b/253229241")
     @Test
     fun testOnConfigChanged_playersAreAddedBack() {
         listener.value.onMediaDataLoaded(
@@ -716,7 +697,7 @@
 
         val playersSize = MediaPlayerData.players().size
 
-        configListener.value.onConfigChanged(capture(newConfig))
+        configListener.value.onConfigChanged(Configuration())
 
         assertEquals(playersSize, MediaPlayerData.players().size)
         assertEquals(
@@ -796,4 +777,59 @@
 
             job.cancel()
         }
+
+    @Test
+    fun testInvisibleToUserAndExpanded_playersNotListening() {
+        // Add players to carousel.
+        testPlayerOrdering()
+
+        // Make the carousel visible to user in expanded layout.
+        mediaCarouselController.currentlyExpanded = true
+        mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = true
+
+        // panel is the player for each MediaPlayerData.
+        // Verify that seekbar listening attribute in media control panel is set to true.
+        verify(panel, times(MediaPlayerData.players().size)).listening = true
+
+        // Make the carousel invisible to user.
+        mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = false
+
+        // panel is the player for each MediaPlayerData.
+        // Verify that seekbar listening attribute in media control panel is set to false.
+        verify(panel, times(MediaPlayerData.players().size)).listening = false
+    }
+
+    @Test
+    fun testVisibleToUserAndExpanded_playersListening() {
+        // Add players to carousel.
+        testPlayerOrdering()
+
+        // Make the carousel visible to user in expanded layout.
+        mediaCarouselController.currentlyExpanded = true
+        mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = true
+
+        // panel is the player for each MediaPlayerData.
+        // Verify that seekbar listening attribute in media control panel is set to true.
+        verify(panel, times(MediaPlayerData.players().size)).listening = true
+    }
+
+    @Test
+    fun testUMOCollapsed_playersNotListening() {
+        // Add players to carousel.
+        testPlayerOrdering()
+
+        // Make the carousel in collapsed layout.
+        mediaCarouselController.currentlyExpanded = false
+
+        // panel is the player for each MediaPlayerData.
+        // Verify that seekbar listening attribute in media control panel is set to false.
+        verify(panel, times(MediaPlayerData.players().size)).listening = false
+
+        // Make the carousel visible to user.
+        reset(panel)
+        mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = true
+
+        // Verify that seekbar listening attribute in media control panel is set to false.
+        verify(panel, times(MediaPlayerData.players().size)).listening = false
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
new file mode 100644
index 0000000..d828e51
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2023 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.screenshot.appclips;
+
+import static android.app.Activity.RESULT_OK;
+
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
+import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.ResultReceiver;
+import android.testing.AndroidTestingRunner;
+import android.widget.ImageView;
+
+import androidx.lifecycle.MutableLiveData;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.intercepting.SingleActivityFactory;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.screenshot.AppClipsActivity;
+import com.android.systemui.screenshot.AppClipsTrampolineActivity;
+import com.android.systemui.screenshot.AppClipsViewModel;
+import com.android.systemui.settings.UserTracker;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.BiConsumer;
+
+
+@RunWith(AndroidTestingRunner.class)
+public final class AppClipsActivityTest extends SysuiTestCase {
+
+    private static final int TEST_UID = 42;
+    private static final int TEST_USER_ID = 43;
+    private static final Bitmap TEST_BITMAP = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
+    private static final String TEST_URI_STRING = "www.test-uri.com";
+    private static final Uri TEST_URI = Uri.parse(TEST_URI_STRING);
+    private static final BiConsumer<Integer, Bundle> FAKE_CONSUMER = (unUsed1, unUsed2) -> {};
+    private static final String TEST_CALLING_PACKAGE = "test-calling-package";
+
+    @Mock
+    private AppClipsViewModel.Factory mViewModelFactory;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private UserTracker mUserTracker;
+    @Mock
+    private UiEventLogger mUiEventLogger;
+    @Mock
+    private AppClipsViewModel mViewModel;
+
+    private MutableLiveData<Bitmap> mScreenshotLiveData;
+    private MutableLiveData<Uri> mResultLiveData;
+    private AppClipsActivity mActivity;
+
+    // Using the deprecated ActivityTestRule and SingleActivityFactory to help with injecting mocks.
+    private final SingleActivityFactory<AppClipsActivityTestable> mFactory =
+            new SingleActivityFactory<>(AppClipsActivityTestable.class) {
+                @Override
+                protected AppClipsActivityTestable create(Intent unUsed) {
+                    return new AppClipsActivityTestable(mViewModelFactory, mPackageManager,
+                            mUserTracker, mUiEventLogger);
+                }
+            };
+
+    @Rule
+    public final ActivityTestRule<AppClipsActivityTestable> mActivityRule =
+            new ActivityTestRule<>(mFactory, false, false);
+
+    @Before
+    public void setUp() throws PackageManager.NameNotFoundException {
+        MockitoAnnotations.initMocks(this);
+
+        mScreenshotLiveData = new MutableLiveData<>();
+        mResultLiveData = new MutableLiveData<>();
+        MutableLiveData<Integer> errorLiveData = new MutableLiveData<>();
+
+        when(mViewModelFactory.create(any(Class.class))).thenReturn(mViewModel);
+        when(mViewModel.getScreenshot()).thenReturn(mScreenshotLiveData);
+        when(mViewModel.getResultLiveData()).thenReturn(mResultLiveData);
+        when(mViewModel.getErrorLiveData()).thenReturn(errorLiveData);
+        when(mUserTracker.getUserId()).thenReturn(TEST_USER_ID);
+
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.uid = TEST_UID;
+        when(mPackageManager.getApplicationInfoAsUser(eq(TEST_CALLING_PACKAGE),
+                any(ApplicationInfoFlags.class), eq(TEST_USER_ID))).thenReturn(applicationInfo);
+
+        doAnswer(invocation -> {
+            runOnMainThread(() -> mScreenshotLiveData.setValue(TEST_BITMAP));
+            return null;
+        }).when(mViewModel).performScreenshot();
+        doAnswer(invocation -> {
+            runOnMainThread(() -> mResultLiveData.setValue(TEST_URI));
+            return null;
+        }).when(mViewModel).saveScreenshotThenFinish(any(Drawable.class), any(Rect.class));
+    }
+
+    @After
+    public void tearDown() {
+        mActivityRule.finishActivity();
+    }
+
+    @Test
+    public void appClipsLaunched_screenshotDisplayed() {
+        launchActivity();
+
+        assertThat(((ImageView) mActivity.findViewById(R.id.preview)).getDrawable()).isNotNull();
+    }
+
+    @Test
+    public void screenshotDisplayed_userConsented_screenshotExportedSuccessfully() {
+        ResultReceiver resultReceiver = createResultReceiver((resultCode, data) -> {
+            assertThat(resultCode).isEqualTo(RESULT_OK);
+            assertThat(
+                    data.getParcelable(AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI, Uri.class))
+                    .isEqualTo(TEST_URI);
+            assertThat(data.getInt(Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE))
+                    .isEqualTo(Intent.CAPTURE_CONTENT_FOR_NOTE_SUCCESS);
+        });
+
+        launchActivity(resultReceiver);
+        runOnMainThread(() -> mActivity.findViewById(R.id.save).performClick());
+        waitForIdleSync();
+
+        assertThat(mActivity.isFinishing()).isTrue();
+        verify(mUiEventLogger).log(SCREENSHOT_FOR_NOTE_ACCEPTED, TEST_UID, TEST_CALLING_PACKAGE);
+    }
+
+    @Test
+    public void screenshotDisplayed_userDeclined() {
+        ResultReceiver resultReceiver = createResultReceiver((resultCode, data) -> {
+            assertThat(resultCode).isEqualTo(RESULT_OK);
+            assertThat(data.getInt(Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE))
+                    .isEqualTo(Intent.CAPTURE_CONTENT_FOR_NOTE_USER_CANCELED);
+            assertThat(data.keySet().contains(AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI))
+                    .isFalse();
+        });
+
+        launchActivity(resultReceiver);
+        runOnMainThread(() -> mActivity.findViewById(R.id.cancel).performClick());
+        waitForIdleSync();
+
+        assertThat(mActivity.isFinishing()).isTrue();
+        verify(mUiEventLogger).log(SCREENSHOT_FOR_NOTE_CANCELLED, TEST_UID, TEST_CALLING_PACKAGE);
+    }
+
+    private void launchActivity() {
+        launchActivity(createResultReceiver(FAKE_CONSUMER));
+    }
+
+    private void launchActivity(ResultReceiver resultReceiver) {
+        Intent intent = new Intent()
+                .putExtra(AppClipsTrampolineActivity.EXTRA_RESULT_RECEIVER, resultReceiver)
+                .putExtra(AppClipsTrampolineActivity.EXTRA_CALLING_PACKAGE_NAME,
+                        TEST_CALLING_PACKAGE);
+
+        mActivity = mActivityRule.launchActivity(intent);
+        waitForIdleSync();
+    }
+
+    private ResultReceiver createResultReceiver(
+            BiConsumer<Integer, Bundle> resultReceiverConsumer) {
+        ResultReceiver testReceiver = new ResultReceiver(mContext.getMainThreadHandler()) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                resultReceiverConsumer.accept(resultCode, resultData);
+            }
+        };
+
+        Parcel parcel = Parcel.obtain();
+        testReceiver.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        testReceiver  = ResultReceiver.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+        return testReceiver;
+    }
+
+    private void runOnMainThread(Runnable runnable) {
+        mContext.getMainExecutor().execute(runnable);
+    }
+
+    public static class AppClipsActivityTestable extends AppClipsActivity {
+
+        public AppClipsActivityTestable(AppClipsViewModel.Factory viewModelFactory,
+                PackageManager packageManager,
+                UserTracker userTracker,
+                UiEventLogger uiEventLogger) {
+            super(viewModelFactory, packageManager, userTracker, uiEventLogger);
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
index 6e8f5fe..ab321f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperServiceTest.java
@@ -76,7 +76,7 @@
     }
 
     @Test
-    public void bubblesPresent_screenshotFailed_ShouldReturnNull() throws RemoteException {
+    public void bubblesPresent_screenshotFailed_shouldReturnNull() throws RemoteException {
         when(mBubblesOptional.isEmpty()).thenReturn(false);
         when(mBubblesOptional.get()).thenReturn(mBubbles);
         when(mBubbles.getScreenshotExcludingBubble(DEFAULT_DISPLAY)).thenReturn(mScreenshotSync);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
index 295d127..e40c49b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
@@ -25,23 +25,25 @@
 import static android.content.Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE;
 
 import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.ACTION_FINISH_FROM_TRAMPOLINE;
 import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.PERMISSION_SELF;
 
 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.eq;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Bundle;
@@ -51,12 +53,15 @@
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.intercepting.SingleActivityFactory;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.notetask.NoteTaskController;
 import com.android.systemui.screenshot.AppClipsTrampolineActivity;
+import com.android.systemui.screenshot.ScreenshotEvent;
+import com.android.systemui.settings.UserTracker;
 import com.android.wm.shell.bubbles.Bubbles;
 
 import org.junit.After;
@@ -74,7 +79,9 @@
 
     private static final String TEST_URI_STRING = "www.test-uri.com";
     private static final Uri TEST_URI = Uri.parse(TEST_URI_STRING);
-    private static final int TIME_OUT = 5000;
+    private static final int TEST_UID = 42;
+    private static final int TEST_USER_ID = 43;
+    private static final String TEST_CALLING_PACKAGE = "test-calling-package";
 
     @Mock
     private DevicePolicyManager mDevicePolicyManager;
@@ -86,6 +93,12 @@
     private Bubbles mBubbles;
     @Mock
     private NoteTaskController mNoteTaskController;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private UserTracker mUserTracker;
+    @Mock
+    private UiEventLogger mUiEventLogger;
     @Main
     private Handler mMainHandler;
 
@@ -96,7 +109,8 @@
                 @Override
                 protected AppClipsTrampolineActivityTestable create(Intent unUsed) {
                     return new AppClipsTrampolineActivityTestable(mDevicePolicyManager,
-                            mFeatureFlags, mOptionalBubbles, mNoteTaskController, mMainHandler);
+                            mFeatureFlags, mOptionalBubbles, mNoteTaskController, mPackageManager,
+                            mUserTracker, mUiEventLogger, mMainHandler);
                 }
             };
 
@@ -104,41 +118,36 @@
     public final ActivityTestRule<AppClipsTrampolineActivityTestable> mActivityRule =
             new ActivityTestRule<>(mFactory, false, false);
 
-    private Context mContext;
     private Intent mActivityIntent;
     private ComponentName mExpectedComponentName;
-    private Intent mKillAppClipsActivityBroadcast;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mContext = getContext();
         mMainHandler = mContext.getMainThreadHandler();
 
         mActivityIntent = new Intent(mContext, AppClipsTrampolineActivityTestable.class);
         mExpectedComponentName = ComponentName.unflattenFromString(
                 mContext.getString(
                         R.string.config_screenshotAppClipsActivityComponent));
-        mKillAppClipsActivityBroadcast = new Intent(ACTION_FINISH_FROM_TRAMPOLINE)
-                .setComponent(mExpectedComponentName)
-                .setPackage(mExpectedComponentName.getPackageName());
     }
 
     @After
     public void tearDown() {
-        mContext.sendBroadcast(mKillAppClipsActivityBroadcast, PERMISSION_SELF);
         mActivityRule.finishActivity();
     }
 
     @Test
-    public void configComponentName_shouldResolve() {
+    public void appClipsActivityConfig_shouldBeConfigured() {
         // Verify component name is setup - has package and class name.
         assertThat(mExpectedComponentName).isNotNull();
         assertThat(mExpectedComponentName.getPackageName()).isNotEmpty();
         assertThat(mExpectedComponentName.getClassName()).isNotEmpty();
+    }
 
-        // Verify an intent when launched with above component resolves to the same component to
-        // confirm that component from above is available in framework.
+    @Test
+    public void configComponentName_shouldResolve() {
+        // Verify an intent when launched with configured component resolves to activity.
         Intent appClipsActivityIntent = new Intent();
         appClipsActivityIntent.setComponent(mExpectedComponentName);
         ResolveInfo resolveInfo = getContext().getPackageManager().resolveActivity(
@@ -205,7 +214,8 @@
     }
 
     @Test
-    public void startAppClipsActivity_userCanceled_shouldReturnUserCanceled() {
+    public void startAppClipsActivity_userCanceled_shouldReturnUserCanceled()
+            throws NameNotFoundException {
         mockToSatisfyAllPrerequisites();
 
         AppClipsTrampolineActivityTestable activity = mActivityRule.launchActivity(mActivityIntent);
@@ -224,7 +234,8 @@
     }
 
     @Test
-    public void startAppClipsActivity_shouldReturnSuccess() {
+    public void startAppClipsActivity_shouldReturnSuccess()
+            throws NameNotFoundException {
         mockToSatisfyAllPrerequisites();
 
         AppClipsTrampolineActivityTestable activity = mActivityRule.launchActivity(mActivityIntent);
@@ -243,12 +254,31 @@
         assertThat(actualResult.getResultData().getData()).isEqualTo(TEST_URI);
     }
 
-    private void mockToSatisfyAllPrerequisites() {
+    @Test
+    public void startAppClipsActivity_shouldLogUiEvent()
+            throws NameNotFoundException {
+        mockToSatisfyAllPrerequisites();
+
+        mActivityRule.launchActivity(mActivityIntent);
+        waitForIdleSync();
+
+        verify(mUiEventLogger).log(ScreenshotEvent.SCREENSHOT_FOR_NOTE_TRIGGERED, TEST_UID,
+                TEST_CALLING_PACKAGE);
+    }
+
+    private void mockToSatisfyAllPrerequisites() throws NameNotFoundException {
         when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true);
         when(mOptionalBubbles.isEmpty()).thenReturn(false);
         when(mOptionalBubbles.get()).thenReturn(mBubbles);
         when(mBubbles.isAppBubbleTaskId(anyInt())).thenReturn(true);
         when(mDevicePolicyManager.getScreenCaptureDisabled(eq(null))).thenReturn(false);
+        when(mUserTracker.getUserId()).thenReturn(TEST_USER_ID);
+
+        ApplicationInfo testApplicationInfo = new ApplicationInfo();
+        testApplicationInfo.uid = TEST_UID;
+        when(mPackageManager.getApplicationInfoAsUser(eq(TEST_CALLING_PACKAGE),
+                any(ApplicationInfoFlags.class),
+                eq(TEST_USER_ID))).thenReturn(testApplicationInfo);
     }
 
     public static final class AppClipsTrampolineActivityTestable extends
@@ -258,8 +288,22 @@
                 FeatureFlags flags,
                 Optional<Bubbles> optionalBubbles,
                 NoteTaskController noteTaskController,
+                PackageManager packageManager,
+                UserTracker userTracker,
+                UiEventLogger uiEventLogger,
                 @Main Handler mainHandler) {
-            super(devicePolicyManager, flags, optionalBubbles, noteTaskController, mainHandler);
+            super(devicePolicyManager, flags, optionalBubbles, noteTaskController, packageManager,
+                    userTracker, uiEventLogger, mainHandler);
+        }
+
+        @Override
+        public String getCallingPackage() {
+            return TEST_CALLING_PACKAGE;
+        }
+
+        @Override
+        public void startActivity(Intent unUsed) {
+            // Ignore this intent to avoid App Clips screenshot editing activity from starting.
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
index 9eccbb6..aa1636d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
@@ -249,6 +249,21 @@
     }
 
     @Test
+    public void addCallback_preCondition_noConditions_reportAllConditionsMet() {
+        final Monitor
+                monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(mCondition1)));
+        final Monitor.Callback callback = mock(
+                Monitor.Callback.class);
+
+        monitor.addSubscription(new Monitor.Subscription.Builder(callback).build());
+        mExecutor.runAllReady();
+        verify(callback, never()).onConditionsChanged(true);
+        mCondition1.fakeUpdateCondition(true);
+        mExecutor.runAllReady();
+        verify(callback).onConditionsChanged(true);
+    }
+
+    @Test
     public void removeCallback_noFailureOnDoubleRemove() {
         final Condition condition = mock(
                 Condition.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 673e559..3853b99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -896,21 +896,31 @@
 
         // Subscription 1
         private const val SUB_1_ID = 1
+        private val GROUP_1 = ParcelUuid(UUID.randomUUID())
         private val SUB_1 =
             mock<SubscriptionInfo>().also {
                 whenever(it.subscriptionId).thenReturn(SUB_1_ID)
-                whenever(it.groupUuid).thenReturn(ParcelUuid(UUID.randomUUID()))
+                whenever(it.groupUuid).thenReturn(GROUP_1)
             }
-        private val MODEL_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
+        private val MODEL_1 =
+            SubscriptionModel(
+                subscriptionId = SUB_1_ID,
+                groupUuid = GROUP_1,
+            )
 
         // Subscription 2
         private const val SUB_2_ID = 2
+        private val GROUP_2 = ParcelUuid(UUID.randomUUID())
         private val SUB_2 =
             mock<SubscriptionInfo>().also {
                 whenever(it.subscriptionId).thenReturn(SUB_2_ID)
-                whenever(it.groupUuid).thenReturn(ParcelUuid(UUID.randomUUID()))
+                whenever(it.groupUuid).thenReturn(GROUP_2)
             }
-        private val MODEL_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
+        private val MODEL_2 =
+            SubscriptionModel(
+                subscriptionId = SUB_2_ID,
+                groupUuid = GROUP_2,
+            )
 
         // Subs 3 and 4 are considered to be in the same group ------------------------------------
         private val GROUP_ID_3_4 = ParcelUuid(UUID.randomUUID())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index f8a9783..bbca001 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
 
+import android.os.ParcelUuid
 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
 import androidx.test.filters.SmallTest
 import com.android.settingslib.mobile.MobileMappings
@@ -34,6 +35,7 @@
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.util.UUID
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -104,6 +106,21 @@
             job.cancel()
         }
 
+    // Based on the logic from the old pipeline, we'll never filter subs when there are more than 2
+    @Test
+    fun filteredSubscriptions_moreThanTwo_doesNotFilter() =
+        testScope.runTest {
+            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
+            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
+
+            var latest: List<SubscriptionModel>? = null
+            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
+
+            job.cancel()
+        }
+
     @Test
     fun filteredSubscriptions_nonOpportunistic_updatesWithMultipleSubs() =
         testScope.runTest {
@@ -118,10 +135,50 @@
         }
 
     @Test
-    fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_3() =
+    fun filteredSubscriptions_opportunistic_differentGroups_doesNotFilter() =
         testScope.runTest {
             connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
             connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
+
+            var latest: List<SubscriptionModel>? = null
+            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(listOf(SUB_3_OPP, SUB_4_OPP))
+
+            job.cancel()
+        }
+
+    @Test
+    fun filteredSubscriptions_opportunistic_nonGrouped_doesNotFilter() =
+        testScope.runTest {
+            val (sub1, sub2) =
+                createSubscriptionPair(
+                    subscriptionIds = Pair(SUB_1_ID, SUB_2_ID),
+                    opportunistic = Pair(true, true),
+                    grouped = false,
+                )
+            connectionsRepository.setSubscriptions(listOf(sub1, sub2))
+            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
+
+            var latest: List<SubscriptionModel>? = null
+            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isEqualTo(listOf(sub1, sub2))
+
+            job.cancel()
+        }
+
+    @Test
+    fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_3() =
+        testScope.runTest {
+            val (sub3, sub4) =
+                createSubscriptionPair(
+                    subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
+                    opportunistic = Pair(true, true),
+                    grouped = true,
+                )
+            connectionsRepository.setSubscriptions(listOf(sub3, sub4))
+            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(false)
 
@@ -129,15 +186,21 @@
             val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
 
             // Filtered subscriptions should show the active one when the config is false
-            assertThat(latest).isEqualTo(listOf(SUB_3_OPP))
+            assertThat(latest).isEqualTo(listOf(sub3))
 
             job.cancel()
         }
 
     @Test
-    fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_4() =
+    fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_4() =
         testScope.runTest {
-            connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
+            val (sub3, sub4) =
+                createSubscriptionPair(
+                    subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
+                    opportunistic = Pair(true, true),
+                    grouped = true,
+                )
+            connectionsRepository.setSubscriptions(listOf(sub3, sub4))
             connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(false)
@@ -146,15 +209,21 @@
             val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
 
             // Filtered subscriptions should show the active one when the config is false
-            assertThat(latest).isEqualTo(listOf(SUB_4_OPP))
+            assertThat(latest).isEqualTo(listOf(sub4))
 
             job.cancel()
         }
 
     @Test
-    fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_active_1() =
+    fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_active_1() =
         testScope.runTest {
-            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
+            val (sub1, sub3) =
+                createSubscriptionPair(
+                    subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
+                    opportunistic = Pair(false, true),
+                    grouped = true,
+                )
+            connectionsRepository.setSubscriptions(listOf(sub1, sub3))
             connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(true)
@@ -164,15 +233,21 @@
 
             // Filtered subscriptions should show the primary (non-opportunistic) if the config is
             // true
-            assertThat(latest).isEqualTo(listOf(SUB_1))
+            assertThat(latest).isEqualTo(listOf(sub1))
 
             job.cancel()
         }
 
     @Test
-    fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_nonActive_1() =
+    fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_nonActive_1() =
         testScope.runTest {
-            connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
+            val (sub1, sub3) =
+                createSubscriptionPair(
+                    subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
+                    opportunistic = Pair(false, true),
+                    grouped = true,
+                )
+            connectionsRepository.setSubscriptions(listOf(sub1, sub3))
             connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(true)
@@ -182,7 +257,7 @@
 
             // Filtered subscriptions should show the primary (non-opportunistic) if the config is
             // true
-            assertThat(latest).isEqualTo(listOf(SUB_1))
+            assertThat(latest).isEqualTo(listOf(sub1))
 
             job.cancel()
         }
@@ -642,6 +717,33 @@
             job.cancel()
         }
 
+    /**
+     * Convenience method for creating a pair of subscriptions to test the filteredSubscriptions
+     * flow.
+     */
+    private fun createSubscriptionPair(
+        subscriptionIds: Pair<Int, Int>,
+        opportunistic: Pair<Boolean, Boolean> = Pair(false, false),
+        grouped: Boolean = false,
+    ): Pair<SubscriptionModel, SubscriptionModel> {
+        val groupUuid = if (grouped) ParcelUuid(UUID.randomUUID()) else null
+        val sub1 =
+            SubscriptionModel(
+                subscriptionId = subscriptionIds.first,
+                isOpportunistic = opportunistic.first,
+                groupUuid = groupUuid,
+            )
+
+        val sub2 =
+            SubscriptionModel(
+                subscriptionId = subscriptionIds.second,
+                isOpportunistic = opportunistic.second,
+                groupUuid = groupUuid,
+            )
+
+        return Pair(sub1, sub2)
+    }
+
     companion object {
         private val tableLogBuffer =
             TableLogBuffer(8, "MobileIconsInteractorTest", FakeSystemClock())
@@ -655,11 +757,21 @@
         private val CONNECTION_2 = FakeMobileConnectionRepository(SUB_2_ID, tableLogBuffer)
 
         private const val SUB_3_ID = 3
-        private val SUB_3_OPP = SubscriptionModel(subscriptionId = SUB_3_ID, isOpportunistic = true)
+        private val SUB_3_OPP =
+            SubscriptionModel(
+                subscriptionId = SUB_3_ID,
+                isOpportunistic = true,
+                groupUuid = ParcelUuid(UUID.randomUUID()),
+            )
         private val CONNECTION_3 = FakeMobileConnectionRepository(SUB_3_ID, tableLogBuffer)
 
         private const val SUB_4_ID = 4
-        private val SUB_4_OPP = SubscriptionModel(subscriptionId = SUB_4_ID, isOpportunistic = true)
+        private val SUB_4_OPP =
+            SubscriptionModel(
+                subscriptionId = SUB_4_ID,
+                isOpportunistic = true,
+                groupUuid = ParcelUuid(UUID.randomUUID()),
+            )
         private val CONNECTION_4 = FakeMobileConnectionRepository(SUB_4_ID, tableLogBuffer)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
index 6d7941f6..48a2e09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
@@ -19,10 +19,16 @@
 import android.bluetooth.BluetoothDevice
 import android.hardware.BatteryState
 import android.hardware.input.InputManager
+import android.hardware.input.InputSettings
 import android.os.Handler
 import android.testing.AndroidTestingRunner
 import android.view.InputDevice
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.dx.mockito.inline.extended.ExtendedMockito.times
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FeatureFlags
@@ -30,18 +36,17 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import java.util.concurrent.Executor
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.inOrder
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.Mockito.verifyZeroInteractions
 import org.mockito.MockitoAnnotations
+import org.mockito.quality.Strictness
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
@@ -67,11 +72,17 @@
 
     @Mock lateinit var otherStylusBatteryCallback: StylusManager.StylusBatteryCallback
 
+    private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var stylusManager: StylusManager
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        mockitoSession =
+            mockitoSession()
+                .mockStatic(InputSettings::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking()
 
         whenever(handler.post(any())).thenAnswer {
             (it.arguments[0] as Runnable).run()
@@ -103,19 +114,25 @@
         whenever(inputManager.getInputDevice(STYLUS_DEVICE_ID)).thenReturn(stylusDevice)
         whenever(inputManager.getInputDevice(BT_STYLUS_DEVICE_ID)).thenReturn(btStylusDevice)
         whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(STYLUS_DEVICE_ID))
-        whenever(inputManager.isStylusEverUsed(mContext)).thenReturn(false)
 
         whenever(bluetoothAdapter.getRemoteDevice(STYLUS_BT_ADDRESS)).thenReturn(bluetoothDevice)
         whenever(bluetoothDevice.address).thenReturn(STYLUS_BT_ADDRESS)
 
         whenever(featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)).thenReturn(true)
 
+        whenever(InputSettings.isStylusEverUsed(mContext)).thenReturn(false)
+
         stylusManager.startListener()
         stylusManager.registerCallback(stylusCallback)
         stylusManager.registerBatteryCallback(stylusBatteryCallback)
         clearInvocations(inputManager)
     }
 
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
+    }
+
     @Test
     fun startListener_hasNotStarted_registersInputDeviceListener() {
         stylusManager =
@@ -209,8 +226,7 @@
     @Test
     fun onInputDeviceAdded_btStylus_firstUsed_setsFlag() {
         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
-
-        verify(inputManager, times(1)).setStylusEverUsed(mContext, true)
+        verify({ InputSettings.setStylusEverUsed(mContext, true) }, times(1))
     }
 
     @Test
@@ -484,7 +500,7 @@
 
         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
 
-        verify(inputManager).setStylusEverUsed(mContext, true)
+        verify({ InputSettings.setStylusEverUsed(mContext, true) }, times(1))
     }
 
     @Test
@@ -539,12 +555,13 @@
 
     @Test
     fun onBatteryStateChanged_batteryPresent_stylusUsed_doesNotUpdateEverUsedFlag() {
-        whenever(inputManager.isStylusEverUsed(mContext)).thenReturn(true)
+        whenever(InputSettings.isStylusEverUsed(mContext)).thenReturn(true)
+
         whenever(batteryState.isPresent).thenReturn(true)
 
         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
 
-        verify(inputManager, never()).setStylusEverUsed(mContext, true)
+        verify({ InputSettings.setStylusEverUsed(mContext, true) }, never())
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
index 5ef62c1..b367a60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
@@ -60,6 +60,11 @@
             mCallback = callback;
         }
 
+        public FakeConditionalCoreStartable(Monitor monitor, Callback callback) {
+            super(monitor);
+            mCallback = callback;
+        }
+
         @Override
         protected void onStart() {
             mCallback.onStart();
@@ -122,6 +127,31 @@
         verify(mMonitor).removeSubscription(mSubscriptionToken);
     }
 
+    @Test
+    public void testOnStartCallbackWithNoConditions() {
+        final CoreStartable coreStartable =
+                new FakeConditionalCoreStartable(mMonitor,
+                        mCallback);
+
+        when(mMonitor.addSubscription(any())).thenReturn(mSubscriptionToken);
+        coreStartable.start();
+
+        final ArgumentCaptor<Monitor.Subscription> subscriptionCaptor = ArgumentCaptor.forClass(
+                Monitor.Subscription.class);
+        verify(mMonitor).addSubscription(subscriptionCaptor.capture());
+
+        final Monitor.Subscription subscription = subscriptionCaptor.getValue();
+
+        assertThat(subscription.getConditions()).isEmpty();
+
+        verify(mCallback, never()).onStart();
+
+        subscription.getCallback().onConditionsChanged(true);
+
+        verify(mCallback).onStart();
+        verify(mMonitor).removeSubscription(mSubscriptionToken);
+    }
+
 
     /**
      * Verifies that {@link ConditionalCoreStartable#bootCompleted()} ()} is predicated on
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/condition/SelfExecutingMonitor.java b/packages/SystemUI/tests/utils/src/com/android/systemui/condition/SelfExecutingMonitor.java
new file mode 100644
index 0000000..7ee05d0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/condition/SelfExecutingMonitor.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 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.condition;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+/**
+ * {@link SelfExecutingMonitor} creates a monitor that independently executes its logic through
+ * a {@link FakeExecutor}, which is ran at when a subscription is added and removed.
+ */
+public class SelfExecutingMonitor extends Monitor {
+    private final FakeExecutor mExecutor;
+
+    /**
+     * Default constructor that allows specifying the FakeExecutor to use.
+     */
+    public SelfExecutingMonitor(FakeExecutor executor) {
+        super(executor);
+        mExecutor = executor;
+    }
+
+    @Override
+    public Subscription.Token addSubscription(@NonNull Subscription subscription) {
+        final Subscription.Token result = super.addSubscription(subscription);
+        mExecutor.runAllReady();
+        return result;
+    }
+
+    @Override
+    public void removeSubscription(@NonNull Subscription.Token token) {
+        super.removeSubscription(token);
+        mExecutor.runNextReady();
+    }
+
+    /**
+     * Creates a {@link SelfExecutingMonitor} with a self-managed {@link FakeExecutor}. Use only
+     * for cases where condition state only will be set at when a subscription is added.
+     */
+    public static SelfExecutingMonitor createInstance() {
+        final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+        final FakeExecutor mExecutor = new FakeExecutor(mFakeSystemClock);
+        return new SelfExecutingMonitor(mExecutor);
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 669be1a..4d5baaf 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -487,6 +487,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BiFunction;
+import java.util.function.Consumer;
 import java.util.function.Supplier;
 
 public class ActivityManagerService extends IActivityManager.Stub
@@ -6452,6 +6453,44 @@
         return entry == null ? null : entry.second;
     }
 
+    private static class GetBackgroundStartPrivilegesFunctor implements Consumer<ProcessRecord> {
+        private BackgroundStartPrivileges mBackgroundStartPrivileges =
+                BackgroundStartPrivileges.NONE;
+        private int mUid;
+
+        void prepare(int uid) {
+            mUid = uid;
+            mBackgroundStartPrivileges = BackgroundStartPrivileges.NONE;
+        }
+
+        @NonNull
+        BackgroundStartPrivileges getResult() {
+            return mBackgroundStartPrivileges;
+        }
+
+        public void accept(ProcessRecord pr) {
+            if (pr.uid == mUid) {
+                mBackgroundStartPrivileges =
+                        mBackgroundStartPrivileges.merge(pr.getBackgroundStartPrivileges());
+            }
+        }
+    }
+
+    private final GetBackgroundStartPrivilegesFunctor mGetBackgroundStartPrivilegesFunctor =
+            new GetBackgroundStartPrivilegesFunctor();
+
+    /**
+     * Returns the current complete {@link BackgroundStartPrivileges} of the UID.
+     */
+    @NonNull
+    private BackgroundStartPrivileges getBackgroundStartPrivileges(int uid) {
+        synchronized (mProcLock) {
+            mGetBackgroundStartPrivilegesFunctor.prepare(uid);
+            mProcessList.forEachLruProcessesLOSP(false, mGetBackgroundStartPrivilegesFunctor);
+            return mGetBackgroundStartPrivilegesFunctor.getResult();
+        }
+    }
+
     /**
      * @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on
      * the allowlist
@@ -17851,6 +17890,11 @@
             return mConstants.mFlagBackgroundActivityStartsEnabled;
         }
 
+        @Override
+        public BackgroundStartPrivileges getBackgroundStartPrivileges(int uid) {
+            return ActivityManagerService.this.getBackgroundStartPrivileges(uid);
+        }
+
         public void reportCurKeyguardUsageEvent(boolean keyguardShowing) {
             ActivityManagerService.this.reportGlobalUsageEvent(keyguardShowing
                     ? UsageEvents.Event.KEYGUARD_SHOWN
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index a707202..fa3f684 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -20,6 +20,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.MY_PID;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ApplicationExitInfo;
@@ -341,6 +342,24 @@
     private boolean mInFullBackup;
 
     /**
+     * A set of tokens that currently contribute to this process being temporarily allowed
+     * to start certain components (eg. activities or foreground services) even if it's not
+     * in the foreground.
+     */
+    @GuardedBy("mBackgroundStartPrivileges")
+    private final ArrayMap<Binder, BackgroundStartPrivileges> mBackgroundStartPrivileges =
+            new ArrayMap<>();
+
+    /**
+     * The merged BackgroundStartPrivileges based on what's in {@link #mBackgroundStartPrivileges}.
+     * This is lazily generated using {@link #getBackgroundStartPrivileges()}.
+     */
+    @Nullable
+    @GuardedBy("mBackgroundStartPrivileges")
+    private BackgroundStartPrivileges mBackgroundStartPrivilegesMerged =
+            BackgroundStartPrivileges.NONE;
+
+    /**
      * Controller for driving the process state on the window manager side.
      */
     private final WindowProcessController mWindowProcessController;
@@ -1325,11 +1344,50 @@
         Objects.requireNonNull(entity);
         mWindowProcessController.addOrUpdateBackgroundStartPrivileges(entity,
                 backgroundStartPrivileges);
+        setBackgroundStartPrivileges(entity, backgroundStartPrivileges);
     }
 
     void removeBackgroundStartPrivileges(Binder entity) {
         Objects.requireNonNull(entity);
         mWindowProcessController.removeBackgroundStartPrivileges(entity);
+        setBackgroundStartPrivileges(entity, null);
+    }
+
+    @NonNull
+    BackgroundStartPrivileges getBackgroundStartPrivileges() {
+        synchronized (mBackgroundStartPrivileges) {
+            if (mBackgroundStartPrivilegesMerged == null) {
+                // Lazily generate the merged version when it's actually needed.
+                mBackgroundStartPrivilegesMerged = BackgroundStartPrivileges.NONE;
+                for (int i = mBackgroundStartPrivileges.size() - 1; i >= 0; --i) {
+                    mBackgroundStartPrivilegesMerged =
+                            mBackgroundStartPrivilegesMerged.merge(
+                                    mBackgroundStartPrivileges.valueAt(i));
+                }
+            }
+            return mBackgroundStartPrivilegesMerged;
+        }
+    }
+
+    private void setBackgroundStartPrivileges(@NonNull Binder entity,
+            @Nullable BackgroundStartPrivileges backgroundStartPrivileges) {
+        synchronized (mBackgroundStartPrivileges) {
+            final boolean changed;
+            if (backgroundStartPrivileges == null) {
+                changed = mBackgroundStartPrivileges.remove(entity) != null;
+            } else {
+                final BackgroundStartPrivileges oldBsp =
+                        mBackgroundStartPrivileges.put(entity, backgroundStartPrivileges);
+                // BackgroundStartPrivileges tries to reuse the same object and avoid creating
+                // additional objects. For now, we just compare the reference to see if something
+                // has changed.
+                // TODO: actually compare the individual values to see if there's a change
+                changed = backgroundStartPrivileges != oldBsp;
+            }
+            if (changed) {
+                mBackgroundStartPrivilegesMerged = null;
+            }
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 172aa20..b2b22a0 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -50,6 +50,7 @@
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
 import android.hardware.input.InputSensorInfo;
+import android.hardware.input.InputSettings;
 import android.hardware.input.KeyboardLayout;
 import android.hardware.input.TouchCalibration;
 import android.hardware.lights.Light;
@@ -1309,7 +1310,7 @@
             throw new SecurityException("Requires SET_POINTER_SPEED permission");
         }
 
-        if (speed < InputManager.MIN_POINTER_SPEED || speed > InputManager.MAX_POINTER_SPEED) {
+        if (speed < InputSettings.MIN_POINTER_SPEED || speed > InputSettings.MAX_POINTER_SPEED) {
             throw new IllegalArgumentException("speed out of range");
         }
 
@@ -1317,8 +1318,8 @@
     }
 
     private void setPointerSpeedUnchecked(int speed) {
-        speed = Math.min(Math.max(speed, InputManager.MIN_POINTER_SPEED),
-                InputManager.MAX_POINTER_SPEED);
+        speed = Math.min(Math.max(speed, InputSettings.MIN_POINTER_SPEED),
+                InputSettings.MAX_POINTER_SPEED);
         mNative.setPointerSpeed(speed);
     }
 
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index a113d01..5b21669 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -21,7 +21,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.ContentObserver;
-import android.hardware.input.InputManager;
+import android.hardware.input.InputSettings;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.UserHandle;
@@ -32,7 +32,6 @@
 import android.view.ViewConfiguration;
 
 import java.util.Map;
-import java.util.Objects;
 import java.util.function.Consumer;
 
 /** Observes settings changes and propagates them to the native side. */
@@ -111,9 +110,9 @@
 
     private int getPointerSpeedValue(String settingName) {
         int speed = Settings.System.getIntForUser(mContext.getContentResolver(),
-                settingName, InputManager.DEFAULT_POINTER_SPEED, UserHandle.USER_CURRENT);
-        return Math.min(Math.max(speed, InputManager.MIN_POINTER_SPEED),
-                InputManager.MAX_POINTER_SPEED);
+                settingName, InputSettings.DEFAULT_POINTER_SPEED, UserHandle.USER_CURRENT);
+        return Math.min(Math.max(speed, InputSettings.MIN_POINTER_SPEED),
+                InputSettings.MAX_POINTER_SPEED);
     }
 
     private void updateMousePointerSpeed() {
@@ -170,8 +169,7 @@
     }
 
     private void updateMaximumObscuringOpacityForTouch() {
-        InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
-        final float opacity = im.getMaximumObscuringOpacityForTouch();
+        final float opacity = InputSettings.getMaximumObscuringOpacityForTouch(mContext);
         if (opacity < 0 || opacity > 1) {
             Log.e(TAG, "Invalid maximum obscuring opacity " + opacity
                     + ", it should be >= 0 and <= 1, rejecting update.");
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 3799193..84ea077 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -320,12 +320,26 @@
                 MOBILE_NETWORK_SETTINGS);
     }
 
+    /** Call intent with tel scheme */
+    private static final DefaultCrossProfileIntentFilter CALL =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_CALL)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addDataScheme("tel")
+                    .build();
+
     /**
      * Returns default telephony related intent filters for managed profile.
      */
     public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileTelephonyFilters() {
         return Arrays.asList(
                 DIAL_DATA,
+                DIAL_MIME,
+                DIAL_RAW,
+                CALL,
                 SMS_MMS);
     }
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7f1679a..449801d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3501,8 +3501,8 @@
     }
 
     @Override
-    public void onKeyguardOccludedChangedLw(boolean occluded) {
-        if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
+    public void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition) {
+        if (mKeyguardDelegate != null && waitAppTransition) {
             mPendingKeyguardOccluded = occluded;
             mKeyguardOccludedChanged = true;
         } else {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index ca5fa5f..3c4dbf2 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -164,9 +164,10 @@
 
     /**
      * Called when the Keyguard occluded state changed.
+     *
      * @param occluded Whether Keyguard is currently occluded or not.
      */
-    void onKeyguardOccludedChangedLw(boolean occluded);
+    void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition);
 
     /**
      * @param notify {@code true} if the status change should be immediately notified via
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index 5fd70a8..fc659c5 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -104,10 +104,10 @@
     }
 
     /**
-     * Returns {@code true} if location time zone detection should run all the time on supported
-     * devices, even when the user has not enabled it explicitly in settings. Enabled for internal
-     * testing only. See {@link #isGeoDetectionExecutionEnabled()} and {@link #getDetectionMode()}
-     * for details.
+     * Returns {@code true} if location time zone detection should run when auto time zone detection
+     * is enabled on supported devices, even when the user has not enabled the algorithm explicitly
+     * in settings. Enabled for internal testing only. See {@link #isGeoDetectionExecutionEnabled()}
+     * and {@link #getDetectionMode()} for details.
      */
     boolean getGeoDetectionRunInBackgroundEnabledSetting() {
         return mGeoDetectionRunInBackgroundEnabled;
@@ -219,6 +219,7 @@
     private boolean getGeoDetectionRunInBackgroundEnabledBehavior() {
         return isGeoDetectionSupported()
                 && getLocationEnabledSetting()
+                && getAutoDetectionEnabledSetting()
                 && getGeoDetectionRunInBackgroundEnabledSetting();
     }
 
@@ -433,9 +434,9 @@
         }
 
         /**
-         * Sets whether location time zone detection should run all the time on supported devices,
-         * even when the user has not enabled it explicitly in settings. Enabled for internal
-         * testing only.
+         * Sets whether location time zone detection should run when auto time zone detection is
+         * enabled on supported devices, even when the user has not enabled the algorithm explicitly
+         * in settings. Enabled for internal testing only.
          */
         public Builder setGeoDetectionRunInBackgroundEnabled(boolean enabled) {
             mGeoDetectionRunInBackgroundEnabled = enabled;
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index e9badef..32b3ccf 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -401,8 +401,10 @@
             return;
         }
 
-        mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY));
-        if (isKeyguardLocked(displayId)) {
+        final boolean waitAppTransition = isKeyguardLocked(displayId);
+        mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY),
+                waitAppTransition);
+        if (waitAppTransition) {
             mService.deferWindowLayout();
             try {
                 mRootWindowContainer.getDefaultDisplay()
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a63f9a3..ae7dc1e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -193,6 +193,7 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.InputManager;
+import android.hardware.input.InputSettings;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -744,7 +745,7 @@
     private final DisplayHashController mDisplayHashController;
 
     volatile float mMaximumObscuringOpacityForTouch =
-            InputManager.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH;
+            InputSettings.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH;
 
     @VisibleForTesting
     final WindowContextListenerController mWindowContextListenerController =
@@ -886,11 +887,11 @@
             ContentResolver resolver = mContext.getContentResolver();
             mMaximumObscuringOpacityForTouch = Settings.Global.getFloat(resolver,
                     Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
-                    InputManager.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
+                    InputSettings.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
             if (mMaximumObscuringOpacityForTouch < 0.0f
                     || mMaximumObscuringOpacityForTouch > 1.0f) {
                 mMaximumObscuringOpacityForTouch =
-                        InputManager.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH;
+                        InputSettings.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH;
             }
         }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 8f16737..5013fb0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -33,6 +33,7 @@
 import android.app.admin.PolicyUpdatesReceiver;
 import android.app.admin.PolicyValue;
 import android.app.admin.TargetUser;
+import android.app.admin.UserRestrictionPolicyKey;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -614,6 +615,47 @@
         }
     }
 
+    /**
+     * Returns all user restriction policies set by the given admin.
+     *
+     * <p>Pass in {@link UserHandle#USER_ALL} for {@code userId} to get global restrictions set by
+     * the admin
+     */
+    @NonNull
+    Set<UserRestrictionPolicyKey> getUserRestrictionPolicyKeysForAdmin(
+            @NonNull EnforcingAdmin admin,
+            int userId) {
+        Objects.requireNonNull(admin);
+        synchronized (mLock) {
+            if (userId == UserHandle.USER_ALL) {
+                return getUserRestrictionPolicyKeysForAdminLocked(mGlobalPolicies, admin);
+            }
+            if (!mLocalPolicies.contains(userId)) {
+                return Set.of();
+            }
+            return getUserRestrictionPolicyKeysForAdminLocked(mLocalPolicies.get(userId), admin);
+        }
+    }
+
+    private Set<UserRestrictionPolicyKey> getUserRestrictionPolicyKeysForAdminLocked(
+            Map<PolicyKey, PolicyState<?>> policies,
+            EnforcingAdmin admin) {
+        Set<UserRestrictionPolicyKey> keys = new HashSet<>();
+        for (PolicyKey key : policies.keySet()) {
+            if (!policies.get(key).getPolicyDefinition().isUserRestrictionPolicy()) {
+                continue;
+            }
+            // User restriction policies are always boolean
+            PolicyValue<Boolean> value = (PolicyValue<Boolean>) policies.get(key)
+                    .getPoliciesSetByAdmins().get(admin);
+            if (value == null || !value.getValue()) {
+                continue;
+            }
+            keys.add((UserRestrictionPolicyKey) key);
+        }
+        return keys;
+    }
+
     private <V> boolean hasLocalPolicyLocked(PolicyDefinition<V> policyDefinition, int userId) {
         if (policyDefinition.isGlobalOnlyPolicy()) {
             return false;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c847aa2..61ee0e7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -264,6 +264,7 @@
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
 import android.app.admin.UnsafeStateException;
+import android.app.admin.UserRestrictionPolicyKey;
 import android.app.admin.WifiSsidPolicy;
 import android.app.backup.IBackupManager;
 import android.app.compat.CompatChanges;
@@ -1351,7 +1352,7 @@
             if (shouldMigrateToDevicePolicyEngine()) {
                 migratePoliciesToDevicePolicyEngine();
             }
-            if (isDevicePolicyEngineFlagEnabled()) {
+            if (isDevicePolicyEngineEnabled()) {
                 mDevicePolicyEngine.handlePackageChanged(packageName, userHandle);
             }
             // Persist updates if the removed package was an admin or delegate.
@@ -2044,7 +2045,7 @@
         mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
 
         mDeviceManagementResourcesProvider.load();
-        if (isDevicePolicyEngineFlagEnabled()) {
+        if (isDevicePolicyEngineEnabled()) {
             mDevicePolicyEngine.load();
         }
 
@@ -2506,8 +2507,7 @@
                 if (profileOwner == null || !mUserManager.isManagedProfile(userId)) {
                     continue;
                 }
-                maybeSetDefaultRestrictionsForAdminLocked(userId, profileOwner,
-                        UserRestrictionsUtils.getDefaultEnabledForManagedProfiles());
+                maybeSetDefaultRestrictionsForAdminLocked(userId, profileOwner);
                 ensureUnknownSourcesRestrictionForProfileOwnerLocked(
                         userId, profileOwner, false /* newOwner */);
             }
@@ -2522,9 +2522,20 @@
             ActiveAdmin profileOwner, boolean newOwner) {
         if (newOwner || mInjector.settingsSecureGetIntForUser(
                 Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId) != 0) {
-            profileOwner.ensureUserRestrictions().putBoolean(
-                    UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
-            saveUserRestrictionsLocked(userId);
+            if (isDevicePolicyEngineEnabled()) {
+                mDevicePolicyEngine.setLocalPolicy(
+                        PolicyDefinition.getPolicyDefinitionForUserRestriction(
+                                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES),
+                        EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                                profileOwner.info.getComponent(),
+                                profileOwner.getUserHandle().getIdentifier()),
+                        new BooleanPolicyValue(true),
+                        userId);
+            } else {
+                profileOwner.ensureUserRestrictions().putBoolean(
+                        UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
+                saveUserRestrictionsLocked(userId);
+            }
             mInjector.settingsSecurePutIntForUser(
                     Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId);
         }
@@ -2533,11 +2544,28 @@
     /**
      * Apply default restrictions that haven't been applied to a given admin yet.
      */
-    private void maybeSetDefaultRestrictionsForAdminLocked(
-            int userId, ActiveAdmin admin, Set<String> defaultRestrictions) {
+    private void maybeSetDefaultRestrictionsForAdminLocked(int userId, ActiveAdmin admin) {
+        Set<String> defaultRestrictions =
+                UserRestrictionsUtils.getDefaultEnabledForManagedProfiles();
         if (defaultRestrictions.equals(admin.defaultEnabledRestrictionsAlreadySet)) {
             return; // The same set of default restrictions has been already applied.
         }
+        if (isDevicePolicyEngineEnabled()) {
+            for (String restriction : defaultRestrictions) {
+                mDevicePolicyEngine.setLocalPolicy(
+                        PolicyDefinition.getPolicyDefinitionForUserRestriction(restriction),
+                        EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                                admin.info.getComponent(),
+                                admin.getUserHandle().getIdentifier()),
+                        new BooleanPolicyValue(true),
+                        userId);
+            }
+            admin.defaultEnabledRestrictionsAlreadySet.addAll(defaultRestrictions);
+            Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " +
+                    defaultRestrictions);
+            return;
+        }
+
         Slogf.i(LOG_TAG, "New user restrictions need to be set by default for user " + userId);
 
         if (VERBOSE_LOG) {
@@ -3388,7 +3416,7 @@
         updateNetworkPreferenceForUser(userId, preferentialNetworkServiceConfigs);
 
         startOwnerService(userId, "start-user");
-        if (isDevicePolicyEngineFlagEnabled()) {
+        if (isDevicePolicyEngineEnabled()) {
             mDevicePolicyEngine.handleStartUser(userId);
         }
     }
@@ -3413,7 +3441,7 @@
 
     void handleUnlockUser(int userId) {
         startOwnerService(userId, "unlock-user");
-        if (isDevicePolicyEngineFlagEnabled()) {
+        if (isDevicePolicyEngineEnabled()) {
             mDevicePolicyEngine.handleUnlockUser(userId);
         }
     }
@@ -3425,7 +3453,7 @@
     void handleStopUser(int userId) {
         updateNetworkPreferenceForUser(userId, List.of(PreferentialNetworkServiceConfig.DEFAULT));
         mDeviceAdminServiceController.stopServicesForUser(userId, /* actionForLog= */ "stop-user");
-        if (isDevicePolicyEngineFlagEnabled()) {
+        if (isDevicePolicyEngineEnabled()) {
             mDevicePolicyEngine.handleStopUser(userId);
         }
     }
@@ -9358,8 +9386,7 @@
 
             mInjector.binderWithCleanCallingIdentity(() -> {
                 if (mUserManager.isManagedProfile(userHandle)) {
-                    maybeSetDefaultRestrictionsForAdminLocked(userHandle, admin,
-                            UserRestrictionsUtils.getDefaultEnabledForManagedProfiles());
+                    maybeSetDefaultRestrictionsForAdminLocked(userHandle, admin);
                     ensureUnknownSourcesRestrictionForProfileOwnerLocked(userHandle, admin,
                             true /* newOwner */);
                 }
@@ -11503,7 +11530,7 @@
 
         final int userId = user.id;
 
-        if (isDevicePolicyEngineFlagEnabled()) {
+        if (isDevicePolicyEngineEnabled()) {
             mDevicePolicyEngine.handleUserCreated(user);
         }
 
@@ -12074,7 +12101,8 @@
     }
 
     @Override
-    public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner,
+    public void setUserRestriction(
+            ComponentName who, String callerPackage, String key, boolean enabledFromThisOwner,
             boolean parent) {
         Objects.requireNonNull(who, "ComponentName is null");
 
@@ -12083,18 +12111,13 @@
         if (!UserRestrictionsUtils.isValidRestriction(key)) {
             return;
         }
-
         if (parent) {
             Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
         } else {
             Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
         }
-
         int userId = caller.getUserId();
         synchronized (getLockObject()) {
-            final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
-                    getProfileOwnerOrDeviceOwnerLocked(userId), parent);
-
             if (isDefaultDeviceOwner(caller)) {
                 if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) {
                     throw new SecurityException("Device owner cannot set user restriction " + key);
@@ -12112,27 +12135,119 @@
                 boolean profileOwnerCanChangeOnItself = !parent
                         && UserRestrictionsUtils.canProfileOwnerChange(
                                 key, userId == getMainUserId());
-                boolean orgOwnedProfileOwnerCanChangesGlobally = parent
+                boolean orgOwnedProfileOwnerCanChangeGlobally = parent
                         && isProfileOwnerOfOrganizationOwnedDevice(caller)
                         && UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange(
                         key);
 
-                if (!profileOwnerCanChangeOnItself && !orgOwnedProfileOwnerCanChangesGlobally) {
+                if (!profileOwnerCanChangeOnItself && !orgOwnedProfileOwnerCanChangeGlobally) {
                     throw new SecurityException("Profile owner cannot set user restriction " + key);
                 }
             }
-            checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
-
-            // Save the restriction to ActiveAdmin.
-            final Bundle restrictions = activeAdmin.ensureUserRestrictions();
-            if (enabledFromThisOwner) {
-                restrictions.putBoolean(key, true);
-            } else {
-                restrictions.remove(key);
-            }
-            saveUserRestrictionsLocked(userId);
         }
-        final int eventId = enabledFromThisOwner
+        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
+
+        if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
+            EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                    who, caller.getUserId());
+            PolicyDefinition<Boolean> policyDefinition =
+                    PolicyDefinition.getPolicyDefinitionForUserRestriction(key);
+            if (enabledFromThisOwner) {
+                mDevicePolicyEngine.setLocalPolicy(
+                        policyDefinition,
+                        admin,
+                        new BooleanPolicyValue(true),
+                        parent ? getProfileParentId(userId) : userId);
+            } else {
+                // Remove any local and global policy that was set by the admin
+                if (!policyDefinition.isLocalOnlyPolicy()) {
+                    mDevicePolicyEngine.removeGlobalPolicy(
+                            policyDefinition,
+                            admin);
+                }
+                if (!policyDefinition.isGlobalOnlyPolicy()) {
+                    mDevicePolicyEngine.removeLocalPolicy(
+                            policyDefinition,
+                            admin,
+                            userId);
+
+                    int parentUserId = getProfileParentId(userId);
+                    if (parentUserId != userId) {
+                        mDevicePolicyEngine.removeLocalPolicy(
+                                policyDefinition,
+                                admin,
+                                parentUserId);
+                    }
+                }
+            }
+        } else {
+            synchronized (getLockObject()) {
+                final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
+                        getProfileOwnerOrDeviceOwnerLocked(userId), parent);
+                // Save the restriction to ActiveAdmin.
+                final Bundle restrictions = activeAdmin.ensureUserRestrictions();
+                if (enabledFromThisOwner) {
+                    restrictions.putBoolean(key, true);
+                } else {
+                    restrictions.remove(key);
+                }
+                saveUserRestrictionsLocked(userId);
+            }
+        }
+        logUserRestrictionCall(key, enabledFromThisOwner, parent, caller);
+    }
+
+    @Override
+    public void setUserRestrictionGlobally(String callerPackage, String key) {
+        final CallerIdentity caller = getCallerIdentity(callerPackage);
+        // TODO: Replace with new permission checks, for now copying this over from
+        //  setUserRestriction
+        if (!UserRestrictionsUtils.isValidRestriction(key)) {
+            return;
+        }
+        Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+
+        int userHandle = caller.getUserId();
+        if (isDefaultDeviceOwner(caller)) {
+            if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) {
+                throw new SecurityException("Device owner cannot set user restriction " + key);
+            }
+        } else if (isFinancedDeviceOwner(caller)) {
+            if (!UserRestrictionsUtils.canFinancedDeviceOwnerChange(key)) {
+                throw new SecurityException("Cannot set user restriction " + key
+                        + " when managing a financed device");
+            }
+        } else {
+            boolean profileOwnerCanChangeOnItself =
+                    UserRestrictionsUtils.canProfileOwnerChange(key, userHandle == getMainUserId());
+            boolean orgOwnedProfileOwnerCanChangeGlobally =
+                    isProfileOwnerOfOrganizationOwnedDevice(caller)
+                    && UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange(
+                    key);
+
+            if (!profileOwnerCanChangeOnItself && !orgOwnedProfileOwnerCanChangeGlobally) {
+                throw new SecurityException("Profile owner cannot set user restriction " + key);
+            }
+        }
+        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
+
+        if (!useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
+            throw new IllegalStateException("One or more admins are not targeting Android 14.");
+        }
+        EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                caller.getPackageName(), caller.getUserId());
+
+        mDevicePolicyEngine.setGlobalPolicy(
+                PolicyDefinition.getPolicyDefinitionForUserRestriction(key),
+                admin,
+                new BooleanPolicyValue(true));
+
+        logUserRestrictionCall(key, /* enabled= */ true, /* parent= */ false, caller);
+    }
+
+    private void logUserRestrictionCall(
+            String key, boolean enabled, boolean parent, CallerIdentity caller) {
+        final int eventId = enabled
                 ? DevicePolicyEnums.ADD_USER_RESTRICTION
                 : DevicePolicyEnums.REMOVE_USER_RESTRICTION;
         DevicePolicyEventLogger
@@ -12141,14 +12256,18 @@
                 .setStrings(key, parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
                 .write();
         if (SecurityLog.isLoggingEnabled()) {
-            final int eventTag = enabledFromThisOwner
+            final int eventTag = enabled
                     ? SecurityLog.TAG_USER_RESTRICTION_ADDED
                     : SecurityLog.TAG_USER_RESTRICTION_REMOVED;
-            SecurityLog.writeEvent(eventTag, who.getPackageName(), userId, key);
+            SecurityLog.writeEvent(eventTag, caller.getPackageName(), caller.getUserId(), key);
         }
     }
 
     private void saveUserRestrictionsLocked(int userId) {
+        if (isDevicePolicyEngineEnabled()) {
+            // User restrictions are handled in the policy engine
+            return;
+        }
         saveSettingsLocked(userId);
         pushUserRestrictions(userId);
         sendChangedNotification(userId);
@@ -12210,7 +12329,7 @@
     }
 
     @Override
-    public Bundle getUserRestrictions(ComponentName who, boolean parent) {
+    public Bundle getUserRestrictions(ComponentName who, String callerPackage, boolean parent) {
         if (!mHasFeature) {
             return null;
         }
@@ -12222,14 +12341,59 @@
                 || isProfileOwner(caller)
                 || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
 
-        synchronized (getLockObject()) {
-            final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
-                    getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
-            return activeAdmin.userRestrictions;
+        if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
+            return getUserRestrictionsFromPolicyEngine(
+                    EnforcingAdmin.createEnterpriseEnforcingAdmin(who, caller.getUserId()),
+                    parent ? getProfileParentId(caller.getUserId()) : caller.getUserId());
+        } else {
+            synchronized (getLockObject()) {
+                final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
+                        getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
+                return activeAdmin.userRestrictions;
+            }
         }
     }
 
     @Override
+    public Bundle getUserRestrictionsGlobally(String callerPackage) {
+        if (!mHasFeature) {
+            return null;
+        }
+        final CallerIdentity caller = getCallerIdentity(callerPackage);
+        if (!useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
+            throw new IllegalStateException("One or more admins are not targeting Android 14.");
+        }
+        // TODO: Replace with new permission checks, for now copying this over from
+        //  setUserRestriction
+        Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+                || isFinancedDeviceOwner(caller)
+                || isProfileOwner(caller));
+
+        return getUserRestrictionsFromPolicyEngine(
+                EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                        caller.getPackageName(), caller.getUserId()),
+                UserHandle.USER_ALL);
+    }
+
+    /**
+     * Returns user restrictions set by the given admin for the provided {@code userId}.
+     *
+     * <p>Pass in {@link UserHandle#USER_ALL} for {@code userId} to get global restrictions set by
+     * the admin
+     */
+    private Bundle getUserRestrictionsFromPolicyEngine(EnforcingAdmin admin, int userId) {
+        Set<UserRestrictionPolicyKey> restrictionKeys =
+                mDevicePolicyEngine.getUserRestrictionPolicyKeysForAdmin(
+                        admin,
+                        userId);
+        Bundle restrictions = new Bundle();
+        for (UserRestrictionPolicyKey key : restrictionKeys) {
+            restrictions.putBoolean(key.getRestriction(), true);
+        }
+        return restrictions;
+    }
+
+    @Override
     public boolean setApplicationHidden(ComponentName who, String callerPackage, String packageName,
             boolean hidden, boolean parent) {
         final CallerIdentity caller = getCallerIdentity(who, callerPackage);
@@ -13639,7 +13803,8 @@
                             + " should be used instead.");
                 } else {
                     try {
-                        setUserRestriction(who, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+                        setUserRestriction(who, who.getPackageName(),
+                                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
                                 (Integer.parseInt(value) == 0) ? true : false, /* parent */ false);
                         DevicePolicyEventLogger
                                 .createEvent(DevicePolicyEnums.SET_SECURE_SETTING)
@@ -13696,7 +13861,8 @@
         checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_MASTER_VOLUME_MUTED);
 
         synchronized (getLockObject()) {
-            setUserRestriction(who, UserManager.DISALLOW_UNMUTE_DEVICE, on, /* parent */ false);
+            setUserRestriction(who, who.getPackageName(), UserManager.DISALLOW_UNMUTE_DEVICE, on,
+                    /* parent */ false);
             DevicePolicyEventLogger
                     .createEvent(DevicePolicyEnums.SET_MASTER_VOLUME_MUTED)
                     .setAdmin(who)
@@ -18308,6 +18474,15 @@
                 }
             });
         }
+        String[] appOpExemptions = new String[exemptions.length];
+        for (int i = 0; i < exemptions.length; i++) {
+            appOpExemptions[i] = APPLICATION_EXEMPTION_CONSTANTS_TO_APP_OPS.get(exemptions[i]);
+        }
+        DevicePolicyEventLogger
+            .createEvent(DevicePolicyEnums.SET_APPLICATION_EXEMPTIONS)
+            .setAdmin(caller.getPackageName())
+            .setStrings(packageName, appOpExemptions)
+            .write();
     }
 
     @Override
@@ -21294,10 +21469,14 @@
             }
             return true;
         } else {
-            return isDevicePolicyEngineFlagEnabled() && !hasDPCsNotSupportingCoexistence();
+            return isDevicePolicyEngineEnabled();
         }
     }
 
+    private boolean isDevicePolicyEngineEnabled() {
+        return isDevicePolicyEngineFlagEnabled() && !hasDPCsNotSupportingCoexistence();
+    }
+
     private boolean isDevicePolicyEngineFlagEnabled() {
         return DeviceConfig.getBoolean(
                 NAMESPACE_DEVICE_POLICY_MANAGER,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
index a303fde..7ed148b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
@@ -93,6 +93,15 @@
                 activeAdmin);
     }
 
+
+    static EnforcingAdmin createEnterpriseEnforcingAdmin(
+            @NonNull String packageName, int userId) {
+        Objects.requireNonNull(packageName);
+        return new EnforcingAdmin(
+                packageName, /* componentName= */ null, Set.of(DPC_AUTHORITY), userId,
+                /* activeAdmin= */ null);
+    }
+
     static EnforcingAdmin createDeviceAdminEnforcingAdmin(ComponentName componentName, int userId) {
         Objects.requireNonNull(componentName);
         return new EnforcingAdmin(
@@ -130,10 +139,9 @@
     }
 
     private EnforcingAdmin(
-            String packageName, ComponentName componentName, Set<String> authorities, int userId,
-            ActiveAdmin activeAdmin) {
+            String packageName, @Nullable ComponentName componentName, Set<String> authorities,
+            int userId, @Nullable ActiveAdmin activeAdmin) {
         Objects.requireNonNull(packageName);
-        Objects.requireNonNull(componentName);
         Objects.requireNonNull(authorities);
 
         // Role authorities should not be using this constructor
@@ -262,10 +270,12 @@
     @Override
     public int hashCode() {
         if (mIsRoleAuthority) {
-            // TODO(b/256854977): should we add UserId?
-            return Objects.hash(mPackageName);
+            return Objects.hash(mPackageName, mUserId);
         } else {
-            return Objects.hash(mComponentName, getAuthorities());
+            return Objects.hash(
+                    mComponentName == null ? mPackageName : mComponentName,
+                    mUserId,
+                    getAuthorities());
         }
     }
 
@@ -274,8 +284,10 @@
         serializer.attributeBoolean(/* namespace= */ null, ATTR_IS_ROLE, mIsRoleAuthority);
         serializer.attributeInt(/* namespace= */ null, ATTR_USER_ID, mUserId);
         if (!mIsRoleAuthority) {
-            serializer.attribute(
-                    /* namespace= */ null, ATTR_CLASS_NAME, mComponentName.getClassName());
+            if (mComponentName != null) {
+                serializer.attribute(
+                        /* namespace= */ null, ATTR_CLASS_NAME, mComponentName.getClassName());
+            }
             // Role authorities get recomputed on load so no need to save them.
             serializer.attribute(
                     /* namespace= */ null,
@@ -295,7 +307,8 @@
             return new EnforcingAdmin(packageName, userId, null);
         } else {
             String className = parser.getAttributeValue(/* namespace= */ null, ATTR_CLASS_NAME);
-            ComponentName componentName = new ComponentName(packageName, className);
+            ComponentName componentName = className == null
+                    ? null :  new ComponentName(packageName, className);
             Set<String> authorities = Set.of(authoritiesStr.split(ATTR_AUTHORITIES_SEPARATOR));
             return new EnforcingAdmin(packageName, componentName, authorities, userId, null);
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 21c9434..a08c2054 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -29,10 +29,12 @@
 import android.app.admin.PackagePolicyKey;
 import android.app.admin.PolicyKey;
 import android.app.admin.PolicyValue;
+import android.app.admin.UserRestrictionPolicyKey;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.IntentFilter;
 import android.os.Bundle;
+import android.os.UserManager;
 
 import com.android.internal.util.function.QuadFunction;
 import com.android.modules.utils.TypedXmlPullParser;
@@ -41,6 +43,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -67,6 +70,11 @@
     // global policy please add support.
     private static final int POLICY_FLAG_NON_COEXISTABLE_POLICY = 1 << 3;
 
+    // Add this flag to any policy that is a user restriction, the reason for this is that there
+    // are some special APIs to handle user restriction policies and this is the way we can identify
+    // them.
+    private static final int POLICY_FLAG_USER_RESTRICTION_POLICY = 1 << 4;
+
     private static final MostRestrictive<Boolean> FALSE_MORE_RESTRICTIVE = new MostRestrictive<>(
             List.of(new BooleanPolicyValue(false), new BooleanPolicyValue(true)));
 
@@ -238,21 +246,124 @@
             (Long value, Context context, Integer userId, PolicyKey policyKey) -> true,
             new LongPolicySerializer());
 
-    private static final Map<String, PolicyDefinition<?>> sPolicyDefinitions = Map.of(
-            DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY, AUTO_TIMEZONE,
-            DevicePolicyIdentifiers.PERMISSION_GRANT_POLICY, GENERIC_PERMISSION_GRANT,
-            DevicePolicyIdentifiers.LOCK_TASK_POLICY, LOCK_TASK,
-            DevicePolicyIdentifiers.USER_CONTROL_DISABLED_PACKAGES_POLICY,
-            USER_CONTROLLED_DISABLED_PACKAGES,
-            DevicePolicyIdentifiers.PERSISTENT_PREFERRED_ACTIVITY_POLICY,
-            GENERIC_PERSISTENT_PREFERRED_ACTIVITY,
-            DevicePolicyIdentifiers.PACKAGE_UNINSTALL_BLOCKED_POLICY,
-            GENERIC_PACKAGE_UNINSTALL_BLOCKED,
-            DevicePolicyIdentifiers.APPLICATION_RESTRICTIONS_POLICY,
-            GENERIC_APPLICATION_RESTRICTIONS,
-            DevicePolicyIdentifiers.RESET_PASSWORD_TOKEN_POLICY,
-            RESET_PASSWORD_TOKEN
-    );
+    private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>();
+    private static Map<String, Integer> USER_RESTRICTION_FLAGS = new HashMap<>();
+
+    static {
+        POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY, AUTO_TIMEZONE);
+        POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PERMISSION_GRANT_POLICY,
+                GENERIC_PERMISSION_GRANT);
+        POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.LOCK_TASK_POLICY, LOCK_TASK);
+        POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.USER_CONTROL_DISABLED_PACKAGES_POLICY,
+                USER_CONTROLLED_DISABLED_PACKAGES);
+        POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PERSISTENT_PREFERRED_ACTIVITY_POLICY,
+                GENERIC_PERSISTENT_PREFERRED_ACTIVITY);
+        POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PACKAGE_UNINSTALL_BLOCKED_POLICY,
+                GENERIC_PACKAGE_UNINSTALL_BLOCKED);
+        POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.APPLICATION_RESTRICTIONS_POLICY,
+                GENERIC_APPLICATION_RESTRICTIONS);
+        POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.RESET_PASSWORD_TOKEN_POLICY,
+                RESET_PASSWORD_TOKEN);
+
+        // User Restriction Policies
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MODIFY_ACCOUNTS, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_WIFI, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_CHANGE_WIFI_STATE, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_WIFI_TETHERING, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_GRANT_ADMIN, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_WIFI_DIRECT, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_ADD_WIFI_CONFIG, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_LOCALE, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_INSTALL_APPS, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_UNINSTALL_APPS, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SHARE_LOCATION, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_AIRPLANE_MODE, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_BRIGHTNESS, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_AMBIENT_DISPLAY, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY,
+                POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_BLUETOOTH, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_BLUETOOTH, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_BLUETOOTH_SHARING, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_USB_FILE_TRANSFER, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_CREDENTIALS, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_REMOVE_USER, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_DEBUGGING_FEATURES, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_VPN, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_LOCATION, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_DATE_TIME, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_CONFIG_TETHERING, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_NETWORK_RESET, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_FACTORY_RESET, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ADD_USER, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ADD_MANAGED_PROFILE, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ADD_CLONE_PROFILE, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.ENSURE_VERIFY_APPS, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_APPS_CONTROL, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_UNMUTE_MICROPHONE, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ADJUST_VOLUME, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_OUTGOING_CALLS, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SMS, /* flags= */ 0);
+        // TODO: check if its global only
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_FUN, /* flags= */ 0);
+        // TODO: check if its global only
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CREATE_WINDOWS, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE, /* flags= */ 0);
+        // TODO: check if its global only
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_OUTGOING_BEAM, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_WALLPAPER, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SET_WALLPAPER, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SAFE_BOOT, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_RECORD_AUDIO, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_RUN_IN_BACKGROUND, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CAMERA, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_UNMUTE_DEVICE, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_DATA_ROAMING, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SET_USER_ICON, /* flags= */ 0);
+            // TODO: double check flags
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_OEM_UNLOCK, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_UNIFIED_PASSWORD, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_AUTOFILL, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONTENT_CAPTURE, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONTENT_SUGGESTIONS, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_USER_SWITCH, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_PRINTING, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_CONFIG_PRIVATE_DNS, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MICROPHONE_TOGGLE, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CAMERA_TOGGLE, /* flags= */ 0);
+        // TODO: check if its global only
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_BIOMETRIC, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_CONFIG_DEFAULT_APPS, /* flags= */ 0);
+        USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_CELLULAR_2G, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+        USER_RESTRICTION_FLAGS.put(
+                UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, POLICY_FLAG_GLOBAL_ONLY_POLICY);
+
+        for (String key : USER_RESTRICTION_FLAGS.keySet()) {
+            createAndAddUserRestrictionPolicyDefinition(key, USER_RESTRICTION_FLAGS.get(key));
+        }
+    }
 
     private final PolicyKey mPolicyKey;
     private final ResolutionMechanism<V> mResolutionMechanism;
@@ -267,6 +378,17 @@
                 mPolicyEnforcerCallback, mPolicySerializer);
     }
 
+    static PolicyDefinition<Boolean> getPolicyDefinitionForUserRestriction(
+            @UserManager.UserRestrictionKey String restriction) {
+        String key = DevicePolicyIdentifiers.getIdentifierForUserRestriction(restriction);
+
+        if (!POLICY_DEFINITIONS.containsKey(key)) {
+            throw new IllegalArgumentException("Unsupported user restriction " + restriction);
+        }
+        // All user restrictions are of type boolean
+        return (PolicyDefinition<Boolean>) POLICY_DEFINITIONS.get(key);
+    }
+
     @NonNull
     PolicyKey getPolicyKey() {
         return mPolicyKey;
@@ -305,6 +427,10 @@
         return (mPolicyFlags & POLICY_FLAG_NON_COEXISTABLE_POLICY) != 0;
     }
 
+    boolean isUserRestrictionPolicy() {
+        return (mPolicyFlags & POLICY_FLAG_USER_RESTRICTION_POLICY) != 0;
+    }
+
     @Nullable
     PolicyValue<V> resolvePolicy(LinkedHashMap<EnforcingAdmin, PolicyValue<V>> adminsPolicy) {
         return mResolutionMechanism.resolve(adminsPolicy);
@@ -314,6 +440,21 @@
         return mPolicyEnforcerCallback.apply(value, context, userId, mPolicyKey);
     }
 
+    private static void createAndAddUserRestrictionPolicyDefinition(
+            String restriction, int flags) {
+        String identifier = DevicePolicyIdentifiers.getIdentifierForUserRestriction(restriction);
+        UserRestrictionPolicyKey key = new UserRestrictionPolicyKey(identifier, restriction);
+        flags |= POLICY_FLAG_USER_RESTRICTION_POLICY;
+        PolicyDefinition<Boolean> definition = new PolicyDefinition<>(
+                key,
+                TRUE_MORE_RESTRICTIVE,
+                flags,
+                PolicyEnforcerCallbacks::setUserRestriction,
+                new BooleanPolicySerializer());
+        POLICY_DEFINITIONS.put(key.getIdentifier(), definition);
+    }
+
+
     /**
      * Callers must ensure that {@code policyType} have implemented an appropriate
      * {@link Object#equals} implementation.
@@ -360,7 +501,7 @@
         // TODO: can we avoid casting?
         PolicyKey policyKey = readPolicyKeyFromXml(parser);
         PolicyDefinition<V> genericPolicyDefinition =
-                (PolicyDefinition<V>) sPolicyDefinitions.get(policyKey.getIdentifier());
+                (PolicyDefinition<V>) POLICY_DEFINITIONS.get(policyKey.getIdentifier());
         return genericPolicyDefinition.createPolicyDefinition(policyKey);
     }
 
@@ -369,7 +510,7 @@
         // TODO: can we avoid casting?
         PolicyKey policyKey = PolicyKey.readGenericPolicyKeyFromXml(parser);
         PolicyDefinition<PolicyValue<V>> genericPolicyDefinition =
-                (PolicyDefinition<PolicyValue<V>>) sPolicyDefinitions.get(
+                (PolicyDefinition<PolicyValue<V>>) POLICY_DEFINITIONS.get(
                         policyKey.getIdentifier());
         return genericPolicyDefinition.mPolicyKey.readFromXml(parser);
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 4ae7ca6..daa8a26 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -25,6 +25,7 @@
 import android.app.admin.PackagePermissionPolicyKey;
 import android.app.admin.PackagePolicyKey;
 import android.app.admin.PolicyKey;
+import android.app.admin.UserRestrictionPolicyKey;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.IntentFilter;
@@ -40,6 +41,7 @@
 import android.util.Slog;
 
 import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.utils.Slogf;
 
 import java.util.Collections;
@@ -202,4 +204,22 @@
             return true;
         }));
     }
+
+    static boolean setUserRestriction(
+            @Nullable Boolean enabled, @NonNull Context context, int userId,
+            @NonNull PolicyKey policyKey) {
+        return Boolean.TRUE.equals(Binder.withCleanCallingIdentity(() -> {
+            if (!(policyKey instanceof UserRestrictionPolicyKey)) {
+                throw new IllegalArgumentException("policyKey is not of type "
+                        + "UserRestrictionPolicyKey");
+            }
+            UserRestrictionPolicyKey parsedKey =
+                    (UserRestrictionPolicyKey) policyKey;
+            // TODO: call into new UserManager API when merged
+            UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
+//            userManager.setUserRestriction(
+//                    userId, parsedKey.getRestriction(), enabled != null && enabled);
+            return true;
+        }));
+    }
 }
diff --git a/services/searchui/java/com/android/server/searchui/SearchUiManagerService.java b/services/searchui/java/com/android/server/searchui/SearchUiManagerService.java
index 29ad537..c259701 100644
--- a/services/searchui/java/com/android/server/searchui/SearchUiManagerService.java
+++ b/services/searchui/java/com/android/server/searchui/SearchUiManagerService.java
@@ -140,12 +140,6 @@
         }
 
         @Override
-        public void requestEmptyQueryResultUpdate(@NonNull SearchSessionId sessionId) {
-            runForUserLocked("requestEmptyQueryResultUpdate", sessionId,
-                    (service) -> service.requestEmptyQueryResultUpdateLocked(sessionId));
-        }
-
-        @Override
         public void destroySearchSession(@NonNull SearchSessionId sessionId) {
             runForUserLocked("destroySearchSession", sessionId,
                     (service) -> service.onDestroyLocked(sessionId));
diff --git a/services/searchui/java/com/android/server/searchui/SearchUiPerUserService.java b/services/searchui/java/com/android/server/searchui/SearchUiPerUserService.java
index 0d70fff..dc150cf 100644
--- a/services/searchui/java/com/android/server/searchui/SearchUiPerUserService.java
+++ b/services/searchui/java/com/android/server/searchui/SearchUiPerUserService.java
@@ -181,16 +181,6 @@
     }
 
     /**
-     * Requests a new set of search targets for empty query result used for zero state.
-     */
-    @GuardedBy("mLock")
-    public void requestEmptyQueryResultUpdateLocked(@NonNull SearchSessionId sessionId) {
-        final SearchSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-        if (sessionInfo == null) return;
-        resolveService(sessionId, s->s.onRequestEmptyQueryResultUpdate(sessionId));
-    }
-
-    /**
      * Notifies the service of the end of an existing search session.
      */
     @GuardedBy("mLock")
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
index 3750aa0..2fbbf0d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
@@ -49,6 +49,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
+import android.app.BackgroundStartPrivileges;
 import android.app.IActivityManager;
 import android.app.job.JobInfo;
 import android.content.ComponentName;
@@ -63,6 +64,7 @@
 import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.SparseIntArray;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -178,6 +180,8 @@
         mGracePeriodObserver = mock(GracePeriodObserver.class);
         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+        doReturn(BackgroundStartPrivileges.NONE)
+                .when(mActivityManagerInternal).getBackgroundStartPrivileges(anyInt());
         mDefaultUserId = mNextUserId;
         createCurrentUser(true);
         mNextUserId = 10;
@@ -601,34 +605,75 @@
 
     @Test
     public void testHasImmediacyPrivilege() {
-        JobStatus job = createJob(mDefaultUserId * UserHandle.PER_USER_RANGE, 0);
+        final int uid = mDefaultUserId * UserHandle.PER_USER_RANGE;
+        JobStatus job = createJob(uid, 0);
         spyOn(job);
-        assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
+        doReturn(BackgroundStartPrivileges.NONE)
+                .when(mActivityManagerInternal).getBackgroundStartPrivileges(uid);
+
+        assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
 
         doReturn(false).when(job).shouldTreatAsExpeditedJob();
         doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
         job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
-        assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
+        assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
 
         doReturn(true).when(job).shouldTreatAsExpeditedJob();
         doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
         job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
-        assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
+        assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
 
         doReturn(false).when(job).shouldTreatAsExpeditedJob();
         doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
         job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
-        assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
+        assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
 
         doReturn(false).when(job).shouldTreatAsExpeditedJob();
         doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
         job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
-        assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
+        assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
 
         doReturn(true).when(job).shouldTreatAsExpeditedJob();
         doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
         job.lastEvaluatedBias = JobInfo.BIAS_TOP_APP;
-        assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job));
+        assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job,
+                new SparseIntArray()));
+
+        doReturn(BackgroundStartPrivileges.ALLOW_FGS)
+                .when(mActivityManagerInternal).getBackgroundStartPrivileges(uid);
+
+        doReturn(false).when(job).shouldTreatAsExpeditedJob();
+        doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
+        job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
+        assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
+
+        doReturn(true).when(job).shouldTreatAsExpeditedJob();
+        doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
+        job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
+        assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
+
+        doReturn(false).when(job).shouldTreatAsExpeditedJob();
+        doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
+        job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
+        assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
+
+        doReturn(BackgroundStartPrivileges.ALLOW_BAL)
+                .when(mActivityManagerInternal).getBackgroundStartPrivileges(uid);
+
+        doReturn(false).when(job).shouldTreatAsExpeditedJob();
+        doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
+        job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
+        assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
+
+        doReturn(true).when(job).shouldTreatAsExpeditedJob();
+        doReturn(false).when(job).shouldTreatAsUserInitiatedJob();
+        job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
+        assertFalse(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
+
+        doReturn(false).when(job).shouldTreatAsExpeditedJob();
+        doReturn(true).when(job).shouldTreatAsUserInitiatedJob();
+        job.lastEvaluatedBias = JobInfo.BIAS_DEFAULT;
+        assertTrue(mJobConcurrencyManager.hasImmediacyPrivilegeLocked(job, new SparseIntArray()));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index b5c5582..120ddf6 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -49,6 +49,7 @@
 import com.android.server.SystemService;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -156,6 +157,7 @@
 
     @SmallTest
     @Test
+    @Ignore("b/269249457")
     public void testCompMigrationAffiliated() throws Exception {
         prepareAdmin1AsDo();
         prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.R);
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 0d6bb8a..566c6b0 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -570,7 +570,7 @@
                     .setAutoDetectionEnabledSetting(false)
                     .build();
             assertFalse(config.getAutoDetectionEnabledBehavior());
-            assertTrue(config.isGeoDetectionExecutionEnabled());
+            assertFalse(config.isGeoDetectionExecutionEnabled());
             assertEquals(DETECTION_MODE_MANUAL, config.getDetectionMode());
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
index 7b1db95..aeb8ec8 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
@@ -24,6 +24,7 @@
 import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_UNKNOWN;
 
 import static com.android.server.timezonedetector.ConfigurationInternal.DETECTION_MODE_MANUAL;
+import static com.android.server.timezonedetector.ConfigurationInternal.DETECTION_MODE_TELEPHONY;
 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;
@@ -1385,6 +1386,37 @@
     }
 
     /**
+     * A controller-state-only test to prove that "run in background" doesn't enable the
+     * location-based time zone detection algorithm to run when auto detection is disabled.
+     */
+    @Test
+    public void geoDetectionRunInBackground_obeysAutoDetectionDisabled() {
+        LocationTimeZoneProviderController controller = new LocationTimeZoneProviderController(
+                mTestThreadingDomain, mTestMetricsLogger, mTestPrimaryLocationTimeZoneProvider,
+                mTestSecondaryLocationTimeZoneProvider, false /* recordStateChanges */);
+
+        // A configuration where the user has auto detection disabled.
+        ConfigurationInternal autoTimeZoneDisabledConfig =
+                new ConfigurationInternal.Builder(USER1_CONFIG_GEO_DETECTION_DISABLED)
+                        .setLocationEnabledSetting(true)
+                        .setAutoDetectionEnabledSetting(false)
+                        .setGeoDetectionEnabledSetting(true)
+                        .setGeoDetectionRunInBackgroundEnabled(true)
+                        .build();
+        assertEquals(DETECTION_MODE_MANUAL, autoTimeZoneDisabledConfig.getDetectionMode());
+        assertFalse(autoTimeZoneDisabledConfig.isGeoDetectionExecutionEnabled());
+
+        TestEnvironment testEnvironment = new TestEnvironment(
+                mTestThreadingDomain, controller, autoTimeZoneDisabledConfig);
+
+        // Initialize and check initial state.
+        controller.initialize(testEnvironment, mTestCallback);
+
+        assertControllerState(controller, STATE_STOPPED);
+        mTestMetricsLogger.assertStateChangesAndCommit(STATE_PROVIDERS_INITIALIZING, STATE_STOPPED);
+    }
+
+    /**
      * A controller-state-only test to prove that "run in background" configuration behaves as
      * intended. Provider states are well covered by other "enabled" tests.
      */
@@ -1398,7 +1430,7 @@
         ConfigurationInternal runInBackgroundDisabledConfig =
                 new ConfigurationInternal.Builder(USER1_CONFIG_GEO_DETECTION_DISABLED)
                         .setLocationEnabledSetting(true)
-                        .setAutoDetectionEnabledSetting(false)
+                        .setAutoDetectionEnabledSetting(true)
                         .setGeoDetectionEnabledSetting(false)
                         .setGeoDetectionRunInBackgroundEnabled(false)
                         .build();
@@ -1408,7 +1440,7 @@
                 new ConfigurationInternal.Builder(runInBackgroundDisabledConfig)
                         .setGeoDetectionRunInBackgroundEnabled(true)
                         .build();
-        assertEquals(DETECTION_MODE_MANUAL, runInBackgroundEnabledConfig.getDetectionMode());
+        assertEquals(DETECTION_MODE_TELEPHONY, runInBackgroundEnabledConfig.getDetectionMode());
         assertTrue(runInBackgroundEnabledConfig.isGeoDetectionExecutionEnabled());
 
         TestEnvironment testEnvironment = new TestEnvironment(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 3344bdb..1d0715a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -222,7 +222,7 @@
     }
 
     @Override
-    public void onKeyguardOccludedChangedLw(boolean occluded) {
+    public void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition) {
     }
 
     public void setSafeMode(boolean safeMode) {