Merge "Don't put non-resizeable activities on secondary displays" into oc-dev

am: b216d0f09f

Change-Id: I026dfe6834c891251731fcf852366d20a112b864
This commit is contained in:
Andrii Kulian
2017-04-22 01:07:58 +00:00
committed by android-build-merger
11 changed files with 255 additions and 54 deletions

View File

@@ -21,6 +21,11 @@ import android.content.ComponentName;
/** @hide */
oneway interface ITaskStackListener {
/** Activity was resized to be displayed in split-screen. */
const int FORCED_RESIZEABLE_REASON_SPLIT_SCREEN = 1;
/** Activity was resized to be displayed on a secondary display. */
const int FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY = 2;
/** Called whenever there are changes to the state of tasks in a stack. */
void onTaskStackChanged();
@@ -49,14 +54,25 @@ oneway interface ITaskStackListener {
/**
* Called when we launched an activity that we forced to be resizable.
*
* @param packageName Package name of the top activity in the task.
* @param taskId Id of the task.
* @param reason {@link #FORCED_RESIZEABLE_REASON_SPLIT_SCREEN} or
* {@link #FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY}.
*/
void onActivityForcedResizable(String packageName, int taskId);
void onActivityForcedResizable(String packageName, int taskId, int reason);
/**
* Callen when we launched an activity that is dismissed the docked stack.
* Called when we launched an activity that dismissed the docked stack.
*/
void onActivityDismissingDockedStack();
/**
* Called when an activity was requested to be launched on a secondary display but was not
* allowed there.
*/
void onActivityLaunchOnSecondaryDisplayFailed();
/**
* Called when a task is added.
*

View File

@@ -51,13 +51,18 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
}
@Override
public void onActivityForcedResizable(String packageName, int taskId) throws RemoteException {
public void onActivityForcedResizable(String packageName, int taskId, int reason)
throws RemoteException {
}
@Override
public void onActivityDismissingDockedStack() throws RemoteException {
}
@Override
public void onActivityLaunchOnSecondaryDisplayFailed() throws RemoteException {
}
@Override
public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
}

View File

@@ -1811,10 +1811,14 @@
<string name="report_rejected_touch" translatable="false">Report rejected touch</string>
<!-- Multi-Window strings -->
<!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity and that things might crash/not work properly [CHAR LIMIT=NONE] -->
<!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed in split-screen and that things might crash/not work properly [CHAR LIMIT=NONE] -->
<string name="dock_forced_resizable">App may not work with split-screen.</string>
<!-- Warning message when we try to dock a non-resizeble tasks and launch it in fullscreen instead. -->
<!-- Warning message when we try to dock a non-resizeable task and launch it in fullscreen instead. -->
<string name="dock_non_resizeble_failed_to_dock_text">App does not support split-screen.</string>
<!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed on a secondary display and that things might crash/not work properly [CHAR LIMIT=NONE] -->
<string name="forced_resizable_secondary_display">App may not work on a secondary display.</string>
<!-- Warning message when we try to launch a non-resizeable activity on a secondary display and launch it on the primary instead. -->
<string name="activity_launch_on_secondary_display_failed_text">App does not support launch on secondary displays.</string>
<!-- accessibility label for button to open settings [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_settings">Open settings.</string>

View File

@@ -156,8 +156,9 @@ public class SystemServicesProxy {
public void onPinnedActivityRestartAttempt() { }
public void onPinnedStackAnimationStarted() { }
public void onPinnedStackAnimationEnded() { }
public void onActivityForcedResizable(String packageName, int taskId) { }
public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
public void onActivityDismissingDockedStack() { }
public void onActivityLaunchOnSecondaryDisplayFailed() { }
public void onTaskProfileLocked(int taskId, int userId) { }
/**
@@ -224,9 +225,9 @@ public class SystemServicesProxy {
}
@Override
public void onActivityForcedResizable(String packageName, int taskId)
public void onActivityForcedResizable(String packageName, int taskId, int reason)
throws RemoteException {
mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, 0, packageName)
mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
.sendToTarget();
}
@@ -235,6 +236,11 @@ public class SystemServicesProxy {
mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK);
}
@Override
public void onActivityLaunchOnSecondaryDisplayFailed() throws RemoteException {
mHandler.sendEmptyMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED);
}
@Override
public void onTaskProfileLocked(int taskId, int userId) {
mHandler.obtainMessage(H.ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
@@ -1235,6 +1241,7 @@ public class SystemServicesProxy {
private static final int ON_TASK_PROFILE_LOCKED = 8;
private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9;
private static final int ON_ACTIVITY_UNPINNED = 10;
private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11;
@Override
public void handleMessage(Message msg) {
@@ -1285,7 +1292,7 @@ public class SystemServicesProxy {
case ON_ACTIVITY_FORCED_RESIZABLE: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onActivityForcedResizable(
(String) msg.obj, msg.arg1);
(String) msg.obj, msg.arg1, msg.arg2);
}
break;
}
@@ -1295,6 +1302,12 @@ public class SystemServicesProxy {
}
break;
}
case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onActivityLaunchOnSecondaryDisplayFailed();
}
break;
}
case ON_TASK_PROFILE_LOCKED: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onTaskProfileLocked(msg.arg1, msg.arg2);

View File

@@ -16,6 +16,9 @@
package com.android.systemui.stackdivider;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
@@ -34,6 +37,8 @@ import com.android.systemui.R;
*/
public class ForcedResizableInfoActivity extends Activity implements OnTouchListener {
public static final String EXTRA_FORCED_RESIZEABLE_REASON = "extra_forced_resizeable_reason";
private static final long DISMISS_DELAY = 2500;
private final Runnable mFinishRunnable = new Runnable() {
@@ -48,8 +53,21 @@ public class ForcedResizableInfoActivity extends Activity implements OnTouchList
super.onCreate(savedInstanceState);
setContentView(R.layout.forced_resizable_activity);
TextView tv = (TextView) findViewById(com.android.internal.R.id.message);
tv.setText(R.string.dock_forced_resizable);
getWindow().setTitle(getString(R.string.dock_forced_resizable));
int reason = getIntent().getIntExtra(EXTRA_FORCED_RESIZEABLE_REASON, -1);
String text;
switch (reason) {
case FORCED_RESIZEABLE_REASON_SPLIT_SCREEN:
text = getString(R.string.dock_forced_resizable);
break;
case FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY:
text = getString(R.string.forced_resizable_secondary_display);
break;
default:
throw new IllegalArgumentException("Unexpected forced resizeable reason: "
+ reason);
}
tv.setText(text);
getWindow().setTitle(text);
getWindow().getDecorView().setOnTouchListener(this);
}

View File

@@ -16,6 +16,9 @@
package com.android.systemui.stackdivider;
import static com.android.systemui.stackdivider.ForcedResizableInfoActivity
.EXTRA_FORCED_RESIZEABLE_REASON;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
@@ -43,7 +46,7 @@ public class ForcedResizableInfoActivityController {
private static final int TIMEOUT = 1000;
private final Context mContext;
private final Handler mHandler = new Handler();
private final ArraySet<Integer> mPendingTaskIds = new ArraySet<>();
private final ArraySet<PendingTaskRecord> mPendingTasks = new ArraySet<>();
private final ArraySet<String> mPackagesShownInSession = new ArraySet<>();
private boolean mDividerDraging;
@@ -54,20 +57,41 @@ public class ForcedResizableInfoActivityController {
}
};
/** Record of force resized task that's pending to be handled. */
private class PendingTaskRecord {
int taskId;
/**
* {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SPLIT_SCREEN} or
* {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY}
*/
int reason;
PendingTaskRecord(int taskId, int reason) {
this.taskId = taskId;
this.reason = reason;
}
}
public ForcedResizableInfoActivityController(Context context) {
mContext = context;
EventBus.getDefault().register(this);
SystemServicesProxy.getInstance(context).registerTaskStackListener(
new TaskStackListener() {
@Override
public void onActivityForcedResizable(String packageName, int taskId) {
activityForcedResizable(packageName, taskId);
public void onActivityForcedResizable(String packageName, int taskId,
int reason) {
activityForcedResizable(packageName, taskId, reason);
}
@Override
public void onActivityDismissingDockedStack() {
activityDismissingDockedStack();
}
@Override
public void onActivityLaunchOnSecondaryDisplayFailed() {
activityLaunchOnSecondaryDisplayFailed();
}
});
}
@@ -93,11 +117,11 @@ public class ForcedResizableInfoActivityController {
showPending();
}
private void activityForcedResizable(String packageName, int taskId) {
private void activityForcedResizable(String packageName, int taskId, int reason) {
if (debounce(packageName)) {
return;
}
mPendingTaskIds.add(taskId);
mPendingTasks.add(new PendingTaskRecord(taskId, reason));
postTimeout();
}
@@ -106,16 +130,23 @@ public class ForcedResizableInfoActivityController {
R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT));
}
private void activityLaunchOnSecondaryDisplayFailed() {
EventBus.getDefault().send(new ShowUserToastEvent(
R.string.activity_launch_on_secondary_display_failed_text, Toast.LENGTH_SHORT));
}
private void showPending() {
mHandler.removeCallbacks(mTimeoutRunnable);
for (int i = mPendingTaskIds.size() - 1; i >= 0; i--) {
for (int i = mPendingTasks.size() - 1; i >= 0; i--) {
PendingTaskRecord pendingRecord = mPendingTasks.valueAt(i);
Intent intent = new Intent(mContext, ForcedResizableInfoActivity.class);
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchTaskId(mPendingTaskIds.valueAt(i));
options.setLaunchTaskId(pendingRecord.taskId);
options.setTaskOverlay(true, false /* canResume */);
intent.putExtra(EXTRA_FORCED_RESIZEABLE_REASON, pendingRecord.reason);
mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
}
mPendingTaskIds.clear();
mPendingTasks.clear();
}
private void postTimeout() {

View File

@@ -68,6 +68,7 @@ import static android.os.Build.VERSION_CODES.HONEYCOMB;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SCREENSHOTS;
@@ -1162,6 +1163,17 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
|| service.mForceResizableActivities);
}
/**
* Check whether this activity can be launched on the specified display.
* @param displayId Target display id.
* @return {@code true} if either it is the default display or this activity is resizeable and
* can be put a secondary screen.
*/
boolean canBeLaunchedOnDisplay(int displayId) {
return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
supportsResizeableMultiWindow());
}
/**
* @param beforeStopping Whether this check is for an auto-enter-pip operation, that is to say
* the activity has requested to enter PiP when it would otherwise be stopped.

View File

@@ -33,6 +33,8 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -108,6 +110,7 @@ import android.app.ActivityManager.StackInfo;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.IActivityContainerCallback;
import android.app.ITaskStackListener;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.app.StatusBarManager;
@@ -485,6 +488,33 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
activityDisplay.onOverrideConfigurationChanged(overrideConfiguration);
}
/** Check if placing task or activity on specified display is allowed. */
boolean canPlaceEntityOnDisplay(int displayId, boolean resizeable) {
return displayId == DEFAULT_DISPLAY || (mService.mSupportsMultiDisplay
&& (resizeable || displayConfigMatchesGlobal(displayId)));
}
/**
* Check if configuration of specified display matches current global config.
* Used to check if we can put a non-resizeable activity on a secondary display and it will get
* the same config as on the default display.
* @param displayId Id of the display to check.
* @return {@code true} if configuration matches.
*/
private boolean displayConfigMatchesGlobal(int displayId) {
if (displayId == DEFAULT_DISPLAY) {
return true;
}
if (displayId == INVALID_DISPLAY) {
return false;
}
final ActivityDisplay targetDisplay = mActivityDisplays.get(displayId);
if (targetDisplay == null) {
throw new IllegalArgumentException("No display found with id: " + displayId);
}
return getConfiguration().equals(targetDisplay.getConfiguration());
}
static class FindTaskResult {
ActivityRecord r;
boolean matchedByRootAffinity;
@@ -2104,8 +2134,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
if (DEBUG_STACK) Slog.d(TAG_STACK,
"findTaskToMoveToFront: moved to front of stack=" + currentStack);
handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, currentStack.mStackId,
forceNonResizeable);
handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, DEFAULT_DISPLAY,
currentStack.mStackId, forceNonResizeable);
}
boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) {
@@ -2156,7 +2186,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// Return the topmost valid stack on the display.
for (int i = activityDisplay.mStacks.size() - 1; i >= 0; --i) {
final ActivityStack stack = activityDisplay.mStacks.get(i);
if (mService.mActivityStarter.isValidLaunchStackId(stack.mStackId, r)) {
if (mService.mActivityStarter.isValidLaunchStackId(stack.mStackId, displayId, r)) {
return stack;
}
}
@@ -2164,7 +2194,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// If there is no valid stack on the external display - check if new dynamic stack will do.
if (displayId != Display.DEFAULT_DISPLAY) {
final int newDynamicStackId = getNextStackId();
if (mService.mActivityStarter.isValidLaunchStackId(newDynamicStackId, r)) {
if (mService.mActivityStarter.isValidLaunchStackId(newDynamicStackId, displayId, r)) {
return createStackOnDisplay(newDynamicStackId, displayId, true /*onTop*/);
}
}
@@ -3987,31 +4017,68 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId, int actualStackId) {
handleNonResizableTaskIfNeeded(task, preferredStackId, actualStackId,
void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId,
int preferredDisplayId, int actualStackId) {
handleNonResizableTaskIfNeeded(task, preferredStackId, preferredDisplayId, actualStackId,
false /* forceNonResizable */);
}
void handleNonResizableTaskIfNeeded(
TaskRecord task, int preferredStackId, int actualStackId, boolean forceNonResizable) {
if ((!isStackDockedInEffect(actualStackId) && preferredStackId != DOCKED_STACK_ID)
|| task.isHomeTask()) {
private void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId,
int preferredDisplayId, int actualStackId, boolean forceNonResizable) {
final boolean isSecondaryDisplayPreferred =
(preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY)
|| StackId.isDynamicStack(preferredStackId);
if (((!isStackDockedInEffect(actualStackId) && preferredStackId != DOCKED_STACK_ID)
&& !isSecondaryDisplayPreferred) || task.isHomeTask()) {
return;
}
// Handle incorrect launch/move to secondary display if needed.
final boolean launchOnSecondaryDisplayFailed;
if (isSecondaryDisplayPreferred) {
final int actualDisplayId = task.getStack().mDisplayId;
if (!task.canBeLaunchedOnDisplay(actualDisplayId)) {
// The task landed on an inappropriate display somehow, move it to the default
// display.
// TODO(multi-display): Find proper stack for the task on the default display.
mService.moveTaskToStack(task.taskId, FULLSCREEN_WORKSPACE_STACK_ID,
true /* toTop */);
launchOnSecondaryDisplayFailed = true;
} else {
// The task might have landed on a display different from requested.
launchOnSecondaryDisplayFailed = actualDisplayId == DEFAULT_DISPLAY
|| (preferredDisplayId != INVALID_DISPLAY
&& preferredDisplayId != actualDisplayId);
}
} else {
// The task wasn't requested to be on a secondary display.
launchOnSecondaryDisplayFailed = false;
}
final ActivityRecord topActivity = task.getTopActivity();
if (!task.supportsSplitScreen() || forceNonResizable) {
// Display a warning toast that we tried to put a non-dockable task in the docked stack.
mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
if (launchOnSecondaryDisplayFailed || !task.supportsSplitScreen() || forceNonResizable) {
if (launchOnSecondaryDisplayFailed) {
// Display a warning toast that we tried to put a non-resizeable task on a secondary
// display with config different from global config.
mService.mTaskChangeNotificationController
.notifyActivityLaunchOnSecondaryDisplayFailed();
} else {
// Display a warning toast that we tried to put a non-dockable task in the docked
// stack.
mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
}
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
// we need to move it to top of fullscreen stack, otherwise it will be covered.
moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, actualStackId == DOCKED_STACK_ID);
} else if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
&& !topActivity.noDisplay) {
String packageName = topActivity.appInfo.packageName;
final String packageName = topActivity.appInfo.packageName;
final int reason = isSecondaryDisplayPreferred
? FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY
: FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
mService.mTaskChangeNotificationController.notifyActivityForcedResizable(
task.taskId, packageName);
task.taskId, reason, packageName);
}
}
@@ -4084,8 +4151,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
resumeFocusedStackTopActivityLocked();
mWindowManager.executeAppTransition();
} else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, task.getStackId(),
true /* forceNonResizable */);
handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, DEFAULT_DISPLAY,
task.getStackId(), true /* forceNonResizable */);
}
}

