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插口。


1 0
原创粉丝点击