From e1cfcf4c4d15417bc912d36d487b4454c19b30fa Mon Sep 17 00:00:00 2001 From: Tarandeep Singh Date: Mon, 10 Jul 2017 18:50:00 -0700 Subject: [PATCH] Fix keyboard focus in VR Consider this VirtualDisplay (VD) scenario: HostActivity creates a VD which holds SettingsActivity. When EditText on SettingsActivity is tapped, it gains focus. On eventual taps, it loses focus i.e. the Window in VD loses focus and the host activity in primary display gets the focus instead. This happens because WM's TaskTapPointerEventListener.onPointerEvent() is called on the default display only. Root cause: 1. Tap detector isn't registered for non-default display. 2. Tap detector has no info on which displayId touch was received. 3. InputFlinger doesn't deliver InputMonitor events for non-default displays (fixed in a separate CL) Fixing above results in onPointerEvent(MotionEvent) to deliver the Touch events successfully to VD. We restrict these changes to physical multi-displays and VR VirtualDisplays (which uses virtual touch device). [VrManagerService calls WMInternal.setVr2dDisplayId(int)] In future, displayId should be part of InputEvent. Bug: 64258305 Bug: 62033391 Test: bit FrameworksServicesTests:com.android.server.wm.DisplayContentTests Change-Id: I3626f4de5aa9bcf905da9abd39f3ab1baefc4c48 --- .../IInputMethodSessionWrapper.java | 12 ++-- .../service/wallpaper/WallpaperService.java | 2 +- .../java/android/view/InputEventReceiver.java | 7 ++- core/java/android/view/ViewRootImpl.java | 2 +- .../android/view/WindowManagerInternal.java | 7 +++ .../android/view/WindowManagerPolicy.java | 12 +++- core/jni/android_view_InputEventReceiver.cpp | 8 ++- core/jni/android_view_InputEventSender.cpp | 3 + .../media/tv/ITvInputSessionWrapper.java | 2 +- .../pip/phone/InputConsumerController.java | 2 +- .../server/policy/PhoneWindowManager.java | 2 +- .../com/android/server/vr/Vr2dDisplay.java | 18 ++++-- .../android/server/vr/VrManagerService.java | 8 ++- .../server/wm/PointerEventDispatcher.java | 6 +- .../server/wm/RootWindowContainer.java | 23 +++++-- .../com/android/server/wm/TaskPositioner.java | 2 +- .../wm/TaskTapPointerEventListener.java | 12 ++++ .../server/wm/WindowManagerService.java | 15 ++++- .../com/android/server/wm/WindowState.java | 2 +- .../server/wm/DisplayContentTests.java | 63 +++++++++++++++++++ .../server/wm/TestWindowManagerPolicy.java | 14 ++++- 21 files changed, 185 insertions(+), 37 deletions(-) diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index ed223d1fd503d..d2e3510ee3b1b 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -16,10 +16,6 @@ package android.inputmethodservice; -import com.android.internal.os.HandlerCaller; -import com.android.internal.os.SomeArgs; -import com.android.internal.view.IInputMethodSession; - import android.content.Context; import android.graphics.Rect; import android.os.Bundle; @@ -34,9 +30,13 @@ import android.view.InputEventReceiver; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.InputMethodSession; -import android.view.inputmethod.CursorAnchorInfo; + +import com.android.internal.os.HandlerCaller; +import com.android.internal.os.SomeArgs; +import com.android.internal.view.IInputMethodSession; class IInputMethodSessionWrapper extends IInputMethodSession.Stub implements HandlerCaller.Callback { @@ -218,7 +218,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub } @Override - public void onInputEvent(InputEvent event) { + public void onInputEvent(InputEvent event, int displayId) { if (mInputMethodSession == null) { // The session has been finished. finishInputEvent(event, false); diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index e4d3142ccefed..ef8256bb32ca8 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -268,7 +268,7 @@ public abstract class WallpaperService extends Service { } @Override - public void onInputEvent(InputEvent event) { + public void onInputEvent(InputEvent event, int displayId) { boolean handled = false; try { if (event instanceof MotionEvent diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index 20ab539f52d23..c566a653da422 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -111,9 +111,10 @@ public abstract class InputEventReceiver { * to indicate whether the event was handled. No new input events will be received * until {@link #finishInputEvent} is called. * + * @param displayId The display id on which input event triggered. * @param event The input event that was received. */ - public void onInputEvent(InputEvent event) { + public void onInputEvent(InputEvent event, int displayId) { finishInputEvent(event, false); } @@ -180,9 +181,9 @@ public abstract class InputEventReceiver { // Called from native code. @SuppressWarnings("unused") - private void dispatchInputEvent(int seq, InputEvent event) { + private void dispatchInputEvent(int seq, InputEvent event, int displayId) { mSeqMap.put(event.getSequenceNumber(), seq); - onInputEvent(event); + onInputEvent(event, displayId); } // Called from native code. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index e38a55f76b7db..7ace841d270c8 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6756,7 +6756,7 @@ public final class ViewRootImpl implements ViewParent, } @Override - public void onInputEvent(InputEvent event) { + public void onInputEvent(InputEvent event, int displayId) { enqueueInputEvent(event, this, 0, true); } diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java index 4c9cf40beb8de..98f8dc8e3a6dd 100644 --- a/core/java/android/view/WindowManagerInternal.java +++ b/core/java/android/view/WindowManagerInternal.java @@ -347,4 +347,11 @@ public abstract class WindowManagerInternal { * Requests the window manager to recompute the windows for accessibility. */ public abstract void computeWindowsForAccessibility(); + + /** + * Called after virtual display Id is updated by + * {@link com.android.server.vr.Vr2dDisplay} with a specific + * {@param vr2dDisplayId}. + */ + public abstract void setVr2dDisplayId(int vr2dDisplayId); } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 668d25e74276b..49b7ed8bac0e1 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -16,6 +16,7 @@ package android.view; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; @@ -616,7 +617,16 @@ public interface WindowManagerPolicy { * 2. motionEvent will be recycled after onPointerEvent returns so if it is needed later a * copy() must be made and the copy must be recycled. **/ - public void onPointerEvent(MotionEvent motionEvent); + void onPointerEvent(MotionEvent motionEvent); + + /** + * @see #onPointerEvent(MotionEvent) + **/ + default void onPointerEvent(MotionEvent motionEvent, int displayId) { + if (displayId == DEFAULT_DISPLAY) { + onPointerEvent(motionEvent); + } + } } /** Window has been added to the screen. */ diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 8293cd8ff88dd..084a3d13a0ed6 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -233,8 +233,9 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, for (;;) { uint32_t seq; InputEvent* inputEvent; + int32_t displayId; status_t status = mInputConsumer.consume(&mInputEventFactory, - consumeBatches, frameTime, &seq, &inputEvent); + consumeBatches, frameTime, &seq, &inputEvent, &displayId); if (status) { if (status == WOULD_BLOCK) { if (!skipCallbacks && !mBatchedInputEventPending @@ -311,7 +312,8 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName()); } env->CallVoidMethod(receiverObj.get(), - gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj); + gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj, + displayId); if (env->ExceptionCheck()) { ALOGE("Exception dispatching input event."); skipCallbacks = true; @@ -417,7 +419,7 @@ int register_android_view_InputEventReceiver(JNIEnv* env) { gInputEventReceiverClassInfo.dispatchInputEvent = GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, - "dispatchInputEvent", "(ILandroid/view/InputEvent;)V"); + "dispatchInputEvent", "(ILandroid/view/InputEvent;I)V"); gInputEventReceiverClassInfo.dispatchBatchedInputEventPending = GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "dispatchBatchedInputEventPending", "()V"); diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp index 3bd6917aedfa1..4769257e5551a 100644 --- a/core/jni/android_view_InputEventSender.cpp +++ b/core/jni/android_view_InputEventSender.cpp @@ -39,6 +39,8 @@ namespace android { // Log debug messages about the dispatch cycle. static const bool kDebugDispatchCycle = false; +// Display id for default(primary) display. +static const int32_t kDefaultDisplayId = 0; static struct { jclass clazz; @@ -136,6 +138,7 @@ status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent publishedSeq = mNextPublishedSeq++; status_t status = mInputPublisher.publishMotionEvent(publishedSeq, event->getDeviceId(), event->getSource(), + kDefaultDisplayId /* TODO(multi-display): propagate display id */, event->getAction(), event->getActionButton(), event->getFlags(), event->getEdgeFlags(), event->getMetaState(), event->getButtonState(), event->getXOffset(), event->getYOffset(), diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java index 07cfbda7ac2b6..df87e0f28cd9a 100644 --- a/media/java/android/media/tv/ITvInputSessionWrapper.java +++ b/media/java/android/media/tv/ITvInputSessionWrapper.java @@ -367,7 +367,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand } @Override - public void onInputEvent(InputEvent event) { + public void onInputEvent(InputEvent event, int displayId) { if (mTvInputSessionImpl == null) { // The session has been finished. finishInputEvent(event, false); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java index 6733421a8d0e7..abc5667251ea4 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java @@ -65,7 +65,7 @@ public class InputConsumerController { } @Override - public void onInputEvent(InputEvent event) { + public void onInputEvent(InputEvent event, int displayId) { boolean handled = true; try { // To be implemented for input handling over Pip windows diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index e3cf459774e92..61ecee38dfdbf 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -4184,7 +4184,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public void onInputEvent(InputEvent event) { + public void onInputEvent(InputEvent event, int displayId) { boolean handled = false; try { if (event instanceof MotionEvent diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java index 69d8ca68522f7..8f50a39a54245 100644 --- a/services/core/java/com/android/server/vr/Vr2dDisplay.java +++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java @@ -24,6 +24,7 @@ import android.service.vr.IPersistentVrStateCallbacks; import android.service.vr.IVrManager; import android.util.Log; import android.view.Surface; +import android.view.WindowManagerInternal; import com.android.server.vr.VrManagerService; @@ -74,6 +75,7 @@ class Vr2dDisplay { public static final int MIN_VR_DISPLAY_DPI = 1; private final ActivityManagerInternal mActivityManagerInternal; + private final WindowManagerInternal mWindowManagerInternal; private final DisplayManager mDisplayManager; private final IVrManager mVrManager; private final Object mVdLock = new Object(); @@ -103,9 +105,11 @@ class Vr2dDisplay { private boolean mBootsToVr = false; // The device boots into VR (standalone VR device) public Vr2dDisplay(DisplayManager displayManager, - ActivityManagerInternal activityManagerInternal, IVrManager vrManager) { + ActivityManagerInternal activityManagerInternal, + WindowManagerInternal windowManagerInternal, IVrManager vrManager) { mDisplayManager = displayManager; mActivityManagerInternal = activityManagerInternal; + mWindowManagerInternal = windowManagerInternal; mVrManager = vrManager; mVirtualDisplayWidth = DEFAULT_VIRTUAL_DISPLAY_WIDTH; mVirtualDisplayHeight = DEFAULT_VIRTUAL_DISPLAY_HEIGHT; @@ -296,13 +300,12 @@ class Vr2dDisplay { UNIQUE_DISPLAY_ID); if (mVirtualDisplay != null) { - mActivityManagerInternal.setVr2dDisplayId( - mVirtualDisplay.getDisplay().getDisplayId()); + updateDisplayId(mVirtualDisplay.getDisplay().getDisplayId()); // Now create the ImageReader to supply a Surface to the new virtual display. startImageReader(); } else { Log.w(TAG, "Virtual display id is null after createVirtualDisplay"); - mActivityManagerInternal.setVr2dDisplayId(INVALID_DISPLAY); + updateDisplayId(INVALID_DISPLAY); return; } } @@ -310,6 +313,11 @@ class Vr2dDisplay { Log.i(TAG, "VD created: " + mVirtualDisplay); } + private void updateDisplayId(int displayId) { + mActivityManagerInternal.setVr2dDisplayId(displayId); + mWindowManagerInternal.setVr2dDisplayId(displayId); + } + /** * Stops the virtual display with a {@link #STOP_VIRTUAL_DISPLAY_DELAY_MILLIS} timeout. * The timeout prevents the virtual display from bouncing in cases where VrMode goes in and out @@ -325,7 +333,7 @@ class Vr2dDisplay { } else { Log.i(TAG, "Stopping Virtual Display"); synchronized (mVdLock) { - mActivityManagerInternal.setVr2dDisplayId(INVALID_DISPLAY); + updateDisplayId(INVALID_DISPLAY); setSurfaceLocked(null); // clean up and release the surface first. if (mVirtualDisplay != null) { mVirtualDisplay.release(); diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index 29968781b26d6..030b74cc7ea84 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -56,6 +56,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; +import android.view.WindowManagerInternal; import com.android.internal.R; import com.android.internal.util.DumpUtils; @@ -627,8 +628,11 @@ public class VrManagerService extends SystemService implements EnabledComponentC DisplayManager dm = (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE); - ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class); - mVr2dDisplay = new Vr2dDisplay(dm, ami, mVrManager); + mVr2dDisplay = new Vr2dDisplay( + dm, + LocalServices.getService(ActivityManagerInternal.class), + LocalServices.getService(WindowManagerInternal.class), + mVrManager); mVr2dDisplay.init(getContext(), mBootsToVr); IntentFilter intentFilter = new IntentFilter(); diff --git a/services/core/java/com/android/server/wm/PointerEventDispatcher.java b/services/core/java/com/android/server/wm/PointerEventDispatcher.java index 6b0e4c9f010a2..484987ec127e2 100644 --- a/services/core/java/com/android/server/wm/PointerEventDispatcher.java +++ b/services/core/java/com/android/server/wm/PointerEventDispatcher.java @@ -36,11 +36,11 @@ public class PointerEventDispatcher extends InputEventReceiver { } @Override - public void onInputEvent(InputEvent event) { + public void onInputEvent(InputEvent event, int displayId) { try { if (event instanceof MotionEvent && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { - final MotionEvent motionEvent = (MotionEvent)event; + final MotionEvent motionEvent = (MotionEvent) event; PointerEventListener[] listeners; synchronized (mListeners) { if (mListenersArray == null) { @@ -50,7 +50,7 @@ public class PointerEventDispatcher extends InputEventReceiver { listeners = mListenersArray; } for (int i = 0; i < listeners.length; ++i) { - listeners[i].onPointerEvent(motionEvent); + listeners[i].onPointerEvent(motionEvent, displayId); } } } finally { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 233e75bbfec70..965a6a79fec52 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -18,6 +18,7 @@ package com.android.server.wm; import android.content.res.Configuration; import android.graphics.Rect; +import android.hardware.display.DisplayManager; import android.hardware.power.V1_0.PowerHint; import android.os.Binder; import android.os.Debug; @@ -243,12 +244,24 @@ class RootWindowContainer extends WindowContainer { displayId, displayInfo); mService.configureDisplayPolicyLocked(dc); - // TODO(multi-display): Create an input channel for each display with touch capability. - if (displayId == DEFAULT_DISPLAY && mService.canDispatchPointerEvents()) { - dc.mTapDetector = new TaskTapPointerEventListener( - mService, dc); + // Tap Listeners are supported for: + // 1. All physical displays (multi-display). + // 2. VirtualDisplays that support virtual touch input. (Only VR for now) + // TODO(multi-display): Support VirtualDisplays with no virtual touch input. + if ((display.getType() != Display.TYPE_VIRTUAL + || (display.getType() == Display.TYPE_VIRTUAL + // Only VR VirtualDisplays + && displayId == mService.mVr2dDisplayId)) + && mService.canDispatchPointerEvents()) { + if (DEBUG_DISPLAY) { + Slog.d(TAG, + "Registering PointerEventListener for DisplayId: " + displayId); + } + dc.mTapDetector = new TaskTapPointerEventListener(mService, dc); mService.registerPointerEventListener(dc.mTapDetector); - mService.registerPointerEventListener(mService.mMousePositionTracker); + if (displayId == DEFAULT_DISPLAY) { + mService.registerPointerEventListener(mService.mMousePositionTracker); + } } } diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index 0c68e2ca6d772..c58212cd8235a 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -133,7 +133,7 @@ class TaskPositioner implements DimLayer.DimLayerUser { } @Override - public void onInputEvent(InputEvent event) { + public void onInputEvent(InputEvent event, int displayId) { if (!(event instanceof MotionEvent) || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) { return; diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java index dd9ba7355d7ca..42a2d9df5fb5d 100644 --- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java +++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java @@ -24,6 +24,7 @@ import android.view.WindowManagerPolicy.PointerEventListener; import com.android.server.wm.WindowManagerService.H; +import static android.view.Display.DEFAULT_DISPLAY; 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; @@ -44,6 +45,13 @@ public class TaskTapPointerEventListener implements PointerEventListener { mDisplayContent = displayContent; } + @Override + public void onPointerEvent(MotionEvent motionEvent, int displayId) { + if (displayId == getDisplayId()) { + onPointerEvent(motionEvent); + } + } + @Override public void onPointerEvent(MotionEvent motionEvent) { final int action = motionEvent.getAction(); @@ -104,4 +112,8 @@ public class TaskTapPointerEventListener implements PointerEventListener { mTouchExcludeRegion.set(newRegion); } } + + private int getDisplayId() { + return mDisplayContent.getDisplayId(); + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 2caac7a2be3f8..b0775dd3a8592 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -79,6 +79,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; @@ -357,6 +358,8 @@ public class WindowManagerService extends IWindowManager.Stub final private KeyguardDisableHandler mKeyguardDisableHandler; boolean mKeyguardGoingAway; + // VR Vr2d Display Id. + int mVr2dDisplayId = INVALID_DISPLAY; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -764,7 +767,7 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void onInputEvent(InputEvent event) { + public void onInputEvent(InputEvent event, int displayId) { boolean handled = false; try { if (mDragState == null) { @@ -7542,6 +7545,16 @@ public class WindowManagerService extends IWindowManager.Stub accessibilityController.performComputeChangedWindowsNotLocked(); } } + + @Override + public void setVr2dDisplayId(int vr2dDisplayId) { + if (DEBUG_DISPLAY) { + Slog.d(TAG, "setVr2dDisplayId called for: " + vr2dDisplayId); + } + synchronized (WindowManagerService.this) { + mVr2dDisplayId = vr2dDisplayId; + } + } } void registerAppFreezeListener(AppFreezeListener listener) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 34ff9e8e338fa..81c5164534a5c 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2038,7 +2038,7 @@ class WindowState extends WindowContainer implements WindowManagerP super(inputChannel, mService.mH.getLooper()); } @Override - public void onInputEvent(InputEvent event) { + public void onInputEvent(InputEvent event, int displayId) { finishInputEvent(event, true); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java index 856e94055ed51..91eb55ba3fcf2 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java @@ -31,10 +31,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import android.content.res.Configuration; +import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import android.util.DisplayMetrics; import android.util.SparseIntArray; +import android.view.MotionEvent; import java.util.Arrays; import java.util.LinkedList; @@ -237,6 +240,52 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(currentConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */); } + /** + * Tests tapping on a stack in different display results in window gaining focus. + */ + @Test + public void testInputEventBringsCorrectDisplayInFocus() throws Exception { + DisplayContent dc0 = sWm.getDefaultDisplayContentLocked(); + // Create a second display + final DisplayContent dc1 = createNewDisplay(); + + // Add stack with activity. + final TaskStack stack0 = createTaskStackOnDisplay(dc0); + final Task task0 = createTaskInStack(stack0, 0 /* userId */); + final WindowTestUtils.TestAppWindowToken token = + new WindowTestUtils.TestAppWindowToken(dc0); + task0.addChild(token, 0); + dc0.mTapDetector = new TaskTapPointerEventListener(sWm, dc0); + sWm.registerPointerEventListener(dc0.mTapDetector); + final TaskStack stack1 = createTaskStackOnDisplay(dc1); + final Task task1 = createTaskInStack(stack1, 0 /* userId */); + final WindowTestUtils.TestAppWindowToken token1 = + new WindowTestUtils.TestAppWindowToken(dc0); + task1.addChild(token1, 0); + dc1.mTapDetector = new TaskTapPointerEventListener(sWm, dc0); + sWm.registerPointerEventListener(dc1.mTapDetector); + + // tap on primary display (by sending ACTION_DOWN followed by ACTION_UP) + DisplayMetrics dm0 = dc0.getDisplayMetrics(); + dc0.mTapDetector.onPointerEvent( + createTapEvent(dm0.widthPixels / 2, dm0.heightPixels / 2, true)); + dc0.mTapDetector.onPointerEvent( + createTapEvent(dm0.widthPixels / 2, dm0.heightPixels / 2, false)); + + // Check focus is on primary display. + assertEquals(sWm.mCurrentFocus, dc0.findFocusedWindow()); + + // Tap on secondary display + DisplayMetrics dm1 = dc1.getDisplayMetrics(); + dc1.mTapDetector.onPointerEvent( + createTapEvent(dm1.widthPixels / 2, dm1.heightPixels / 2, true)); + dc1.mTapDetector.onPointerEvent( + createTapEvent(dm1.widthPixels / 2, dm1.heightPixels / 2, false)); + + // Check focus is on secondary. + assertEquals(sWm.mCurrentFocus, dc1.findFocusedWindow()); + } + @Test @Ignore public void testFocusedWindowMultipleDisplays() throws Exception { @@ -355,4 +404,18 @@ public class DisplayContentTests extends WindowTestsBase { } assertTrue(actualWindows.isEmpty()); } + + private MotionEvent createTapEvent(float x, float y, boolean isDownEvent) { + final long downTime = SystemClock.uptimeMillis(); + final long eventTime = SystemClock.uptimeMillis() + 100; + final int metaState = 0; + + return MotionEvent.obtain( + downTime, + eventTime, + isDownEvent ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP, + x, + y, + metaState); + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index 828d405b5e3b2..95adc9cd5a1a7 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -18,6 +18,10 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManagerPolicy.NAV_BAR_BOTTOM; + +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -39,6 +43,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.view.Display; import android.view.IWindowManager; +import android.view.InputChannel; import android.view.KeyEvent; import android.view.WindowManager; import android.view.WindowManagerPolicy; @@ -91,7 +96,14 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { }).when(am).notifyKeyguardFlagsChanged(any()); } - sWm = WindowManagerService.main(context, mock(InputManagerService.class), true, false, + InputManagerService ims = mock(InputManagerService.class); + // InputChannel is final and can't be mocked. + InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM); + if (input != null && input.length > 1) { + doReturn(input[1]).when(ims).monitorInput(anyString()); + } + + sWm = WindowManagerService.main(context, ims, true, false, false, new TestWindowManagerPolicy()); } return sWm;