您的位置:首页 > 移动开发 > Android开发

Android 源码分析VR返回键无法用Accessibility拦截的问题

2017-02-28 16:11 274 查看
上一篇《Android 源码分析AccessibilityService拦截VR眼镜Key事件以及key事件在View体系的传递》我们分析到了系统对虚拟按键的BACK键和VR眼镜的BACK键处理是不同的。AccessibilityService很容易就拦截到了虚拟按键的BACK键(以下简称BACK),但始终拦截不到VR眼镜的BACK键(以下简称VBACK)。经过从源头InputReader.cpp到DecorView.java事件传递整个流程的追踪,我们发现VBACK确确实实传递到了View。下面我们从VBACK和BACK的区别来继续分析。(ps:VR设备一般被认为是鼠标类型设备)

我们从AccessibilityManagerService(以下简称ACMS)进行分析。

boolean notifyKeyEvent(KeyEvent event, int policyFlags) {
synchronized (mLock) {
List<Service> boundServices = getCurrentUserStateLocked().mBoundServices;
if (boundServices.isEmpty()) {
return false;
}
return getKeyEventDispatcher().notifyKeyEventLocked(event, policyFlags, boundServices);
}
}

ACMS对于 Key事件的处理并不是走AccessibilityEvent这条路,AccessibilityEvent主要是跟View焦点,点击事件,窗口变化有关,这是我们之前分析得到的结论。我们看onKeyEvent这条线。

ACMS对key事件的处理包含两个比较重要的类KeyEventDispatcher和AccessibilityInputFilter,KeyEventDispatcher发送收到KeyEvent的消息,notifyKeyEvent之后调用KeyEventDispatcher的notifyKeyEventLocked。

public boolean notifyKeyEventLocked(
KeyEvent event, int policyFlags, List<Service> boundServices) {
PendingKeyEvent pendingKeyEvent = null;
KeyEvent localClone = KeyEvent.obtain(event);
for (int i = 0; i < boundServices.size(); i++) {
Service service = boundServices.get(i);
// Key events are handled only by services that declared
// this capability and requested to filter key events.
if (!service.mRequestFilterKeyEvents || (service.mServiceInterface == null)) {
continue;
}
int filterKeyEventBit = service.mAccessibilityServiceInfo.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
if (filterKeyEventBit == 0) {
continue;
}

try {
// The event will be cloned in the IPC call, so it doesn't need to be here.
service.mServiceInterface.onKeyEvent(localClone, localClone.getSequenceNumber());
} catch (RemoteException re) {
continue;
}

if (pendingKeyEvent == null) {
pendingKeyEvent = obtainPendingEventLocked(localClone, policyFlags);
}
ArrayList<PendingKeyEvent> pendingEventList = mPendingEventsMap.get(service);
if (pendingEventList == null) {
pendingEventList = new ArrayList<>();
mPendingEventsMap.put(service, pendingEventList);
}
pendingEventList.add(pendingKeyEvent);
pendingKeyEvent.referenceCount++;
}

if (pendingKeyEvent == null) {
localClone.recycle();
return false;
}

Message message = mKeyEventTimeoutHandler.obtainMessage(
MSG_ON_KEY_EVENT_TIMEOUT, pendingKeyEvent);
mKeyEventTimeoutHandler.sendMessageDelayed(message, ON_KEY_EVENT_TIMEOUT_MILLIS);
return true;
}


KeyEventDispatcher会把这个key事件传给对应监听key事件的AccessibilityService端。另外ACMS还有一个专门检测过滤事件的类AccessibilityInputFilter,里面包含KeyboradIntercepter这个类。

@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
mAms.notifyKeyEvent(event, policyFlags);
}

在收到key事件时会调用ACMS的notifyKeyEvent方法。

if (!mHasInputFilter) {
mHasInputFilter = true;
if (mInputFilter == null) {
mInputFilter = new AccessibilityInputFilter(mContext,
AccessibilityManagerService.this);
}
inputFilter = mInputFilter;
setInputFilter = true;
}
mInputFilter.setUserAndEnabledFeatures(userState.mUserId, flags);
} else {
if (mHasInputFilter) {
mHasInputFilter = false;
mInputFilter.setUserAndEnabledFeatures(userState.mUserId, 0);
inputFilter = null;
setInputFilter = true;
}
}
}
if (setInputFilter) {
mWindowManagerService.setInputFilter(inputFilter);
}


private final WindowManagerInternal mWindowManagerService
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);


AccessibilityInputFilter是InputFilter的子类,ACMS中将AccessibilityInputFilter设置给一个mWindowManagerService的类,它是WindowManagerInternal抽象类型的。LocalServices包含主要是为了进程内部调用使用的service,跟SystemService包含进程间调用的service是相对的。WindowManagerInternal主要包含了很多接口。

private final class LocalService extends WindowManagerInternal {

......

@Override
public void setInputFilter(IInputFilter filter) {
mInputManager.setInputFilter(filter);
}

@Override
public IBinder getFocusedWindowToken() {
synchronized (mWindowMap) {
WindowState windowState = getFocusedWindowLocked();
if (windowState != null) {
return windowState.mClient.asBinder();
}
return null;
}
}

@Override
public boolean isKeyguardLocked() {
return WindowManagerService.this.isKeyguardLocked();
}

.....

@Override
public boolean isHardKeyboardAvailable() {
synchronized (mWindowMap) {
return mHardKeyboardAvailable;
}
}

@Override
public void setOnHardKeyboardStatusChangeListener(
OnHardKeyboardStatusChangeListener listener) {
synchronized (mWindowMap) {
mHardKeyboardStatusChangeListener = listener;
}
}
......
}


LocalService主要由WindowManagerService实现。ACMS的setInputFilter就是在这里实现,ACMS通过这个方法将自己和WMS中的InputManagerService对象连起来了。至于key事件如何传递到InputManagerService的请看我们之前的分析《Android
源码分析鼠标事件传递》,这里AccessibilityService的key事件传递是通了。

key事件传递到了AccessibilityInputFilter,可能在过滤的过程中被忽略了。

@Override
public void onInputEvent(InputEvent event, int policyFlags) {
if (DEBUG) {
Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
+ Integer.toHexString(policyFlags));
}

if (mEventHandler == null) {
super.onInputEvent(event, policyFlags);
return;
}

EventStreamState state = getEventStreamState(event);
if (state == null) {
super.onInputEvent(event, policyFlags);
return;
}

int eventSource = event.getSource();
if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
state.reset();
mEventHandler.clearEvents(eventSource);
super.onInputEvent(event, policyFlags);
return;
}

if (state.updateDeviceId(event.getDeviceId())) {
mEventHandler.clearEvents(eventSource);
}

if (!state.deviceIdValid()) {
super.onInputEvent(event, policyFlags);
return;
}

if (event instanceof MotionEvent) {
if ((mEnabledFeatures & FEATURES_AFFECTING_MOTION_EVENTS) != 0) {
MotionEvent motionEvent = (MotionEvent) event;
processMotionEvent(state, motionEvent, policyFlags);
return;
} else {
super.onInputEvent(event, policyFlags);
}
} else if (event instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent) event;
processKeyEvent(state, keyEvent, policyFlags);
}
}


我们看主要的过滤方法,特别关注EventStreamState state = getEventStreamState()这一句,我们继续看这个方法

private EventStreamState getEventStreamState(InputEvent event) {
if (event instanceof MotionEvent) {
if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
if (mTouchScreenStreamState == null) {
mTouchScreenStreamState = new TouchScreenEventStreamState();
}
return mTouchScreenStreamState;
}
if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (mMouseStreamState == null) {
mMouseStreamState = new MouseEventStreamState();
}
return mMouseStreamState;
}
} else if (event instanceof KeyEvent) {
if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
if (mKeyboardStreamState == null) {
mKeyboardStreamState = new KeyboardEventStreamState();
}
return mKeyboardStreamState;
}
}
return null;
}


鼠标的source是SOURCE_MOUSE,所以返回一个MouseEventStreamState,我们继续看MouseEventStreamState。

/**
* Keeps state of stream of events from a mouse device.
*/
private static class MouseEventStreamState extends EventStreamState {
private boolean mMotionSequenceStarted;

public MouseEventStreamState() {
reset();
}

@Override
final public void reset() {
super.reset();
mMotionSequenceStarted = false;
}

@Override
final public boolean shouldProcessScroll() {
return true;
}

@Override
final public boolean shouldProcessMotionEvent(MotionEvent event) {
if (mMotionSequenceStarted) {
return true;
}
// Wait for down or move event to start processing mouse events.
int action = event.getActionMasked();
mMotionSequenceStarted =
action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_HOVER_MOVE;
return mMotionSequenceStarted;
}
}


居然没有处理Key事件的方法。所以processKeyEvent执行的是基类EventStreamState的方法,我们看基类怎么实现的。

private static class EventStreamState {
private int mDeviceId;

......

/**
* @param event An observed motion event.
* @return Whether the event should be handled by event transformations.
*/
public boolean shouldProcessMotionEvent(MotionEvent event) {
return false;
}

/**
* @param event An observed key event.
* @return Whether the event should be handled by event transformations.
*/
public boolean shouldProcessKeyEvent(KeyEvent event) {
return false;
}
}


返回false,鼠标产生的Key事件就这样被忽略了。终于找到了AccessibilityService忽略鼠标按键事件的原因了。

VR做为新生事物,Google对它的支持不到位可以理解,何况DayDream根本就没有usb插口。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