Merge "Log BROADCAST_DELIVERY_EVENT_REPORTED only when delivery is attempted." into udc-dev
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 70bf353..4088a48 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -478,7 +478,6 @@
                         case Constants.KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS:
                         case Constants.KEY_RUNTIME_MIN_GUARANTEE_MS:
                         case Constants.KEY_RUNTIME_MIN_EJ_GUARANTEE_MS:
-                        case Constants.KEY_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS:
                         case Constants.KEY_RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS:
                         case Constants.KEY_RUNTIME_DATA_TRANSFER_LIMIT_MS:
                         case Constants.KEY_RUNTIME_MIN_USER_INITIATED_GUARANTEE_MS:
@@ -575,8 +574,6 @@
                 "runtime_free_quota_max_limit_ms";
         private static final String KEY_RUNTIME_MIN_GUARANTEE_MS = "runtime_min_guarantee_ms";
         private static final String KEY_RUNTIME_MIN_EJ_GUARANTEE_MS = "runtime_min_ej_guarantee_ms";
-        private static final String KEY_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS =
-                "runtime_min_high_priority_guarantee_ms";
         private static final String KEY_RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS =
                 "runtime_min_data_transfer_guarantee_ms";
         private static final String KEY_RUNTIME_DATA_TRANSFER_LIMIT_MS =
@@ -619,8 +616,6 @@
         public static final long DEFAULT_RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS;
         @VisibleForTesting
         public static final long DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS = 3 * MINUTE_IN_MILLIS;
-        @VisibleForTesting
-        static final long DEFAULT_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS = 5 * MINUTE_IN_MILLIS;
         public static final long DEFAULT_RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS =
                 DEFAULT_RUNTIME_MIN_GUARANTEE_MS;
         public static final long DEFAULT_RUNTIME_DATA_TRANSFER_LIMIT_MS =
@@ -744,12 +739,6 @@
         public long RUNTIME_MIN_EJ_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS;
 
         /**
-         * The minimum amount of time we try to guarantee high priority jobs will run for.
-         */
-        public long RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS =
-                DEFAULT_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS;
-
-        /**
          * The minimum amount of time we try to guarantee normal data transfer jobs will run for.
          */
         public long RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS =
@@ -895,7 +884,6 @@
                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
                     KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
                     KEY_RUNTIME_MIN_GUARANTEE_MS, KEY_RUNTIME_MIN_EJ_GUARANTEE_MS,
-                    KEY_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
                     KEY_RUNTIME_MIN_USER_INITIATED_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR,
                     KEY_RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS,
                     KEY_RUNTIME_DATA_TRANSFER_LIMIT_MS,
@@ -908,11 +896,6 @@
             RUNTIME_MIN_GUARANTEE_MS = Math.max(10 * MINUTE_IN_MILLIS,
                     properties.getLong(
                             KEY_RUNTIME_MIN_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_GUARANTEE_MS));
-            // Make sure min runtime for high priority jobs is at least 4 minutes.
-            RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS = Math.max(4 * MINUTE_IN_MILLIS,
-                    properties.getLong(
-                            KEY_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
-                            DEFAULT_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS));
             // Make sure min runtime for expedited jobs is at least one minute.
             RUNTIME_MIN_EJ_GUARANTEE_MS = Math.max(MINUTE_IN_MILLIS,
                     properties.getLong(
@@ -1008,8 +991,6 @@
 
             pw.print(KEY_RUNTIME_MIN_GUARANTEE_MS, RUNTIME_MIN_GUARANTEE_MS).println();
             pw.print(KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, RUNTIME_MIN_EJ_GUARANTEE_MS).println();
-            pw.print(KEY_RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
-                    RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS).println();
             pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS)
                     .println();
             pw.print(KEY_RUNTIME_MIN_DATA_TRANSFER_GUARANTEE_MS,
@@ -3333,8 +3314,6 @@
                 return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX
                         ? mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS
                         : Math.min(mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 5 * MINUTE_IN_MILLIS);
-            } else if (job.getEffectivePriority() >= JobInfo.PRIORITY_HIGH) {
-                return mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS;
             } else {
                 return mConstants.RUNTIME_MIN_GUARANTEE_MS;
             }
diff --git a/core/api/current.txt b/core/api/current.txt
index eef16be..651669c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -41246,6 +41246,7 @@
     method public void setUiEnabled(boolean);
     method public void show(android.os.Bundle, int);
     method public void startAssistantActivity(android.content.Intent);
+    method public void startAssistantActivity(@NonNull android.content.Intent, @NonNull android.os.Bundle);
     method public void startVoiceActivity(android.content.Intent);
     method public final void unregisterVisibleActivityCallback(@NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback);
     field public static final String KEY_SHOW_SESSION_ID = "android.service.voice.SHOW_SESSION_ID";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index c0cd638..8ff1a84 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -985,7 +985,7 @@
     method public boolean isInitialized();
     method public boolean isMain();
     method public boolean isManagedProfile();
-    method public boolean isPrimary();
+    method @Deprecated public boolean isPrimary();
     method public boolean isProfile();
     method public boolean isQuietModeEnabled();
     method public boolean isRestricted();
@@ -1002,7 +1002,7 @@
     field public static final int FLAG_INITIALIZED = 16; // 0x10
     field public static final int FLAG_MAIN = 16384; // 0x4000
     field @Deprecated public static final int FLAG_MANAGED_PROFILE = 32; // 0x20
-    field public static final int FLAG_PRIMARY = 1; // 0x1
+    field @Deprecated public static final int FLAG_PRIMARY = 1; // 0x1
     field public static final int FLAG_PROFILE = 4096; // 0x1000
     field public static final int FLAG_QUIET_MODE = 128; // 0x80
     field @Deprecated public static final int FLAG_RESTRICTED = 8; // 0x8
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3a3ad8ce..cc9e0ec 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4634,7 +4634,7 @@
          * Set whether this is an "ongoing" notification.
          *
          * Ongoing notifications cannot be dismissed by the user on locked devices, or by
-         * notification listeners, and some notifications cannnot be dismissed on unlocked 
+         * notification listeners, and some notifications cannnot be dismissed on unlocked
          * devices (system, device management, media), so your application or service must take 
          * care of canceling them.
          *
@@ -6992,8 +6992,10 @@
     /**
      * Returns whether an app can colorize due to the android.permission.USE_COLORIZED_NOTIFICATIONS
      * permission. The permission is checked when a notification is enqueued.
+     *
+     * @hide
      */
-    private boolean hasColorizedPermission() {
+    public boolean hasColorizedPermission() {
         return (flags & Notification.FLAG_CAN_COLORIZE) != 0;
     }
 
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 6386f75..81fc029 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -60,10 +60,17 @@
      */
 
     /**
-     * Primary user. Only one user can have this flag set. It identifies the first human user
-     * on a device. This flag is not supported in headless system user mode.
+     * Primary user. In practice, this is just synonymous with {@link #FLAG_SYSTEM}.
+     *
+     * <p>On many devices, this will also be the first human user.
+     * However, in {@link UserManager#isHeadlessSystemUserMode() headless system user mode}, this
+     * should be regarded as unsupported since the system user may not be a human.
+     *
+     * @deprecated For checking for user 0, use {@link #FLAG_SYSTEM}.
+     *             For checking for the designated "main human user", use {@link #FLAG_MAIN}.
      */
     @UnsupportedAppUsage
+    @Deprecated
     public static final int FLAG_PRIMARY = 0x00000001;
 
     /**
@@ -335,7 +342,12 @@
         }
     }
 
+    /**
+     * @deprecated For checking for user 0, compare {@link #id} to {@link UserHandle#USER_SYSTEM}.
+     *             For checking for the designated "main human user", use {@link #isMain()}.
+     */
     @UnsupportedAppUsage
+    @Deprecated
     public boolean isPrimary() {
         return (flags & FLAG_PRIMARY) == FLAG_PRIMARY;
     }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 9c55ad6..bbf7f81 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4381,11 +4381,16 @@
     }
 
     /**
-     * Returns information for Primary user.
+     * Returns information for Primary user (which in practice is the same as the System user).
      *
      * @return the Primary user, null if not found.
+     * @deprecated For the system user, call {@link #getUserInfo} on {@link UserHandle#USER_SYSTEM},
+     *             or just use {@link UserHandle#SYSTEM} or {@link UserHandle#USER_SYSTEM}.
+     *             For the designated MainUser, use {@link #getMainUser()}.
+     *
      * @hide
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public @Nullable UserInfo getPrimaryUser() {
         try {
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 5778518..cabcae3 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -26,6 +26,7 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.app.Activity;
+import android.app.ActivityOptions;
 import android.app.Dialog;
 import android.app.DirectAction;
 import android.app.Instrumentation;
@@ -1527,8 +1528,34 @@
      * <p>By default, the system will create a window for the UI for this session.  If you are using
      * an assistant activity instead, then you can disable the window creation by calling
      * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
+     *
+     * NOTE: if the app would like to override some options to start the Activity,
+     * use {@link #startAssistantActivity(Intent, Bundle)} instead.
      */
     public void startAssistantActivity(Intent intent) {
+        startAssistantActivity(intent, ActivityOptions.makeBasic().toBundle());
+    }
+
+    /**
+     * <p>Ask that a new assistant activity be started.  This will create a new task in the
+     * in activity manager: this means that
+     * {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
+     * will be set for you to make it a new task.</p>
+     *
+     * <p>The newly started activity will be displayed on top of other activities in the system
+     * in a new layer that is not affected by multi-window mode.  Tasks started from this activity
+     * will go into the normal activity layer and not this new layer.</p>
+     *
+     * <p>By default, the system will create a window for the UI for this session.  If you are using
+     * an assistant activity instead, then you can disable the window creation by calling
+     * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
+     *
+     * @param intent the intent used to start an assistant activity
+     * @param bundle Additional options for how the Activity should be started. See
+     * {@link ActivityOptions} for how to build the Bundle supplied here.
+     */
+    public void startAssistantActivity(@NonNull Intent intent, @NonNull Bundle bundle) {
+        Objects.requireNonNull(bundle);
         if (mToken == null) {
             throw new IllegalStateException("Can't call before onCreate()");
         }
@@ -1537,7 +1564,7 @@
             intent.prepareToLeaveProcess(mContext);
             int res = mSystemService.startAssistantActivity(mToken, intent,
                     intent.resolveType(mContext.getContentResolver()),
-                    mContext.getAttributionTag());
+                    mContext.getAttributionTag(), bundle);
             Instrumentation.checkStartActivityResult(res, intent);
         } catch (RemoteException e) {
         }
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 5eb9786..6b40d98 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -51,7 +51,7 @@
     int startVoiceActivity(IBinder token, in Intent intent, String resolvedType,
             String attributionTag);
     int startAssistantActivity(IBinder token, in Intent intent, String resolvedType,
-            String attributionTag);
+            String attributionTag, in Bundle bundle);
     void setKeepAwake(IBinder token, boolean keepAwake);
     void closeSystemDialogs(IBinder token);
     void finish(IBinder token);
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index d726b67..71050fa 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -506,6 +506,8 @@
         <permission name="android.permission.ACCESS_BROADCAST_RADIO"/>
         <!-- Permission required for CTS test - CtsAmbientContextServiceTestCases -->
         <permission name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT"/>
