Merge "Introduce IInputMethodManager.InitParams"
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index e8bcfd2..d6b246a 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -335,18 +335,12 @@
             "REORDER_ALARMS_FOR_TARE",
     });
 
-    BroadcastOptions mOptsWithFgs = makeBasicAlarmBroadcastOptions();
-    BroadcastOptions mOptsWithFgsForAlarmClock = makeBasicAlarmBroadcastOptions();
-    BroadcastOptions mOptsWithoutFgs = makeBasicAlarmBroadcastOptions();
-    BroadcastOptions mOptsTimeBroadcast = makeBasicAlarmBroadcastOptions();
+    BroadcastOptions mOptsWithFgs = BroadcastOptions.makeBasic();
+    BroadcastOptions mOptsWithFgsForAlarmClock = BroadcastOptions.makeBasic();
+    BroadcastOptions mOptsWithoutFgs = BroadcastOptions.makeBasic();
+    BroadcastOptions mOptsTimeBroadcast = BroadcastOptions.makeBasic();
     ActivityOptions mActivityOptsRestrictBal = ActivityOptions.makeBasic();
-    BroadcastOptions mBroadcastOptsRestrictBal = makeBasicAlarmBroadcastOptions();
-
-    private static BroadcastOptions makeBasicAlarmBroadcastOptions() {
-        final BroadcastOptions b = BroadcastOptions.makeBasic();
-        b.setAlarmBroadcast(true);
-        return b;
-    }
+    BroadcastOptions mBroadcastOptsRestrictBal = BroadcastOptions.makeBasic();
 
     // TODO(b/172085676): Move inside alarm store.
     private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b1d458c..3a808c7 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -167,6 +167,7 @@
     method public void setEligibleForLegacyPermissionPrompt(boolean);
     method public static void setExitTransitionTimeout(long);
     method public void setLaunchActivityType(int);
+    method public void setLaunchTaskDisplayAreaFeatureId(int);
     method public void setLaunchWindowingMode(int);
     method public void setLaunchedFromBubble(boolean);
     method public void setTaskAlwaysOnTop(boolean);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index d6441a2..00ab559 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -223,6 +224,14 @@
             "android.activity.launchTaskDisplayAreaToken";
 
     /**
+     * The task display area feature id the activity should be launched into.
+     * @see #setLaunchTaskDisplayAreaFeatureId(int)
+     * @hide
+     */
+    private static final String KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID =
+            "android.activity.launchTaskDisplayAreaFeatureId";
+
+    /**
      * The root task token the activity should be launched into.
      * @see #setLaunchRootTask(WindowContainerToken)
      * @hide
@@ -432,6 +441,7 @@
     private int mLaunchDisplayId = INVALID_DISPLAY;
     private int mCallerDisplayId = INVALID_DISPLAY;
     private WindowContainerToken mLaunchTaskDisplayArea;
+    private int mLaunchTaskDisplayAreaFeatureId = FEATURE_UNDEFINED;
     private WindowContainerToken mLaunchRootTask;
     private IBinder mLaunchTaskFragmentToken;
     @WindowConfiguration.WindowingMode
@@ -1225,6 +1235,8 @@
         mLaunchDisplayId = opts.getInt(KEY_LAUNCH_DISPLAY_ID, INVALID_DISPLAY);
         mCallerDisplayId = opts.getInt(KEY_CALLER_DISPLAY_ID, INVALID_DISPLAY);
         mLaunchTaskDisplayArea = opts.getParcelable(KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN);
+        mLaunchTaskDisplayAreaFeatureId = opts.getInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID,
+                FEATURE_UNDEFINED);
         mLaunchRootTask = opts.getParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN);
         mLaunchTaskFragmentToken = opts.getBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN);
         mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED);
@@ -1585,6 +1597,23 @@
     }
 
     /** @hide */
+    public int getLaunchTaskDisplayAreaFeatureId() {
+        return mLaunchTaskDisplayAreaFeatureId;
+    }
+
+    /**
+     * Sets the TaskDisplayArea feature Id the activity should launch into.
+     * Note: It is possible to have TaskDisplayAreas with the same featureId on multiple displays.
+     * If launch display id is not specified, the TaskDisplayArea on the default display will be
+     * used.
+     * @hide
+     */
+    @TestApi
+    public void setLaunchTaskDisplayAreaFeatureId(int launchTaskDisplayAreaFeatureId) {
+        mLaunchTaskDisplayAreaFeatureId = launchTaskDisplayAreaFeatureId;
+    }
+
+    /** @hide */
     public WindowContainerToken getLaunchRootTask() {
         return mLaunchRootTask;
     }
@@ -2075,6 +2104,9 @@
         if (mLaunchTaskDisplayArea != null) {
             b.putParcelable(KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN, mLaunchTaskDisplayArea);
         }
+        if (mLaunchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
+            b.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, mLaunchTaskDisplayAreaFeatureId);
+        }
         if (mLaunchRootTask != null) {
             b.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, mLaunchRootTask);
         }
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index f0e1448..56f8760 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -53,7 +53,6 @@
     private String[] mRequireNoneOfPermissions;
     private long mRequireCompatChangeId = CHANGE_INVALID;
     private boolean mRequireCompatChangeEnabled = true;
-    private boolean mIsAlarmBroadcast = false;
     private long mIdForResponseEvent;
 
     /**
@@ -150,13 +149,6 @@
             "android:broadcast.requireCompatChangeEnabled";
 
     /**
-     * Corresponds to {@link #setAlarmBroadcast(boolean)}
-     * @hide
-     */
-    public static final String KEY_ALARM_BROADCAST =
-            "android:broadcast.is_alarm";
-
-    /**
      * @hide
      * @deprecated Use {@link android.os.PowerExemptionManager#
      * TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED} instead.
@@ -215,7 +207,6 @@
         mRequireCompatChangeId = opts.getLong(KEY_REQUIRE_COMPAT_CHANGE_ID, CHANGE_INVALID);
         mRequireCompatChangeEnabled = opts.getBoolean(KEY_REQUIRE_COMPAT_CHANGE_ENABLED, true);
         mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT);
-        mIsAlarmBroadcast = opts.getBoolean(KEY_ALARM_BROADCAST, false);
     }
 
     /**
@@ -507,27 +498,6 @@
         mRequireCompatChangeEnabled = true;
     }
 
-    /**
-     * When set, this broadcast will be understood as having originated from an
-     * alarm going off.  Only the OS itself can use this option; uses by other
-     * senders will be ignored.
-     * @hide
-     *
-     * @param senderIsAlarm Whether the broadcast is alarm-triggered.
-     */
-    public void setAlarmBroadcast(boolean senderIsAlarm) {
-        mIsAlarmBroadcast = senderIsAlarm;
-    }
-
-    /**
-     * Did this broadcast originate from an alarm triggering?
-     * @return true if this broadcast is an alarm message, false otherwise
-     * @hide
-     */
-    public boolean isAlarmBroadcast() {
-        return mIsAlarmBroadcast;
-    }
-
     /** {@hide} */
     public long getRequireCompatChangeId() {
         return mRequireCompatChangeId;
@@ -590,9 +560,6 @@
             b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE, mTemporaryAppAllowlistReasonCode);
             b.putString(KEY_TEMPORARY_APP_ALLOWLIST_REASON, mTemporaryAppAllowlistReason);
         }
-        if (mIsAlarmBroadcast) {
-            b.putBoolean(KEY_ALARM_BROADCAST, true);
-        }
         if (mMinManifestReceiverApiLevel != 0) {
             b.putInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, mMinManifestReceiverApiLevel);
         }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 41791af..2f79cae 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -43,6 +43,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.SystemProperties;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
