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);