Android 源码分析VR返回键无法用Accessibility拦截的问题
来源:互联网 发布:2017淘宝卖什么利润大 编辑:程序博客网 时间:2024/04/29 22:42
上一篇《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插口。
- Android 源码分析VR返回键无法用Accessibility拦截的问题
- Android 源码分析AccessibilityService拦截VR眼镜Key事件以及key事件在View体系的传递
- Android AccessibilityService拦截事件及VR眼镜返回按键捕捉
- 【源码分析】Android触摸事件的分发拦截
- Android 拦截返回键事件
- 苦逼的Android Accessibility
- Android Accessibility 的简单用法
- Listview加载更多后返回头部问题的源码分析
- Android Accessibility(辅助功能) 安全相关问题
- Android WebView无法返回上一页的问题。
- android accessibility
- Android Accessibility
- android 拦截机制的分析
- C#中,出现Inconsistent accessibility返回类型不一致问题
- 9、Mybatis拦截器的源码分析
- 关于无法返回的问题
- android Fragment 无法用返回键监听
- Android拦截、监听系统返回键事件
- 设计模式之单例模式
- WebApi中跨域解决办法(转)
- C++笔记函数重载函数模板
- 并查集(寻找关系使用)
- zynq-mpsoc系列之zcu102的linux完整启动log信息
- Android 源码分析VR返回键无法用Accessibility拦截的问题
- c语言一行一行的读取txt文件
- Canvas 基本用法
- 快速获取想要的基因序列(批量)+后续blast
- 【录教程必备】推荐几款屏幕录制工具(可录制GIF)
- Longest Substring Without Repeating Characters
- DAX 语法规范
- 我理解的task_struct
- iOS 中如何添加应用自己的字体