Android之辅助服务下篇————AccessibilityServic源码分析

文章目录

  • Android之辅助服务下篇————AccessibilityServic源码分析
      • 一.前言
      • 二.接收AccessibilityEvent事件
        • 1.View对点击事件的分发
        • 2.AccessibilityManager
        • 3.AccessibilityService
      • 三.findAccessibilityNodeInfosByText
        • 1.AccessibilityNodeInfo
        • 2.AccessibilityManagerService
        • 3.ViewRootImpl
        • 4.小结
      • 四.performAction
        • 1.AccessibilityInteractionController
        • 2.View

一.前言

在上一篇博客中,我介绍了辅助服务的大致使用。这一篇我们来看看AccessibilityServic的原理。

通过上篇,我们知道将AccessibilityServic配置完成后。之后的使用可以分为下面三个步骤

  • onAccessibilityEvent接收事件(屏幕变化,点击事件)
  • 通过控件文字或者id在当前屏幕里寻找对应的控件
  • 调用控件的performAction进行相应的操作

所以我们源码分析也是着重于上面的三个流程

  • 点击事件如何分发到onAccessibilityEvent
  • findAccessibilityNodeInfosByText如果通过控件文字找到对应的控件
  • performAction如何模拟操作控件

二.接收AccessibilityEvent事件

1.View对点击事件的分发

我们以View的点击事件发送到AccessibilityEvent为例。
从View的事件分发onTouchEvent为起点

所在类:View.java

onTouchEvent