+        <!-- Permission required for CTS test - CtsTelephonyProviderTestCases -->
+        <permission name="android.permission.WRITE_APN_SETTINGS"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0f67124..82ca63d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -819,6 +819,9 @@
     <!-- Permission required for CTS test - ActivityCaptureCallbackTests -->
     <uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
 
+    <!-- Permission required for CTS test - CtsTelephonyProviderTestCases -->
+    <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index f712629..2b6327f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.shade;
 
-import static android.os.Trace.TRACE_TAG_ALWAYS;
+import static android.os.Trace.TRACE_TAG_APP;
 import static android.view.WindowInsets.Type.systemBars;
 
 import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
@@ -328,7 +328,7 @@
 
     @Override
     public void requestLayout() {
-        Trace.instant(TRACE_TAG_ALWAYS, "NotificationShadeWindowView#requestLayout");
+        Trace.instant(TRACE_TAG_APP, "NotificationShadeWindowView#requestLayout");
         super.requestLayout();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 32b8e09..c9f31ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -166,6 +166,11 @@
 
 
     private Queue<NotifEvent> mEventQueue = new ArrayDeque<>();
+    private final Runnable mRebuildListRunnable = () -> {
+        if (mBuildListener != null) {
+            mBuildListener.onBuildList(mReadOnlyNotificationSet, "asynchronousUpdate");
+        }
+    };
 
     private boolean mAttached = false;
     private boolean mAmDispatchingToOtherCode;
@@ -462,7 +467,7 @@
             int modificationType) {
         Assert.isMainThread();
         mEventQueue.add(new ChannelChangedEvent(pkgName, user, channel, modificationType));
-        dispatchEventsAndRebuildList("onNotificationChannelModified");
+        dispatchEventsAndAsynchronouslyRebuildList();
     }
 
     private void onNotificationsInitialized() {
@@ -621,15 +626,39 @@
 
     private void dispatchEventsAndRebuildList(String reason) {
         Trace.beginSection("NotifCollection.dispatchEventsAndRebuildList");
+        if (mMainHandler.hasCallbacks(mRebuildListRunnable)) {
+            mMainHandler.removeCallbacks(mRebuildListRunnable);
+        }
+
+        dispatchEvents();
+
+        if (mBuildListener != null) {
+            mBuildListener.onBuildList(mReadOnlyNotificationSet, reason);
+        }
+        Trace.endSection();
+    }
+
+    private void dispatchEventsAndAsynchronouslyRebuildList() {
+        Trace.beginSection("NotifCollection.dispatchEventsAndAsynchronouslyRebuildList");
+
+        dispatchEvents();
+
+        if (!mMainHandler.hasCallbacks(mRebuildListRunnable)) {
+            mMainHandler.postDelayed(mRebuildListRunnable, 1000L);
+        }
+
+        Trace.endSection();
+    }
+
+    private void dispatchEvents() {
+        Trace.beginSection("NotifCollection.dispatchEvents");
+
         mAmDispatchingToOtherCode = true;
         while (!mEventQueue.isEmpty()) {
             mEventQueue.remove().dispatchTo(mNotifCollectionListeners);
         }
         mAmDispatchingToOtherCode = false;
 
-        if (mBuildListener != null) {
-            mBuildListener.onBuildList(mReadOnlyNotificationSet, reason);
-        }
         Trace.endSection();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 1fb7eb5..d2087ba6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static android.os.Trace.TRACE_TAG_ALWAYS;
+import static android.os.Trace.TRACE_TAG_APP;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
@@ -1121,7 +1121,7 @@
 
     @Override
     public void requestLayout() {
-        Trace.instant(TRACE_TAG_ALWAYS, "NotificationStackScrollLayout#requestLayout");
+        Trace.instant(TRACE_TAG_APP, "NotificationStackScrollLayout#requestLayout");
         super.requestLayout();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 005c80a..540bda6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -106,6 +106,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
+import org.mockito.stubbing.Answer;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -378,6 +379,90 @@
     }
 
     @Test
+    public void testScheduleBuildNotificationListWhenChannelChanged() {
+        // GIVEN
+        final NotificationEntryBuilder neb = buildNotif(TEST_PACKAGE, 48);
+        final NotificationChannel channel = new NotificationChannel(
+                "channelId",
+                "channelName",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        neb.setChannel(channel);
+
+        final NotifEvent notif = mNoMan.postNotif(neb);
+        final NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+        when(mMainHandler.hasCallbacks(any())).thenReturn(false);
+
+        clearInvocations(mBuildListener);
+
+        // WHEN
+        mNotifHandler.onNotificationChannelModified(TEST_PACKAGE,
+                entry.getSbn().getUser(), channel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+        // THEN
+        verify(mMainHandler).postDelayed(any(), eq(1000L));
+    }
+
+    @Test
+    public void testCancelScheduledBuildNotificationListEventWhenNotifUpdatedSynchronously() {
+        // GIVEN
+        final NotificationEntry entry1 = buildNotif(TEST_PACKAGE, 1)
+                .setGroup(mContext, "group_1")
+                .build();
+        final NotificationEntry entry2 = buildNotif(TEST_PACKAGE, 2)
+                .setGroup(mContext, "group_1")
+                .setContentTitle(mContext, "New version")
+                .build();
+        final NotificationEntry entry3 = buildNotif(TEST_PACKAGE, 3)
+                .setGroup(mContext, "group_1")
+                .build();
+
+        final List<CoalescedEvent> entriesToBePosted = Arrays.asList(
+                new CoalescedEvent(entry1.getKey(), 0, entry1.getSbn(), entry1.getRanking(), null),
+                new CoalescedEvent(entry2.getKey(), 1, entry2.getSbn(), entry2.getRanking(), null),
+                new CoalescedEvent(entry3.getKey(), 2, entry3.getSbn(), entry3.getRanking(), null)
+        );
+
+        when(mMainHandler.hasCallbacks(any())).thenReturn(true);
+
+        // WHEN
+        mNotifHandler.onNotificationBatchPosted(entriesToBePosted);
+
+        // THEN
+        verify(mMainHandler).removeCallbacks(any());
+    }
+
+    @Test
+    public void testBuildNotificationListWhenChannelChanged() {
+        // GIVEN
+        final NotificationEntryBuilder neb = buildNotif(TEST_PACKAGE, 48);
+        final NotificationChannel channel = new NotificationChannel(
+                "channelId",
+                "channelName",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        neb.setChannel(channel);
+
+        final NotifEvent notif = mNoMan.postNotif(neb);
+        final NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+        when(mMainHandler.hasCallbacks(any())).thenReturn(false);
+        when(mMainHandler.postDelayed(any(), eq(1000L))).thenAnswer((Answer) invocation -> {
+            final Runnable runnable = invocation.getArgument(0);
+            runnable.run();
+            return null;
+        });
+
+        clearInvocations(mBuildListener);
+
+        // WHEN
+        mNotifHandler.onNotificationChannelModified(TEST_PACKAGE,
+                entry.getSbn().getUser(), channel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+        // THEN
+        verifyBuiltList(List.of(entry));
+    }
+
+    @Test
     public void testRankingsAreUpdatedForOtherNotifs() {
         // GIVEN a collection with one notif
         NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
diff --git a/services/companion/java/com/android/server/companion/RolesUtils.java b/services/companion/java/com/android/server/companion/RolesUtils.java
index f674a7d..163f614 100644
--- a/services/companion/java/com/android/server/companion/RolesUtils.java
+++ b/services/companion/java/com/android/server/companion/RolesUtils.java
@@ -27,6 +27,7 @@
 import android.app.role.RoleManager;
 import android.companion.AssociationInfo;
 import android.content.Context;
+import android.os.Binder;
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.Slog;
@@ -84,7 +85,9 @@
 
         Slog.i(TAG, "Removing CDM role holder, role=" + deviceProfile
                 + ", package=u" + userId + "\\" + packageName);
-        roleManager.removeRoleHolderAsUser(deviceProfile, packageName,
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            roleManager.removeRoleHolderAsUser(deviceProfile, packageName,
                 MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
                 success -> {
                     if (!success) {
@@ -92,6 +95,9 @@
                                 + " from the list of " + deviceProfile + " holders.");
                     }
                 });
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     private RolesUtils() {};
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a01b2c1..a112297 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.notification;
 
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
 import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
@@ -6517,9 +6518,17 @@
 
         checkRestrictedCategories(notification);
 
+        // Notifications passed to setForegroundService() have FLAG_FOREGROUND_SERVICE,
+        // but it's also possible that the app has called notify() with an update to an
+        // FGS notification that hasn't yet been displayed.  Make sure we check for any
+        // FGS-related situation up front, outside of any locks so it's safe to call into
+        // the Activity Manager.
+        final ServiceNotificationPolicy policy = mAmi.applyForegroundServiceNotification(
+                notification, tag, id, pkg, userId);
+
         // Fix the notification as best we can.
         try {
-            fixNotification(notification, pkg, tag, id, userId, notificationUid);
+            fixNotification(notification, pkg, tag, id, userId, notificationUid, policy);
         } catch (Exception e) {
             if (notification.isForegroundService()) {
                 throw new SecurityException("Invalid FGS notification", e);
@@ -6528,13 +6537,7 @@
             return;
         }
 
-        // Notifications passed to setForegroundService() have FLAG_FOREGROUND_SERVICE,
-        // but it's also possible that the app has called notify() with an update to an
-        // FGS notification that hasn't yet been displayed.  Make sure we check for any
-        // FGS-related situation up front, outside of any locks so it's safe to call into
-        // the Activity Manager.
-        final ServiceNotificationPolicy policy = mAmi.applyForegroundServiceNotification(
-                notification, tag, id, pkg, userId);
+
         if (policy == ServiceNotificationPolicy.UPDATE_ONLY) {
             // Proceed if the notification is already showing/known, otherwise ignore
             // because the service lifecycle logic has retained responsibility for its
@@ -6707,14 +6710,17 @@
 
     @VisibleForTesting
     protected void fixNotification(Notification notification, String pkg, String tag, int id,
-            @UserIdInt int userId, int notificationUid) throws NameNotFoundException,
-            RemoteException {
-
+            @UserIdInt int userId, int notificationUid, ServiceNotificationPolicy fgsPolicy)
+            throws NameNotFoundException, RemoteException {
         final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
                 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                 (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
         Notification.addFieldsFromContext(ai, notification);
 
+        if (notification.isForegroundService() && fgsPolicy == NOT_FOREGROUND_SERVICE) {
+            notification.flags &= ~FLAG_FOREGROUND_SERVICE;
+        }
+
         // Only notifications that can be non-dismissible can have the flag FLAG_NO_DISMISS
         if (mFlagResolver.isEnabled(ALLOW_DISMISS_ONGOING)) {
             if (((notification.flags & FLAG_ONGOING_EVENT) > 0)
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 11efd41..58ae955 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3938,22 +3938,22 @@
 
     @GuardedBy({"mPackagesLock"})
     private void fallbackToSingleUserLP() {
-        int flags = UserInfo.FLAG_SYSTEM | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN
-                | UserInfo.FLAG_PRIMARY;
         // Create the system user
-        String systemUserType = isDefaultHeadlessSystemUserMode()
+        final String systemUserType = isDefaultHeadlessSystemUserMode()
                 ? UserManager.USER_TYPE_SYSTEM_HEADLESS
                 : UserManager.USER_TYPE_FULL_SYSTEM;
-        flags |= mUserTypes.get(systemUserType).getDefaultUserInfoFlags();
-        UserInfo system = new UserInfo(UserHandle.USER_SYSTEM, null, null, flags, systemUserType);
-        UserData userData = putUserInfo(system);
+        final int flags = mUserTypes.get(systemUserType).getDefaultUserInfoFlags()
+                | UserInfo.FLAG_INITIALIZED;
+        final UserInfo system = new UserInfo(UserHandle.USER_SYSTEM,
+                /* name= */ null, /* iconPath= */ null, flags, systemUserType);
+        final UserData userData = putUserInfo(system);
         userData.userProperties = new UserProperties(
                 mUserTypes.get(userData.info.userType).getDefaultUserPropertiesReference());
         mNextSerialNumber = MIN_USER_ID;
         mUserVersion = USER_VERSION;
         mUserTypeVersion = UserTypeFactory.getUserTypeVersion();
 
-        Bundle restrictions = new Bundle();
+        final Bundle restrictions = new Bundle();
         try {
             final String[] defaultFirstUserRestrictions = mContext.getResources().getStringArray(
                     com.android.internal.R.array.config_defaultFirstUserRestrictions);
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index f86ee90..6065372 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -76,7 +76,7 @@
     private final @UserInfoFlag int mBaseType;
 
     // TODO(b/143784345): Update doc/name when we clean up UserInfo.
-    /** The {@link UserInfo.UserInfoFlag}s that all users of this type will automatically have. */
+    /** The {@link UserInfoFlag}s to apply by default to newly created users of this type. */
     private final @UserInfoFlag int mDefaultUserInfoPropertyFlags;
 
     /**
@@ -224,7 +224,7 @@
     }
 
     // TODO(b/143784345): Update comment when UserInfo is reorganized.
-    /** The {@link UserInfo.UserInfoFlag}s that all users of this type will automatically have. */
+    /** The {@link UserInfoFlag}s to apply by default to newly created users of this type. */
     public int getDefaultUserInfoFlags() {
         return mDefaultUserInfoPropertyFlags | mBaseType;
     }
@@ -526,6 +526,7 @@
             Preconditions.checkArgument(hasValidPropertyFlags(),
                     "UserTypeDetails " + mName + " has invalid flags: "
                             + Integer.toHexString(mDefaultUserInfoPropertyFlags));
+            checkSystemAndMainUserPreconditions();
             if (hasBadge()) {
                 Preconditions.checkArgument(mBadgeLabels != null && mBadgeLabels.length != 0,
                         "UserTypeDetails " + mName + " has badge but no badgeLabels.");
@@ -578,8 +579,6 @@
         // TODO(b/143784345): Refactor this when we clean up UserInfo.
         private boolean hasValidPropertyFlags() {
             final int forbiddenMask =
-                    UserInfo.FLAG_PRIMARY |
-                    UserInfo.FLAG_ADMIN |
                     UserInfo.FLAG_INITIALIZED |
                     UserInfo.FLAG_QUIET_MODE |
                     UserInfo.FLAG_FULL |
@@ -587,6 +586,18 @@
                     UserInfo.FLAG_PROFILE;
             return (mDefaultUserInfoPropertyFlags & forbiddenMask) == 0;
         }
+
+        private void checkSystemAndMainUserPreconditions() {
+            // Primary must be synonymous with System.
+            Preconditions.checkArgument(
+                    ((mBaseType & UserInfo.FLAG_SYSTEM) != 0) ==
+                            ((mDefaultUserInfoPropertyFlags & UserInfo.FLAG_PRIMARY) != 0),
+                    "UserTypeDetails " + mName + " cannot be SYSTEM xor PRIMARY.");
+            // At most one MainUser is ever allowed at a time.
+            Preconditions.checkArgument(
+                    ((mDefaultUserInfoPropertyFlags & UserInfo.FLAG_MAIN) == 0) || mMaxAllowed == 1,
+                    "UserTypeDetails " + mName + " must not sanction more than one MainUser.");
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 4cf8c09..b7a2b86 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -16,11 +16,14 @@
 
 package com.android.server.pm;
 
+import static android.content.pm.UserInfo.FLAG_ADMIN;
 import static android.content.pm.UserInfo.FLAG_DEMO;
 import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
 import static android.content.pm.UserInfo.FLAG_FULL;
 import static android.content.pm.UserInfo.FLAG_GUEST;
+import static android.content.pm.UserInfo.FLAG_MAIN;
 import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.content.pm.UserInfo.FLAG_PRIMARY;
 import static android.content.pm.UserInfo.FLAG_PROFILE;
 import static android.content.pm.UserInfo.FLAG_RESTRICTED;
 import static android.content.pm.UserInfo.FLAG_SYSTEM;
@@ -62,7 +65,7 @@
  * This class is responsible both for defining the AOSP use types, as well as reading in customized
  * user types from {@link com.android.internal.R.xml#config_user_types}.
  *
- * Tests are located in UserManagerServiceUserTypeTest.java.
+ * Tests are located in {@link UserManagerServiceUserTypeTest}.
  * @hide
  */
 public final class UserTypeFactory {
@@ -277,7 +280,8 @@
         return new UserTypeDetails.Builder()
                 .setName(USER_TYPE_FULL_SYSTEM)
                 .setBaseType(FLAG_SYSTEM | FLAG_FULL)
-                .setDefaultUserInfoPropertyFlags(UserInfo.FLAG_MAIN);
+                .setDefaultUserInfoPropertyFlags(FLAG_PRIMARY | FLAG_ADMIN | FLAG_MAIN)
+                .setMaxAllowed(1);
     }
 
     /**
@@ -287,7 +291,9 @@
     private static UserTypeDetails.Builder getDefaultTypeSystemHeadless() {
         return new UserTypeDetails.Builder()
                 .setName(USER_TYPE_SYSTEM_HEADLESS)
-                .setBaseType(FLAG_SYSTEM);
+                .setBaseType(FLAG_SYSTEM)
+                .setDefaultUserInfoPropertyFlags(FLAG_PRIMARY | FLAG_ADMIN)
+                .setMaxAllowed(1);
     }
 
     private static Bundle getDefaultSecondaryUserRestrictions() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index e16b0f7..c42a457 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -71,6 +71,10 @@
 import java.util.stream.Collectors;
 
 class ActiveAdmin {
+
+    private final int userId;
+    public final boolean isPermissionBased;
+
     private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
     private static final String TAG_TEST_ONLY_ADMIN = "test-only-admin";
     private static final String TAG_DISABLE_CAMERA = "disable-camera";
@@ -356,8 +360,20 @@
     String mSmsPackage;
 
     ActiveAdmin(DeviceAdminInfo info, boolean isParent) {
+        this.userId = -1;
         this.info = info;
         this.isParent = isParent;
+        this.isPermissionBased = false;
+    }
+
+    ActiveAdmin(int userId, boolean permissionBased) {
+        if (permissionBased == false) {
+            throw new IllegalArgumentException("Can only pass true for permissionBased admin");
+        }
+        this.userId = userId;
+        this.isPermissionBased = permissionBased;
+        this.isParent = false;
+        this.info = null;
     }
 
     ActiveAdmin getParentActiveAdmin() {
@@ -374,10 +390,16 @@
     }
 
     int getUid() {
+        if (isPermissionBased) {
+            return -1;
+        }
         return info.getActivityInfo().applicationInfo.uid;
     }
 
     public UserHandle getUserHandle() {
+        if (isPermissionBased) {
+            return UserHandle.of(userId);
+        }
         return UserHandle.of(UserHandle.getUserId(info.getActivityInfo().applicationInfo.uid));
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index a5b9d43..6d51bd7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -133,9 +133,9 @@
 
     // Create or get the permission-based admin. The permission-based admin will not have a
     // DeviceAdminInfo or ComponentName.
-    ActiveAdmin createOrGetPermissionBasedAdmin() {
+    ActiveAdmin createOrGetPermissionBasedAdmin(int userId) {
         if (mPermissionBasedAdmin == null) {
-            mPermissionBasedAdmin = new ActiveAdmin(/* info= */ null, /* parent= */ false);
+            mPermissionBasedAdmin = new ActiveAdmin(userId, /* permissionBased= */ true);
         }
         return mPermissionBasedAdmin;
     }
@@ -509,7 +509,7 @@
                         Slogf.w(TAG, e, "Failed loading admin %s", name);
                     }
                 } else if ("permission-based-admin".equals(tag)) {
-                    ActiveAdmin ap = new ActiveAdmin(/* info= */ null, /* parent= */ false);
+                    ActiveAdmin ap = new ActiveAdmin(policy.mUserId, /* permissionBased= */ true);
                     ap.readFromXml(parser, /* overwritePolicies= */ false);
                     policy.mPermissionBasedAdmin = ap;
                 } else if ("delegation".equals(tag)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 821a5f6..2f22197 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3616,7 +3616,7 @@
             final int N = admins.size();
             for (int i = 0; i < N; i++) {
                 ActiveAdmin admin = admins.get(i);
-                if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)
+                if ((admin.isPermissionBased || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD))
                         && admin.passwordExpirationTimeout > 0L
                         && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS
                         && admin.passwordExpirationDate > 0L) {
@@ -4296,15 +4296,26 @@
         }
     }
 
+    @GuardedBy("getLockObject()")
     private List<ActiveAdmin> getActiveAdminsForLockscreenPoliciesLocked(int userHandle) {
         if (isSeparateProfileChallengeEnabled(userHandle)) {
+
+            if (isPermissionCheckFlagEnabled()) {
+                return getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(userHandle);
+            }
             // If this user has a separate challenge, only return its restrictions.
             return getUserDataUnchecked(userHandle).mAdminList;
         }
         // If isSeparateProfileChallengeEnabled is false and userHandle points to a managed profile
         // we need to query the parent user who owns the credential.
-        return getActiveAdminsForUserAndItsManagedProfilesLocked(getProfileParentId(userHandle),
-                (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
+        if (isPermissionCheckFlagEnabled()) {
+            return getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(getProfileParentId(userHandle),
+                    (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
+        } else {
+            return getActiveAdminsForUserAndItsManagedProfilesLocked(getProfileParentId(userHandle),
+                    (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
+        }
+
     }
 
     /**
@@ -4340,7 +4351,14 @@
     @GuardedBy("getLockObject()")
     private List<ActiveAdmin> getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(
             int userHandle) {
-        List<ActiveAdmin> list = getActiveAdminsForAffectedUserLocked(userHandle);
+        List<ActiveAdmin> list;
+
+        if (isManagedProfile(userHandle)) {
+            list = getUserDataUnchecked(userHandle).mAdminList;
+        }
+        list = getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(userHandle,
+                /* shouldIncludeProfileAdmins */ (user) -> false);
+
         if (getUserData(userHandle).mPermissionBasedAdmin != null) {
             list.add(getUserData(userHandle).mPermissionBasedAdmin);
         }
@@ -4378,6 +4396,44 @@
         return admins;
     }
 
+    /**
+     * Returns the list of admins on the given user, as well as parent admins for each managed
+     * profile associated with the given user. Optionally also include the admin of each managed
+     * profile.
+     * <p> Should not be called on a profile user.
+     */
+    @GuardedBy("getLockObject()")
+    private List<ActiveAdmin> getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(int userHandle,
+            Predicate<UserInfo> shouldIncludeProfileAdmins) {
+        ArrayList<ActiveAdmin> admins = new ArrayList<>();
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
+                DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
+                if (userInfo.id == userHandle) {
+                    admins.addAll(policy.mAdminList);
+                    if (policy.mPermissionBasedAdmin != null) {
+                        admins.add(policy.mPermissionBasedAdmin);
+                    }
+                } else if (userInfo.isManagedProfile()) {
+                    for (int i = 0; i < policy.mAdminList.size(); i++) {
+                        ActiveAdmin admin = policy.mAdminList.get(i);
+                        if (admin.hasParentActiveAdmin()) {
+                            admins.add(admin.getParentActiveAdmin());
+                        }
+                        if (shouldIncludeProfileAdmins.test(userInfo)) {
+                            admins.add(admin);
+                        }
+                    }
+                    if (policy.mPermissionBasedAdmin != null
+                            && shouldIncludeProfileAdmins.test(userInfo)) {
+                        admins.add(policy.mPermissionBasedAdmin);
+                    }
+                }
+            }
+        });
+        return admins;
+    }
+
     private boolean isSeparateProfileChallengeEnabled(int userHandle) {
         return mInjector.binderWithCleanCallingIdentity(() ->
                 mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle));
@@ -4697,6 +4753,7 @@
      * Return a single admin's expiration date/time, or the min (soonest) for all admins.
      * Returns 0 if not configured.
      */
+    @GuardedBy("getLockObject()")
     private long getPasswordExpirationLocked(ComponentName who, int userHandle, boolean parent) {
         long timeout = 0L;
 
@@ -5282,11 +5339,12 @@
                     adminPackageName, userId, affectedUserId, complexity);
         }
     }
-
+    @GuardedBy("getLockObject()")
     private int getAggregatedPasswordComplexityLocked(@UserIdInt int userHandle) {
         return getAggregatedPasswordComplexityLocked(userHandle, false);
     }
 
+    @GuardedBy("getLockObject()")
     private int getAggregatedPasswordComplexityLocked(@UserIdInt int userHandle,
             boolean deviceWideOnly) {
         ensureLocked();
@@ -5469,6 +5527,7 @@
      * profile.
      * Returns {@code null} if no participating admin has that policy set.
      */
+    @GuardedBy("getLockObject()")
     private ActiveAdmin getAdminWithMinimumFailedPasswordsForWipeLocked(
             int userHandle, boolean parent) {
         int count = 0;
@@ -5699,6 +5758,7 @@
         }
     }
 
+    @GuardedBy("getLockObject()")
     private void updateMaximumTimeToLockLocked(@UserIdInt int userId) {
         // Update the profile's timeout
         if (isManagedProfile(userId)) {
@@ -5727,6 +5787,7 @@
         });
     }
 
+    @GuardedBy("getLockObject()")
     private void updateProfileLockTimeoutLocked(@UserIdInt int userId) {
         final long timeMs;
         if (isSeparateProfileChallengeEnabled(userId)) {
@@ -7459,9 +7520,15 @@
         final String adminName;
         final ComponentName adminComp;
         if (admin != null) {
-            adminComp = admin.info.getComponent();
-            adminName = adminComp.flattenToShortString();
-            event.setAdmin(adminComp);
+            if (admin.isPermissionBased) {
+                adminComp = null;
+                adminName = caller.getPackageName();
+                event.setAdmin(adminName);
+            } else {
+                adminComp = admin.info.getComponent();
+                adminName = adminComp.flattenToShortString();
+                event.setAdmin(adminComp);
+            }
         } else {
             adminComp = null;
             adminName = mInjector.getPackageManager().getPackagesForUid(caller.getUid())[0];
@@ -7750,13 +7817,7 @@
                                 || hasCallingPermission(permission.MASTER_CLEAR)
                                 || hasCallingPermission(MANAGE_DEVICE_POLICY_FACTORY_RESET),
                         "Must be called by the FRP management agent on device");
-                // TODO(b/261999445): Remove
-                if (isHeadlessFlagEnabled()) {
-                    admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
-                } else {
-                    admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
-                            UserHandle.getUserId(frpManagementAgentUid));
-                }
+                admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked();
             } else {
                 Preconditions.checkCallAuthorization(
                         isDefaultDeviceOwner(caller)
@@ -7927,12 +7988,13 @@
      *
      * @return the set of user IDs that have been affected
      */
+    @GuardedBy("getLockObject()")
     private Set<Integer> updatePasswordExpirationsLocked(int userHandle) {
         final ArraySet<Integer> affectedUserIds = new ArraySet<>();
         List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle);
         for (int i = 0; i < admins.size(); i++) {
             ActiveAdmin admin = admins.get(i);
-            if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
+            if (admin.isPermissionBased || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
                 affectedUserIds.add(admin.getUserHandle().getIdentifier());
                 long timeout = admin.passwordExpirationTimeout;
                 admin.passwordExpirationDate =
@@ -8026,6 +8088,9 @@
      */
     private int getUserIdToWipeForFailedPasswords(ActiveAdmin admin) {
         final int userId = admin.getUserHandle().getIdentifier();
+        if (admin.isPermissionBased) {
+            return userId;
+        }
         final ComponentName component = admin.info.getComponent();
         return isProfileOwnerOfOrganizationOwnedDevice(component, userId)
                 ? getProfileParentId(userId) : userId;
@@ -9653,6 +9718,15 @@
         return admin;
     }
 
+    ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked() {
+        ensureLocked();
+        ActiveAdmin doOrPo = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
+        if (isPermissionCheckFlagEnabled() && doOrPo == null) {
+            return getUserData(0).mPermissionBasedAdmin;
+        }
+        return doOrPo;
+    }
+
     ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceParentLocked(int userId) {
         ensureLocked();
         ActiveAdmin admin = getDeviceOwnerAdminLocked();
@@ -10659,9 +10733,12 @@
             return false;
         }
 
-        final ComponentName profileOwner = getProfileOwnerAsUser(userId);
-        if (profileOwner == null) {
-            return false;
+        if (!isPermissionCheckFlagEnabled()) {
+            // TODO: Figure out if something like this needs to be restored for policy engine
+            final ComponentName profileOwner = getProfileOwnerAsUser(userId);
+            if (profileOwner == null) {
+                return false;
+            }
         }
 
         // Managed profiles are not allowed to use lock task
@@ -11884,7 +11961,7 @@
         synchronized (getLockObject()) {
             List<String> result = null;
             // Only device or profile owners can have permitted lists set.
-            List<ActiveAdmin> admins = getActiveAdminsForAffectedUserLocked(userId);
+            List<ActiveAdmin> admins = getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(userId);
             for (ActiveAdmin admin: admins) {
                 List<String> fromAdmin = admin.permittedInputMethods;
                 if (fromAdmin != null) {
@@ -12597,7 +12674,6 @@
             }
             return policies.get(enforcingAdmin).getValue();
         } else {
-            Objects.requireNonNull(who, "ComponentName is null");
             Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                     && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
                     || (caller.hasPackage() && isCallerDelegate(caller,
@@ -12823,11 +12899,12 @@
         checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_USER_RESTRICTION);
 
         if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
+            int affectedUserId = parent ? getProfileParentId(userId) : userId;
             EnforcingAdmin admin = enforcePermissionForUserRestriction(
                     who,
                     key,
                     caller.getPackageName(),
-                    userId);
+                    affectedUserId);
             PolicyDefinition<Boolean> policyDefinition =
                     PolicyDefinition.getPolicyDefinitionForUserRestriction(key);
             if (enabledFromThisOwner) {
@@ -12835,7 +12912,7 @@
                         policyDefinition,
                         admin,
                         new BooleanPolicyValue(true),
-                        parent ? getProfileParentId(userId) : userId);
+                        affectedUserId);
             } else {
                 // Remove any local and global policy that was set by the admin
                 if (!policyDefinition.isLocalOnlyPolicy()) {
@@ -13623,7 +13700,6 @@
                     new BooleanPolicyValue(uninstallBlocked),
                     caller.getUserId());
         } else {
-            Objects.requireNonNull(who, "ComponentName is null");
             Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                     && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
                     || isFinancedDeviceOwner(caller)))
@@ -14265,7 +14341,7 @@
 
         final int userId = mInjector.userHandleGetCallingUserId();
         // Is it ok to just check that no active policies exist currently?
-        if (mDevicePolicyEngine.hasActivePolicies()) {
+        if (isDevicePolicyEngineFlagEnabled() && mDevicePolicyEngine.hasActivePolicies()) {
             LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
                     PolicyDefinition.LOCK_TASK, userId);
             if (policy == null) {
@@ -15814,7 +15890,7 @@
         if (admin.mPasswordPolicy.quality < minPasswordQuality) {
             return false;
         }
-        return admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+        return admin.isPermissionBased || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
     }
 
     @Override
@@ -21451,13 +21527,7 @@
         }
         synchronized (getLockObject()) {
             ActiveAdmin admin;
-            // TODO(b/261999445): remove
-            if (isHeadlessFlagEnabled()) {
-                admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
-            } else {
-                admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
-                        UserHandle.USER_SYSTEM);
-            }
+            admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked();
             return admin != null ? admin.mWifiSsidPolicy : null;
         }
     }
@@ -22454,7 +22524,7 @@
             return EnforcingAdmin.createDeviceAdminEnforcingAdmin(who, userId, admin);
         }
         if (admin == null) {
-            admin = getUserData(userId).createOrGetPermissionBasedAdmin();
+            admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId);
         }
         return  EnforcingAdmin.createEnforcingAdmin(caller.getPackageName(), userId, admin);
     }
@@ -23014,26 +23084,12 @@
         return admins;
     }
 
-    // TODO: This can actually accept an EnforcingAdmin that gets created in the permission check
-    //  method.
     private boolean useDevicePolicyEngine(CallerIdentity caller, @Nullable String delegateScope) {
-        if (!isCallerActiveAdminOrDelegate(caller, delegateScope)) {
-            if (!isDevicePolicyEngineFlagEnabled()) {
-                throw new IllegalStateException("Non DPC caller can't set device policies.");
-            }
-            if (hasDPCsNotSupportingCoexistence()) {
-                throw new IllegalStateException("Non DPC caller can't set device policies with "
-                        + "existing legacy admins on the device.");
-            }
-            return true;
-        } else {
-            return isDevicePolicyEngineEnabled();
-        }
+        return isDevicePolicyEngineEnabled();
     }
 
     private boolean isDevicePolicyEngineEnabled() {
-        return isDevicePolicyEngineFlagEnabled() && !hasDPCsNotSupportingCoexistence()
-                && isPermissionCheckFlagEnabled();
+        return isDevicePolicyEngineFlagEnabled() && isPermissionCheckFlagEnabled();
     }
 
     private boolean isDevicePolicyEngineFlagEnabled() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index daa8a26..fd91249 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -215,10 +215,9 @@
             }
             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);
+            userManager.setUserRestriction(
+                    userId, parsedKey.getRestriction(), enabled != null && enabled);
             return true;
         }));
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 63a5ff1..991d566 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -276,11 +276,11 @@
                 mService.getMinJobExecutionGuaranteeMs(ejMax));
         assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS,
                 mService.getMinJobExecutionGuaranteeMs(ejHigh));
-        assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMinJobExecutionGuaranteeMs(ejMaxDowngraded));
-        assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMinJobExecutionGuaranteeMs(ejHighDowngraded));
-        assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
+        assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMinJobExecutionGuaranteeMs(jobHigh));
         assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
                 mService.getMinJobExecutionGuaranteeMs(jobDef));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 96ec2b8..66f36de 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -18,6 +18,8 @@
 
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
+import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.SHOW_IMMEDIATELY;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.Notification.EXTRA_ALLOW_DURING_SETUP;
 import static android.app.Notification.FLAG_AUTO_CANCEL;
