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继续将点击事件进行分发。

小结:

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。这个后面会用。

小结:

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.小结

时序图:

四.performAction

点击事件和上面查找控件的流程很想,着重分析一下AccessibilityInteractionController之后的流程

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. 点击LinearLayout使用selector改变TextView字体颜色
  2. Android(安卓)RadioGroup和RadioButton使用
  3. [置顶] Android(安卓)View系统学习文章汇总
  4. Android中事件分发机制分析
  5. 菜鸟学Android开发系列之:初探Button和TextView
  6. Android解析XML-详尽
  7. Android实现朋友圈评论回复列表
  8. android简单学习总结
  9. android 环境搭建helloworld

随机推荐

  1. 自定义ProgressBar的样式失效
  2. Android(安卓)数据库的简单使用
  3. android listview 自动加载
  4. Android中Activity之间访问互传参数
  5. 转:Android之通信录中的联系人操作
  6. Android实现圆角弹框功能
  7. android读取assets大于1M文件的解决方法
  8. android 日期对话框
  9. Android动态获取当前手机IP地址
  10. react-native 升级版本号