Merge "Make per-display focus optional (1/2)"
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 5e6d3d1..c06a1fe 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -71,41 +71,18 @@
void dispatchGetNewSurface();
/**
- * Tell the window that it is either gaining or losing focus.
- *
- * @param hasFocus {@code true} if window has focus, {@code false} otherwise.
- * @param inTouchMode {@code true} if screen is in touch mode, {@code false} otherwise.
- * @param reportToClient {@code true} when need to report to child view with
- * {@link View#onWindowFocusChanged(boolean)}, {@code false} otherwise.
- * <p>
- * Note: In the previous design, there is only one window focus state tracked by
- * WindowManagerService.
- * For multi-display, the window focus state is tracked by each display independently.
- * <p>
- * It will introduce a problem if the window was already focused on one display and then
- * switched to another display, since the window focus state on each display is independent,
- * there is no global window focus state in WindowManagerService, so the window focus state of
- * the former display remains unchanged.
- * <p>
- * When switched back to former display, some flows that rely on the global window focus state
- * in view root will be missed due to the window focus state remaining unchanged.
- * (i.e: Showing single IME window when switching between displays.)
- * <p>
- * To solve the problem, WindowManagerService tracks the top focused display change and then
- * callbacks to the client via this method to make sure that the client side will request the
- * IME on the top focused display, and then set {@param reportToClient} as {@code false} to
- * ignore reporting to the application, since its focus remains unchanged on its display.
- *
+ * Tell the window that it is either gaining or losing focus. Keep it up
+ * to date on the current state showing navigational focus (touch mode) too.
*/
- void windowFocusChanged(boolean hasFocus, boolean inTouchMode, boolean reportToClient);
-
+ void windowFocusChanged(boolean hasFocus, boolean inTouchMode);
+
void closeSystemDialogs(String reason);
-
+
/**
* Called for wallpaper windows when their offsets change.
*/
void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync);
-
+
void dispatchWallpaperCommand(String action, int x, int y,
int z, in Bundle extras, boolean sync);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9a1e931..3f7a512 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2765,7 +2765,7 @@
}
}
- private void handleWindowFocusChanged(boolean reportToClient) {
+ private void handleWindowFocusChanged() {
final boolean hasWindowFocus;
final boolean inTouchMode;
synchronized (this) {
@@ -2800,9 +2800,8 @@
} catch (RemoteException ex) {
}
// Retry in a bit.
- final Message msg = mHandler.obtainMessage(MSG_WINDOW_FOCUS_CHANGED);
- msg.arg1 = reportToClient ? 1 : 0;
- mHandler.sendMessageDelayed(msg, 500);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ MSG_WINDOW_FOCUS_CHANGED), 500);
return;
}
}
@@ -2819,15 +2818,8 @@
}
if (mView != null) {
mAttachInfo.mKeyDispatchState.reset();
- // We dispatch onWindowFocusChanged to child view only when window is gaining /
- // losing focus.
- // If the focus is updated from top display change but window focus on the display
- // remains unchanged, will not callback onWindowFocusChanged again since it may
- // be redundant & can affect the state when it callbacks.
- if (reportToClient) {
- mView.dispatchWindowFocusChanged(hasWindowFocus);
- mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
- }
+ mView.dispatchWindowFocusChanged(hasWindowFocus);
+ mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
if (mAttachInfo.mTooltipHost != null) {
mAttachInfo.mTooltipHost.hideTooltip();
}
@@ -4428,7 +4420,7 @@
}
break;
case MSG_WINDOW_FOCUS_CHANGED: {
- handleWindowFocusChanged(msg.arg1 != 0 /* reportToClient */);
+ handleWindowFocusChanged();
} break;
case MSG_DIE:
doDie();
@@ -7372,7 +7364,7 @@
}
if (stage != null) {
- handleWindowFocusChanged(true /* reportToClient */);
+ handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
@@ -7712,11 +7704,6 @@
}
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
- windowFocusChanged(hasFocus, inTouchMode, true /* reportToClient */);
- }
-
- public void windowFocusChanged(boolean hasFocus, boolean inTouchMode,
- boolean reportToClient) {
synchronized (this) {
mWindowFocusChanged = true;
mUpcomingWindowFocus = hasFocus;
@@ -7724,7 +7711,6 @@
}
Message msg = Message.obtain();
msg.what = MSG_WINDOW_FOCUS_CHANGED;
- msg.arg1 = reportToClient ? 1 : 0;
mHandler.sendMessage(msg);
}
@@ -8286,11 +8272,10 @@
}
@Override
- public void windowFocusChanged(boolean hasFocus, boolean inTouchMode,
- boolean reportToClient) {
+ public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
- viewAncestor.windowFocusChanged(hasFocus, inTouchMode, reportToClient);
+ viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
}
}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index c8834a8..ae5c67d 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -26,9 +26,9 @@
import android.view.DragEvent;
import android.view.IWindow;
import android.view.IWindowSession;
-import android.view.PointerIcon;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.PointerIcon;
import com.android.internal.os.IResultReceiver;
@@ -76,7 +76,7 @@
}
@Override
- public void windowFocusChanged(boolean hasFocus, boolean touchEnabled, boolean reportToClient) {
+ public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
}
@Override
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 97a21a5..ecff835 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2019,6 +2019,10 @@
This is intended to allow packaging drivers or tools for installation on a PC. -->
<string translatable="false" name="config_isoImagePath"></string>
+ <!-- Whether the system enables per-display focus. If the system has the input method for each
+ display, this value should be true. -->
+ <bool name="config_perDisplayFocusEnabled">false</bool>
+
<!-- Whether a software navigation bar should be shown. NOTE: in the future this may be
autodetected from the Configuration. -->
<bool name="config_showNavigationBar">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 161e416..f44976e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1670,6 +1670,7 @@
<java-symbol type="bool" name="config_lockDayNightMode" />
<java-symbol type="bool" name="config_lockUiMode" />
<java-symbol type="bool" name="config_reverseDefaultRotation" />
+ <java-symbol type="bool" name="config_perDisplayFocusEnabled" />
<java-symbol type="bool" name="config_showNavigationBar" />
<java-symbol type="bool" name="config_supportAutoRotation" />
<java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3ba1155..ebe202e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -599,6 +599,9 @@
private boolean mAodShowing;
+ private boolean mPerDisplayFocusEnabled = false;
+ private int mTopFocusedDisplayId = INVALID_DISPLAY;
+
private static final int MSG_ENABLE_POINTER_LOCATION = 1;
private static final int MSG_DISABLE_POINTER_LOCATION = 2;
private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
@@ -1811,6 +1814,9 @@
mHandleVolumeKeysInWM = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_handleVolumeKeysInWindowManager);
+ mPerDisplayFocusEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_perDisplayFocusEnabled);
+
readConfigurationDependentBehaviors();
mAccessibilityManager = (AccessibilityManager) context.getSystemService(
@@ -2542,6 +2548,20 @@
/** {@inheritDoc} */
@Override
public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
+ final long result = interceptKeyBeforeDispatchingInner(win, event, policyFlags);
+ if (result == 0 && !mPerDisplayFocusEnabled
+ && event.getDisplayId() != mTopFocusedDisplayId) {
+ // Someone tries to send a key event to a display which doesn't have a focused window.
+ // We drop the event here, or it will cause ANR.
+ // TODO (b/121057974): The user may be confused about why the key doesn't work, so we
+ // may need to deal with this problem.
+ return -1;
+ }
+ return result;
+ }
+
+ private long interceptKeyBeforeDispatchingInner(WindowState win, KeyEvent event,
+ int policyFlags) {
final boolean keyguardOn = keyguardOn();
final int keyCode = event.getKeyCode();
final int repeatCount = event.getRepeatCount();
@@ -3123,6 +3143,11 @@
}
@Override
+ public void setTopFocusedDisplay(int displayId) {
+ mTopFocusedDisplayId = displayId;
+ }
+
+ @Override
public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
throws RemoteException {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 3d474e3..3da325c 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1034,6 +1034,13 @@
public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags);
/**
+ * Called when the top focused display is changed.
+ *
+ * @param displayId The ID of the top focused display.
+ */
+ void setTopFocusedDisplay(int displayId);
+
+ /**
* Apply the keyguard policy to a specific window.
*
* @param win The window to apply the keyguard policy.
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 8624bff..8d49bf3 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -91,6 +91,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.Build;
import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
@@ -733,6 +734,17 @@
}
boolean windowsAreFocusable() {
+ if (mTargetSdk < Build.VERSION_CODES.Q) {
+ final int pid = mActivityRecord != null
+ ? (mActivityRecord.app != null ? mActivityRecord.app.getPid() : 0) : 0;
+ final AppWindowToken topFocusedAppOfMyProcess =
+ mWmService.mRoot.mTopFocusedAppByProcess.get(pid);
+ if (topFocusedAppOfMyProcess != null && topFocusedAppOfMyProcess != this) {
+ // For the apps below Q, there can be only one app which has the focused window per
+ // process, because legacy apps may not be ready for a multi-focus system.
+ return false;
+ }
+ }
return getWindowConfiguration().canReceiveKeys() || mAlwaysFocusable;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2f4c5ca..415357d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -61,9 +61,9 @@
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
-
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -103,6 +103,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.CUSTOM_SCREEN_ROTATION;
+import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE;
import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE;
import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
@@ -2832,6 +2833,11 @@
forAllWindows(mScheduleToastTimeout, false /* traverseTopToBottom */);
}
+ WindowState findFocusedWindowIfNeeded() {
+ return (mWmService.mPerDisplayFocusEnabled
+ || mWmService.mRoot.mTopFocusedAppByProcess.isEmpty()) ? findFocusedWindow() : null;
+ }
+
WindowState findFocusedWindow() {
mTmpWindow = null;
@@ -2844,7 +2850,6 @@
return mTmpWindow;
}
-
/**
* Update the focused window and make some adjustments if the focus has changed.
*
@@ -2856,31 +2861,31 @@
* @param updateInputWindows Whether to sync the window information to the input module.
* @return {@code true} if the focused window has changed.
*/
- boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows, boolean focusFound) {
- final WindowState newFocus = findFocusedWindow();
+ boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
+ WindowState newFocus = findFocusedWindowIfNeeded();
if (mCurrentFocus == newFocus) {
return false;
}
boolean imWindowChanged = false;
- // TODO (b/111080190): Multi-Session IME
- if (!focusFound) {
- final WindowState imWindow = mInputMethodWindow;
- if (imWindow != null) {
- final WindowState prevTarget = mInputMethodTarget;
+ final WindowState imWindow = mInputMethodWindow;
+ if (imWindow != null) {
+ final WindowState prevTarget = mInputMethodTarget;
+ final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
+ imWindowChanged = prevTarget != newTarget;
- final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
- imWindowChanged = prevTarget != newTarget;
-
- if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
- && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
- assignWindowLayers(false /* setLayoutNeeded */);
- }
+ if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
+ && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
+ assignWindowLayers(false /* setLayoutNeeded */);
}
}
if (imWindowChanged) {
mWmService.mWindowsChanged = true;
setLayoutNeeded();
+ newFocus = findFocusedWindowIfNeeded();
+ }
+ if (mCurrentFocus != newFocus) {
+ mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
}
if (DEBUG_FOCUS_LIGHT || mWmService.localLOGV) Slog.v(TAG_WM, "Changing focus from "
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 3bbef92..6e4f69e 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -79,6 +79,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.function.Consumer;
/** Root {@link WindowContainer} for the device. */
@@ -122,7 +123,10 @@
// The ID of the display which is responsible for receiving display-unspecified key and pointer
// events.
- int mTopFocusedDisplayId = INVALID_DISPLAY;
+ private int mTopFocusedDisplayId = INVALID_DISPLAY;
+
+ // Map from the PID to the top most app which has a focused window of the process.
+ final HashMap<Integer, AppWindowToken> mTopFocusedAppByProcess = new HashMap<>();
// Only a separate transaction until we separate the apply surface changes
// transaction from the global transaction.
@@ -157,50 +161,32 @@
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
+ mTopFocusedAppByProcess.clear();
boolean changed = false;
int topFocusedDisplayId = INVALID_DISPLAY;
-
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent dc = mChildren.get(i);
- changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows,
- topFocusedDisplayId != INVALID_DISPLAY /* focusFound */);
- if (topFocusedDisplayId == INVALID_DISPLAY && dc.mCurrentFocus != null) {
- topFocusedDisplayId = dc.getDisplayId();
+ changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows);
+ final WindowState newFocus = dc.mCurrentFocus;
+ if (newFocus != null) {
+ final int pidOfNewFocus = newFocus.mSession.mPid;
+ if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) {
+ mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mAppToken);
+ }
+ if (topFocusedDisplayId == INVALID_DISPLAY) {
+ topFocusedDisplayId = dc.getDisplayId();
+ }
}
}
if (topFocusedDisplayId == INVALID_DISPLAY) {
topFocusedDisplayId = DEFAULT_DISPLAY;
}
- // TODO(b/118865114): Review if need callback top focus display change to view component.
- // (i.e. Activity or View)
- // Currently we only tracked topFocusedDisplayChanged for notifying InputMethodManager via
- // ViewRootImpl.windowFocusChanged to refocus IME window when top display focus changed
- // but window focus remain the same case.
- // It may need to review if any use case that need to add new callback for reporting
- // this change.
- final boolean topFocusedDisplayChanged =
- mTopFocusedDisplayId != topFocusedDisplayId && mode == UPDATE_FOCUS_NORMAL;
if (mTopFocusedDisplayId != topFocusedDisplayId) {
mTopFocusedDisplayId = topFocusedDisplayId;
- mWmService.mInputManager.setFocusedDisplay(mTopFocusedDisplayId);
+ mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId="
- + mTopFocusedDisplayId);
+ + topFocusedDisplayId);
}
-
- // Report window focus or top display focus changed through REPORT_FOCUS_CHANGE.
- forAllDisplays((dc) -> {
- final boolean windowFocusChanged =
- dc.mCurrentFocus != null && dc.mCurrentFocus != dc.mLastFocus;
- final boolean isTopFocusedDisplay =
- topFocusedDisplayChanged && dc.getDisplayId() == mTopFocusedDisplayId;
- if (windowFocusChanged || isTopFocusedDisplay) {
- final Message msg = mWmService.mH.obtainMessage(
- WindowManagerService.H.REPORT_FOCUS_CHANGE, dc);
- msg.arg1 = topFocusedDisplayChanged ? 1 : 0;
- mWmService.mH.sendMessage(msg);
- }
- });
-
return changed;
}
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index 53d2cb0..c006a7b 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -16,6 +16,12 @@
package com.android.server.wm;
+import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.TYPE_NOT_SPECIFIED;
+import static android.view.PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
+
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
@@ -25,21 +31,18 @@
import com.android.server.wm.WindowManagerService.H;
-import static android.view.PointerIcon.TYPE_NOT_SPECIFIED;
-import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
-import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
-import static android.view.PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
-import static android.view.PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
-
public class TaskTapPointerEventListener implements PointerEventListener {
- final private Region mTouchExcludeRegion = new Region();
+ private final Region mTouchExcludeRegion = new Region();
+ private final Region mTmpRegion = new Region();
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final Handler mHandler;
private final Runnable mMoveDisplayToTop;
private final Rect mTmpRect = new Rect();
private int mPointerIconType = TYPE_NOT_SPECIFIED;
+ private int mLastDownX;
+ private int mLastDownY;
public TaskTapPointerEventListener(WindowManagerService service,
DisplayContent displayContent) {
@@ -47,7 +50,22 @@
mDisplayContent = displayContent;
mHandler = new Handler(mService.mH.getLooper());
mMoveDisplayToTop = () -> {
+ int x;
+ int y;
+ synchronized (this) {
+ x = mLastDownX;
+ y = mLastDownY;
+ }
synchronized (mService.mGlobalLock) {
+ if (!mService.mPerDisplayFocusEnabled
+ && mService.mRoot.getTopFocusedDisplayContent() != mDisplayContent
+ && inputMethodWindowContains(x, y)) {
+ // In a single focus system, if the input method window and the input method
+ // target window are on the different displays, when the user is tapping on the
+ // input method window, we don't move its display to top. Otherwise, the input
+ // method target window will lose the focus.
+ return;
+ }
mDisplayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP,
mDisplayContent, true /* includingParents */);
}
@@ -70,6 +88,8 @@
mService.mTaskPositioningController.handleTapOutsideTask(
mDisplayContent, x, y);
}
+ mLastDownX = x;
+ mLastDownY = y;
mHandler.post(mMoveDisplayToTop);
}
}
@@ -122,4 +142,13 @@
private int getDisplayId() {
return mDisplayContent.getDisplayId();
}
+
+ private boolean inputMethodWindowContains(int x, int y) {
+ final WindowState inputMethodWindow = mDisplayContent.mInputMethodWindow;
+ if (inputMethodWindow == null || !inputMethodWindow.isVisibleLw()) {
+ return false;
+ }
+ inputMethodWindow.getTouchableRegion(mTmpRegion);
+ return mTmpRegion.contains(x, y);
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d0521b4..e3ced83 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -611,6 +611,9 @@
boolean mClientFreezingScreen = false;
int mAppsFreezingScreen = 0;
+ @VisibleForTesting
+ boolean mPerDisplayFocusEnabled;
+
// State while inside of layoutAndPlaceSurfacesLocked().
boolean mFocusMayChange;
@@ -944,6 +947,8 @@
com.android.internal.R.integer.config_maxUiWidth);
mDisableTransitionAnimation = context.getResources().getBoolean(
com.android.internal.R.bool.config_disableTransitionAnimation);
+ mPerDisplayFocusEnabled = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_perDisplayFocusEnabled);
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mDisplayWindowSettings = new DisplayWindowSettings(this);
@@ -4473,7 +4478,6 @@
AccessibilityController accessibilityController = null;
- final boolean topFocusedDisplayChanged = msg.arg1 != 0;
synchronized (mGlobalLock) {
// TODO(multidisplay): Accessibility supported only of default desiplay.
if (mAccessibilityController != null && displayContent.isDefaultDisplay) {
@@ -4484,19 +4488,7 @@
newFocus = displayContent.mCurrentFocus;
}
if (lastFocus == newFocus) {
- // Report focus to ViewRootImpl when top focused display changes.
- // Or, nothing to do for no window focus change.
- if (topFocusedDisplayChanged && newFocus != null) {
- if (DEBUG_FOCUS_LIGHT) {
- Slog.d(TAG, "Reporting focus: " + newFocus
- + " due to top focused display change.");
- }
- // See {@link IWindow#windowFocusChanged} to know why set
- // reportToClient as false.
- newFocus.reportFocusChangedSerialized(true, mInTouchMode,
- false /* reportToClient */);
- notifyFocusChanged();
- }
+ // Focus is not changing, so nothing to do.
return;
}
synchronized (mGlobalLock) {
@@ -4518,15 +4510,13 @@
if (newFocus != null) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Gaining focus: " + newFocus);
- newFocus.reportFocusChangedSerialized(true, mInTouchMode,
- true /* reportToClient */);
+ newFocus.reportFocusChangedSerialized(true, mInTouchMode);
notifyFocusChanged();
}
if (lastFocus != null) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Losing focus: " + lastFocus);
- lastFocus.reportFocusChangedSerialized(false, mInTouchMode,
- true /* reportToClient */);
+ lastFocus.reportFocusChangedSerialized(false, mInTouchMode);
}
} break;
@@ -4543,8 +4533,7 @@
for (int i = 0; i < N; i++) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Losing delayed focus: " +
losers.get(i));
- losers.get(i).reportFocusChangedSerialized(false, mInTouchMode,
- true /* reportToClient */);
+ losers.get(i).reportFocusChangedSerialized(false, mInTouchMode);
}
} break;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d2dfa76..f86d089 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2866,13 +2866,12 @@
* Report a focus change. Must be called with no locks held, and consistently
* from the same serialized thread (such as dispatched from a handler).
*/
- void reportFocusChangedSerialized(boolean focused, boolean inTouchMode,
- boolean reportToClient) {
+ void reportFocusChangedSerialized(boolean focused, boolean inTouchMode) {
try {
- mClient.windowFocusChanged(focused, inTouchMode, reportToClient);
+ mClient.windowFocusChanged(focused, inTouchMode);
} catch (RemoteException e) {
}
- if (mFocusCallbacks != null && reportToClient) {
+ if (mFocusCallbacks != null) {
final int N = mFocusCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
IWindowFocusObserver obs = mFocusCallbacks.getBroadcastItem(i);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 7c83ecc..8430616 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -19,6 +19,8 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
@@ -309,9 +311,27 @@
@Test
public void testFocusedWindowMultipleDisplays() {
+ doTestFocusedWindowMultipleDisplays(false /* perDisplayFocusEnabled */, Q);
+ }
+
+ @Test
+ public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabled() {
+ doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, Q);
+ }
+
+ @Test
+ public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabledLegacyApp() {
+ doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, P);
+ }
+
+ private void doTestFocusedWindowMultipleDisplays(boolean perDisplayFocusEnabled,
+ int targetSdk) {
+ mWm.mPerDisplayFocusEnabled = perDisplayFocusEnabled;
+
// Create a focusable window and check that focus is calculated correctly
final WindowState window1 =
createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1");
+ window1.mAppToken.mTargetSdk = targetSdk;
updateFocusedWindow();
assertTrue(window1.isFocused());
assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
@@ -324,16 +344,17 @@
// Add a window to the second display, and it should be focused
final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2");
+ window2.mAppToken.mTargetSdk = targetSdk;
updateFocusedWindow();
- assertTrue(window1.isFocused());
assertTrue(window2.isFocused());
+ assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window1.isFocused());
assertEquals(window2, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
- // Move the first window to the to including parents, and make sure focus is updated
+ // Move the first window to top including parents, and make sure focus is updated
window1.getParent().positionChildAt(POSITION_TOP, window1, true);
updateFocusedWindow();
assertTrue(window1.isFocused());
- assertTrue(window2.isFocused());
+ assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window2.isFocused());
assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 29738ff..c5df85c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -64,8 +64,7 @@
}
@Override
- public void windowFocusChanged(boolean hasFocus, boolean inTouchMode, boolean reportToClient)
- throws RemoteException {
+ public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) throws RemoteException {
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index ba81bd1..d1fe48a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -170,6 +170,10 @@
}
@Override
+ public void setTopFocusedDisplay(int displayId) {
+ }
+
+ @Override
public void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index 65cde77..20fc5ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -30,6 +30,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.view.Display;
import android.view.IApplicationToken;
@@ -161,6 +162,7 @@
return null;
}
}, new ComponentName("", ""), false, dc, true /* fillsParent */);
+ mTargetSdk = Build.VERSION_CODES.CUR_DEVELOPMENT;
}
TestAppWindowToken(WindowManagerService service, IApplicationToken token,