@@ -70,6 +71,8 @@
 public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback,
         ActivityEmbeddingComponent {
     static final String TAG = "SplitController";
+    static final boolean ENABLE_SHELL_TRANSITIONS =
+            SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
 
     @VisibleForTesting
     @GuardedBy("mLock")
@@ -332,6 +335,11 @@
      * bounds is large enough for at least one split rule.
      */
     private void updateAnimationOverride(@NonNull TaskContainer taskContainer) {
+        if (ENABLE_SHELL_TRANSITIONS) {
+            // TODO(b/207070762): cleanup with legacy app transition
+            // Animation will be handled by WM Shell with Shell transition enabled.
+            return;
+        }
         if (!taskContainer.isTaskBoundsInitialized()
                 || !taskContainer.isWindowingModeInitialized()) {
             // We don't know about the Task bounds/windowingMode yet.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index d4298b8..49eca63 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -25,11 +25,13 @@
 import androidx.test.uiautomator.BySelector
 import androidx.test.uiautomator.UiDevice
 import androidx.test.uiautomator.Until
+import com.android.launcher3.tapl.LauncherInstrumentation
 import com.android.server.wm.traces.common.FlickerComponentName
 import com.android.server.wm.traces.parser.toFlickerComponent
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
 import com.android.wm.shell.flicker.testapp.Components
+import org.junit.Assert
 
 class SplitScreenHelper(
     instrumentation: Instrumentation,
@@ -187,5 +189,23 @@
                 SystemClock.sleep(GESTURE_STEP_MS)
             }
         }
+
+        fun createShortcutOnHotseatIfNotExist(
+            taplInstrumentation: LauncherInstrumentation,
+            appName: String
+        ) {
+            taplInstrumentation.workspace
+                .deleteAppIcon(taplInstrumentation.workspace.getHotseatAppIcon(0))
+            val allApps = taplInstrumentation.workspace.switchToAllApps()
+            allApps.freeze()
+            try {
+                val appIconSrc = allApps.getAppIcon(appName)
+                Assert.assertNotNull("Unable to find app icon", appIconSrc)
+                val appIconDest = appIconSrc.dragToHotseat(0)
+                Assert.assertNotNull("Unable to drag app icon on hotseat", appIconDest)
+            } finally {
+                allApps.unfreeze()
+            }
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
new file mode 100644
index 0000000..60b0f8e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test enter split screen by dragging app icon from taskbar.
+ * This test is only for large screen devices.
+ *
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromTaskbar`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class EnterSplitScreenByDragFromTaskbar(
+    testSpec: FlickerTestParameter
+) : SplitScreenBase(testSpec) {
+
+    @Before
+    fun before() {
+        Assume.assumeTrue(taplInstrumentation.isTablet)
+    }
+
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            super.transition(this)
+            setup {
+                eachRun {
+                    taplInstrumentation.goHome()
+                    SplitScreenHelper.createShortcutOnHotseatIfNotExist(
+                        taplInstrumentation, secondaryApp.appName
+                    )
+                    primaryApp.launchViaIntent(wmHelper)
+                }
+            }
+            transitions {
+                taplInstrumentation.launchedAppState.taskbar
+                    .getAppIcon(secondaryApp.appName)
+                    .dragToSplitscreen(
+                        secondaryApp.`package`,
+                        primaryApp.`package`
+                    )
+            }
+        }
+
+    @Presubmit
+    @Test
+    fun dividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+
+    @Presubmit
+    @Test
+    fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp.component)
+
+    @Presubmit
+    @Test
+    fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp.component)
+
+    @Presubmit
+    @Test
+    fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+        testSpec.endRotation, primaryApp.component, false /* splitLeftTop */
+    )
+
+    @Presubmit
+    @Test
+    fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
+        testSpec.endRotation, secondaryApp.component, true /* splitLeftTop */
+    )
+
+    @Presubmit
+    @Test
+    fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp.component)
+
+    @Presubmit
+    @Test
+    fun secondaryAppWindowBecomesVisible() =
+        testSpec.appWindowBecomesVisible(secondaryApp.component)
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<FlickerTestParameter> {
+            return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+                repetitions = SplitScreenHelper.TEST_REPETITIONS,
+                supportedNavigationModes =
+                    listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 90cca15..d0da18a 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -27,6 +27,8 @@
 import com.android.systemui.log.LogcatEchoTracker;
 import com.android.systemui.log.LogcatEchoTrackerDebug;
 import com.android.systemui.log.LogcatEchoTrackerProd;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
+import com.android.systemui.util.Compile;
 
 import dagger.Module;
 import dagger.Provides;
@@ -48,8 +50,14 @@
     @Provides
     @SysUISingleton
     @NotificationLog
-    public static LogBuffer provideNotificationsLogBuffer(LogBufferFactory factory) {
-        return factory.create("NotifLog", 1000 /* maxSize */, false /* systrace */);
+    public static LogBuffer provideNotificationsLogBuffer(
+            LogBufferFactory factory,
+            NotifPipelineFlags notifPipelineFlags) {
+        int maxSize = 1000;
+        if (Compile.IS_DEBUG && notifPipelineFlags.isDevLoggingEnabled()) {
+            maxSize *= 10;
+        }
+        return factory.create("NotifLog", maxSize, false /* systrace */);
     }
 
     /** Provides a logging buffer for logs related to heads up presentation of notifications. */
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 6a01df6..3b5b333 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
@@ -310,7 +310,7 @@
         }
 
         locallyDismissNotifications(entriesToLocallyDismiss);
-        dispatchEventsAndRebuildList();
+        dispatchEventsAndRebuildList("dismissNotifications");
     }
 
     /**
@@ -354,7 +354,7 @@
         }
 
         locallyDismissNotifications(entries);
-        dispatchEventsAndRebuildList();
+        dispatchEventsAndRebuildList("dismissAllNotifications");
     }
 
     /**
@@ -401,7 +401,7 @@
 
         postNotification(sbn, requireRanking(rankingMap, sbn.getKey()));
         applyRanking(rankingMap);
-        dispatchEventsAndRebuildList();
+        dispatchEventsAndRebuildList("onNotificationPosted");
     }
 
     private void onNotificationGroupPosted(List<CoalescedEvent> batch) {
@@ -412,7 +412,7 @@
         for (CoalescedEvent event : batch) {
             postNotification(event.getSbn(), event.getRanking());
         }
-        dispatchEventsAndRebuildList();
+        dispatchEventsAndRebuildList("onNotificationGroupPosted");
     }
 
     private void onNotificationRemoved(
@@ -433,14 +433,14 @@
         entry.mCancellationReason = reason;
         tryRemoveNotification(entry);
         applyRanking(rankingMap);
-        dispatchEventsAndRebuildList();
+        dispatchEventsAndRebuildList("onNotificationRemoved");
     }
 
     private void onNotificationRankingUpdate(RankingMap rankingMap) {
         Assert.isMainThread();
         mEventQueue.add(new RankingUpdatedEvent(rankingMap));
         applyRanking(rankingMap);
-        dispatchEventsAndRebuildList();
+        dispatchEventsAndRebuildList("onNotificationRankingUpdate");
     }
 
     private void onNotificationChannelModified(
@@ -450,7 +450,7 @@
             int modificationType) {
         Assert.isMainThread();
         mEventQueue.add(new ChannelChangedEvent(pkgName, user, channel, modificationType));
-        dispatchEventsAndRebuildList();
+        dispatchEventsAndRebuildList("onNotificationChannelModified");
     }
 
     private void onNotificationsInitialized() {
@@ -610,7 +610,7 @@
         mEventQueue.add(new RankingAppliedEvent());
     }
 
-    private void dispatchEventsAndRebuildList() {
+    private void dispatchEventsAndRebuildList(String reason) {
         Trace.beginSection("NotifCollection.dispatchEventsAndRebuildList");
         mAmDispatchingToOtherCode = true;
         while (!mEventQueue.isEmpty()) {
@@ -619,7 +619,7 @@
         mAmDispatchingToOtherCode = false;
 
         if (mBuildListener != null) {
-            mBuildListener.onBuildList(mReadOnlyNotificationSet);
+            mBuildListener.onBuildList(mReadOnlyNotificationSet, reason);
         }
         Trace.endSection();
     }
@@ -654,7 +654,7 @@
 
         if (!isLifetimeExtended(entry)) {
             if (tryRemoveNotification(entry)) {
-                dispatchEventsAndRebuildList();
+                dispatchEventsAndRebuildList("onEndLifetimeExtension");
             }
         }
     }
@@ -963,7 +963,7 @@
         mEventQueue.add(new EntryUpdatedEvent(entry, false /* fromSystem */));
 
         // Skip the applyRanking step and go straight to dispatching the events
-        dispatchEventsAndRebuildList();
+        dispatchEventsAndRebuildList("updateNotificationInternally");
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 26d2ee3..8a18d31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -304,11 +304,11 @@
     private final CollectionReadyForBuildListener mReadyForBuildListener =
             new CollectionReadyForBuildListener() {
                 @Override
-                public void onBuildList(Collection<NotificationEntry> entries) {
+                public void onBuildList(Collection<NotificationEntry> entries, String reason) {
                     Assert.isMainThread();
                     mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
 
-                    mLogger.logOnBuildList();
+                    mLogger.logOnBuildList(reason);
                     mAllEntries = entries;
                     mChoreographer.schedule();
                 }
@@ -456,7 +456,8 @@
         mLogger.logEndBuildList(
                 mIterationCount,
                 mReadOnlyNotifList.size(),
-                countChildren(mReadOnlyNotifList));
+                countChildren(mReadOnlyNotifList),
+                /* enforcedVisualStability */ !mNotifStabilityManager.isEveryChangeAllowed());
         if (mAlwaysLogList || mIterationCount % 10 == 0) {
             Trace.beginSection("ShadeListBuilder.logFinalList");
             mLogger.logFinalList(mNotifList);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index 8d1759b..10a627d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -21,31 +21,42 @@
 import com.android.systemui.log.LogLevel.INFO
 import com.android.systemui.log.LogLevel.WARNING
 import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
 import com.android.systemui.statusbar.notification.logKey
+import com.android.systemui.util.Compile
 import javax.inject.Inject
 
 class ShadeListBuilderLogger @Inject constructor(
+    notifPipelineFlags: NotifPipelineFlags,
     @NotificationLog private val buffer: LogBuffer
 ) {
-    fun logOnBuildList() {
+    fun logOnBuildList(reason: String?) {
         buffer.log(TAG, INFO, {
+            str1 = reason
         }, {
-            "Request received from NotifCollection"
+            "Request received from NotifCollection for $str1"
         })
     }
 
-    fun logEndBuildList(buildId: Int, topLevelEntries: Int, numChildren: Int) {
+    fun logEndBuildList(
+        buildId: Int,
+        topLevelEntries: Int,
+        numChildren: Int,
+        enforcedVisualStability: Boolean
+    ) {
         buffer.log(TAG, INFO, {
             long1 = buildId.toLong()
             int1 = topLevelEntries
             int2 = numChildren
+            bool1 = enforcedVisualStability
         }, {
-            "(Build $long1) Build complete ($int1 top-level entries, $int2 children)"
+            "(Build $long1) Build complete ($int1 top-level entries, $int2 children)" +
+                    " enforcedVisualStability=$bool1"
         })
     }
 
@@ -280,6 +291,8 @@
         })
     }
 
+    val logRankInFinalList = Compile.IS_DEBUG && notifPipelineFlags.isDevLoggingEnabled()
+
     fun logFinalList(entries: List<ListEntry>) {
         if (entries.isEmpty()) {
             buffer.log(TAG, DEBUG, {}, { "(empty list)" })
@@ -289,16 +302,20 @@
             buffer.log(TAG, DEBUG, {
                 int1 = i
                 str1 = entry.logKey
+                bool1 = logRankInFinalList
+                int2 = entry.representativeEntry!!.ranking.rank
             }, {
-                "[$int1] $str1"
+                "[$int1] $str1".let { if (bool1) "$it rank=$int2" else it }
             })
 
             if (entry is GroupEntry) {
                 entry.summary?.let {
                     buffer.log(TAG, DEBUG, {
                         str1 = it.logKey
+                        bool1 = logRankInFinalList
+                        int2 = it.ranking.rank
                     }, {
-                        "  [*] $str1 (summary)"
+                        "  [*] $str1 (summary)".let { if (bool1) "$it rank=$int2" else it }
                     })
                 }
                 for (j in entry.children.indices) {
@@ -306,8 +323,10 @@
                     buffer.log(TAG, DEBUG, {
                         int1 = j
                         str1 = child.logKey
+                        bool1 = logRankInFinalList
+                        int2 = child.ranking.rank
                     }, {
-                        "  [$int1] $str1"
+                        "  [$int1] $str1".let { if (bool1) "$it rank=$int2" else it }
                     })
                 }
             }
@@ -318,4 +337,4 @@
         buffer.log(TAG, INFO, {}) { "Suppressing pipeline run during animation." }
 }
 
-private const val TAG = "ShadeListBuilder"
\ No newline at end of file
+private const val TAG = "ShadeListBuilder"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CollectionReadyForBuildListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CollectionReadyForBuildListener.java
index 4023474..941b2ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CollectionReadyForBuildListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CollectionReadyForBuildListener.java
@@ -29,5 +29,5 @@
      * Called by the NotifCollection to indicate that something in the collection has changed and
      * that the list builder should regenerate the list.
      */
-    void onBuildList(Collection<NotificationEntry> entries);
+    void onBuildList(Collection<NotificationEntry> entries, String reason);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
index 4e9030f..dac8a0b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
@@ -92,7 +92,15 @@
     }
 
     private void updateConditionMetState(Condition condition) {
-        mConditions.get(condition).stream().forEach(token -> mSubscriptions.get(token).update());
+        final ArraySet<Subscription.Token> subscriptions = mConditions.get(condition);
+
+        // It's possible the condition was removed between the time the callback occurred and
+        // update was executed on the main thread.
+        if (subscriptions == null) {
+            return;
+        }
+
+        subscriptions.stream().forEach(token -> mSubscriptions.get(token).update());
     }
 
     /**
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 f286349..af43826 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
@@ -1684,9 +1684,9 @@
         return new CollectionEvent(rawEvent, requireNonNull(mEntryCaptor.getValue()));
     }
 
-    private void verifyBuiltList(Collection<NotificationEntry> list) {
-        verify(mBuildListener).onBuildList(mBuildListCaptor.capture());
-        assertEquals(new ArraySet<>(list), new ArraySet<>(mBuildListCaptor.getValue()));
+    private void verifyBuiltList(Collection<NotificationEntry> expectedList) {
+        verify(mBuildListener).onBuildList(mBuildListCaptor.capture(), any());
+        assertThat(mBuildListCaptor.getValue()).containsExactly(expectedList.toArray());
     }
 
     private static class RecordingCollectionListener implements NotifCollectionListener {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 9546058..555adfd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -570,7 +570,7 @@
         assertTrue(entry.hasFinishedInitialization());
 
         // WHEN the pipeline is kicked off
-        mReadyForBuildListener.onBuildList(singletonList(entry));
+        mReadyForBuildListener.onBuildList(singletonList(entry), "test");
         mPipelineChoreographer.runIfScheduled();
 
         // THEN the entry's initialization time is reset
@@ -2092,7 +2092,7 @@
             mPendingSet.clear();
         }
 
-        mReadyForBuildListener.onBuildList(mEntrySet);
+        mReadyForBuildListener.onBuildList(mEntrySet, "test");
         mPipelineChoreographer.runIfScheduled();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
index 1e35b0f..125b362 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
@@ -159,6 +159,24 @@
         Mockito.clearInvocations(callback);
     }
 
+    // Ensure that updating a callback that is removed doesn't result in an exception due to the
+    // absence of the condition.
+    @Test
+    public void testUpdateRemovedCallback() {
+        final Monitor.Callback callback1 =
+                mock(Monitor.Callback.class);
+        final Monitor.Subscription.Token subscription1 =
+                mConditionMonitor.addSubscription(getDefaultBuilder(callback1).build());
+        ArgumentCaptor<Condition.Callback> monitorCallback =
+                ArgumentCaptor.forClass(Condition.Callback.class);
+        mExecutor.runAllReady();
+        verify(mCondition1).addCallback(monitorCallback.capture());
+        // This will execute first before the handler for onConditionChanged.
+        mConditionMonitor.removeSubscription(subscription1);
+        monitorCallback.getValue().onConditionChanged(mCondition1);
+        mExecutor.runAllReady();
+    }
+
     @Test
     public void addCallback_addFirstCallback_addCallbackToAllConditions() {
         final Monitor.Callback callback1 =
diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
index f01bdf2..4a99009 100644
--- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
+++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
@@ -200,6 +200,9 @@
             try {
                 sendMessage(message, sequence, data);
             } catch (IOException e) {
+                synchronized (mPendingRequests) {
+                    mPendingRequests.remove(sequence);
+                }
                 pending.completeExceptionally(e);
             }
             return pending;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e49e63a..60286be 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -14445,19 +14445,6 @@
             final int callingPid = Binder.getCallingPid();
             final int callingUid = Binder.getCallingUid();
 
-            // Non-system callers can't declare that a broadcast is alarm-related.
-            // The PendingIntent invocation case is handled in PendingIntentRecord.
-            if (bOptions != null && callingUid != SYSTEM_UID) {
-                if (bOptions.containsKey(BroadcastOptions.KEY_ALARM_BROADCAST)) {
-                    if (DEBUG_BROADCAST) {
-                        Slog.w(TAG, "Non-system caller " + callingUid
-                                + " may not flag broadcast as alarm-related");
-                    }
-                    throw new SecurityException(
-                            "Non-system callers may not flag broadcasts as alarm-related");
-                }
-            }
-
             final long origId = Binder.clearCallingIdentity();
             try {
                 return broadcastIntentLocked(callerApp,
@@ -14471,7 +14458,6 @@
         }
     }
 
-    // Not the binder call surface
     int broadcastIntentInPackage(String packageName, @Nullable String featureId, int uid,
             int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 2ec744f..a3ae35e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -24,6 +24,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
 
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
@@ -175,6 +176,7 @@
     private String mAgent;  // Agent to attach on startup.
     private boolean mAttachAgentDuringBind;  // Whether agent should be attached late.
     private int mDisplayId;
+    private int mTaskDisplayAreaFeatureId;
     private int mWindowingMode;
     private int mActivityType;
     private int mTaskId;
@@ -368,6 +370,7 @@
         mStreaming = false;
         mUserId = defUser;
         mDisplayId = INVALID_DISPLAY;
+        mTaskDisplayAreaFeatureId = FEATURE_UNDEFINED;
         mWindowingMode = WINDOWING_MODE_UNDEFINED;
         mActivityType = ACTIVITY_TYPE_UNDEFINED;
         mTaskId = INVALID_TASK_ID;
@@ -423,6 +426,8 @@
                     mReceiverPermission = getNextArgRequired();
                 } else if (opt.equals("--display")) {
                     mDisplayId = Integer.parseInt(getNextArgRequired());
+                } else if (opt.equals("--task-display-area-feature-id")) {
+                    mTaskDisplayAreaFeatureId = Integer.parseInt(getNextArgRequired());
                 } else if (opt.equals("--windowingMode")) {
                     mWindowingMode = Integer.parseInt(getNextArgRequired());
                 } else if (opt.equals("--activityType")) {
@@ -554,6 +559,12 @@
                 options = ActivityOptions.makeBasic();
                 options.setLaunchDisplayId(mDisplayId);
             }
+            if (mTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
+                if (options == null) {
+                    options = ActivityOptions.makeBasic();
+                }
+                options.setLaunchTaskDisplayAreaFeatureId(mTaskDisplayAreaFeatureId);
+            }
             if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
                 if (options == null) {
                     options = ActivityOptions.makeBasic();
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index ae91d75..19ffc173 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -70,7 +70,6 @@
     final boolean callerInstantApp; // caller is an Instant App?
     final boolean ordered;  // serialize the send to receivers?
     final boolean sticky;   // originated from existing sticky data?
-    final boolean alarm;    // originated from an alarm triggering?
     final boolean initialSticky; // initial broadcast from register to sticky?
     final int userId;       // user id this broadcast was for
     final String resolvedType; // the resolved data type
@@ -306,7 +305,6 @@
         this.allowBackgroundActivityStarts = allowBackgroundActivityStarts;
         mBackgroundActivityStartsToken = backgroundActivityStartsToken;
         this.timeoutExempt = timeoutExempt;
-        alarm = options != null && options.isAlarmBroadcast();
     }
 
     /**
@@ -359,7 +357,6 @@
         allowBackgroundActivityStarts = from.allowBackgroundActivityStarts;
         mBackgroundActivityStartsToken = from.mBackgroundActivityStartsToken;
         timeoutExempt = from.timeoutExempt;
-        alarm = from.alarm;
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 63b8acd..4044cce 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -18,7 +18,6 @@
 
 import static android.app.ActivityManager.START_SUCCESS;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
@@ -35,7 +34,6 @@
 import android.os.IBinder;
 import android.os.PowerWhitelistManager;
 import android.os.PowerWhitelistManager.ReasonCode;
-import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.TransactionTooLargeException;
@@ -418,22 +416,6 @@
 
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
-
-        // Only system senders can declare a broadcast to be alarm-originated.  We check
-        // this here rather than in the general case handling below to fail before the other
-        // invocation side effects such as allowlisting.
-        if (callingUid != Process.SYSTEM_UID
-                && key.type == ActivityManager.INTENT_SENDER_BROADCAST) {
-            if (options.containsKey(BroadcastOptions.KEY_ALARM_BROADCAST)) {
-                if (DEBUG_BROADCAST_LIGHT) {
-                    Slog.w(TAG, "Non-system caller " + callingUid
-                            + " may not flag broadcast as alarm-related");
-                }
-                throw new SecurityException(
-                        "Non-system callers may not flag broadcasts as alarm-related");
-            }
-        }
-
         final long origId = Binder.clearCallingIdentity();
 
         int res = START_SUCCESS;
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 157057d..aaa9ee5 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -337,6 +337,8 @@
     static final int AUDIO_CODEC_WMAPRO = 0xE; // Support WMA-Pro
     static final int AUDIO_CODEC_MAX = 0xF;
 
+    static final int AUDIO_FORMAT_MASK = 0b0111_1000;
+
     @StringDef({
         AUDIO_DEVICE_ARC_IN,
         AUDIO_DEVICE_SPDIF,
diff --git a/services/core/java/com/android/server/hdmi/RequestSadAction.java b/services/core/java/com/android/server/hdmi/RequestSadAction.java
index 23aaf32..0188e96 100644
--- a/services/core/java/com/android/server/hdmi/RequestSadAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestSadAction.java
@@ -200,8 +200,14 @@
     }
 
     private boolean isValidCodec(byte codec) {
-        return Constants.AUDIO_CODEC_NONE < (codec & 0xFF)
-                && (codec & 0xFF) <= Constants.AUDIO_CODEC_MAX;
+        // Bit 7 needs to be 0.
+        if ((codec & (1 << 7)) != 0) {
+            return false;
+        }
+        // Bit [6, 3] is the audio format code.
+        int audioFormatCode = (codec & Constants.AUDIO_FORMAT_MASK) >> 3;
+        return Constants.AUDIO_CODEC_NONE < audioFormatCode
+                && audioFormatCode <= Constants.AUDIO_CODEC_MAX;
     }
 
     private void updateResult(byte[] sad) {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index b0b9e61c..075fb47 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -163,7 +163,8 @@
                 return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
             }
 
-            if (PackageManagerServiceUtils.isSystemApp(uninstalledPs)) {
+            if (PackageManagerServiceUtils.isUpdatedSystemApp(uninstalledPs)
+                    && ((deleteFlags & PackageManager.DELETE_SYSTEM_APP) == 0)) {
                 UserInfo userInfo = mUserManagerInternal.getUserInfo(userId);
                 if (userInfo == null || (!userInfo.isAdmin() && !mUserManagerInternal.getUserInfo(
                         mUserManagerInternal.getProfileParentId(userId)).isAdmin())) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 9637aca4c..c92cf33 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -79,6 +79,10 @@
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
 import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
+import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
+import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
+import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK;
+import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import android.annotation.NonNull;
@@ -132,6 +136,7 @@
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
 import com.android.server.wm.LaunchParamsController.LaunchParams;
+import com.android.server.wm.TaskFragment.EmbeddingCheckResult;
 
 import java.io.PrintWriter;
 import java.text.DateFormat;
@@ -2074,24 +2079,6 @@
             }
         }
 
-        if (mInTaskFragment != null && !canEmbedActivity(mInTaskFragment, r, newTask, targetTask)) {
-            final StringBuilder errorMsg = new StringBuilder("Permission denied: Cannot embed " + r
-                    + " to " + mInTaskFragment.getTask() + ". newTask=" + newTask + ", targetTask= "
-                    + targetTask);
-            if (newTask && isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE,
-                    LAUNCH_SINGLE_INSTANCE_PER_TASK, LAUNCH_SINGLE_TASK)) {
-                errorMsg.append("\nActivity tries to launch on a new task because the launch mode"
-                        + " is " + launchModeToString(mLaunchMode));
-            } else if (newTask && (mLaunchFlags & (FLAG_ACTIVITY_NEW_DOCUMENT
-                    | FLAG_ACTIVITY_NEW_TASK)) != 0) {
-                errorMsg.append("\nActivity tries to launch on a new task because the launch flags"
-                        + " contains FLAG_ACTIVITY_NEW_DOCUMENT or FLAG_ACTIVITY_NEW_TASK. "
-                        + "mLaunchFlag=" + mLaunchFlags);
-            }
-            Slog.e(TAG, errorMsg.toString());
-            return START_PERMISSION_DENIED;
-        }
-
         // Do not start the activity if target display's DWPC does not allow it.
         // We can't return fatal error code here because it will crash the caller of
         // startActivity() if they don't catch the exception. We don't expect 3P apps to make
@@ -2118,19 +2105,21 @@
     }
 
     /**
-     * Return {@code true} if an activity can be embedded to the TaskFragment.
+     * Returns whether embedding of {@code starting} is allowed.
+     *
      * @param taskFragment the TaskFragment for embedding.
      * @param starting the starting activity.
-     * @param newTask whether the starting activity is going to be launched on a new task.
      * @param targetTask the target task for launching activity, which could be different from
      *                   the one who hosting the embedding.
      */
-    private boolean canEmbedActivity(@NonNull TaskFragment taskFragment,
-            @NonNull ActivityRecord starting, boolean newTask, Task targetTask) {
+    @VisibleForTesting
+    @EmbeddingCheckResult
+    static int canEmbedActivity(@NonNull TaskFragment taskFragment,
+            @NonNull ActivityRecord starting, @NonNull Task targetTask) {
         final Task hostTask = taskFragment.getTask();
         // Not allowed embedding a separate task or without host task.
-        if (hostTask == null || newTask || targetTask != hostTask) {
-            return false;
+        if (hostTask == null || targetTask != hostTask) {
+            return EMBEDDING_DISALLOWED_NEW_TASK;
         }
 
         return taskFragment.isAllowedToEmbedActivity(starting);
@@ -2970,19 +2959,16 @@
         mIntentDelivered = true;
     }
 
+    /** Places {@link #mStartActivity} in {@code task} or an embedded {@link TaskFragment}. */
     private void addOrReparentStartingActivity(@NonNull Task task, String reason) {
         TaskFragment newParent = task;
         if (mInTaskFragment != null) {
-            // TODO(b/234351413): remove remaining embedded Task logic.
-            // mInTaskFragment is created and added to the leaf task by task fragment organizer's
-            // request. If the task was resolved and different than mInTaskFragment, reparent the
-            // task to mInTaskFragment for embedding.
-            if (mInTaskFragment.getTask() != task) {
-                if (shouldReparentInTaskFragment(task)) {
-                    task.reparent(mInTaskFragment, POSITION_TOP);
-                }
-            } else {
+            int embeddingCheckResult = canEmbedActivity(mInTaskFragment, mStartActivity, task);
+            if (embeddingCheckResult == EMBEDDING_ALLOWED) {
                 newParent = mInTaskFragment;
+            } else {
+                // Start mStartActivity to task instead if it can't be embedded to mInTaskFragment.
+                sendCanNotEmbedActivityError(mInTaskFragment, embeddingCheckResult);
             }
         } else {
             TaskFragment candidateTf = mAddingToTaskFragment != null ? mAddingToTaskFragment : null;
@@ -2994,20 +2980,12 @@
                 }
             }
             if (candidateTf != null && candidateTf.isEmbedded()
-                    && canEmbedActivity(candidateTf, mStartActivity, false /* newTask */, task)) {
+                    && canEmbedActivity(candidateTf, mStartActivity, task) == EMBEDDING_ALLOWED) {
                 // Use the embedded TaskFragment of the top activity as the new parent if the
                 // activity can be embedded.
                 newParent = candidateTf;
             }
         }
-        // Start Activity to the Task if mStartActivity's min dimensions are not satisfied.
-        if (newParent.isEmbedded() && newParent.smallerThanMinDimension(mStartActivity)) {
-            reason += " - MinimumDimensionViolation";
-            mService.mWindowOrganizerController.sendMinimumDimensionViolation(
-                    newParent, mStartActivity.getMinDimensions(), mRequest.errorCallbackToken,
-                    reason);
-            newParent = task;
-        }
         if (mStartActivity.getTaskFragment() == null
                 || mStartActivity.getTaskFragment() == newParent) {
             newParent.addChild(mStartActivity, POSITION_TOP);
@@ -3016,16 +2994,41 @@
         }
     }
 
-    private boolean shouldReparentInTaskFragment(Task task) {
-        // The task has not been embedded. We should reparent the task to TaskFragment.
-        if (!task.isEmbedded()) {
-            return true;
+    /**
+     * Notifies the client side that {@link #mStartActivity} cannot be embedded to
+     * {@code taskFragment}.
+     */
+    private void sendCanNotEmbedActivityError(TaskFragment taskFragment,
+            @EmbeddingCheckResult int result) {
+        final String errMsg;
+        switch(result) {
+            case EMBEDDING_DISALLOWED_NEW_TASK: {
+                errMsg = "Cannot embed " + mStartActivity + " that launched on another task"
+                        + ",mLaunchMode=" + launchModeToString(mLaunchMode)
+                        + ",mLaunchFlag=" + Integer.toHexString(mLaunchFlags);
+                break;
+            }
+            case EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION: {
+                errMsg = "Cannot embed " + mStartActivity
+                        + ". TaskFragment's bounds:" + taskFragment.getBounds()
+                        + ", minimum dimensions:" + mStartActivity.getMinDimensions();
+                break;
+            }
+            case EMBEDDING_DISALLOWED_UNTRUSTED_HOST: {
+                errMsg = "The app:" + mCallingUid + "is not trusted to " + mStartActivity;
+                break;
+            }
+            default:
+                errMsg = "Unhandled embed result:" + result;
         }
-        WindowContainer<?> parent = task.getParent();
-        // If the Activity is going to launch on top of embedded Task in the same TaskFragment,
-        // we don't need to reparent the Task. Otherwise, the embedded Task should reparent to
-        // another TaskFragment.
-        return parent.asTaskFragment() != mInTaskFragment;
+        if (taskFragment.isOrganized()) {
+            mService.mWindowOrganizerController.sendTaskFragmentOperationFailure(
+                    taskFragment.getTaskFragmentOrganizer(), mRequest.errorCallbackToken,
+                    new SecurityException(errMsg));
+        } else {
+            // If the taskFragment is not organized, just dump error message as warning logs.
+            Slog.w(TAG, errMsg);
+        }
     }
 
     private int adjustLaunchFlagsToDocumentMode(ActivityRecord r, boolean launchSingleInstance,
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index baa31a0..927604e 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -26,7 +26,9 @@
 import static android.app.WindowConfiguration.activityTypeToString;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
 
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -249,8 +251,25 @@
         }
         // Check if the caller is allowed to launch on the specified display area.
         final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
-        final TaskDisplayArea taskDisplayArea = daToken != null
+        TaskDisplayArea taskDisplayArea = daToken != null
                 ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
+
+        // If we do not have a task display area token, check if the launch task display area
+        // feature id is specified.
+        if (taskDisplayArea == null) {
+            final int launchTaskDisplayAreaFeatureId = options.getLaunchTaskDisplayAreaFeatureId();
+            if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
+                final int launchDisplayId = options.getLaunchDisplayId() == INVALID_DISPLAY
+                        ? DEFAULT_DISPLAY : options.getLaunchDisplayId();
+                final DisplayContent dc = supervisor.mRootWindowContainer
+                        .getDisplayContent(launchDisplayId);
+                if (dc != null) {
+                    taskDisplayArea = dc.getItemFromTaskDisplayAreas(tda ->
+                            tda.mFeatureId == launchTaskDisplayAreaFeatureId ? tda : null);
+                }
+            }
+        }
+
         if (aInfo != null && taskDisplayArea != null
                 && !supervisor.isCallerAllowedToLaunchOnTaskDisplayArea(callingPid, callingUid,
                 taskDisplayArea, aInfo)) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index a1c22d6..3e6546e 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -140,6 +140,45 @@
     static final boolean SHOW_APP_STARTING_PREVIEW = true;
 
     /**
+     * An embedding check result of {@link #isAllowedToEmbedActivity(ActivityRecord)} or
+     * {@link ActivityStarter#canEmbedActivity(TaskFragment, ActivityRecord, Task)}:
+     * indicate that an Activity can be embedded successfully.
+     */
+    static final int EMBEDDING_ALLOWED = 0;
+    /**
+     * An embedding check result of {@link #isAllowedToEmbedActivity(ActivityRecord)} or
+     * {@link ActivityStarter#canEmbedActivity(TaskFragment, ActivityRecord, Task)}:
+     * indicate that an Activity can't be embedded because either the Activity does not allow
+     * untrusted embedding, and the embedding host app is not trusted.
+     */
+    static final int EMBEDDING_DISALLOWED_UNTRUSTED_HOST = 1;
+    /**
+     * An embedding check result of {@link #isAllowedToEmbedActivity(ActivityRecord)} or
+     * {@link ActivityStarter#canEmbedActivity(TaskFragment, ActivityRecord, Task)}:
+     * indicate that an Activity can't be embedded because this taskFragment's bounds are
+     * {@link #smallerThanMinDimension(ActivityRecord)}.
+     */
+    static final int EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION = 2;
+    /**
+     * An embedding check result of
+     * {@link ActivityStarter#canEmbedActivity(TaskFragment, ActivityRecord, Task)}:
+     * indicate that an Activity can't be embedded because the Activity is started on a new task.
+     */
+    static final int EMBEDDING_DISALLOWED_NEW_TASK = 3;
+
+    /**
+     * Embedding check results of {@link #isAllowedToEmbedActivity(ActivityRecord)} or
+     * {@link ActivityStarter#canEmbedActivity(TaskFragment, ActivityRecord, Task)}.
+     */
+    @IntDef(prefix = {"EMBEDDING_"}, value = {
+            EMBEDDING_ALLOWED,
+            EMBEDDING_DISALLOWED_UNTRUSTED_HOST,
+            EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION,
+            EMBEDDING_DISALLOWED_NEW_TASK,
+    })
+    @interface EmbeddingCheckResult {}
+
+    /**
      * Indicate that the minimal width/height should use the default value.
      *
      * @see #mMinWidth
@@ -509,20 +548,29 @@
         return false;
     }
 
-    boolean isAllowedToEmbedActivity(@NonNull ActivityRecord a) {
+    @EmbeddingCheckResult
+    int isAllowedToEmbedActivity(@NonNull ActivityRecord a) {
         return isAllowedToEmbedActivity(a, mTaskFragmentOrganizerUid);
     }
 
     /**
      * Checks if the organized task fragment is allowed to have the specified activity, which is
-     * allowed if an activity allows embedding in untrusted mode, or if the trusted mode can be
-     * enabled.
-     * @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
+     * allowed if an activity allows embedding in untrusted mode, if the trusted mode can be
+     * enabled, or if the organized task fragment bounds are not
+     * {@link #smallerThanMinDimension(ActivityRecord)}.
+     *
      * @param uid   uid of the TaskFragment organizer.
+     * @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
      */
-    boolean isAllowedToEmbedActivity(@NonNull ActivityRecord a, int uid) {
-        return isAllowedToEmbedActivityInUntrustedMode(a)
-                || isAllowedToEmbedActivityInTrustedMode(a, uid);
+    @EmbeddingCheckResult
+    int isAllowedToEmbedActivity(@NonNull ActivityRecord a, int uid) {
+        if (!isAllowedToEmbedActivityInUntrustedMode(a)
+                && !isAllowedToEmbedActivityInTrustedMode(a, uid)) {
+            return EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
+        } else if (smallerThanMinDimension(a)) {
+            return EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
+        }
+        return EMBEDDING_ALLOWED;
     }
 
     boolean smallerThanMinDimension(@NonNull ActivityRecord activity) {
@@ -539,9 +587,8 @@
         }
         final int minWidth = minDimensions.x;
         final int minHeight = minDimensions.y;
-        final boolean smaller = taskFragBounds.width() < minWidth
+        return taskFragBounds.width() < minWidth
                 || taskFragBounds.height() < minHeight;
-        return smaller;
     }
 
     /**
@@ -598,7 +645,7 @@
         // The system is trusted to embed other apps securely and for all users.
         return UserHandle.getAppId(uid) == SYSTEM_UID
                 // Activities from the same UID can be embedded freely by the host.
-                || uid == a.getUid();
+                || a.isUid(uid);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 9aff23d..392d4c2 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -19,6 +19,7 @@
 import static android.window.TaskFragmentOrganizer.putExceptionInBundle;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
 import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
 
 import android.annotation.IntDef;
@@ -235,7 +236,7 @@
                         + " is not in a task belong to the organizer app.");
                 return;
             }
-            if (!task.isAllowedToEmbedActivity(activity, mOrganizerUid)) {
+            if (task.isAllowedToEmbedActivity(activity, mOrganizerUid) != EMBEDDING_ALLOWED) {
                 Slog.d(TAG, "Reparent activity=" + activity.token
                         + " is not allowed to be embedded.");
                 return;
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 9bb0271..4141156 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -35,6 +35,7 @@
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
 
 import static com.android.server.wm.ActivityStarter.Request;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -307,7 +308,8 @@
         TaskDisplayArea taskDisplayArea = suggestedDisplayArea;
         // If launch task display area is set in options we should just use it. We assume the
         // suggestedDisplayArea has the right one in this case.
-        if (options == null || options.getLaunchTaskDisplayArea() == null) {
+        if (options == null || (options.getLaunchTaskDisplayArea() == null
+                && options.getLaunchTaskDisplayAreaFeatureId() == FEATURE_UNDEFINED)) {
             final int activityType =
                     mSupervisor.mRootWindowContainer.resolveActivityType(root, options, task);
             display.forAllTaskDisplayAreas(displayArea -> {
@@ -391,7 +393,22 @@
         if (optionLaunchTaskDisplayAreaToken != null) {
             taskDisplayArea = (TaskDisplayArea) WindowContainer.fromBinder(
                     optionLaunchTaskDisplayAreaToken.asBinder());
-            if (DEBUG) appendLog("display-area-from-option=" + taskDisplayArea);
+            if (DEBUG) appendLog("display-area-token-from-option=" + taskDisplayArea);
+        }
+
+        if (taskDisplayArea == null && options != null) {
+            final int launchTaskDisplayAreaFeatureId = options.getLaunchTaskDisplayAreaFeatureId();
+            if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
+                final int launchDisplayId = options.getLaunchDisplayId() == INVALID_DISPLAY
+                        ? DEFAULT_DISPLAY : options.getLaunchDisplayId();
+                final DisplayContent dc = mSupervisor.mRootWindowContainer
+                        .getDisplayContent(launchDisplayId);
+                if (dc != null) {
+                    taskDisplayArea = dc.getItemFromTaskDisplayAreas(tda ->
+                            tda.mFeatureId == launchTaskDisplayAreaFeatureId ? tda : null);
+                    if (DEBUG) appendLog("display-area-feature-from-option=" + taskDisplayArea);
+                }
+            }
         }
 
         // If task display area is not specified in options - try display id
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 7f1096b..00ee94a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2818,6 +2818,10 @@
      *                     snapshot from {@link #getFreezeSnapshotTarget()}.
      */
     void initializeChangeTransition(Rect startBounds, @Nullable SurfaceControl freezeTarget) {
+        if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
+            // TODO(b/207070762): request shell transition for activityEmbedding change.
+            return;
+        }
         mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
         mDisplayContent.mChangingContainers.add(this);
         // Calculate the relative position in parent container.
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 1d93c89..97dcb75 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -46,6 +46,7 @@
 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
+import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -814,7 +815,7 @@
                     sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
                     break;
                 }
-                if (!parent.isAllowedToEmbedActivity(activity)) {
+                if (parent.isAllowedToEmbedActivity(activity) != EMBEDDING_ALLOWED) {
                     final Throwable exception = new SecurityException(
                             "The task fragment is not trusted to embed the given activity.");
                     sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
@@ -1057,7 +1058,7 @@
     }
 
     /** A helper method to send minimum dimension violation error to the client. */
-    void sendMinimumDimensionViolation(TaskFragment taskFragment, Point minDimensions,
+    private void sendMinimumDimensionViolation(TaskFragment taskFragment, Point minDimensions,
             IBinder errorCallbackToken, String reason) {
         if (taskFragment == null || taskFragment.getTaskFragmentOrganizer() == null) {
             return;
@@ -1672,7 +1673,7 @@
             // We are reparenting activities to a new embedded TaskFragment, this operation is only
             // allowed if the new parent is trusted by all reparent activities.
             final boolean isEmbeddingDisallowed = oldParent.forAllActivities(activity ->
-                    !newParentTF.isAllowedToEmbedActivity(activity));
+                    newParentTF.isAllowedToEmbedActivity(activity) == EMBEDDING_ALLOWED);
             if (isEmbeddingDisallowed) {
                 final Throwable exception = new SecurityException(
                         "The new parent is not trusted to embed the activities.");
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 67ef7f5..4942464 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -722,25 +722,6 @@
     }
 
     @Test
-    public void testAlarmBroadcastOption() throws Exception {
-        final long triggerTime = mNowElapsedTest + 5000;
-        final PendingIntent alarmPi = getNewMockPendingIntent();
-        setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi);
-
-        mNowElapsedTest = mTestTimer.getElapsed();
-        mTestTimer.expire();
-
-        final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor =
-                ArgumentCaptor.forClass(PendingIntent.OnFinished.class);
-        final ArgumentCaptor<Bundle> optionsCaptor = ArgumentCaptor.forClass(Bundle.class);
-        verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
-                onFinishedCaptor.capture(), any(Handler.class), isNull(),
-                optionsCaptor.capture());
-        assertTrue(optionsCaptor.getValue()
-                .getBoolean(BroadcastOptions.KEY_ALARM_BROADCAST, false));
-    }
-
-    @Test
     public void testUpdateConstants() {
         setDeviceConfigLong(KEY_MIN_FUTURITY, 5);
         setDeviceConfigLong(KEY_MIN_INTERVAL, 10);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
index 69584e0..7ccd6d9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
@@ -70,13 +70,12 @@
     @Test
     fun deleteSystemPackageFailsIfNotAdminAndNotProfile() {
         val ps = mPms.mSettings.getPackageLPr("a.data.package")
-        whenever(PackageManagerServiceUtils.isSystemApp(ps)).thenReturn(true)
+        whenever(PackageManagerServiceUtils.isUpdatedSystemApp(ps)).thenReturn(true)
         whenever(mUserManagerInternal.getUserInfo(1)).thenReturn(UserInfo(1, "test", 0))
         whenever(mUserManagerInternal.getProfileParentId(1)).thenReturn(1)
 
         val dph = DeletePackageHelper(mPms)
-        val result = dph.deletePackageX("a.data.package", 1L, 1,
-            PackageManager.DELETE_SYSTEM_APP, false)
+        val result = dph.deletePackageX("a.data.package", 1L, 1, 0, false)
 
         assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED)
     }
@@ -86,7 +85,7 @@
         val userId = 1
         val parentId = 5
         val ps = mPms.mSettings.getPackageLPr("a.data.package")
-        whenever(PackageManagerServiceUtils.isSystemApp(ps)).thenReturn(true)
+        whenever(PackageManagerServiceUtils.isUpdatedSystemApp(ps)).thenReturn(true)
         whenever(mUserManagerInternal.getUserInfo(userId)).thenReturn(
             UserInfo(userId, "test", UserInfo.FLAG_PROFILE))
         whenever(mUserManagerInternal.getProfileParentId(userId)).thenReturn(parentId)
@@ -94,8 +93,7 @@
             UserInfo(userId, "testparent", 0))
 
         val dph = DeletePackageHelper(mPms)
-        val result = dph.deletePackageX("a.data.package", 1L, userId,
-            PackageManager.DELETE_SYSTEM_APP, false)
+        val result = dph.deletePackageX("a.data.package", 1L, userId, 0, false)
 
         assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED)
     }
@@ -132,4 +130,18 @@
 
         assertThat(result).isEqualTo(PackageManager.DELETE_SUCCEEDED)
     }
-}
\ No newline at end of file
+
+    @Test
+    fun deleteSystemPackageSucceedsIfNotAdminButDeleteSystemAppSpecified() {
+        val ps = mPms.mSettings.getPackageLPr("a.data.package")
+        whenever(PackageManagerServiceUtils.isUpdatedSystemApp(ps)).thenReturn(true)
+        whenever(mUserManagerInternal.getUserInfo(1)).thenReturn(UserInfo(1, "test", 0))
+        whenever(mUserManagerInternal.getProfileParentId(1)).thenReturn(1)
+
+        val dph = DeletePackageHelper(mPms)
+        val result = dph.deletePackageX("a.data.package", 1L, 1,
+                PackageManager.DELETE_SYSTEM_APP, false)
+
+        assertThat(result).isEqualTo(PackageManager.DELETE_SUCCEEDED)
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
index 8b314cd..c2519caa 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
@@ -295,10 +295,10 @@
                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_1 = new byte[]{
-                0x01, 0x18, 0x4A,
-                0x02, 0x64, 0x5A,
-                0x03, 0x4B, 0x00,
-                0x04, 0x20, 0x0A};
+                0x0F, 0x18, 0x4A,
+                0x17, 0x64, 0x5A,
+                0x1F, 0x4B, 0x00,
+                0x27, 0x20, 0x0A};
         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
@@ -310,10 +310,10 @@
                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_2 = new byte[]{
-                0x05, 0x18, 0x4A,
-                0x06, 0x64, 0x5A,
-                0x07, 0x4B, 0x00,
-                0x08, 0x20, 0x0A};
+                0x2F, 0x18, 0x4A,
+                0x37, 0x64, 0x5A,
+                0x3F, 0x4B, 0x00,
+                0x47, 0x20, 0x0A};
         HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_2);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
@@ -325,10 +325,10 @@
                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_3 = new byte[]{
-                0x09, 0x18, 0x4A,
-                0x0A, 0x64, 0x5A,
-                0x0B, 0x4B, 0x00,
-                0x0C, 0x20, 0x0A};
+                0x4F, 0x18, 0x4A,
+                0x57, 0x64, 0x5A,
+                0x5F, 0x4B, 0x00,
+                0x67, 0x20, 0x0A};
         HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_3);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
@@ -340,9 +340,9 @@
                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_4 = new byte[]{
-                0x0D, 0x18, 0x4A,
-                0x0E, 0x64, 0x5A,
-                0x0F, 0x4B, 0x00};
+                0x6F, 0x18, 0x4A,
+                0x77, 0x64, 0x5A,
+                0x7F, 0x4B, 0x00};
         HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_4);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
@@ -371,9 +371,9 @@
                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_1 = new byte[]{
-                0x01, 0x18, 0x4A,
-                0x03, 0x4B, 0x00,
-                0x04, 0x20, 0x0A};
+                0x0F, 0x18, 0x4A,
+                0x1F, 0x4B, 0x00,
+                0x27, 0x20, 0x0A};
         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
@@ -385,7 +385,7 @@
                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_2 = new byte[]{
-                0x08, 0x20, 0x0A};
+                0x2F, 0x20, 0x0A};
         HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_2);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
@@ -397,10 +397,10 @@
                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_3 = new byte[]{
-                0x09, 0x18, 0x4A,
-                0x0A, 0x64, 0x5A,
-                0x0B, 0x4B, 0x00,
-                0x0C, 0x20, 0x0A};
+                0x4F, 0x18, 0x4A,
+                0x57, 0x64, 0x5A,
+                0x5F, 0x4B, 0x00,
+                0x67, 0x20, 0x0A};
         HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_3);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
@@ -412,7 +412,7 @@
                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_4 = new byte[]{
-                0x0F, 0x4B, 0x00};
+                0x7F, 0x4B, 0x00};
         HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_4);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
@@ -440,11 +440,12 @@
         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
+        // Negative values require explicit casting
         byte[] sadsToRespond_1 = new byte[]{
-                0x20, 0x18, 0x4A,
-                0x21, 0x64, 0x5A,
-                0x22, 0x4B, 0x00,
-                0x23, 0x20, 0x0A};
+                (byte) 0x80, 0x18, 0x4A,
+                (byte) 0x82, 0x64, 0x5A,
+                (byte) 0x87, 0x4B, 0x00,
+                (byte) 0x8F, 0x20, 0x0A};
         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
@@ -456,10 +457,10 @@
                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_2 = new byte[]{
-                0x24, 0x18, 0x4A,
-                0x25, 0x64, 0x5A,
-                0x26, 0x4B, 0x00,
-                0x27, 0x20, 0x0A};
+                (byte) 0x92, 0x18, 0x4A,
+                (byte) 0x98, 0x64, 0x5A,
+                (byte) 0xA1, 0x4B, 0x00,
+                (byte) 0xA4, 0x20, 0x0A};
         HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_2);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
@@ -471,10 +472,10 @@
                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_3 = new byte[]{
-                0x28, 0x18, 0x4A,
-                0x29, 0x64, 0x5A,
-                0x2A, 0x4B, 0x00,
-                0x2B, 0x20, 0x0A};
+                (byte) 0xB3, 0x18, 0x4A,
+                (byte) 0xBA, 0x64, 0x5A,
+                (byte) 0xCB, 0x4B, 0x00,
+                (byte) 0xCF, 0x20, 0x0A};
         HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_3);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
@@ -486,9 +487,9 @@
                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_4 = new byte[]{
-                0x2C, 0x18, 0x4A,
-                0x2D, 0x64, 0x5A,
-                0x2E, 0x4B, 0x00};
+                (byte) 0xF0, 0x18, 0x4A,
+                (byte) 0xF1, 0x64, 0x5A,
+                (byte) 0xF3, 0x4B, 0x00};
         HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_4);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
@@ -510,10 +511,10 @@
                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
         byte[] sadsToRespond_1 = new byte[]{
-                0x01, 0x18,
-                0x02, 0x64, 0x5A,
-                0x03, 0x4B, 0x00,
-                0x04, 0x20, 0x0A};
+                0x0F, 0x18,
+                0x17, 0x64, 0x5A,
+                0x1F, 0x4B, 0x00,
+                0x27, 0x20, 0x0A};
         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
@@ -580,10 +581,10 @@
                 new int[]{Constants.AUDIO_CODEC_LPCM, Constants.AUDIO_CODEC_DD,
                         Constants.AUDIO_CODEC_MP3, Constants.AUDIO_CODEC_MPEG2});
         byte[] sadsToRespond_1 = new byte[]{
-                0x01, 0x18, 0x4A,
-                0x02, 0x64, 0x5A,
-                0x04, 0x20, 0x0A,
-                0x05, 0x18, 0x4A};
+                0x0F, 0x18, 0x4A,
+                0x17, 0x64, 0x5A,
+                0x27, 0x20, 0x0A,
+                0x2F, 0x18, 0x4A};
         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
@@ -596,10 +597,10 @@
                 new int[]{Constants.AUDIO_CODEC_AAC, Constants.AUDIO_CODEC_DTS,
                         Constants.AUDIO_CODEC_ATRAC, Constants.AUDIO_CODEC_DDP});
         byte[] sadsToRespond_2 = new byte[]{
-                0x06, 0x64, 0x5A,
-                0x07, 0x4B, 0x00,
-                0x08, 0x20, 0x0A,
-                0x09, 0x18, 0x4A};
+                0x37, 0x64, 0x5A,
+                0x3F, 0x4B, 0x00,
+                0x47, 0x20, 0x0A,
+                0x4F, 0x18, 0x4A};
         HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_2);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
@@ -612,10 +613,10 @@
                 new int[]{Constants.AUDIO_CODEC_DTSHD, Constants.AUDIO_CODEC_TRUEHD,
                         Constants.AUDIO_CODEC_DST, Constants.AUDIO_CODEC_MAX});
         byte[] sadsToRespond_3 = new byte[]{
-                0x0B, 0x4B, 0x00,
-                0x0C, 0x20, 0x0A,
-                0x0D, 0x18, 0x4A,
-                0x0F, 0x4B, 0x00};
+                0x5F, 0x4B, 0x00,
+                0x67, 0x20, 0x0A,
+                0x6F, 0x18, 0x4A,
+                0x7F, 0x4B, 0x00};
         HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_3);
         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 5be1ece..fe3c26a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -37,6 +37,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
 import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
+import static android.content.pm.ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
 import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
@@ -52,6 +53,11 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityStarter.canEmbedActivity;
+import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
+import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
+import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK;
+import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -59,6 +65,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -87,6 +94,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.Pair;
+import android.util.Size;
 import android.view.Gravity;
 import android.window.TaskFragmentOrganizerToken;
 
@@ -1171,6 +1179,7 @@
                 null /* inTask */, taskFragment);
 
         assertFalse(taskFragment.hasChild());
+        assertNotNull("Target record must be started on Task.", targetRecord.getParent().asTask());
     }
 
     @Test
@@ -1341,6 +1350,58 @@
                 any());
     }
 
+    @Test
+    public void testCanEmbedActivity() {
+        final Size minDimensions = new Size(1000, 1000);
+        final WindowLayout windowLayout = new WindowLayout(0, 0, 0, 0, 0,
+                minDimensions.getWidth(), minDimensions.getHeight());
+        final ActivityRecord starting = new ActivityBuilder(mAtm)
+                .setUid(UNIMPORTANT_UID)
+                .setWindowLayout(windowLayout)
+                .build();
+
+        // Task fragment hasn't attached to a task yet. Start activity to a new task.
+        TaskFragment taskFragment = new TaskFragmentBuilder(mAtm).build();
+        final Task task = new TaskBuilder(mSupervisor).build();
+
+        assertEquals(EMBEDDING_DISALLOWED_NEW_TASK,
+                canEmbedActivity(taskFragment, starting, task));
+
+        // Starting activity is going to be started on a task different from task fragment's parent
+        // task. Start activity to a new task.
+        task.addChild(taskFragment, POSITION_TOP);
+        final Task newTask = new TaskBuilder(mSupervisor).build();
+
+        assertEquals(EMBEDDING_DISALLOWED_NEW_TASK,
+                canEmbedActivity(taskFragment, starting, newTask));
+
+        // Make task fragment bounds exceed task bounds.
+        final Rect taskBounds = task.getBounds();
+        taskFragment.setBounds(taskBounds.left, taskBounds.top, taskBounds.right + 1,
+                taskBounds.bottom + 1);
+
+        assertEquals(EMBEDDING_DISALLOWED_UNTRUSTED_HOST,
+                canEmbedActivity(taskFragment, starting, task));
+
+        taskFragment.setBounds(taskBounds);
+        starting.info.flags |= FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
+
+        assertEquals(EMBEDDING_ALLOWED, canEmbedActivity(taskFragment, starting, task));
+
+        starting.info.flags &= ~FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
+        // Set task fragment's uid as the same as starting activity's uid.
+        taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class),
+                UNIMPORTANT_UID, "test");
+
+        assertEquals(EMBEDDING_ALLOWED, canEmbedActivity(taskFragment, starting, task));
+
+        // Make task fragment bounds smaller than starting activity's minimum dimensions
+        taskFragment.setBounds(0, 0, minDimensions.getWidth() - 1, minDimensions.getHeight() - 1);
+
+        assertEquals(EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION,
+                canEmbedActivity(taskFragment, starting, task));
+    }
+
     private static void startActivityInner(ActivityStarter starter, ActivityRecord target,
             ActivityRecord source, ActivityOptions options, Task inTask,
             TaskFragment inTaskFragment) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 1c3b869..e47bcc9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -21,6 +21,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.testing.Assert.assertThrows;
 
@@ -530,7 +531,7 @@
         mWindowOrganizerController.mLaunchTaskFragments
                 .put(mFragmentToken, mTaskFragment);
         mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token);
-        doReturn(true).when(mTaskFragment).isAllowedToEmbedActivity(activity);
+        doReturn(EMBEDDING_ALLOWED).when(mTaskFragment).isAllowedToEmbedActivity(activity);
         clearInvocations(mAtm.mRootWindowContainer);
 
         mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
@@ -920,7 +921,6 @@
                 .setOrganizer(mOrganizer)
                 .setBounds(mTaskFragBounds)
                 .build();
-        doReturn(true).when(mTaskFragment).isAllowedToEmbedActivity(activity);
         mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
 
         // Reparent activity to mTaskFragment, which is smaller than activity's
@@ -956,7 +956,6 @@
                 .setOrganizer(mOrganizer)
                 .setBounds(mTaskFragBounds)
                 .build();
-        doReturn(true).when(mTaskFragment).isAllowedToEmbedActivity(activity);
         mWindowOrganizerController.mLaunchTaskFragments.put(oldFragToken, oldTaskFrag);
         mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index c0759c1..22101c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -196,6 +196,28 @@
     }
 
     @Test
+    public void testUsesOptionsDisplayAreaFeatureIdIfSet() {
+        final TestDisplayContent freeformDisplay = createNewDisplayContent(
+                WINDOWING_MODE_FREEFORM);
+        final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
+                WINDOWING_MODE_FULLSCREEN);
+
+        mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
+        ActivityRecord source = createSourceActivity(freeformDisplay);
+
+        ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(fullscreenDisplay.mDisplayId);
+        options.setLaunchTaskDisplayAreaFeatureId(
+                fullscreenDisplay.getDefaultTaskDisplayArea().mFeatureId);
+
+        assertEquals(RESULT_CONTINUE,
+                new CalculateRequestBuilder().setSource(source).setOptions(options).calculate());
+
+        assertEquals(fullscreenDisplay.getDefaultTaskDisplayArea(),
+                mResult.mPreferredTaskDisplayArea);
+    }
+
+    @Test
     public void testUsesSourcesDisplayAreaIdPriorToTaskIfSet() {
         final TestDisplayContent freeformDisplay = createNewDisplayContent(
                 WINDOWING_MODE_FREEFORM);
@@ -453,7 +475,7 @@
     }
 
     @Test
-    public void testNotOverrideDisplayAreaWhenActivityOptionsHasDisplayArea() {
+    public void testNotOverrideDisplayAreaWhenActivityOptionsHasDisplayAreaToken() {
         final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(mDefaultDisplay,
                 mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
         final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
@@ -475,6 +497,52 @@
     }
 
     @Test
+    public void testNotOverrideDisplayAreaWhenActivityOptionsHasDisplayAreaFeatureId() {
+        final TaskDisplayArea secondaryDisplayArea = createTaskDisplayArea(mDefaultDisplay,
+                mWm, "SecondaryDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
+        final Task launchRoot = createTask(secondaryDisplayArea, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD);
+        launchRoot.mCreatedByOrganizer = true;
+
+        secondaryDisplayArea.setLaunchRootTask(launchRoot, new int[] { WINDOWING_MODE_FULLSCREEN },
+                new int[] { ACTIVITY_TYPE_STANDARD });
+
+        ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchTaskDisplayAreaFeatureId(
+                mDefaultDisplay.getDefaultTaskDisplayArea().mFeatureId);
+
+        assertEquals(RESULT_CONTINUE,
+                new CalculateRequestBuilder().setOptions(options).calculate());
+
+        assertEquals(
+                mDefaultDisplay.getDefaultTaskDisplayArea(), mResult.mPreferredTaskDisplayArea);
+    }
+
+    @Test
+    public void testUsesOptionsDisplayAreaFeatureIdDisplayIdNotSet() {
+        final TestDisplayContent secondaryDisplay = createNewDisplayContent(
+                WINDOWING_MODE_FULLSCREEN);
+        final TaskDisplayArea tdaOnSecondaryDisplay = createTaskDisplayArea(secondaryDisplay,
+                mWm, "TestTaskDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
+
+        final TaskDisplayArea tdaOnDefaultDisplay = createTaskDisplayArea(mDefaultDisplay,
+                mWm, "TestTaskDisplayArea", FEATURE_RUNTIME_TASK_CONTAINER_FIRST);
+
+        mCurrent.mPreferredTaskDisplayArea = tdaOnSecondaryDisplay;
+        ActivityRecord source = createSourceActivity(tdaOnSecondaryDisplay,
+                WINDOWING_MODE_FULLSCREEN);
+
+        ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchTaskDisplayAreaFeatureId(tdaOnSecondaryDisplay.mFeatureId);
+
+        assertEquals(RESULT_CONTINUE,
+                new CalculateRequestBuilder().setSource(source).setOptions(options).calculate());
+        // Display id wasn't specified in ActivityOptions - the activity should be placed on the
+        // default display, into the TaskDisplayArea with the same feature id.
+        assertEquals(tdaOnDefaultDisplay, mResult.mPreferredTaskDisplayArea);
+    }
+
+    @Test
     public void testRecalculateFreeformInitialBoundsWithOverrideDisplayArea() {
         final TestDisplayContent freeformDisplay = createNewDisplayContent(
                 WINDOWING_MODE_FREEFORM);
@@ -1822,6 +1890,13 @@
         return new ActivityBuilder(mAtm).setTask(rootTask).build();
     }
 
+    private ActivityRecord createSourceActivity(TaskDisplayArea taskDisplayArea,
+            int windowingMode) {
+        final Task rootTask = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD,
+                true);
+        return new ActivityBuilder(mAtm).setTask(rootTask).build();
+    }
+
     private void addFreeformTaskTo(TestDisplayContent display, Rect bounds) {
         final Task rootTask = display.getDefaultTaskDisplayArea()
                 .createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);