blob: 4a7fcd232ce977942f0058173aeac057786b9fbd [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.DisplayMetrics;
import android.util.Singleton;
import android.view.RemoteAnimationDefinition;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import java.util.List;
/**
* This class gives information about, and interacts with activities and their containers like task,
* stacks, and displays.
*
* @hide
*/
@TestApi
@SystemService(Context.ACTIVITY_TASK_SERVICE)
public class ActivityTaskManager {
/** Invalid stack ID. */
public static final int INVALID_STACK_ID = -1;
/**
* Invalid task ID.
* @hide
*/
public static final int INVALID_TASK_ID = -1;
/**
* Input parameter to {@link IActivityTaskManager#resizeTask} which indicates
* that the resize doesn't need to preserve the window, and can be skipped if bounds
* is unchanged. This mode is used by window manager in most cases.
* @hide
*/
public static final int RESIZE_MODE_SYSTEM = 0;
/**
* Input parameter to {@link IActivityTaskManager#resizeTask} which indicates
* that the resize should preserve the window if possible.
* @hide
*/
public static final int RESIZE_MODE_PRESERVE_WINDOW = (0x1 << 0);
/**
* Input parameter to {@link IActivityTaskManager#resizeTask} used when the
* resize is due to a drag action.
* @hide
*/
public static final int RESIZE_MODE_USER = RESIZE_MODE_PRESERVE_WINDOW;
/**
* Input parameter to {@link IActivityTaskManager#resizeTask} used by window
* manager during a screen rotation.
* @hide
*/
public static final int RESIZE_MODE_SYSTEM_SCREEN_ROTATION = RESIZE_MODE_PRESERVE_WINDOW;
/**
* Input parameter to {@link IActivityTaskManager#resizeTask} which indicates
* that the resize should be performed even if the bounds appears unchanged.
* @hide
*/
public static final int RESIZE_MODE_FORCED = (0x1 << 1);
/**
* Input parameter to {@link IActivityTaskManager#resizeTask} which indicates
* that the resize should preserve the window if possible, and should not be skipped
* even if the bounds is unchanged. Usually used to force a resizing when a drag action
* is ending.
* @hide
*/
public static final int RESIZE_MODE_USER_FORCED =
RESIZE_MODE_PRESERVE_WINDOW | RESIZE_MODE_FORCED;
/**
* Extra included on intents that are delegating the call to
* ActivityManager#startActivityAsCaller to another app. This token is necessary for that call
* to succeed. Type is IBinder.
* @hide
*/
public static final String EXTRA_PERMISSION_TOKEN = "android.app.extra.PERMISSION_TOKEN";
/**
* Extra included on intents that contain an EXTRA_INTENT, with options that the contained
* intent may want to be started with. Type is Bundle.
* TODO: remove once the ChooserActivity moves to systemui
* @hide
*/
public static final String EXTRA_OPTIONS = "android.app.extra.OPTIONS";
/**
* Extra included on intents that contain an EXTRA_INTENT, use this boolean value for the
* parameter of the same name when starting the contained intent.
* TODO: remove once the ChooserActivity moves to systemui
* @hide
*/
public static final String EXTRA_IGNORE_TARGET_SECURITY =
"android.app.extra.EXTRA_IGNORE_TARGET_SECURITY";
/** The minimal size of a display's long-edge needed to support split-screen multi-window. */
public static final int DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP = 440;
private static int sMaxRecentTasks = -1;
private static final Singleton<ActivityTaskManager> sInstance =
new Singleton<ActivityTaskManager>() {
@Override
protected ActivityTaskManager create() {
return new ActivityTaskManager();
}
};
private ActivityTaskManager() {
}
/** @hide */
public static ActivityTaskManager getInstance() {
return sInstance.get();
}
/** @hide */
public static IActivityTaskManager getService() {
return IActivityTaskManagerSingleton.get();
}
@UnsupportedAppUsage(trackingBug = 129726065)
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
new Singleton<IActivityTaskManager>() {
@Override
protected IActivityTaskManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
return IActivityTaskManager.Stub.asInterface(b);
}
};
/**
* Removes root tasks in the windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
public void removeRootTasksInWindowingModes(@NonNull int[] windowingModes) {
try {
getService().removeRootTasksInWindowingModes(windowingModes);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/** Removes root tasks of the activity types from the system. */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
public void removeRootTasksWithActivityTypes(@NonNull int[] activityTypes) {
try {
getService().removeRootTasksWithActivityTypes(activityTypes);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes all visible recent tasks from the system.
* @hide
*/
@RequiresPermission(android.Manifest.permission.REMOVE_TASKS)
public void removeAllVisibleRecentTasks() {
try {
getService().removeAllVisibleRecentTasks();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Return the maximum number of recents entries that we will maintain and show.
* @hide
*/
public static int getMaxRecentTasksStatic() {
if (sMaxRecentTasks < 0) {
return sMaxRecentTasks = ActivityManager.isLowRamDeviceStatic() ? 36 : 48;
}
return sMaxRecentTasks;
}
/**
* Notify the server that splash screen of the given task has been copied"
*
* @param taskId Id of task to handle the material to reconstruct the splash screen view.
* @param parcelable Used to reconstruct the view, null means the surface is un-copyable.
* @hide
*/
public void onSplashScreenViewCopyFinished(int taskId,
@Nullable SplashScreenViewParcelable parcelable) {
try {
getService().onSplashScreenViewCopyFinished(taskId, parcelable);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Return the default limit on the number of recents that an app can make.
* @hide
*/
public static int getDefaultAppRecentsLimitStatic() {
return getMaxRecentTasksStatic() / 6;
}
/**
* Return the maximum limit on the number of recents that an app can make.
* @hide
*/
public static int getMaxAppRecentsLimitStatic() {
return getMaxRecentTasksStatic() / 2;
}
/**
* Returns true if the system supports at least one form of multi-window.
* E.g. freeform, split-screen, picture-in-picture.
*/
public static boolean supportsMultiWindow(Context context) {
// On watches, multi-window is used to present essential system UI, and thus it must be
// supported regardless of device memory characteristics.
boolean isWatch = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WATCH);
return (!ActivityManager.isLowRamDeviceStatic() || isWatch)
&& Resources.getSystem().getBoolean(
com.android.internal.R.bool.config_supportsMultiWindow);
}
/**
* Returns {@code true} if the display the context is associated with supports split screen
* multi-window.
*
* @throws UnsupportedOperationException if the supplied {@link Context} is not associated with
* a display.
*/
public static boolean supportsSplitScreenMultiWindow(Context context) {
DisplayMetrics dm = new DisplayMetrics();
context.getDisplay().getRealMetrics(dm);
int widthDp = (int) (dm.widthPixels / dm.density);
int heightDp = (int) (dm.heightPixels / dm.density);
if (Math.max(widthDp, heightDp) < DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP) {
return false;
}
return supportsMultiWindow(context)
&& Resources.getSystem().getBoolean(
com.android.internal.R.bool.config_supportsSplitScreenMultiWindow);
}
/**
* Start to enter lock task mode for given task by system(UI).
* @param taskId Id of task to lock.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
public void startSystemLockTaskMode(int taskId) {
try {
getService().startSystemLockTaskMode(taskId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Stop lock task mode by system(UI).
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
public void stopSystemLockTaskMode() {
try {
getService().stopSystemLockTaskMode();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Move task to root task with given id.
* @param taskId Id of the task to move.
* @param rootTaskId Id of the rootTask for task moving.
* @param toTop Whether the given task should shown to top of stack.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) {
try {
getService().moveTaskToRootTask(taskId, rootTaskId, toTop);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Resize task to given bounds.
* @param taskId Id of task to resize.
* @param bounds Bounds to resize task.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
public void resizeTask(int taskId, Rect bounds) {
try {
getService().resizeTask(taskId, bounds, RESIZE_MODE_SYSTEM);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Clears launch params for the given package.
* @param packageNames the names of the packages of which the launch params are to be cleared
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
public void clearLaunchParamsForPackages(List<String> packageNames) {
try {
getService().clearLaunchParamsForPackages(packageNames);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* @return whether the UI mode of the given config supports error dialogs (ANR, crash, etc).
* @hide
*/
public static boolean currentUiModeSupportsErrorDialogs(@NonNull Configuration config) {
int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
return (modeType != Configuration.UI_MODE_TYPE_CAR
&& !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER)
&& modeType != Configuration.UI_MODE_TYPE_TELEVISION
&& modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
}
/** @return whether the current UI mode supports error dialogs (ANR, crash, etc). */
public static boolean currentUiModeSupportsErrorDialogs(@NonNull Context context) {
final Configuration config = context.getResources().getConfiguration();
return currentUiModeSupportsErrorDialogs(config);
}
/** @return max allowed number of actions in picture-in-picture mode. */
public static int getMaxNumPictureInPictureActions(@NonNull Context context) {
return context.getResources().getInteger(
com.android.internal.R.integer.config_pictureInPictureMaxNumberOfActions);
}
/**
* @return List of running tasks.
* @hide
*/
public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
return getTasks(maxNum, false /* filterForVisibleRecents */);
}
/**
* @return List of running tasks that can be filtered by visibility in recents.
* @hide
*/
public List<ActivityManager.RunningTaskInfo> getTasks(
int maxNum, boolean filterOnlyVisibleRecents) {
return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */);
}
/**
* @return List of running tasks that can be filtered by visibility in recents and keep intent
* extra.
* @hide
*/
public List<ActivityManager.RunningTaskInfo> getTasks(
int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
try {
return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* @return List of recent tasks.
* @hide
*/
public List<ActivityManager.RecentTaskInfo> getRecentTasks(
int maxNum, int flags, int userId) {
try {
return getService().getRecentTasks(maxNum, flags, userId).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/** @hide */
public void registerTaskStackListener(TaskStackListener listener) {
try {
getService().registerTaskStackListener(listener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/** @hide */
public void unregisterTaskStackListener(TaskStackListener listener) {
try {
getService().unregisterTaskStackListener(listener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/** @hide */
public Rect getTaskBounds(int taskId) {
try {
return getService().getTaskBounds(taskId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Registers remote animations for a display.
* @hide
*/
public void registerRemoteAnimationsForDisplay(
int displayId, RemoteAnimationDefinition definition) {
try {
getService().registerRemoteAnimationsForDisplay(displayId, definition);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/** @hide */
public boolean isInLockTaskMode() {
try {
return getService().isInLockTaskMode();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/** Removes task by a given taskId */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
public boolean removeTask(int taskId) {
try {
return getService().removeTask(taskId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Information you can retrieve about a root task in the system.
* @hide
*/
public static class RootTaskInfo extends TaskInfo implements Parcelable {
// TODO(b/148895075): Move some of the fields to TaskInfo.
public Rect bounds = new Rect();
public int[] childTaskIds;
public String[] childTaskNames;
public Rect[] childTaskBounds;
public int[] childTaskUserIds;
public boolean visible;
// Index of the stack in the display's stack list, can be used for comparison of stack order
public int position;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedObject(bounds, flags);
dest.writeIntArray(childTaskIds);
dest.writeStringArray(childTaskNames);
dest.writeTypedArray(childTaskBounds, flags);
dest.writeIntArray(childTaskUserIds);
dest.writeInt(visible ? 1 : 0);
dest.writeInt(position);
super.writeToParcel(dest, flags);
}
@Override
void readFromParcel(Parcel source) {
bounds = source.readTypedObject(Rect.CREATOR);
childTaskIds = source.createIntArray();
childTaskNames = source.createStringArray();
childTaskBounds = source.createTypedArray(Rect.CREATOR);
childTaskUserIds = source.createIntArray();
visible = source.readInt() > 0;
position = source.readInt();
super.readFromParcel(source);
}
public static final @NonNull Creator<RootTaskInfo> CREATOR = new Creator<>() {
@Override
public RootTaskInfo createFromParcel(Parcel source) {
return new RootTaskInfo(source);
}
@Override
public RootTaskInfo[] newArray(int size) {
return new RootTaskInfo[size];
}
};
public RootTaskInfo() {
}
private RootTaskInfo(Parcel source) {
readFromParcel(source);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(256);
sb.append("RootTask id="); sb.append(taskId);
sb.append(" bounds="); sb.append(bounds.toShortString());
sb.append(" displayId="); sb.append(displayId);
sb.append(" userId="); sb.append(userId);
sb.append("\n");
sb.append(" configuration="); sb.append(configuration);
sb.append("\n");
for (int i = 0; i < childTaskIds.length; ++i) {
sb.append(" taskId="); sb.append(childTaskIds[i]);
sb.append(": "); sb.append(childTaskNames[i]);
if (childTaskBounds != null) {
sb.append(" bounds="); sb.append(childTaskBounds[i].toShortString());
}
sb.append(" userId=").append(childTaskUserIds[i]);
sb.append(" visible=").append(visible);
if (topActivity != null) {
sb.append(" topActivity=").append(topActivity);
}
sb.append("\n");
}
return sb.toString();
}
}
}