Show a scrim activity if task is not resizable
Add a callback to TaskStackChangeListener which gets fired when the system
might need to inform the user that a specific app might not work in
multi-window.
Use that callback in SysUI to show a translucent activity which scrims the
activity behind to inform that it might not be resizable.
Debounce the information to once per multi-window session, to not make it
annoying.
Introduce launchTaskId to start an activity in an existing task, and protect
that with START_TASKS_FROM_RECENTS permission.
Bug: 27327287
Bug: 27431869
Change-Id: I89e8d653872ab01ba3c1e252b426e5481da0e6ca
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1abb5ff..df983b1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1471,6 +1471,7 @@
static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 64;
static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 65;
static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 66;
+ static final int NOTIFY_FORCED_RESIZABLE_MSG = 67;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2019,6 +2020,21 @@
}
break;
}
+ case NOTIFY_FORCED_RESIZABLE_MSG: {
+ synchronized (ActivityManagerService.this) {
+ for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
+ try {
+ // Make a one-way callback to the listener
+ mTaskStackListeners.getBroadcastItem(i).onActivityForcedResizable(
+ (String) msg.obj, msg.arg1);
+ } catch (RemoteException e){
+ // Handled by the RemoteCallbackList
+ }
+ }
+ mTaskStackListeners.finishBroadcast();
+ }
+ break;
+ }
case NOTIFY_CLEARTEXT_NETWORK_MSG: {
final int uid = msg.arg1;
final byte[] firstPacket = (byte[]) msg.obj;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 7def1bd..fc003b8 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -101,7 +101,9 @@
import java.util.Objects;
import java.util.Set;
+import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.START_ANY_ACTIVITY;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
@@ -151,6 +153,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.ANIMATE;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
+import static com.android.server.am.ActivityManagerService.NOTIFY_FORCED_RESIZABLE_MSG;
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
@@ -1304,7 +1307,7 @@
boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo,
String resultWho, int requestCode, int callingPid, int callingUid,
String callingPackage, boolean ignoreTargetSecurity, ProcessRecord callerApp,
- ActivityRecord resultRecord, ActivityStack resultStack) {
+ ActivityRecord resultRecord, ActivityStack resultStack, ActivityOptions options) {
final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
callingUid);
if (startAnyPerm == PERMISSION_GRANTED) {
@@ -1358,6 +1361,19 @@
Slog.w(TAG, message);
return false;
}
+ if (options != null && options.getLaunchTaskId() != -1) {
+ final int startInTaskPerm = mService.checkPermission(START_TASKS_FROM_RECENTS,
+ callingPid, callingUid);
+ if (startInTaskPerm != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with launchTaskId="
+ + options.getLaunchTaskId();
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ }
+
return true;
}
@@ -1788,6 +1804,8 @@
if (DEBUG_STACK) Slog.d(TAG_STACK,
"findTaskToMoveToFront: moved to front of stack=" + task.stack);
+
+ showNonResizeableDockToastIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId);
}
boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) {
@@ -3309,10 +3327,14 @@
return;
}
- if (!task.canGoInDockedStack() || task.mResizeMode == RESIZE_MODE_FORCE_RESIZEABLE) {
- // Display warning toast if we tried to put a non-dockable task in the docked stack or
- // the task was forced to be resizable by the system.
+ if (!task.canGoInDockedStack()) {
+ // Display a warning toast that we tried to put a non-dockable task in the docked stack.
mWindowManager.scheduleShowNonResizeableDockToast(task.taskId);
+ } else if (task.mResizeMode == RESIZE_MODE_FORCE_RESIZEABLE) {
+ String packageName = task.getTopActivity() != null
+ ? task.getTopActivity().appInfo.packageName : null;
+ mService.mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, task.taskId, 0,
+ packageName).sendToTarget();
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index af69c93..f244c93 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -362,7 +362,7 @@
boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp,
- resultRecord, resultStack);
+ resultRecord, resultStack, options);
abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
@@ -879,6 +879,9 @@
ActivityRecord intentActivity = getReusableIntentActivity();
+ final int preferredLaunchStackId =
+ (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;
+
if (intentActivity != null) {
// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
// still needs to be a lock task mode violation since the task gets cleared out and
@@ -938,6 +941,8 @@
// We didn't do anything... but it was needed (a.k.a., client don't use that
// intent!) And for paranoia, make sure we have correctly resumed the top activity.
resumeTargetStackIfNeeded();
+ mSupervisor.showNonResizeableDockToastIfNeeded(mStartActivity.task,
+ preferredLaunchStackId, mTargetStack.mStackId);
return START_TASK_TO_FRONT;
}
}
@@ -977,6 +982,8 @@
}
top.deliverNewIntentLocked(
mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+ mSupervisor.showNonResizeableDockToastIfNeeded(mStartActivity.task,
+ preferredLaunchStackId, mTargetStack.mStackId);
return START_DELIVERED_TO_TOP;
}
@@ -1063,8 +1070,6 @@
}
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
- final int preferredLaunchStackId =
- (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;
mSupervisor.showNonResizeableDockToastIfNeeded(
mStartActivity.task, preferredLaunchStackId, mTargetStack.mStackId);
@@ -1297,7 +1302,10 @@
// same component, then instead of launching bring that one to the front.
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
- if (putIntoExistingTask) {
+ if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
+ final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
+ intentActivity = task != null ? task.getTopActivity() : null;
+ } else if (putIntoExistingTask) {
// See if there is a task to bring to the front. If this is a SINGLE_INSTANCE
// activity, there can be one and only one instance of it in the history, and it is
// always in its own unique task, so we do a special search.
diff --git a/services/core/java/com/android/server/policy/StatusBarController.java b/services/core/java/com/android/server/policy/StatusBarController.java
index 9d353c6..86d0468 100644
--- a/services/core/java/com/android/server/policy/StatusBarController.java
+++ b/services/core/java/com/android/server/policy/StatusBarController.java
@@ -29,6 +29,8 @@
import android.view.animation.TranslateAnimation;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
import static android.view.WindowManagerInternal.*;
@@ -103,6 +105,20 @@
}
});
}
+
+ @Override
+ public void onAppTransitionFinishedLocked(IBinder token) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ StatusBarManagerInternal statusbar = LocalServices.getService(
+ StatusBarManagerInternal.class);
+ if (statusbar != null) {
+ statusbar.appTransitionFinished();
+ }
+ }
+ });
+ }
};
public StatusBarController() {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 6bda4ed..9614417 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -34,4 +34,5 @@
void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
Rect fullscreenBounds, Rect dockedBounds, String cause);
void toggleSplitScreen();
+ void appTransitionFinished();
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 403a4c6..4a00ebd 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -214,6 +214,15 @@
} catch (RemoteException ex) {}
}
}
+
+ public void appTransitionFinished() {
+ enforceStatusBarService();
+ if (mBar != null) {
+ try {
+ mBar.appTransitionFinished();
+ } catch (RemoteException ex) {}
+ }
+ }
};
// ================================================================================
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f097eb2..5c86155 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -140,41 +140,7 @@
final String text =
mService.mContext.getString(R.string.dock_non_resizeble_failed_to_dock_text);
mService.mH.obtainMessage(SHOW_NON_RESIZEABLE_DOCK_TOAST, 0, 0, text).sendToTarget();
- return;
}
-
- final int dockSide = mStack.getDockSide();
- if (mResizeMode != RESIZE_MODE_FORCE_RESIZEABLE || dockSide == DOCKED_INVALID) {
- return;
- }
-
- int xOffset = 0;
- int yOffset = 0;
- mStack.getBounds(mTmpRect);
-
- if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
- // The toast was originally placed at the bottom and centered. To place it at the
- // bottom-center of the stack, we offset it horizontally by the diff between the center
- // of the stack bounds vs. the center of the screen.
- displayContent.getLogicalDisplayRect(mTmpRect2);
- xOffset = mTmpRect.centerX() - mTmpRect2.centerX();
- } else if (dockSide == DOCKED_TOP) {
- // The toast was originally placed at the bottom and centered. To place it at the bottom
- // center of the top stack, we offset it vertically by the diff between the bottom of
- // the stack bounds vs. the bottom of the content rect.
- //
- // Note here we use the content rect instead of the display rect, as we want the toast's
- // distance to the dock divider (when it's placed at the top half) to be the same as
- // it's distance to the top of the navigation bar (when it's placed at the bottom).
-
- // We don't adjust for DOCKED_BOTTOM case since it's already at the bottom.
- displayContent.getContentRect(mTmpRect2);
- yOffset = mTmpRect2.bottom - mTmpRect.bottom;
- }
- final String text =
- mService.mContext.getString(R.string.dock_forced_resizable);
- mService.mH.obtainMessage(SHOW_NON_RESIZEABLE_DOCK_TOAST,
- xOffset, yOffset, text).sendToTarget();
}
void addAppToken(int addPos, AppWindowToken wtoken, int resizeMode, boolean homeTask) {