Added support for android.R.attr#alwaysFocusable
Allows an activity to always be focusable regardless of if it is in a
stack whose activities are normally not focusable. For example, activities
in pinned stack aren't focusable. This flag allows them to be focusable.
Also, changed ActivityInfo.#{resizeable, supportsPip} to use flags.
Bug: 26273032
Bug: 26034613
Change-Id: I8c63e6d3256757e2e6931e08b8a65269f5169d35
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 326735e..dedf07f5 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -281,6 +281,29 @@
* {@see android.app.Activity#setVrMode(boolean)}.
*/
public static final int FLAG_ENABLE_VR_MODE = 0x8000;
+
+ /**
+ * Bit in {@link #flags} indicating if the activity is resizeable to any dimension.
+ * See {@link android.R.attr#resizeableActivity}.
+ * @hide
+ */
+ public static final int FLAG_RESIZEABLE = 0x10000;
+
+ /**
+ * Bit in {@link #flags} indicating if the activity is supports picture-in-picture form of
+ * multi-window mode. See {@link android.R.attr#supportsPictureInPicture}.
+ * @hide
+ */
+ public static final int FLAG_SUPPORTS_PICTURE_IN_PICTURE = 0x20000;
+
+ /**
+ * Bit in {@link #flags} indicating if the activity is always focusable regardless of if it is
+ * in a task/stack whose activities are normally not focusable.
+ * See android.R.attr#alwaysFocusable.
+ * @hide
+ */
+ public static final int FLAG_ALWAYS_FOCUSABLE = 0x40000;
+
/**
* @hide Bit in {@link #flags}: If set, this component will only be seen
* by the system user. Only works with broadcast receivers. Set from the
@@ -670,20 +693,6 @@
*/
public String parentActivityName;
- /**
- * Value indicating if the activity is resizeable to any dimension.
- * See {@link android.R.attr#resizeableActivity}.
- * @hide
- */
- public boolean resizeable;
-
- /**
- * Value indicating if the activity is supports picture-in-picture form of multi-window mode.
- * See {@link android.R.attr#supportsPictureInPicture}.
- * @hide
- */
- public boolean supportsPip;
-
/** @hide */
public static final int LOCK_TASK_LAUNCH_MODE_DEFAULT = 0;
/** @hide */
@@ -735,8 +744,6 @@
uiOptions = orig.uiOptions;
parentActivityName = orig.parentActivityName;
maxRecents = orig.maxRecents;
- resizeable = orig.resizeable;
- supportsPip = orig.supportsPip;
lockTaskLaunchMode = orig.lockTaskLaunchMode;
layout = orig.layout;
}
@@ -791,7 +798,6 @@
pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions));
}
if ((flags&DUMP_FLAG_DETAILS) != 0) {
- pw.println(prefix + "resizeable=" + resizeable + " supportsPip=" + supportsPip);
pw.println(prefix + "lockTaskLaunchMode="
+ lockTaskLaunchModeToString(lockTaskLaunchMode));
}
@@ -829,8 +835,6 @@
dest.writeString(parentActivityName);
dest.writeInt(persistableMode);
dest.writeInt(maxRecents);
- dest.writeInt(resizeable ? 1 : 0);
- dest.writeInt(supportsPip ? 1 : 0);
dest.writeInt(lockTaskLaunchMode);
if (layout != null) {
dest.writeInt(1);
@@ -871,8 +875,6 @@
parentActivityName = source.readString();
persistableMode = source.readInt();
maxRecents = source.readInt();
- resizeable = (source.readInt() == 1);
- supportsPip = (source.readInt() == 1);
lockTaskLaunchMode = source.readInt();
if (source.readInt() == 1) {
layout = new Layout(source);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f445cf8..a145231 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3223,12 +3223,19 @@
a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
}
- a.info.resizeable = sa.getBoolean(
- R.styleable.AndroidManifestActivity_resizeableActivity,
- owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N);
+ if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity,
+ owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N)) {
+ a.info.flags |= ActivityInfo.FLAG_RESIZEABLE;
- a.info.supportsPip = a.info.resizeable ? sa.getBoolean(
- R.styleable.AndroidManifestActivity_supportsPictureInPicture, false) : false;
+ if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
+ false)) {
+ a.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+ }
+ }
+
+ if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
+ a.info.flags |= ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+ }
a.info.screenOrientation = sa.getInt(
R.styleable.AndroidManifestActivity_screenOrientation,
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7a379d50..2ef082c 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -101,11 +101,14 @@
* the task doesn't exist yet.
* @param configuration Configuration that is being used with this task.
* @param cropWindowsToStack True if the app windows should be cropped to the stack bounds.
+ * @param alwaysFocusable True if the app windows are always focusable regardless of the stack
+ * they are in.
*/
void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
int configChanges, boolean voiceInteraction, boolean launchTaskBehind,
- in Rect taskBounds, in Configuration configuration, boolean cropWindowsToStack);
+ in Rect taskBounds, in Configuration configuration, boolean cropWindowsToStack,
+ boolean alwaysFocusable);
/**
*
* @param token The token we are adding to the input task Id.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 2a11081..58a77e8 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1857,6 +1857,11 @@
<attr name="lockTaskMode" />
<attr name="showForAllUsers" />
<attr name="encryptionAware" />
+ <!-- @hide This activity is always focusable regardless of if it is in a task/stack whose
+ activities are normally not focusable.
+ For example, {@link android.R.attr#supportsPictureInPicture} activities are placed
+ in a task/stack that isn't focusable. This flag allows them to be focusable.-->
+ <attr name="alwaysFocusable" format="boolean" />
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 295fea0..05b3800 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7274,7 +7274,7 @@
+ "Can't find activity for token=" + token);
}
- if (!r.info.supportsPip) {
+ if (!r.supportsPictureInPicture()) {
throw new IllegalArgumentException("enterPictureInPictureMode: "
+ "Picture-In-Picture not supported for r=" + r);
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index f0df0c4..4fb87c3 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -18,6 +18,9 @@
import static android.app.ActivityManager.StackId;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.FLAG_RESIZEABLE;
+import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
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_SWITCH;
@@ -741,7 +744,19 @@
}
boolean isFocusable() {
- return StackId.canReceiveKeys(task.stack.mStackId);
+ return StackId.canReceiveKeys(task.stack.mStackId) || isAlwaysFocusable();
+ }
+
+ boolean isResizeable() {
+ return (info.flags & FLAG_RESIZEABLE) != 0;
+ }
+
+ boolean supportsPictureInPicture() {
+ return (info.flags & FLAG_SUPPORTS_PICTURE_IN_PICTURE) != 0;
+ }
+
+ boolean isAlwaysFocusable() {
+ return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0;
}
void makeFinishingLocked() {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 9a6a7f7..4e9d1b1 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -4749,9 +4749,9 @@
task.updateOverrideConfiguration(bounds);
mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
- (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
- r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind,
- bounds, task.mOverrideConfig, !r.isHomeActivity());
+ (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges,
+ task.voiceSession != null, r.mLaunchTaskBehind, bounds, task.mOverrideConfig,
+ !r.isHomeActivity(), r.isAlwaysFocusable());
mWindowManager.setTaskResizeable(task.taskId, task.mResizeable);
r.taskConfigOverride = task.mOverrideConfig;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 688243d..043a2e7 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1375,7 +1375,7 @@
Rect getOverrideBounds(ActivityRecord r, ActivityOptions options, TaskRecord inTask) {
Rect newBounds = null;
- if (options != null && (r.info.resizeable || (inTask != null && inTask.mResizeable))) {
+ if (options != null && (r.isResizeable() || (inTask != null && inTask.mResizeable))) {
if (canUseActivityOptionsLaunchBounds(options)) {
newBounds = options.getLaunchBounds();
}
@@ -2172,7 +2172,7 @@
return false;
}
- if (!mService.mForceResizableActivities && !r.info.supportsPip) {
+ if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
Slog.w(TAG,
"moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
+ " r=" + r);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index bcb2215..2794f74 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1444,7 +1444,7 @@
final boolean canUseFocusedStack =
focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
|| focusedStackId == DOCKED_STACK_ID
- || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable);
+ || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.isResizeable());
if (canUseFocusedStack && (!newTask
|| mSupervisor.mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index e77bb20..c97d09c 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -23,6 +23,7 @@
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+import static android.content.pm.ActivityInfo.FLAG_RESIZEABLE;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
@@ -441,7 +442,7 @@
} else {
autoRemoveRecents = false;
}
- mResizeable = info.resizeable || mService.mForceResizableActivities;
+ mResizeable = (info.flags & FLAG_RESIZEABLE) != 0 || mService.mForceResizableActivities;
mLockTaskMode = info.lockTaskLaunchMode;
mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0;
setLockTaskAuth();
@@ -697,7 +698,7 @@
if (mActivities.isEmpty()) {
taskType = r.mActivityType;
if (taskType == HOME_ACTIVITY_TYPE && mService.mForceResizableActivities) {
- mResizeable = r.info.resizeable;
+ mResizeable = r.isResizeable();
}
isPersistable = r.isPersistable();
mCallingUid = r.launchedFromUid;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index b49641f..9b9f14b 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.StackId;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -123,6 +124,8 @@
// True if the windows associated with this token should be cropped to their stack bounds.
boolean mCropWindowsToStack;
+ boolean mAlwaysFocusable;
+
AppWindowToken(WindowManagerService _service, IApplicationToken _token,
boolean _voiceInteraction) {
super(_service, _token.asBinder(),
@@ -256,8 +259,8 @@
return candidate;
}
- boolean stackCanReceiveKeys() {
- return (windows.size() > 0) ? windows.get(windows.size() - 1).stackCanReceiveKeys() : false;
+ boolean windowsAreFocusable() {
+ return StackId.canReceiveKeys(mTask.mStack.mStackId) || mAlwaysFocusable;
}
boolean isVisible() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6385caa..06e2e30 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3203,7 +3203,8 @@
public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId,
int configChanges, boolean voiceInteraction, boolean launchTaskBehind,
- Rect taskBounds, Configuration config, boolean cropWindowsToStack) {
+ Rect taskBounds, Configuration config, boolean cropWindowsToStack,
+ boolean alwaysFocusable) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"addAppToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3238,6 +3239,7 @@
(ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
atoken.mLaunchTaskBehind = launchTaskBehind;
atoken.mCropWindowsToStack = cropWindowsToStack;
+ atoken.mAlwaysFocusable = alwaysFocusable;
if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
+ " to stack=" + stackId + " task=" + taskId + " at " + addPos);
@@ -9078,8 +9080,8 @@
if (wtoken == token) {
break;
}
- if (mFocusedApp == token && token.stackCanReceiveKeys()) {
- // Whoops, we are below the focused app whose stack can receive keys...
+ if (mFocusedApp == token && token.windowsAreFocusable()) {
+ // Whoops, we are below the focused app whose windows are focusable...
// No focus for you!!!
if (localLOGV || DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM,
"findFocusedWindow: Reached focused app=" + mFocusedApp);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 733bc29..a825e80 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1558,12 +1558,7 @@
return isVisibleOrAdding()
&& (mViewVisibility == View.VISIBLE)
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
- && stackCanReceiveKeys();
- }
-
- boolean stackCanReceiveKeys() {
- final TaskStack stack = getStack();
- return stack != null && StackId.canReceiveKeys(stack.mStackId);
+ && (mAppToken == null || mAppToken.windowsAreFocusable());
}
@Override
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 3c2659f..b78fd49 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -93,7 +93,7 @@
try {
mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false, null,
- Configuration.EMPTY, false);
+ Configuration.EMPTY, false, false);
fail("IWindowManager.addAppToken did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {