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)进行分析。
ACMS对于 Key事件的处理并不是走AccessibilityEvent这条路,AccessibilityEvent主要是跟View焦点,点击事件,窗口变化有关,这是我们之前分析得到的结论。我们看onKeyEvent这条线。
ACMS对key事件的处理包含两个比较重要的类KeyEventDispatcher和AccessibilityInputFilter,KeyEventDispatcher发送收到KeyEvent的消息,notifyKeyEvent之后调用KeyEventDispatcher的notifyKeyEventLocked。
KeyEventDispatcher会把这个key事件传给对应监听key事件的AccessibilityService端。另外ACMS还有一个专门检测过滤事件的类AccessibilityInputFilter,里面包含KeyboradIntercepter这个类。
在收到key事件时会调用ACMS的notifyKeyEvent方法。
AccessibilityInputFilter是InputFilter的子类,ACMS中将AccessibilityInputFilter设置给一个mWindowManagerService的类,它是WindowManagerInternal抽象类型的。LocalServices包含主要是为了进程内部调用使用的service,跟SystemService包含进程间调用的service是相对的。WindowManagerInternal主要包含了很多接口。
LocalService主要由WindowManagerService实现。ACMS的setInputFilter就是在这里实现,ACMS通过这个方法将自己和WMS中的InputManagerService对象连起来了。至于key事件如何传递到InputManagerService的请看我们之前的分析《Android
源码分析鼠标事件传递》,这里AccessibilityService的key事件传递是通了。
key事件传递到了AccessibilityInputFilter,可能在过滤的过程中被忽略了。
我们看主要的过滤方法,特别关注EventStreamState state = getEventStreamState()这一句,我们继续看这个方法
鼠标的source是SOURCE_MOUSE,所以返回一个MouseEventStreamState,我们继续看MouseEventStreamState。
居然没有处理Key事件的方法。所以processKeyEvent执行的是基类EventStreamState的方法,我们看基类怎么实现的。
返回false,鼠标产生的Key事件就这样被忽略了。终于找到了AccessibilityService忽略鼠标按键事件的原因了。
VR做为新生事物,Google对它的支持不到位可以理解,何况DayDream根本就没有usb插口。
我们从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插口。
相关文章推荐
- 解决Ubuntu升级12.10后无法编码Android源码问题
- 如何在ubuntu10.04下载android4.0的源码(并解决以往无法下载android源码的问题)
- 通过Intent传输图片导致广播消息异常的问题根因分析(通过分析Android源码反向推理)
- 解决Mac AndroidStudio无法关联源码问题
- Android学习之实现WebView中input="file"选择文件,处理选择图片无法返回类型问题
- Android 无法查看外部依赖jar的源码的问题
- android LinearLayout无法居中问题分析
- Android4.4KK下遮盖p-sensor拨打电话无法自动灭屏的问题分析
- Android 无法查看外部依赖jar的源码的问题
- js解析php返回的json数据无法获取length的问题分析
- android开发环境遇到adt无法启动的问题分析及解决方法
- Android private Libraires 无法查看源码的问题
- 通过Intent传输图片导致广播消息异常的问题根因分析(通过分析Android源码反向推理)
- Android源码角度分析View的scrollBy()和scrollTo()的参数正负问题
- 如何在ubuntu10.04下载android4.0的源码(并解决以往无法下载android源码的问题)
- 解决下载android源码时出现的无法连接问题
- android webview中加入无线淘宝客页面点击宝贝详情链接进入宝贝详情页面后无法返回问题
- Android——源码角度分析View的scrollBy()和scrollTo()的参数正负问题
- android Webview中注入js,用于解决第三方页中出现返回事件window.history.go(-1),无法响应问题
- android中onActivityResult方法无法获取返回的resultCode和data问题