View File

@@ -979,6 +979,8 @@ class ActivityStarter {
final int preferredLaunchStackId =
(mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;
final int preferredLaunchDisplayId =
(mOptions != null) ? mOptions.getLaunchDisplayId() : DEFAULT_DISPLAY;
if (reusedActivity != null) {
// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
@@ -1100,8 +1102,8 @@ class ActivityStarter {
// Don't use mStartActivity.task to show the toast. We're not starting a new activity
// but reusing 'top'. Fields in mStartActivity may not be fully initialized.
mSupervisor.handleNonResizableTaskIfNeeded(
top.getTask(), preferredLaunchStackId, topStack.mStackId);
mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredLaunchStackId,
preferredLaunchDisplayId, topStack.mStackId);
return START_DELIVERED_TO_TOP;
}
@@ -1183,8 +1185,8 @@ class ActivityStarter {
}
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
mSupervisor.handleNonResizableTaskIfNeeded(
mStartActivity.getTask(), preferredLaunchStackId, mTargetStack.mStackId);
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredLaunchStackId,
preferredLaunchDisplayId, mTargetStack.mStackId);
return START_SUCCESS;
}
@@ -1580,7 +1582,7 @@ class ActivityStarter {
}
mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(), INVALID_STACK_ID,
mTargetStack.mStackId);
DEFAULT_DISPLAY, mTargetStack.mStackId);
// If the caller has requested that the target task be reset, then do so.
if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
@@ -2033,16 +2035,19 @@ class ActivityStarter {
canUseFocusedStack = r.isAssistantActivity();
break;
case DOCKED_STACK_ID:
// Any activty which supports split screen can go in the docked stack.
// Any activity which supports split screen can go in the docked stack.
canUseFocusedStack = r.supportsSplitScreen();
break;
case FREEFORM_WORKSPACE_STACK_ID:
// Any activty which supports freeform can go in the freeform stack.
// Any activity which supports freeform can go in the freeform stack.
canUseFocusedStack = r.supportsFreeform();
break;
default:
// Dynamic stacks behave similarly to the fullscreen stack and can contain any task.
canUseFocusedStack = isDynamicStack(focusedStackId);
// Dynamic stacks behave similarly to the fullscreen stack and can contain any
// resizeable task.
// TODO: Check ActivityView after fixing b/35349678.
canUseFocusedStack = isDynamicStack(focusedStackId)
&& r.canBeLaunchedOnDisplay(focusedStack.mDisplayId);
}
return canUseFocusedStack
@@ -2082,7 +2087,7 @@ class ActivityStarter {
"Stack and display id can't be set at the same time.");
}
if (isValidLaunchStackId(launchStackId, r)) {
if (isValidLaunchStackId(launchStackId, launchDisplayId, r)) {
return mSupervisor.getStack(launchStackId, CREATE_IF_NEEDED, ON_TOP);
}
if (launchStackId == DOCKED_STACK_ID) {
@@ -2148,7 +2153,7 @@ class ActivityStarter {
}
}
boolean isValidLaunchStackId(int stackId, ActivityRecord r) {
boolean isValidLaunchStackId(int stackId, int displayId, ActivityRecord r) {
switch (stackId) {
case INVALID_STACK_ID:
case HOME_STACK_ID:
@@ -2167,8 +2172,8 @@ class ActivityStarter {
return r.isAssistantActivity();
default:
// TODO: Check ActivityView after fixing b/35349678.
if (StackId.isDynamicStack(stackId) && mService.mSupportsMultiDisplay) {
return true;
if (StackId.isDynamicStack(stackId)) {
return r.canBeLaunchedOnDisplay(displayId);
}
Slog.e(TAG, "isValidLaunchStackId: Unexpected stackId=" + stackId);
return false;

View File

@@ -48,6 +48,7 @@ class TaskChangeNotificationController {
static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15;
static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16;
static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17;
static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18;
// Delay in notifying task stack change listeners (in millis)
static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -115,13 +116,17 @@ class TaskChangeNotificationController {
};
private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> {
l.onActivityForcedResizable((String) m.obj, m.arg1);
l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2);
};
private final TaskStackConsumer mNotifyActivityDismissingDockedStack = (l, m) -> {
l.onActivityDismissingDockedStack();
};
private final TaskStackConsumer mNotifyActivityLaunchOnSecondaryDisplayFailed = (l, m) -> {
l.onActivityLaunchOnSecondaryDisplayFailed();
};
private final TaskStackConsumer mNotifyTaskProfileLocked = (l, m) -> {
l.onTaskProfileLocked(m.arg1, m.arg2);
};
@@ -191,6 +196,9 @@ class TaskChangeNotificationController {
case NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG:
forAllRemoteListeners(mNotifyActivityDismissingDockedStack, msg);
break;
case NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG:
forAllRemoteListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg);
break;
case NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG:
forAllRemoteListeners(mNotifyTaskProfileLocked, msg);
break;
@@ -324,14 +332,22 @@ class TaskChangeNotificationController {
forAllLocalListeners(mNotifyActivityDismissingDockedStack, message);
}
void notifyActivityForcedResizable(int taskId, String packageName) {
void notifyActivityForcedResizable(int taskId, int reason, String packageName) {
mHandler.removeMessages(NOTIFY_FORCED_RESIZABLE_MSG);
final Message msg = mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, taskId,
0 /* unused */, packageName);
final Message msg = mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, taskId, reason,
packageName);
forAllLocalListeners(mNotifyActivityForcedResizable, msg);
msg.sendToTarget();
}
void notifyActivityLaunchOnSecondaryDisplayFailed() {
mHandler.removeMessages(NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG);
final Message msg = mHandler.obtainMessage(
NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG);
forAllLocalListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg);
msg.sendToTarget();
}
void notifyTaskCreated(int taskId, ComponentName componentName) {
final Message msg = mHandler.obtainMessage(NOTIFY_TASK_ADDED_LISTENERS_MSG,
taskId, 0 /* unused */, componentName);

View File

@@ -97,6 +97,8 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VER
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
@@ -748,7 +750,8 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta
supervisor.resumeFocusedStackTopActivityLocked();
}
supervisor.handleNonResizableTaskIfNeeded(this, preferredStackId, stackId);
// TODO: Handle incorrect request to move before the actual move, not after.
supervisor.handleNonResizableTaskIfNeeded(this, preferredStackId, DEFAULT_DISPLAY, stackId);
boolean successful = (preferredStackId == stackId);
if (successful && stackId == DOCKED_STACK_ID) {
@@ -1560,6 +1563,17 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta
&& !ActivityInfo.isPreserveOrientationMode(mResizeMode);
}
/**
* Check whether this task can be launched on the specified display.
* @param displayId Target display id.
* @return {@code true} if either it is the default display or this activity is resizeable and
* can be put a secondary screen.
*/
boolean canBeLaunchedOnDisplay(int displayId) {
return mService.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
isResizeable(false /* checkSupportsPip */));
}
/**
* Check that a given bounds matches the application requested orientation.
*