public boolean onTouchEvent(MotionEvent event) {  ....        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {            switch (action) {                case MotionEvent.ACTION_UP:                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;                    if ((viewFlags & TOOLTIP) == TOOLTIP) {                        handleTooltipUp();                    }                    if (!clickable) {                        removeTapCallback();                        removeLongPressCallback();                        mInContextButtonPress = false;                        mHasPerformedLongPress = false;                        mIgnoreNextUpEvent = false;                        break;                    }                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {                        // take focus if we don't have it already and we should in                        // touch mode.                        boolean focusTaken = false;                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {                            focusTaken = requestFocus();                        }                        if (prepressed) {                            // The button is being released before we actually                            // showed it as pressed.  Make it show the pressed                            // state now (before scheduling the click) to ensure                            // the user sees it.                            setPressed(true, x, y);                        }                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {                            // This is a tap, so remove the longpress check                            removeLongPressCallback();                            // Only perform take click actions if we were in the pressed state                            if (!focusTaken) {                                // Use a Runnable and post this rather than calling                                // performClick directly. This lets other visual state                                // of the view update before click actions start.                                if (mPerformClick == null) {                                    mPerformClick = new PerformClick();                                }                                if (!post(mPerformClick)) {                                    performClickInternal();                                }                            }                        }                        if (mUnsetPressedState == null) {                            mUnsetPressedState = new UnsetPressedState();                        }                        if (prepressed) {                            postDelayed(mUnsetPressedState,                                    ViewConfiguration.getPressedStateDuration());                        } else if (!post(mUnsetPressedState)) {                            // If the post failed, unpress right now               ....        return false;    }

点击事件是在 performClickInternal()中,我们继续追踪

所在类:View.java

    private boolean performClickInternal() {        // Must notify autofill manager before performing the click actions to avoid scenarios where        // the app has a click listener that changes the state of views the autofill service might        // be interested on.        notifyAutofillManagerOnClick();        return performClick();    }public boolean performClick() {        // We still need to call this method to handle the cases where performClick() was called        // externally, instead of through performClickInternal()        notifyAutofillManagerOnClick();        final boolean result;        final ListenerInfo li = mListenerInfo;        if (li != null && li.mOnClickListener != null) {            playSoundEffect(SoundEffectConstants.CLICK);            li.mOnClickListener.onClick(this);            result = true;        } else {            result = false;        }        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);        notifyEnterOrExitForAutoFillIfNeeded(true);        return result;    }

很明显在sendAccessibilityEvent中将点击事件进行了传递

所在类:View.java

    public void sendAccessibilityEvent(int eventType) {        if (mAccessibilityDelegate != null) {            mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);        } else {            sendAccessibilityEventInternal(eventType);//见下        }    }    public void sendAccessibilityEventInternal(int eventType) {        if (AccessibilityManager.getInstance(mContext).isEnabled()) {            sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType));//见下        }    }    public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {        if (mAccessibilityDelegate != null) {            mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event);        } else {            sendAccessibilityEventUncheckedInternal(event);//见下        }    }        public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) {        // Panes disappearing are relevant even if though the view is no longer visible.        boolean isWindowStateChanged =                (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);        boolean isWindowDisappearedEvent = isWindowStateChanged && ((event.getContentChangeTypes()                & AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED) != 0);        if (!isShown() && !isWindowDisappearedEvent) {            return;        }        onInitializeAccessibilityEvent(event);        // Only a subset of accessibility events populates text content.        if ((event.getEventType() & POPULATING_ACCESSIBILITY_EVENT_TYPES) != 0) {            dispatchPopulateAccessibilityEvent(event);        }        // In the beginning we called #isShown(), so we know that getParent() is not null.        ViewParent parent = getParent();        if (parent != null) {            getParent().requestSendAccessibilityEvent(this, event);//重点        }    }

在这里我们看到直接调用了getParent().requestSendAccessibilityEvent(this, event);,对这个事件进行分发。即调用了View的父类进行实现,这里其实有点像双亲委托模型。我们都知道View的最终父类是ViewRootImpl,我们直接ViewRootImpl中去看requestSendAccessibilityEvent的实现

所在类:ViewRootImpl

 @Override    public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {        if (mView == null || mStopped || mPausedForTransition) {            return false;        }        // Immediately flush pending content changed event (if any) to preserve event order        if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED                && mSendWindowContentChangedAccessibilityEvent != null                && mSendWindowContentChangedAccessibilityEvent.mSource != null) {            mSendWindowContentChangedAccessibilityEvent.removeCallbacksAndRun();        }        // Intercept accessibility focus events fired by virtual nodes to keep        // track of accessibility focus position in such nodes.        final int eventType = event.getEventType();        switch (eventType) {            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {                final long sourceNodeId = event.getSourceNodeId();                final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(                        sourceNodeId);                View source = mView.findViewByAccessibilityId(accessibilityViewId);                if (source != null) {                    AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();                    if (provider != null) {                        final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(                                sourceNodeId);                        final AccessibilityNodeInfo node;                        node = provider.createAccessibilityNodeInfo(virtualNodeId);                        setAccessibilityFocus(source, node);                    }                }            } break;            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {                final long sourceNodeId = event.getSourceNodeId();                final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(                        sourceNodeId);                View source = mView.findViewByAccessibilityId(accessibilityViewId);                if (source != null) {                    AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();                    if (provider != null) {                        setAccessibilityFocus(null, null);                    }                }            } break;            case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {                handleWindowContentChangedEvent(event);            } break;        }        mAccessibilityManager.sendAccessibilityEvent(event);//重点        return true;    }

最后在requestSendAccessibilityEvent里面调用mAccessibilityManager.sendAccessibilityEvent继续将点击事件进行分发。

小结:
Android之辅助服务下篇————AccessibilityServic源码分析_第1张图片

2.AccessibilityManager

从AS跳转到AccessibilityManager.sendAccessibilityEvent,发现sendAccessibilityEvent是一个空实现,不应该啊,然后我就去打开Source Insight 4.0,在里面阅读AccessibilityManager的相关源码.
源码版本:Android8.1

目录:frameworks\base\core\java\android\view\accessibility\AccessibilityManager.java

    public void sendAccessibilityEvent(AccessibilityEvent event) {        final IAccessibilityManager service;        final int userId;        synchronized (mLock) {            service = getServiceLocked();//关键代码            if (service == null) {                return;            }            if (!mIsEnabled) {                Looper myLooper = Looper.myLooper();                if (myLooper == Looper.getMainLooper()) {                    throw new IllegalStateException(                            "Accessibility off. Did you forget to check that?");                } else {                    // If we're not running on the thread with the main looper, it's possible for                    // the state of accessibility to change between checking isEnabled and                    // calling this method. So just log the error rather than throwing the                    // exception.                    Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");                    return;                }            }            if ((event.getEventType() & mRelevantEventTypes) == 0) {                if (DEBUG) {                    Log.i(LOG_TAG, "Not dispatching irrelevant event: " + event                            + " that is not among "                            + AccessibilityEvent.eventTypeToString(mRelevantEventTypes));                }                return;            }            userId = mUserId;        }        try {            event.setEventTime(SystemClock.uptimeMillis());            // it is possible that this manager is in the same process as the service but            // client using it is called through Binder from another process. Example: MMS            // app adds a SMS notification and the NotificationManagerService calls this method            long identityToken = Binder.clearCallingIdentity();            service.sendAccessibilityEvent(event, userId);//关键代码            Binder.restoreCallingIdentity(identityToken);            if (DEBUG) {                Log.i(LOG_TAG, event + " sent");            }        } catch (RemoteException re) {            Log.e(LOG_TAG, "Error during sending " + event + " ", re);        } finally {            event.recycle();        }    }

从上的代码可以看出通过 service.sendAccessibilityEvent,将AccessibilityEvent进行下一步传递。那么我们来分析一下,service是什么类型的对象。

Service的来源

目录:frameworks\base\core\java\android\view\accessibility\AccessibilityManager.java

    private IAccessibilityManager getServiceLocked() {        if (mService == null) {            tryConnectToServiceLocked(null);//往下追踪        }        return mService;    }    private void tryConnectToServiceLocked(IAccessibilityManager service) {        if (service == null) {            IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);            if (iBinder == null) {                return;            }            service = IAccessibilityManager.Stub.asInterface(iBinder);        }        try {            final long userStateAndRelevantEvents = service.addClient(mClient, mUserId);            setStateLocked(IntPair.first(userStateAndRelevantEvents));            mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents);            mService = service;        } catch (RemoteException re) {            Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);        }    } public static final String ACCESSIBILITY_SERVICE = "accessibility";

追踪发现 mService其实是一个binder代理类,代表了accessibility的服务。

所以我们直接看AccessibilityManagerService中sendAccessibilityEvent的实现

目录: frameworks\base\services\accessibility\java\com\android\server\accessibility\AccessibilityManagerService.java

@Override    public void sendAccessibilityEvent(AccessibilityEvent event, int userId) {        boolean dispatchEvent = false;        synchronized (mLock) {           ....        if (dispatchEvent) {            // Make sure clients receiving this event will be able to get the            // current state of the windows as the window manager may be delaying            // the computation for performance reasons.            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED                    && mWindowsForAccessibilityCallback != null) {                WindowManagerInternal wm = LocalServices.getService(WindowManagerInternal.class);                wm.computeWindowsForAccessibility();            }            synchronized (mLock) {                notifyAccessibilityServicesDelayedLocked(event, false); //重点代码                notifyAccessibilityServicesDelayedLocked(event, true);            }        }        if (OWN_PROCESS_ID != Binder.getCallingPid()) {            event.recycle();        }    }private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,            boolean isDefault) {        try {            UserState state = getCurrentUserStateLocked();            for (int i = 0, count = state.mBoundServices.size(); i < count; i++) { //遍历                Service service = state.mBoundServices.get(i);                if (service.mIsDefault == isDefault) {                    if (doesServiceWantEventLocked(service, event)) {                        service.notifyAccessibilityEvent(event, true);                    } else if (service.mUsesAccessibilityCache                            && (AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK                                & event.getEventType()) != 0) {                        service.notifyAccessibilityEvent(event, false);                    }                }            }        } catch (IndexOutOfBoundsException oobe) {            // An out of bounds exception can happen if services are going away            // as the for loop is running. If that happens, just bail because            // there are no more services to notify.        }    }

在notifyAccessibilityServicesDelayedLocked方法中,对所有存储在mBoundServices的Service进行遍历,并调用Service.notifyAccessibilityEvent。这里的Service是AccessibilityManagerService中的一个内部类。
先看看Service的定义

 class Service extends IAccessibilityServiceConnection.Stub            implements ServiceConnection, DeathRecipient, KeyEventDispatcher.KeyEventFilter,            FingerprintGestureDispatcher.FingerprintGestureClient {            ....              }
  • IAccessibilityServiceConnection.Stub Aidl接口
  • ServiceConnection bander连接成功后的回调
  • aidl的内容可以参考之前博客:Android之IPC2————AIDL

目录: frameworks\base\services\accessibility\java\com\android\server\accessibility\AccessibilityManagerService.java#Service

        public void notifyAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {            synchronized (mLock) {                final int eventType = event.getEventType();                // Make a copy since during dispatch it is possible the event to                // be modified to remove its source if the receiving service does                // not have permission to access the window content.                AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);                Message message;                if ((mNotificationTimeout > 0)                        && (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)) {                    // Allow at most one pending event                    final AccessibilityEvent oldEvent = mPendingEvents.get(eventType);                    mPendingEvents.put(eventType, newEvent);                    if (oldEvent != null) {                        mEventDispatchHandler.removeMessages(eventType);                        oldEvent.recycle();                    }                    message = mEventDispatchHandler.obtainMessage(eventType);                } else {                    // Send all messages, bypassing mPendingEvents                    message = mEventDispatchHandler.obtainMessage(eventType, newEvent);                }                message.arg1 = serviceWantsEvent ? 1 : 0;                mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout);//在Handler里进行处理            }        }        public Handler mEventDispatchHandler = new Handler(mMainHandler.getLooper()) {            @Override            public void handleMessage(Message message) {                final int eventType =  message.what;                AccessibilityEvent event = (AccessibilityEvent) message.obj;                boolean serviceWantsEvent = message.arg1 != 0;                notifyAccessibilityEventInternal(eventType, event, serviceWantsEvent);            }        };                /**         * Notifies an accessibility service client for a scheduled event given the event type.         *         * @param eventType The type of the event to dispatch.         */        private void notifyAccessibilityEventInternal(                int eventType,                AccessibilityEvent event,                boolean serviceWantsEvent) {            IAccessibilityServiceClient listener;            synchronized (mLock) {                listener = mServiceInterface;                if (listener == null) {                    return;                }                if (event == null) {                    event = mPendingEvents.get(eventType);                    if (event == null) {                        return;                    }                    mPendingEvents.remove(eventType);                }                if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) {                    event.setConnectionId(mId);                } else {                    event.setSource((View) null);                }                event.setSealed(true);            }            try {                listener.onAccessibilityEvent(event, serviceWantsEvent);//重点                if (DEBUG) {                    Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);                }            } catch (RemoteException re) {                Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re);            } finally {                event.recycle();            }        }

继续最终追踪一下mServiceInterface的来源。
通过全局搜索,发现在onServiceConnected里面,完成的mServiceInterface,即binder连接成功后的回调

目录: frameworks\base\services\accessibility\java\com\android\server\accessibility\AccessibilityManagerService.java#Service

        @Override        public void onServiceConnected(ComponentName componentName, IBinder service) {            synchronized (mLock) {                if (mService != service) {                    if (mService != null) {                        mService.unlinkToDeath(this, 0);                    }                    mService = service;                    try {                        mService.linkToDeath(this, 0);                    } catch (RemoteException re) {                        Slog.e(LOG_TAG, "Failed registering death link");                        binderDied();                        return;                    }                }                mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service); //被赋值                UserState userState = getUserStateLocked(mUserId);                addServiceLocked(this, userState);                if (userState.mBindingServices.contains(mComponentName) || mWasConnectedAndDied) {                    userState.mBindingServices.remove(mComponentName);                    mWasConnectedAndDied = false;                    onUserStateChangedLocked(userState);                    // Initialize the service on the main handler after we're done setting up for                    // the new configuration (for example, initializing the input filter).                    mMainHandler.obtainMessage(MainHandler.MSG_INIT_SERVICE, this).sendToTarget();                } else {                    binderDied();                }            }        }

我们注意一下,mServiceInterface的类型是IAccessibilityServiceClient。这个后面会用。

小结:

Android之辅助服务下篇————AccessibilityServic源码分析_第2张图片

3.AccessibilityService

前面分析从View到IAccessibilityServiceClient的过程,但是对于IAccessibilityServiceClient的实现。我一直没有找到,然后就回过同从AccessibilityService 重新分析。

AccessibilityService是继承的Service类,只不过它实现了onBind

目录: frameworks\base\core\java\android\accessibilityservice\AccessibilityService.java

public abstract class AccessibilityService extends Service {..../**     * Implement to return the implementation of the internal accessibility     * service interface.     */    @Override    public final IBinder onBind(Intent intent) {        return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {            @Override            public void onServiceConnected() {                AccessibilityService.this.dispatchServiceConnected();            }            @Override            public void onInterrupt() {                AccessibilityService.this.onInterrupt();            }            @Override            public void onAccessibilityEvent(AccessibilityEvent event) {                AccessibilityService.this.onAccessibilityEvent(event);            }            @Override            public void init(int connectionId, IBinder windowToken) {                mConnectionId = connectionId;                mWindowToken = windowToken;                // The client may have already obtained the window manager, so                // update the default token on whatever manager we gave them.                final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);                wm.setDefaultToken(windowToken);            }            @Override            public boolean onGesture(int gestureId) {                return AccessibilityService.this.onGesture(gestureId);            }            @Override            public boolean onKeyEvent(KeyEvent event) {                return AccessibilityService.this.onKeyEvent(event);            }            @Override            public void onMagnificationChanged(@NonNull Region region,                    float scale, float centerX, float centerY) {                AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);            }            @Override            public void onSoftKeyboardShowModeChanged(int showMode) {                AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode);            }            @Override            public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {                AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully);            }            @Override            public void onFingerprintCapturingGesturesChanged(boolean active) {                AccessibilityService.this.onFingerprintCapturingGesturesChanged(active);            }            @Override            public void onFingerprintGesture(int gesture) {                AccessibilityService.this.onFingerprintGesture(gesture);            }            @Override            public void onAccessibilityButtonClicked() {                AccessibilityService.this.onAccessibilityButtonClicked();            }            @Override            public void onAccessibilityButtonAvailabilityChanged(boolean available) {                AccessibilityService.this.onAccessibilityButtonAvailabilityChanged(available);            }        });    }....}

继续看看IAccessibilityServiceClientWrapper这个内部类

目录: frameworks\base\core\java\android\accessibilityservice\AccessibilityService#IAccessibilityServiceClientWrapper

 public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub            implements HandlerCaller.Callback {      .....        public IAccessibilityServiceClientWrapper(Context context, Looper looper,                Callbacks callback) {            mCallback = callback;            mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);        }        public void init(IAccessibilityServiceConnection connection, int connectionId,                IBinder windowToken) {            Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,                    connection, windowToken);            mCaller.sendMessage(message);        }        public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {            Message message = mCaller.obtainMessageBO(                    DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event);            mCaller.sendMessage(message);        }....        @Override        public void executeMessage(Message message) {            switch (message.what) {                case DO_ON_ACCESSIBILITY_EVENT: {                    AccessibilityEvent event = (AccessibilityEvent) message.obj;                    boolean serviceWantsEvent = message.arg1 != 0;                    if (event != null) {                        // Send the event to AccessibilityCache via AccessibilityInteractionClient                        AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);                        if (serviceWantsEvent                                && (mConnectionId != AccessibilityInteractionClient.NO_ID)) {                            // Send the event to AccessibilityService                            mCallback.onAccessibilityEvent(event);                        }                        // Make sure the event is recycled.                        try {                            event.recycle();                        } catch (IllegalStateException ise) {                            /* ignore - best effort */                        }                    }                } return;                case DO_ON_INTERRUPT: {                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {                        mCallback.onInterrupt();                    }                } return;                case DO_INIT: {                    mConnectionId = message.arg1;                    SomeArgs args = (SomeArgs) message.obj;                    IAccessibilityServiceConnection connection =                            (IAccessibilityServiceConnection) args.arg1;                    IBinder windowToken = (IBinder) args.arg2;                    args.recycle();                    if (connection != null) {                        AccessibilityInteractionClient.getInstance().addConnection(mConnectionId,                                connection);                        mCallback.init(mConnectionId, windowToken);                        mCallback.onServiceConnected();                    } else {                        AccessibilityInteractionClient.getInstance().removeConnection(                                mConnectionId);                        mConnectionId = AccessibilityInteractionClient.NO_ID;                        AccessibilityInteractionClient.getInstance().clearCache();                        mCallback.init(AccessibilityInteractionClient.NO_ID, null);                    }                } return;                case DO_ON_GESTURE: {                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {                        final int gestureId = message.arg1;                        mCallback.onGesture(gestureId);                    }                } return;                case DO_CLEAR_ACCESSIBILITY_CACHE: {                    AccessibilityInteractionClient.getInstance().clearCache();                } return;                case DO_ON_KEY_EVENT: {                    KeyEvent event = (KeyEvent) message.obj;                    try {                        IAccessibilityServiceConnection connection = AccessibilityInteractionClient                                .getInstance().getConnection(mConnectionId);                        if (connection != null) {                            final boolean result = mCallback.onKeyEvent(event);                            final int sequence = message.arg1;                            try {                                connection.setOnKeyEventResult(result, sequence);                            } catch (RemoteException re) {                                /* ignore */                            }                        }                    } finally {                        // Make sure the event is recycled.                        try {                            event.recycle();                        } catch (IllegalStateException ise) {                            /* ignore - best effort */                        }                    }                } return;                case DO_ON_MAGNIFICATION_CHANGED: {                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {                        final SomeArgs args = (SomeArgs) message.obj;                        final Region region = (Region) args.arg1;                        final float scale = (float) args.arg2;                        final float centerX = (float) args.arg3;                        final float centerY = (float) args.arg4;                        mCallback.onMagnificationChanged(region, scale, centerX, centerY);                    }                } return;                case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: {                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {                        final int showMode = (int) message.arg1;                        mCallback.onSoftKeyboardShowModeChanged(showMode);                    }                } return;                case DO_GESTURE_COMPLETE: {                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {                        final boolean successfully = message.arg2 == 1;                        mCallback.onPerformGestureResult(message.arg1, successfully);                    }                } return;                case DO_ON_FINGERPRINT_ACTIVE_CHANGED: {                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {                        mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1);                    }                } return;                case DO_ON_FINGERPRINT_GESTURE: {                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {                        mCallback.onFingerprintGesture(message.arg1);                    }                } return;                case (DO_ACCESSIBILITY_BUTTON_CLICKED): {                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {                        mCallback.onAccessibilityButtonClicked();                    }                } return;                case (DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED): {                    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {                        final boolean available = (message.arg1 != 0);                        mCallback.onAccessibilityButtonAvailabilityChanged(available);                    }                } return;                default :                    Log.w(LOG_TAG, "Unknown message type " + message.what);            }        }    }

在这里我们找到了IAccessibilityServiceClient.Stub。

所以可知,在AccessibilityService中,当AccessibilityManagerService和建立连接后,获得onBind返回的IAccessibilityServiceClientWrapper,在onServiceConnected回调中,将IAccessibilityServiceClientWrapper赋值给我们上面说的mServiceInterface。

三.findAccessibilityNodeInfosByText

1.AccessibilityNodeInfo

上面分析了点击事件是如何通知给AccessibilityService,现在让我们来看看AccessibilityService是如何选择的对应的控件的,以findAccessibilityNodeInfosByText为例

目录:frameworks\base\core\java\android\view\accessibility\AccessibilityNodeInfo.java

    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {        enforceSealed();        if (!canPerformRequestOverConnection(mSourceNodeId)) {            return Collections.emptyList();        }        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();        return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,                text);    }

继续追踪AccessibilityInteractionClient.findAccessibilityNodeInfosByText

目录:frameworks\base\core\java\android\view\accessibility\AccessibilityInteractionClient.java

    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(int connectionId,            int accessibilityWindowId, long accessibilityNodeId, String text) {        try {            IAccessibilityServiceConnection connection = getConnection(connectionId);            if (connection != null) {                final int interactionId = mInteractionIdCounter.getAndIncrement();                final long identityToken = Binder.clearCallingIdentity();                final boolean success = connection.findAccessibilityNodeInfosByText(                        accessibilityWindowId, accessibilityNodeId, text, interactionId, this,                        Thread.currentThread().getId()); //重点                Binder.restoreCallingIdentity(identityToken);                if (success) {                    List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(                            interactionId);                    if (infos != null) {                        finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);                        return infos;                    }                }            } else {                if (DEBUG) {                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);                }            }        } catch (RemoteException re) {            Log.w(LOG_TAG, "Error while calling remote"                    + " findAccessibilityNodeInfosByViewText", re);        }        return Collections.emptyList();    } 

这里出现了IAccessibilityServiceConnection类型,他是在哪定义的呢?其实我们在上一节中,有分析过这个内容,它其实就是AccessibilityManagerService中的内部类Service。

上面这个过程也是通过Binder完成的IPC

2.AccessibilityManagerService

我们继续分析AccessibilityManagerService中的内部类Service中的实现

    class Service extends IAccessibilityServiceConnection.Stub            implements ServiceConnection, DeathRecipient, KeyEventDispatcher.KeyEventFilter,            FingerprintGestureDispatcher.FingerprintGestureClient {            }

目录" frameworks\base\services\accessibility\java\com\android\server\accessibility\AccessibilityManagerService.java#Service

        @Override        public boolean findAccessibilityNodeInfosByText(int accessibilityWindowId,                long accessibilityNodeId, String text, int interactionId,                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)                throws RemoteException {            final int resolvedWindowId;            IAccessibilityInteractionConnection connection = null;            Region partialInteractiveRegion = Region.obtain();            MagnificationSpec spec;            synchronized (mLock) {                mUsesAccessibilityCache = true;                if (!isCalledForCurrentUserLocked()) {                    return false;                }                resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);                final boolean permissionGranted =                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);                if (!permissionGranted) {                    return false;                } else {                    connection = getConnectionLocked(resolvedWindowId);                    if (connection == null) {                        return false;                    }                }                if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(                        resolvedWindowId, partialInteractiveRegion)) {                    partialInteractiveRegion.recycle();                    partialInteractiveRegion = null;                }                spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);            }            final int interrogatingPid = Binder.getCallingPid();            callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,                    interrogatingPid, interrogatingTid);            final long identityToken = Binder.clearCallingIdentity();            try {                connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,                        partialInteractiveRegion, interactionId, callback, mFetchFlags,                        interrogatingPid, interrogatingTid, spec); //重点代码                return true;            } catch (RemoteException re) {                if (DEBUG) {                    Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()");                }            } finally {                Binder.restoreCallingIdentity(identityToken);                // Recycle if passed to another process.                if (partialInteractiveRegion != null && Binder.isProxy(connection)) {                    partialInteractiveRegion.recycle();                }            }            return false;        }

这里又出现一个connection,类型是IAccessibilityInteractionConnection。这个我找了半天,后来通过上面一节内容。我猜测是在ViewRootImpl中,最后搜索了一些。果然是在ViewRootImpl中实现的

3.ViewRootImpl

我们继续来看ViewRootImpl中的实现

目录:\frameworks\base\core\java\android\view\ViewRootImpl.java

AccessibilityInteractionConnection内部类定义

    static final class AccessibilityInteractionConnection            extends IAccessibilityInteractionConnection.Stub {....}

findAccessibilityNodeInfosByText的实现

        @Override        public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,                Region interactiveRegion, int interactionId,                IAccessibilityInteractionConnectionCallback callback, int flags,                int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {            ViewRootImpl viewRootImpl = mViewRootImpl.get();            if (viewRootImpl != null && viewRootImpl.mView != null) {                viewRootImpl.getAccessibilityInteractionController()                    .findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text,                            interactiveRegion, interactionId, callback, flags, interrogatingPid,                            interrogatingTid, spec);            } else {                // We cannot make the call and notify the caller so it does not wait.                try {                    callback.setFindAccessibilityNodeInfosResult(null, interactionId);                } catch (RemoteException re) {                    /* best effort - ignore */                }            }        }

viewRootImpl.getAccessibilityInteractionController() 返回的是AccessibilityInteractionController,我们看看

目录:frameworks\base\core\java\android\view\AccessibilityInteractionController.java

     public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,            String text, Region interactiveRegion, int interactionId,            IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,            long interrogatingTid, MagnificationSpec spec) {        Message message = mHandler.obtainMessage();        message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT;        message.arg1 = flags;        SomeArgs args = SomeArgs.obtain();        args.arg1 = text;        args.arg2 = callback;        args.arg3 = spec;        args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);        args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);        args.argi3 = interactionId;        args.arg4 = interactiveRegion;        message.obj = args;        scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS); //追踪    }                private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid,           boolean ignoreRequestPreparers) {       if (ignoreRequestPreparers               || !holdOffMessageIfNeeded(message, interrogatingPid, interrogatingTid)) {           // If the interrogation is performed by the same thread as the main UI           // thread in this process, set the message as a static reference so           // after this call completes the same thread but in the interrogating           // client can handle the message to generate the result.           if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {               AccessibilityInteractionClient.getInstanceForThread(                       interrogatingTid).setSameThreadMessage(message);           } else {               mHandler.sendMessage(message); //去handler的实现类           }       }   }private class PrivateHandler extends Handler {       private static final int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;       private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;       private static final int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3;       private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;       private static final int MSG_FIND_FOCUS = 5;       private static final int MSG_FOCUS_SEARCH = 6;       private static final int MSG_PREPARE_FOR_EXTRA_DATA_REQUEST = 7;       private static final int MSG_APP_PREPARATION_FINISHED = 8;       private static final int MSG_APP_PREPARATION_TIMEOUT = 9;       public PrivateHandler(Looper looper) {           super(looper);       }       @Override       public String getMessageName(Message message) {           final int type = message.what;           switch (type) {               case MSG_PERFORM_ACCESSIBILITY_ACTION:                   return "MSG_PERFORM_ACCESSIBILITY_ACTION";               case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID:                   return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID";               case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID:                   return "MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID";               case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT:                   return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT";               case MSG_FIND_FOCUS:                   return "MSG_FIND_FOCUS";               case MSG_FOCUS_SEARCH:                   return "MSG_FOCUS_SEARCH";               case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST:                   return "MSG_PREPARE_FOR_EXTRA_DATA_REQUEST";               case MSG_APP_PREPARATION_FINISHED:                   return "MSG_APP_PREPARATION_FINISHED";               case MSG_APP_PREPARATION_TIMEOUT:                   return "MSG_APP_PREPARATION_TIMEOUT";               default:                   throw new IllegalArgumentException("Unknown message type: " + type);           }       }       @Override       public void handleMessage(Message message) {           final int type = message.what;           switch (type) {               case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: {                   findAccessibilityNodeInfoByAccessibilityIdUiThread(message);               } break;               case MSG_PERFORM_ACCESSIBILITY_ACTION: {                   performAccessibilityActionUiThread(message);               } break;               case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: {                   findAccessibilityNodeInfosByViewIdUiThread(message);               } break;               case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: {                   findAccessibilityNodeInfosByTextUiThread(message);               } break;               case MSG_FIND_FOCUS: {                   findFocusUiThread(message);               } break;               case MSG_FOCUS_SEARCH: {                   focusSearchUiThread(message);               } break;               case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST: {                   prepareForExtraDataRequestUiThread(message);               } break;               case MSG_APP_PREPARATION_FINISHED: {                   requestPreparerDoneUiThread(message);               } break;               case MSG_APP_PREPARATION_TIMEOUT: {                   requestPreparerTimeoutUiThread();               } break;               default:                   throw new IllegalArgumentException("Unknown message type: " + type);           }       }   }//重点代码private void findAccessibilityNodeInfosByTextUiThread(Message message) {       final int flags = message.arg1;       SomeArgs args = (SomeArgs) message.obj;       final String text = (String) args.arg1;       final IAccessibilityInteractionConnectionCallback callback =           (IAccessibilityInteractionConnectionCallback) args.arg2;       final MagnificationSpec spec = (MagnificationSpec) args.arg3;       final int accessibilityViewId = args.argi1;       final int virtualDescendantId = args.argi2;       final int interactionId = args.argi3;       final Region interactiveRegion = (Region) args.arg4;       args.recycle();       List<AccessibilityNodeInfo> infos = null;       try {           if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {               return;           }           mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;           View root = null;           if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {               root = findViewByAccessibilityId(accessibilityViewId);           } else {               root = mViewRootImpl.mView;           }           if (root != null && isShown(root)) {               AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();               if (provider != null) {                   infos = provider.findAccessibilityNodeInfosByText(text,                           virtualDescendantId);               } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {                   ArrayList<View> foundViews = mTempArrayList;                   foundViews.clear();                   //重点代码                   root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT                           | View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION                           | View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);                   if (!foundViews.isEmpty()) {                       infos = mTempAccessibilityNodeInfoList;                       infos.clear();                       final int viewCount = foundViews.size();                       for (int i = 0; i < viewCount; i++) {                           View foundView = foundViews.get(i);                           if (isShown(foundView)) {                               provider = foundView.getAccessibilityNodeProvider();                               if (provider != null) {                                   List<AccessibilityNodeInfo> infosFromProvider =                                       provider.findAccessibilityNodeInfosByText(text,                                               AccessibilityNodeProvider.HOST_VIEW_ID);                                   if (infosFromProvider != null) {                                       infos.addAll(infosFromProvider);                                   }                               } else  {                                   infos.add(foundView.createAccessibilityNodeInfo());                               }                           }                       }                   }               }           }       } finally {           updateInfosForViewportAndReturnFindNodeResult(                   infos, callback, interactionId, spec, interactiveRegion);       }   }

在最后 root.findViewsWithText。root的来源是 root = mViewRootImpl.mView。我们知道mViewRootImpl.mView其实是通过setView来赋值的。一般都是ViewGrop类型。我们直接去看ViewGrup的findViewsWithText

目录:frameworks\base\core\java\android\view\ViewGroup.java

    @Override    public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) {        super.findViewsWithText(outViews, text, flags);        final int childrenCount = mChildrenCount;        final View[] children = mChildren;        for (int i = 0; i < childrenCount; i++) {            View child = children[i];            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE                    && (child.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {                child.findViewsWithText(outViews, text, flags);            }        }    }

继续看看View的实现

目录:frameworks\base\core\java\android\view\View.java

    public void findViewsWithText(ArrayList<View> outViews, CharSequence searched,            @FindViewFlags int flags) {        if (getAccessibilityNodeProvider() != null) {            if ((flags & FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS) != 0) {                outViews.add(this);            }        } else if ((flags & FIND_VIEWS_WITH_CONTENT_DESCRIPTION) != 0                && (searched != null && searched.length() > 0)                && (mContentDescription != null && mContentDescription.length() > 0)) {            String searchedLowerCase = searched.toString().toLowerCase();            String contentDescriptionLowerCase = mContentDescription.toString().toLowerCase();            if (contentDescriptionLowerCase.contains(searchedLowerCase)) {                outViews.add(this);            }        }    }

至此我们可以看出,最终会调用View的findViewsWithText,将符合条件的View放入到 outViews集合中。

4.小结

时序图:
Android之辅助服务下篇————AccessibilityServic源码分析_第3张图片

四.performAction

点击事件和上面查找控件的流程很想,着重分析一下AccessibilityInteractionController之后的流程
Android之辅助服务下篇————AccessibilityServic源码分析_第4张图片

1.AccessibilityInteractionController

从performAccessibilityActionClientThread开始分析

目录:frameworks\base\core\java\android\view\AccessibilityInteractionController.java

public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,            Bundle arguments, int interactionId,            IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,            long interrogatingTid) {        Message message = mHandler.obtainMessage();        message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;        message.arg1 = flags;        message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);        SomeArgs args = SomeArgs.obtain();        args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);        args.argi2 = action;        args.argi3 = interactionId;        args.arg1 = callback;        args.arg2 = arguments;        message.obj = args;        scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);    }//Hander的handleMessage处理 @Override        public void handleMessage(Message message) {            final int type = message.what;            switch (type) {                case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: {                    findAccessibilityNodeInfoByAccessibilityIdUiThread(message);                } break;                case MSG_PERFORM_ACCESSIBILITY_ACTION: {                    performAccessibilityActionUiThread(message);//这里                } break;                case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: {                    findAccessibilityNodeInfosByViewIdUiThread(message);                } break;                case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: {                    findAccessibilityNodeInfosByTextUiThread(message);                } break;                case MSG_FIND_FOCUS: {                    findFocusUiThread(message);                } break;                case MSG_FOCUS_SEARCH: {                    focusSearchUiThread(message);                } break;                case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST: {                    prepareForExtraDataRequestUiThread(message);                } break;                case MSG_APP_PREPARATION_FINISHED: {                    requestPreparerDoneUiThread(message);                } break;                case MSG_APP_PREPARATION_TIMEOUT: {                    requestPreparerTimeoutUiThread();                } break;                default:                    throw new IllegalArgumentException("Unknown message type: " + type);            }        }    }private void performAccessibilityActionUiThread(Message message) {        final int flags = message.arg1;        final int accessibilityViewId = message.arg2;        SomeArgs args = (SomeArgs) message.obj;        final int virtualDescendantId = args.argi1;        final int action = args.argi2;        final int interactionId = args.argi3;        final IAccessibilityInteractionConnectionCallback callback =            (IAccessibilityInteractionConnectionCallback) args.arg1;        Bundle arguments = (Bundle) args.arg2;        args.recycle();        boolean succeeded = false;        try {            if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null ||                    mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {                return;            }            mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;            View target = null;            if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {                target = findViewByAccessibilityId(accessibilityViewId);            } else {                target = mViewRootImpl.mView;            }            if (target != null && isShown(target)) {                if (action == R.id.accessibilityActionClickOnClickableSpan) {                    // Handle this hidden action separately                    succeeded = handleClickableSpanActionUiThread(                            target, virtualDescendantId, arguments);                } else {                    AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();                    if (provider != null) {                        succeeded = provider.performAction(virtualDescendantId, action,                                arguments);                    } else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {                        succeeded = target.performAccessibilityAction(action, arguments); //这里                    }                }            }        } finally {            try {                mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;                callback.setPerformAccessibilityActionResult(succeeded, interactionId);            } catch (RemoteException re) {                /* ignore - the other side will time out */            }        }    }

我们可以看到最终通过target.performAccessibilityAction调用到View的相关方法里

2.View

目录:

    public boolean performAccessibilityAction(int action, Bundle arguments) {      if (mAccessibilityDelegate != null) {          return mAccessibilityDelegate.performAccessibilityAction(this, action, arguments);      } else {          return performAccessibilityActionInternal(action, arguments);      }    } public boolean performAccessibilityActionInternal(int action, Bundle arguments) {        if (isNestedScrollingEnabled()                && (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD                || action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD                || action == R.id.accessibilityActionScrollUp                || action == R.id.accessibilityActionScrollLeft                || action == R.id.accessibilityActionScrollDown                || action == R.id.accessibilityActionScrollRight)) {            if (dispatchNestedPrePerformAccessibilityAction(action, arguments)) {                return true;            }        }        switch (action) {        //点击事件            case AccessibilityNodeInfo.ACTION_CLICK: {                if (isClickable()) {                    performClick();                    return true;                }            } break;            case AccessibilityNodeInfo.ACTION_LONG_CLICK: {                if (isLongClickable()) {                    performLongClick();                    return true;                }            } break;            case AccessibilityNodeInfo.ACTION_FOCUS: {                if (!hasFocus()) {                    // Get out of touch mode since accessibility                    // wants to move focus around.                    getViewRootImpl().ensureTouchMode(false);                    return requestFocus();                }            } break;            case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {                if (hasFocus()) {                    clearFocus();                    return !isFocused();                }            } break;            case AccessibilityNodeInfo.ACTION_SELECT: {                if (!isSelected()) {                    setSelected(true);                    return isSelected();                }            } break;            case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {                if (isSelected()) {                    setSelected(false);                    return !isSelected();                }            } break;            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {                if (!isAccessibilityFocused()) {                    return requestAccessibilityFocus();                }            } break;            case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {                if (isAccessibilityFocused()) {                    clearAccessibilityFocus();                    return true;                }            } break;            case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: {                if (arguments != null) {                    final int granularity = arguments.getInt(                            AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);                    final boolean extendSelection = arguments.getBoolean(                            AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN);                    return traverseAtGranularity(granularity, true, extendSelection);                }            } break;            case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: {                if (arguments != null) {                    final int granularity = arguments.getInt(                            AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);                    final boolean extendSelection = arguments.getBoolean(                            AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN);                    return traverseAtGranularity(granularity, false, extendSelection);                }            } break;            case AccessibilityNodeInfo.ACTION_SET_SELECTION: {                CharSequence text = getIterableTextForAccessibility();                if (text == null) {                    return false;                }                final int start = (arguments != null) ? arguments.getInt(                        AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, -1) : -1;                final int end = (arguments != null) ? arguments.getInt(                AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, -1) : -1;                // Only cursor position can be specified (selection length == 0)                if ((getAccessibilitySelectionStart() != start                        || getAccessibilitySelectionEnd() != end)                        && (start == end)) {                    setAccessibilitySelection(start, end);                    notifyViewAccessibilityStateChangedIfNeeded(                            AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);                    return true;                }            } break;            case R.id.accessibilityActionShowOnScreen: {                if (mAttachInfo != null) {                    final Rect r = mAttachInfo.mTmpInvalRect;                    getDrawingRect(r);                    return requestRectangleOnScreen(r, true);                }            } break;            case R.id.accessibilityActionContextClick: {                if (isContextClickable()) {                    performContextClick();                    return true;                }            } break;        }        return false;    }    public boolean performClick() {        final boolean result;        final ListenerInfo li = mListenerInfo;        if (li != null && li.mOnClickListener != null) {            playSoundEffect(SoundEffectConstants.CLICK);            li.mOnClickListener.onClick(this);//点击事件回调            result = true;        } else {            result = false;        }        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);        notifyEnterOrExitForAutoFillIfNeeded(true);        return result;    }

至此AccessibilityServic所有内容基本都分析完了

更多相关文章

  1. Android中事件分发机制分析
  2. android设置EditText不可编辑内容,响应点击事件
  3. 求助: Android 加载 webview, 点击webview 中网页时间, 如何触发
  4. android之实现各个组件点击事件处理
  5. android 处理鼠标滚轮事件
  6. Android拦截、监听系统返回键事件
  7. Android 复制单个文件到指定目录,Android copy file
  8. sdk\tools目录下的traceview.bat无法找到

随机推荐

  1. mysql在项目中怎么选事务隔离级别
  2. .Net Core导入千万级数据至Mysql的步骤
  3. MySQL大小写敏感的注意事项
  4. MySQL 使用事件(Events)完成计划任务
  5. MySQL触发器的使用
  6. Oracle更换为MySQL遇到的问题及解决
  7. MySQL 重命名表的操作方法及注意事项
  8. Mysql官方性能测试工具mysqlslap的使用简
  9. MySQL官方导出工具mysqlpump的使用
  10. 新手必备之MySQL msi版本下载安装图文详