From 226de13773db3b648954e585bd1f861f4cfd0d8b Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Fri, 3 Jan 2020 18:00:29 -0800 Subject: [PATCH] Add focusability into the hierarchy and Container transaction Split-screen will eventually be replaced by a hierarchical mechinasm. This means the RAC.mIsDockMinimized and primary split checking will go away. In its place, add focusable to windowcontainer and have it override all children's focusability. Also, fixes a bug where activity configuration wasn't updated properly after a WC transaction. Bug: 133381284 Test: added wmtest Change-Id: I35ed1561cc32785528854a8a53e52a13144f31c1 --- .../view/WindowContainerTransaction.java | 35 ++++++++ .../com/android/server/wm/ActivityRecord.java | 6 +- .../com/android/server/wm/ActivityStack.java | 13 ++- .../android/server/wm/ActivityStarter.java | 4 +- .../server/wm/ActivityTaskManagerService.java | 88 ++++++++++++++----- .../server/wm/ConfigurationContainer.java | 15 ++++ .../com/android/server/wm/DisplayContent.java | 4 +- .../wm/EnsureActivitiesVisibleHelper.java | 3 +- .../server/wm/RootWindowContainer.java | 8 -- .../android/server/wm/WindowContainer.java | 17 ++++ .../wm/ActivityTaskManagerServiceTests.java | 14 +++ .../server/wm/RootActivityContainerTests.java | 10 +-- 12 files changed, 173 insertions(+), 44 deletions(-) diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java index 253794f70ef27..c55caa3213d8a 100644 --- a/core/java/android/view/WindowContainerTransaction.java +++ b/core/java/android/view/WindowContainerTransaction.java @@ -74,6 +74,18 @@ public class WindowContainerTransaction implements Parcelable { return this; } + /** + * Sets whether a container or any of its children can be focusable. When {@code false}, no + * child can be focused; however, when {@code true}, it is still possible for children to be + * non-focusable due to WM policy. + */ + public WindowContainerTransaction setFocusable(IWindowContainer container, boolean focusable) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mFocusable = focusable; + chg.mChangeMask |= Change.CHANGE_FOCUSABLE; + return this; + } + public Map getChanges() { return mChanges; } @@ -112,7 +124,11 @@ public class WindowContainerTransaction implements Parcelable { * @hide */ public static class Change implements Parcelable { + public static final int CHANGE_FOCUSABLE = 1; + private final Configuration mConfiguration = new Configuration(); + private boolean mFocusable = true; + private int mChangeMask = 0; private @ActivityInfo.Config int mConfigSetMask = 0; private @WindowConfiguration.WindowConfig int mWindowSetMask = 0; @@ -123,6 +139,8 @@ public class WindowContainerTransaction implements Parcelable { protected Change(Parcel in) { mConfiguration.readFromParcel(in); + mFocusable = in.readBoolean(); + mChangeMask = in.readInt(); mConfigSetMask = in.readInt(); mWindowSetMask = in.readInt(); mSchedulePipCallback = (in.readInt() != 0); @@ -136,6 +154,18 @@ public class WindowContainerTransaction implements Parcelable { return mConfiguration; } + /** Gets the requested focusable value */ + public boolean getFocusable() { + if ((mChangeMask & CHANGE_FOCUSABLE) == 0) { + throw new RuntimeException("Focusable not set. check CHANGE_FOCUSABLE first"); + } + return mFocusable; + } + + public int getChangeMask() { + return mChangeMask; + } + @ActivityInfo.Config public int getConfigSetMask() { return mConfigSetMask; @@ -170,6 +200,9 @@ public class WindowContainerTransaction implements Parcelable { if (changesSss) { sb.append("ssw:" + mConfiguration.smallestScreenWidthDp + ","); } + if ((mChangeMask & CHANGE_FOCUSABLE) != 0) { + sb.append("focusable:" + mFocusable + ","); + } sb.append("}"); return sb.toString(); } @@ -177,6 +210,8 @@ public class WindowContainerTransaction implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { mConfiguration.writeToParcel(dest, flags); + dest.writeBoolean(mFocusable); + dest.writeInt(mChangeMask); dest.writeInt(mConfigSetMask); dest.writeInt(mWindowSetMask); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 26d76a8d6e281..f0c0ae5ad4fe0 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2130,8 +2130,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A (intent == null || (intent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0); } + @Override boolean isFocusable() { - return mRootWindowContainer.isFocusable(this, isAlwaysFocusable()); + return super.isFocusable() + && (getWindowConfiguration().canReceiveKeys() || isAlwaysFocusable()); } boolean isResizeable() { @@ -2482,7 +2484,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // We are finishing the top focused activity and its stack has nothing to be focused so // the next focusable stack should be focused. if (mayAdjustTop - && (stack.topRunningActivity() == null || !stack.isFocusable())) { + && (stack.topRunningActivity() == null || !stack.isTopActivityFocusable())) { if (shouldAdjustGlobalFocus) { // Move the entire hierarchy to top with updating global top resumed activity // and focused application if needed. diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 9fd3ea4fc090d..f5fba8e85d648 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -161,9 +161,9 @@ import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.DisplayCutout; import android.view.DisplayInfo; +import android.view.ITaskOrganizer; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; -import android.view.ITaskOrganizer; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -1248,13 +1248,20 @@ class ActivityStack extends WindowContainer implements BoundsAn } } + @Override boolean isFocusable() { + return super.isFocusable() && !(inSplitScreenPrimaryWindowingMode() + && mRootWindowContainer.mIsDockMinimized); + } + + boolean isTopActivityFocusable() { final ActivityRecord r = topRunningActivity(); - return mRootWindowContainer.isFocusable(this, r != null && r.isFocusable()); + return r != null ? r.isFocusable() + : (isFocusable() && getWindowConfiguration().canReceiveKeys()); } boolean isFocusableAndVisible() { - return isFocusable() && shouldBeVisible(null /* starting */); + return isTopActivityFocusable() && shouldBeVisible(null /* starting */); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 61ba15c0013b2..6587226b14d7a 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1569,7 +1569,7 @@ class ActivityStarter { if (mDoResume) { final ActivityRecord topTaskActivity = mStartActivity.getTask().topRunningActivityLocked(); - if (!mTargetStack.isFocusable() + if (!mTargetStack.isTopActivityFocusable() || (topTaskActivity != null && topTaskActivity.isTaskOverlay() && mStartActivity != topTaskActivity)) { // If the activity is not focusable, we can't resume it, but still would like to @@ -1588,7 +1588,7 @@ class ActivityStarter { // will not update the focused stack. If starting the new activity now allows the // task stack to be focusable, then ensure that we now update the focused stack // accordingly. - if (mTargetStack.isFocusable() + if (mTargetStack.isTopActivityFocusable() && !mRootWindowContainer.isTopDisplayFocusedStack(mTargetStack)) { mTargetStack.moveToFront("startActivityInner"); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index ded603c9fd77f..7fb1152770faa 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -248,7 +248,6 @@ import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.KeyguardDismissCallback; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; -import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledFunction; import com.android.internal.util.function.pooled.PooledLambda; @@ -345,6 +344,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** This activity is being relaunched due to a free-resize operation. */ public static final int RELAUNCH_REASON_FREE_RESIZE = 2; + /** Flag indicating that an applied transaction may have effected lifecycle */ + private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1; + private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1; + Context mContext; /** @@ -3300,7 +3303,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - private void sanitizeAndApplyConfigChange(ConfigurationContainer container, + private int sanitizeAndApplyChange(ConfigurationContainer container, WindowContainerTransaction.Change change) { if (!(container instanceof Task || container instanceof ActivityStack)) { throw new RuntimeException("Invalid token in task transaction"); @@ -3312,12 +3315,22 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { configMask &= ActivityInfo.CONFIG_WINDOW_CONFIGURATION | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; windowMask &= WindowConfiguration.WINDOW_CONFIG_BOUNDS; - Configuration c = new Configuration(container.getRequestedOverrideConfiguration()); - c.setTo(change.getConfiguration(), configMask, windowMask); - container.onRequestedOverrideConfigurationChanged(c); - // TODO(b/145675353): remove the following once we could apply new bounds to the - // pinned stack together with its children. - resizePinnedStackIfNeeded(container, configMask, windowMask, c); + int effects = 0; + if (configMask != 0) { + Configuration c = new Configuration(container.getRequestedOverrideConfiguration()); + c.setTo(change.getConfiguration(), configMask, windowMask); + container.onRequestedOverrideConfigurationChanged(c); + // TODO(b/145675353): remove the following once we could apply new bounds to the + // pinned stack together with its children. + resizePinnedStackIfNeeded(container, configMask, windowMask, c); + effects |= TRANSACT_EFFECTS_CLIENT_CONFIG; + } + if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) { + if (container.setFocusable(change.getFocusable())) { + effects |= TRANSACT_EFFECTS_LIFECYCLE; + } + } + return effects; } private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask, @@ -3334,9 +3347,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - private void applyWindowContainerChange(ConfigurationContainer cc, + private int applyWindowContainerChange(ConfigurationContainer cc, WindowContainerTransaction.Change c) { - sanitizeAndApplyConfigChange(cc, c); + int effects = sanitizeAndApplyChange(cc, c); Rect enterPipBounds = c.getEnterPipBounds(); if (enterPipBounds != null) { @@ -3344,25 +3357,58 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mStackSupervisor.updatePictureInPictureMode(tr, enterPipBounds, true); } + return effects; } @Override public void applyContainerTransaction(WindowContainerTransaction t) { mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "applyContainerTransaction()"); + if (t == null) { + return; + } long ident = Binder.clearCallingIdentity(); try { - if (t == null) { - return; - } synchronized (mGlobalLock) { - Iterator> entries = - t.getChanges().entrySet().iterator(); - while (entries.hasNext()) { - final Map.Entry entry = - entries.next(); - final ConfigurationContainer cc = ConfigurationContainer.RemoteToken.fromBinder( - entry.getKey()).getContainer(); - applyWindowContainerChange(cc, entry.getValue()); + int effects = 0; + deferWindowLayout(); + try { + ArraySet haveConfigChanges = new ArraySet<>(); + Iterator> entries = + t.getChanges().entrySet().iterator(); + while (entries.hasNext()) { + final Map.Entry entry = + entries.next(); + final ConfigurationContainer cc = + ConfigurationContainer.RemoteToken.fromBinder( + entry.getKey()).getContainer(); + int containerEffect = applyWindowContainerChange(cc, entry.getValue()); + effects |= containerEffect; + // Lifecycle changes will trigger ensureConfig for everything. + if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0 + && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { + if (cc instanceof WindowContainer) { + haveConfigChanges.add((WindowContainer) cc); + } + } + } + if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) { + // Already calls ensureActivityConfig + mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); + } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { + final PooledConsumer f = PooledLambda.obtainConsumer( + ActivityRecord::ensureActivityConfiguration, + PooledLambda.__(ActivityRecord.class), 0, + false /* preserveWindow */); + try { + for (int i = haveConfigChanges.size() - 1; i >= 0; --i) { + haveConfigChanges.valueAt(i).forAllActivities(f); + } + } finally { + f.recycle(); + } + } + } finally { + continueWindowLayout(); } } } finally { diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index d0310f1a76077..1e60ce83b5fb4 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -624,6 +624,21 @@ public abstract class ConfigurationContainer { return mFullConfiguration.windowConfiguration.isAlwaysOnTop(); } + /** + * Returns {@code true} if this container is focusable. Generally, if a parent is not focusable, + * this will not be focusable either. + */ + boolean isFocusable() { + // TODO(split): Move this to WindowContainer once Split-screen is based on a WindowContainer + // like DisplayArea vs. TaskTiles. + ConfigurationContainer parent = getParent(); + return parent == null || parent.isFocusable(); + } + + boolean setFocusable(boolean focusable) { + return false; + } + boolean hasChild() { return getChildCount() > 0; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ba9d757d979f3..908c4f1ddd66d 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5915,7 +5915,7 @@ class DisplayContent extends WindowContainer