@@ -1183,6 +1185,8 @@
     public void testEnqueuedBlockedNotifications_appBlockedChannelForegroundService()
             throws Exception {
         when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
 
         NotificationChannel channel = new NotificationChannel("blocked", "name",
                 NotificationManager.IMPORTANCE_NONE);
@@ -1205,6 +1209,8 @@
     public void testEnqueuedBlockedNotifications_userBlockedChannelForegroundService()
             throws Exception {
         when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
 
         NotificationChannel channel =
                 new NotificationChannel("blockedbyuser", "name", IMPORTANCE_HIGH);
@@ -1284,6 +1290,8 @@
     public void testEnqueuedBlockedNotifications_blockedAppForegroundService() throws Exception {
         when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
         when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
 
         final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -1613,6 +1621,8 @@
 
     @Test
     public void testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
         mContext.getTestablePermissions().setPermission(
                 android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
         DeviceConfig.setProperty(
@@ -1643,6 +1653,8 @@
 
     @Test
     public void testEnqueueNotificationWithTag_FGSaddsFlags_dismissalNotAllowed() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
         mContext.getTestablePermissions().setPermission(
                 android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
         DeviceConfig.setProperty(
@@ -1931,6 +1943,8 @@
 
     @Test
     public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
         final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
@@ -1945,7 +1959,27 @@
     }
 
     @Test
+    public void testCancelAllNotifications_FgsFlag_NoFgs_Allowed() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(NOT_FOREGROUND_SERVICE);
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
+        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelAllNotifications_IgnoreForegroundService",
+                sbn.getId(), sbn.getNotification(), sbn.getUserId());
+        mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(sbn.getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
     public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
@@ -2033,6 +2067,9 @@
 
     @Test
     public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         Notification n =
                 new Notification.Builder(mContext, mTestNotificationChannel.getId())
                         .setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -2070,6 +2107,9 @@
     @Test
     public void testCancelNotificationWithTag_fromApp_cannotCancelFgsChild()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         mService.isSystemUid = false;
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
@@ -2093,6 +2133,9 @@
     @Test
     public void testCancelNotificationWithTag_fromApp_cannotCancelFgsParent()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         mService.isSystemUid = false;
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
@@ -2162,6 +2205,9 @@
     @Test
     public void testCancelAllNotificationsFromApp_cannotCancelFgsChild()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         mService.isSystemUid = false;
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
@@ -2187,6 +2233,9 @@
     @Test
     public void testCancelAllNotifications_fromApp_cannotCancelFgsParent()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         mService.isSystemUid = false;
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
@@ -2308,6 +2357,9 @@
     @Test
     public void testCancelNotificationsFromListener_clearAll_GroupWithFgsParent()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2331,6 +2383,9 @@
     @Test
     public void testCancelNotificationsFromListener_clearAll_GroupWithFgsChild()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         final NotificationRecord child = generateNotificationRecord(
@@ -2429,6 +2484,9 @@
     @Test
     public void testCancelNotificationsFromListener_clearAll_Fgs()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord child2 = generateNotificationRecord(
                 mTestNotificationChannel, 3, null, false);
         child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2493,6 +2551,9 @@
     @Test
     public void testCancelNotificationsFromListener_byKey_GroupWithFgsParent()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2518,6 +2579,9 @@
     @Test
     public void testCancelNotificationsFromListener_byKey_GroupWithFgsChild()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         final NotificationRecord child = generateNotificationRecord(
@@ -2623,6 +2687,9 @@
     @Test
     public void testCancelNotificationsFromListener_byKey_Fgs()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord child2 = generateNotificationRecord(
                 mTestNotificationChannel, 3, null, false);
         child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2789,6 +2856,9 @@
 
     @Test
     public void testUserInitiatedCancelAllWithGroup_ForegroundServiceFlag() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         final NotificationRecord child = generateNotificationRecord(
@@ -6249,6 +6319,9 @@
 
     @Test
     public void testRemoveForegroundServiceFlagFromNotification_enqueued() {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         Notification n = new Notification.Builder(mContext, "").build();
         n.flags |= FLAG_FOREGROUND_SERVICE;
 
@@ -6268,6 +6341,9 @@
 
     @Test
     public void testRemoveForegroundServiceFlagFromNotification_posted() {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         Notification n = new Notification.Builder(mContext, "").build();
         n.flags |= FLAG_FOREGROUND_SERVICE;
 
@@ -6291,6 +6367,9 @@
 
     @Test
     public void testCannotRemoveForegroundFlagWhenOverLimit_enqueued() {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
             Notification n = new Notification.Builder(mContext, "").build();
             StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, i, null, mUid, 0,
@@ -6319,6 +6398,9 @@
 
     @Test
     public void testCannotRemoveForegroundFlagWhenOverLimit_posted() {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
             Notification n = new Notification.Builder(mContext, "").build();
             StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, i, null, mUid, 0,
@@ -8327,7 +8409,7 @@
         assertNotNull(n.publicVersion.bigContentView);
         assertNotNull(n.publicVersion.headsUpContentView);
 
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         assertNull(n.contentView);
         assertNull(n.bigContentView);
@@ -9018,6 +9100,9 @@
 
     @Test
     public void testCanPostFgsWhenOverLimit() throws RemoteException {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
             StatusBarNotification sbn = generateNotificationRecord(mTestNotificationChannel,
                     i, null, false).getSbn();
@@ -9043,6 +9128,9 @@
 
     @Test
     public void testCannotPostNonFgsWhenOverLimit() throws RemoteException {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
             StatusBarNotification sbn = generateNotificationRecord(mTestNotificationChannel,
                     i, null, false).getSbn();
@@ -9065,6 +9153,17 @@
                 "testCanPostFgsWhenOverLimit - non fgs over limit!",
                 sbn2.getId(), sbn2.getNotification(), sbn2.getUserId());
 
+
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(NOT_FOREGROUND_SERVICE);
+        final StatusBarNotification sbn3 = generateNotificationRecord(mTestNotificationChannel,
+                101, null, false).getSbn();
+        sbn3.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCanPostFgsWhenOverLimit - fake fgs over limit!",
+                sbn3.getId(), sbn3.getNotification(), sbn3.getUserId());
+
         waitForIdle();
 
         StatusBarNotification[] notifs =
@@ -10137,7 +10236,7 @@
                 .setFullScreenIntent(mock(PendingIntent.class), true)
                 .build();
 
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         final int stickyFlag = n.flags & Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
 
@@ -10203,6 +10302,23 @@
     }
 
     @Test
+    public void fixNotification_withFgsFlag_butIsNotFgs() throws Exception {
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+                .thenReturn(applicationInfo);
+
+        Notification n = new Notification.Builder(mContext, "test")
+                .setFlag(FLAG_FOREGROUND_SERVICE, true)
+                .setFlag(FLAG_CAN_COLORIZE, true)
+                .build();
+
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
+
+        assertFalse(n.isForegroundService());
+        assertFalse(n.hasColorizedPermission());
+    }
+
+    @Test
     public void fixSystemNotification_withOnGoingFlag_shouldBeNonDismissible()
             throws Exception {
         // Given: a notification from an app on the system partition has the flag
@@ -10219,7 +10335,7 @@
                 .thenReturn(systemAppInfo);
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be set
         assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
@@ -10238,7 +10354,7 @@
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be set
         assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
@@ -10254,7 +10370,7 @@
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         // Then: the notification's flag FLAG_NO_DISMISS should not be set
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
@@ -10272,7 +10388,7 @@
         n.flags |= Notification.FLAG_NO_DISMISS;
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be cleared
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
@@ -10294,7 +10410,7 @@
                 .thenReturn(systemAppInfo);
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         // Then: the notification's flag FLAG_NO_DISMISS should not be set
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
@@ -10318,7 +10434,7 @@
                 .thenReturn(systemAppInfo);
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be cleared
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
@@ -10336,7 +10452,7 @@
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         // Then: the notification's flag FLAG_NO_DISMISS should not be set
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
@@ -10357,7 +10473,7 @@
         n.flags |= Notification.FLAG_NO_DISMISS;
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be cleared
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
@@ -10374,7 +10490,7 @@
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         // Then: the notification's flag FLAG_NO_DISMISS should not be set
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
@@ -10393,7 +10509,7 @@
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be set
         assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
@@ -10414,7 +10530,7 @@
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be set
         assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
@@ -10437,7 +10553,7 @@
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         // Then: the notification's flag FLAG_NO_DISMISS should not be set
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 495a433..1400886 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -1647,11 +1647,13 @@
 
         @Override
         public List<ModuleProperties> listModuleProperties(Identity originatorIdentity) {
-            Identity middlemanIdentity = new Identity();
-            middlemanIdentity.packageName = ActivityThread.currentOpPackageName();
+            Identity identity = new Identity();
+            identity.packageName = ActivityThread.currentOpPackageName();
             ArrayList<ModuleProperties> moduleList = new ArrayList<>();
-            SoundTrigger.listModulesAsMiddleman(moduleList, middlemanIdentity,
-                                            originatorIdentity);
+            // Overwrite with our own identity to fix permission issues.
+            // VIMService always does its own validation, so this is fine.
+            // TODO(b/269765333)
+            SoundTrigger.listModulesAsOriginator(moduleList, identity);
             return moduleList;
         }
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index d4374a9..fd54293 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1049,7 +1049,8 @@
 
         @Override
         public int startAssistantActivity(@NonNull IBinder token, @NonNull Intent intent,
-                @Nullable String resolvedType, @Nullable String attributionTag) {
+                @Nullable String resolvedType, @NonNull String attributionTag,
+                @NonNull Bundle bundle) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "startAssistantActivity without running voice interaction service");
@@ -1060,7 +1061,7 @@
                 final long caller = Binder.clearCallingIdentity();
                 try {
                     return mImpl.startAssistantActivityLocked(attributionTag, callingPid,
-                            callingUid, token, intent, resolvedType);
+                            callingUid, token, intent, resolvedType, bundle);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index ad0e921..96b69f8 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -29,7 +29,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.AppGlobals;
 import android.app.ApplicationExitInfo;
@@ -376,7 +375,8 @@
 
     @GuardedBy("this")
     public int startAssistantActivityLocked(@Nullable String callingFeatureId, int callingPid,
-            int callingUid, IBinder token, Intent intent, String resolvedType) {
+            int callingUid, IBinder token, Intent intent, String resolvedType,
+            @NonNull Bundle bundle) {
         try {
             if (mActiveSession == null || token != mActiveSession.mToken) {
                 Slog.w(TAG, "startAssistantActivity does not match active session");
@@ -388,10 +388,10 @@
             }
             intent = new Intent(intent);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            final ActivityOptions options = ActivityOptions.makeBasic();
-            options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT);
+            // TODO: make the key public hidden
+            bundle.putInt("android.activity.activityType", ACTIVITY_TYPE_ASSISTANT);
             return mAtm.startAssistantActivity(mComponent.getPackageName(), callingFeatureId,
-                    callingPid, callingUid, intent, resolvedType, options.toBundle(), mUser);
+                    callingPid, callingUid, intent, resolvedType, bundle, mUser);
         } catch (RemoteException e) {
             throw new IllegalStateException("Unexpected remote error", e);
         }
diff --git a/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
index d6f8012..abf2b55 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
@@ -16,6 +16,7 @@
 
 package android.telephony.satellite;
 
+import android.telephony.satellite.ISatelliteDatagramReceiverAck;
 import android.telephony.satellite.SatelliteDatagram;
 
 /**
@@ -24,8 +25,15 @@
  */
 oneway interface ISatelliteDatagramCallback {
     /**
-     * Called when there are incoming datagrams to be received.
-     * @param datagrams Array of datagrams to be received over satellite.
+     * Called when datagrams are received from satellite.
+     *
+     * @param datagramId An id that uniquely identifies incoming datagram.
+     * @param datagram datagram received from satellite.
+     * @param pendingCount Number of datagrams yet to be received from satellite.
+     * @param callback This callback will be used by datagram receiver app to send ack back to
+     *                 Telephony. If the callback is not received within five minutes,
+     *                 Telephony will resend the datagrams.
      */
-    void onSatelliteDatagrams(in SatelliteDatagram[] datagrams);
+    void onSatelliteDatagramReceived(long datagramId, in SatelliteDatagram datagram,
+            int pendingCount, ISatelliteDatagramReceiverAck callback);
 }
diff --git a/telephony/java/android/telephony/satellite/ISatelliteDatagramReceiverAck.aidl b/telephony/java/android/telephony/satellite/ISatelliteDatagramReceiverAck.aidl
new file mode 100644
index 0000000..eeb0ac5
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/ISatelliteDatagramReceiverAck.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 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.telephony.satellite;
+
+import android.telephony.satellite.PointingInfo;
+import android.telephony.satellite.SatelliteDatagram;
+
+/**
+ * Interface for satellite datagram receiver acknowledgement.
+ * @hide
+ */
+oneway interface ISatelliteDatagramReceiverAck {
+     /**
+      * This callback will be used by datagram receiver app to send ack back to
+      * Telephony. If the callback is not received within five minutes,
+      * then Telephony will resend the datagram again.
+      *
+      * @param datagramId An id that uniquely identifies datagram
+      *                   received by satellite datagram receiver app.
+      *                   This should match with datagramId passed in
+      *                   {@link SatelliteDatagramCallback#onSatelliteDatagramReceived(
+      *                   long, SatelliteDatagram, int, ISatelliteDatagramReceiverAck)}.
+      *                   Upon receiving the ack, Telephony will remove the datagram from
+      *                   the persistent memory.
+      */
+    void acknowledgeSatelliteDatagramReceived(in long datagramId);
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
index 484b783..2c3884c 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
@@ -38,10 +38,12 @@
         }
 
         @Override
-        public void onSatelliteDatagrams(SatelliteDatagram[] datagrams) {
+        public void onSatelliteDatagramReceived(long datagramId, SatelliteDatagram datagram,
+                int pendingCount, ISatelliteDatagramReceiverAck callback) {
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                mExecutor.execute(() -> mLocalCallback.onSatelliteDatagrams(datagrams));
+                mExecutor.execute(() -> mLocalCallback.onSatelliteDatagramReceived(datagramId,
+                        datagram, pendingCount, callback));
             } finally {
                 restoreCallingIdentity(callingIdentity);
             }
@@ -54,9 +56,14 @@
 
     /**
      * Called when there are incoming datagrams to be received.
-     * @param datagrams Datagrams to be received over satellite.
+     * @param datagramId An id that uniquely identifies incoming datagram.
+     * @param datagram datagram to be received over satellite.
+     * @param pendingCount Number of datagrams yet to be received by the app.
+     * @param callback This callback will be used by datagram receiver app to send ack back to
+     *                 Telephony.
      */
-    public void onSatelliteDatagrams(SatelliteDatagram[] datagrams) {
+    public void onSatelliteDatagramReceived(long datagramId, SatelliteDatagram datagram,
+            int pendingCount, ISatelliteDatagramReceiverAck callback) {
         // Base Implementation
     }
 
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 8fbf640..dbc0ed9 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -130,7 +130,7 @@
 
     /**
      * Bundle key to get the response from
-     * {@link #requestMaxCharactersPerSatelliteTextMessage(Executor, OutcomeReceiver)}.
+     * {@link #requestMaxSizePerSendingDatagram(Executor, OutcomeReceiver)} .
      * @hide
      */
     public static final String KEY_MAX_CHARACTERS_PER_SATELLITE_TEXT =
@@ -159,6 +159,13 @@
     public static final String KEY_SATELLITE_NEXT_VISIBILITY = "satellite_next_visibility";
 
     /**
+     * Bundle key to get the respoonse from
+     * {@link #sendSatelliteDatagram(long, int, SatelliteDatagram, Executor, OutcomeReceiver)}.
+     * @hide
+     */
+    public static final String KEY_SEND_SATELLITE_DATAGRAM = "send_satellite_datagram";
+
+    /**
      * The request was successfully processed.
      */
     public static final int SATELLITE_ERROR_NONE = 0;
@@ -541,8 +548,8 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface SatelliteModemState {}
 
-    /** Datagram type indicating that the datagram to be sent or received is of type SOS SMS. */
-    public static final int DATAGRAM_TYPE_SOS_SMS = 0;
+    /** Datagram type indicating that the datagram to be sent or received is of type SOS message. */
+    public static final int DATAGRAM_TYPE_SOS_MESSAGE = 0;
 
     /** Datagram type indicating that the datagram to be sent or received is of type
      * location sharing. */
@@ -551,7 +558,7 @@
     @IntDef(
             prefix = "DATAGRAM_TYPE_",
             value = {
-                    DATAGRAM_TYPE_SOS_SMS,
+                    DATAGRAM_TYPE_SOS_MESSAGE,
                     DATAGRAM_TYPE_LOCATION_SHARING,
             })
     @Retention(RetentionPolicy.SOURCE)
@@ -651,19 +658,20 @@
     }
 
     /**
-     * Request to get the maximum number of characters per text message on satellite.
+     * Request to get the maximum number of bytes per datagram that can be sent to satellite.
      *
      * @param executor The executor on which the callback will be called.
      * @param callback The callback object to which the result will be delivered.
      *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
-     *                 will return the maximum number of characters per text message on satellite.
+     *                 will return the maximum number of bytes per datagram that can be sent to
+     *                 satellite.
      *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
      *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    public void requestMaxCharactersPerSatelliteTextMessage(
+    public void requestMaxSizePerSendingDatagram(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Integer, SatelliteException> callback) {
         Objects.requireNonNull(executor);
@@ -693,7 +701,7 @@
                         }
                     }
                 };
-                telephony.requestMaxCharactersPerSatelliteTextMessage(mSubId, receiver);
+                telephony.requestMaxSizePerSendingDatagram(mSubId, receiver);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -1039,7 +1047,8 @@
      *
      * This method requests modem to check if there are any pending datagrams to be received over
      * satellite. If there are any incoming datagrams, they will be received via
-     * {@link SatelliteDatagramCallback#onSatelliteDatagrams(SatelliteDatagram[])})}.
+     * {@link SatelliteDatagramCallback#onSatelliteDatagramReceived(long, SatelliteDatagram, int,
+     *          ISatelliteDatagramReceiverAck)}
      *
      * @param executor The executor on which the result listener will be called.
      * @param resultListener Listener for the {@link SatelliteError} result of the operation.
@@ -1076,39 +1085,60 @@
     /**
      * Send datagram over satellite.
      *
-     * Gateway encodes SOS SMS or location sharing message into a datagram and passes it as input to
-     * this method. Datagram received here will be passed down to modem without any encoding or
-     * encryption.
+     * Gateway encodes SOS message or location sharing message into a datagram and passes it as
+     * input to this method. Datagram received here will be passed down to modem without any
+     * encoding or encryption.
      *
+     * @param datagramId An id that uniquely identifies datagram requested to be sent.
      * @param datagramType datagram type indicating whether the datagram is of type
      *                     SOS_SMS or LOCATION_SHARING.
      * @param datagram encoded gateway datagram which is encrypted by the caller.
      *                 Datagram will be passed down to modem without any encoding or encryption.
      * @param executor The executor on which the result listener will be called.
-     * @param resultListener Listener for the {@link SatelliteError} result of the operation.
+     * @param callback The callback object to which the result will be returned.
+     *                 If datagram is sent successfully, then
+     *                 {@link OutcomeReceiver#onResult(Object)} will return datagramId.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteError}.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    public void sendSatelliteDatagram(@DatagramType int datagramType,
+    public void sendSatelliteDatagram(long datagramId, @DatagramType int datagramType,
             @NonNull SatelliteDatagram datagram, @NonNull @CallbackExecutor Executor executor,
-            @SatelliteError @NonNull Consumer<Integer> resultListener) {
+            @NonNull OutcomeReceiver<Long, SatelliteException> callback) {
         Objects.requireNonNull(datagram);
         Objects.requireNonNull(executor);
-        Objects.requireNonNull(resultListener);
+        Objects.requireNonNull(callback);
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+                ResultReceiver receiver = new ResultReceiver(null) {
                     @Override
-                    public void accept(int result) {
-                        executor.execute(() -> Binder.withCleanCallingIdentity(
-                                () -> resultListener.accept(result)));
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_ERROR_NONE) {
+                            if (resultData.containsKey(KEY_SEND_SATELLITE_DATAGRAM)) {
+                                long resultDatagramId = resultData
+                                        .getLong(KEY_SEND_SATELLITE_DATAGRAM);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(resultDatagramId)));
+                            } else {
+                                loge("KEY_SEND_SATELLITE_DATAGRAM does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(
+                                                new SatelliteException(SATELLITE_REQUEST_FAILED))));
+                            }
+
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
                     }
                 };
-                telephony.sendSatelliteDatagram(mSubId, datagramType, datagram, internalCallback);
+                telephony.sendSatelliteDatagram(mSubId, datagramId, datagramType, datagram,
+                        receiver);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 62e087f..5bf55ef 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2772,15 +2772,15 @@
             in ISatellitePositionUpdateCallback callback);
 
     /**
-     * Request to get the maximum number of characters per text message on satellite.
+     * Request to get the maximum number of bytes per datagram that can be sent to satellite.
      *
      * @param subId The subId of the subscription to get the maximum number of characters for.
      * @param receiver Result receiver to get the error code of the request and the requested
-     *                 maximum number of characters per text message on satellite.
+     *                 maximum number of bytes per datagram that can be sent to satellite.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void requestMaxCharactersPerSatelliteTextMessage(int subId, in ResultReceiver receiver);
+    void requestMaxSizePerSendingDatagram(int subId, in ResultReceiver receiver);
 
     /**
      * Register the subscription with a satellite provider.
@@ -2912,14 +2912,16 @@
     * Send datagram over satellite.
     *
     * @param subId The subId of the subscription to send satellite datagrams for.
+    * @param datagramId An id that uniquely identifies datagram requested to be sent.
     * @param datagramType Type of datagram.
     * @param datagram Datagram to send over satellite.
-    * @param callback The callback to get the error code of the request.
+    * @param receiver Result receiver to get the datagramId if datagram is sent successfully else
+    *                 error code of the request.
     */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void sendSatelliteDatagram(int subId, int datagramType, in SatelliteDatagram datagram,
-            IIntegerConsumer callback);
+    void sendSatelliteDatagram(int subId, long datagramId, int datagramType,
+            in SatelliteDatagram datagram, in ResultReceiver receiver);
 
     /**
      * Request to get whether satellite communication is allowed for the current location.
diff --git a/tools/processors/immutability/TEST_MAPPING b/tools/processors/immutability/TEST_MAPPING
deleted file mode 100644
index 4e8e238..0000000
--- a/tools/processors/immutability/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-    "presubmit": [
-        {
-            "name": "ImmutabilityAnnotationProcessorUnitTests"
-        }
-    ]
-}