Merge "Fix keyboard focus in VR" into oc-dr1-dev

This commit is contained in:
Tarandeep Singh
2017-08-02 20:33:02 +00:00
committed by Android (Google) Code Review
21 changed files with 185 additions and 37 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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. */

View File

@@ -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");

View File

@@ -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(),

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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();

View File

@@ -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();

View File

@@ -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 {

View File

@@ -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<DisplayContent> {
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);
}
}
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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) {

View File

@@ -2038,7 +2038,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
super(inputChannel, mService.mH.getLooper());
}
@Override
public void onInputEvent(InputEvent event) {
public void onInputEvent(InputEvent event, int displayId) {
finishInputEvent(event, true);
}
}

View File

@@ -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);
}
}

View File

@@ -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;