美文网首页
Android AccessibilityService 事件分

Android AccessibilityService 事件分

作者: 我爱田Hebe | 来源:发表于2022-08-25 09:51 被阅读0次

    在了解了无障碍服务基础使用之后,我们来探究一下 AccessibilityService 的事件接收方法回调的时机和它深层次的实现逻辑。

    AccessibilityService 监听事件的调用逻辑

    AccessibilityService 有很多用来接收外部调用事件变化的方法,这些方法封装在内部接口 Callbacks 中:

    public interface Callbacks {
        void onAccessibilityEvent(AccessibilityEvent event);
        void onInterrupt();
        void onServiceConnected();
        void init(int connectionId, IBinder windowToken);
        boolean onGesture(AccessibilityGestureEvent gestureInfo);
        boolean onKeyEvent(KeyEvent event);
        void onMagnificationChanged(int displayId, @NonNull Region region, float scale, float centerX, float centerY);
        void onSoftKeyboardShowModeChanged(int showMode);
        void onPerformGestureResult(int sequence, boolean completedSuccessfully);
        void onFingerprintCapturingGesturesChanged(boolean active);
        void onFingerprintGesture(int gesture);
        void onAccessibilityButtonClicked(int displayId);
        void onAccessibilityButtonAvailabilityChanged(boolean available);
        void onSystemActionsChanged();
    } 
    

    以最常用的 onAccessibilityEvent 为例,介绍一下调用流程。

    onAccessibilityEventAccessibilityService 中,AccessibilityServiceonBind 生命周期中,返回了一个IAccessibilityServiceClientWrapper 对象,它是一个 Binder ,所以外部实际上通过 Binder 机制跨进程调用到无障碍服务的。

    外部通过 Binder 调用到 Service 具体的实现方法的调用栈是:

    - frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#IAccessibilityServiceClientWrapper#onAccessibilityEvent
    - frameworks/base/core/java/com/android/internal/os/HandlerCaller.java#sendMessage
    - frameworks/base/core/java/com/android/internal/os/HandlerCaller.java#Callback#executeMessage
    - frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#IAccessibilityServiceClientWrapper#executeMessage
    - frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#Callbacks#onAccessibilityEvent
    - frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#onAccessibilityEvent 
    

    首先是外部调用到 IAccessibilityServiceClientWrapper 的 onAccessibilityEvent 方法:

    // IAccessibilityServiceClientWrapper
    public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {
        Message message = mCaller.obtainMessageBO(
                DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event);
        mCaller.sendMessage(message);
    } 
    

    在这个方法中通过 HandlerCaller 切换到主线程,然后发送了一个消息。

    这里的 HandlerCaller 的源码是:

    public class HandlerCaller {
        final Looper mMainLooper;
        final Handler mH;
    
        final Callback mCallback;
    
        class MyHandler extends Handler {
            MyHandler(Looper looper, boolean async) {
                super(looper, null, async);
            }
    
            @Override
            public void handleMessage(Message msg) {
                mCallback.executeMessage(msg);
            }
        }
    
        public interface Callback {
            public void executeMessage(Message msg);
        }
    
        public HandlerCaller(Context context, Looper looper, Callback callback,
                boolean asyncHandler) {
            mMainLooper = looper != null ? looper : context.getMainLooper();
            mH = new MyHandler(mMainLooper, asyncHandler);
            mCallback = callback;
        }
        ...
    } 
    

    从它的源码中可以看出,这是一个向主线程发消息的 Handler 。 在主线程中执行它的内部类 CallbackexecuteMessage 方法。

    IAccessibilityServiceClientWrapper 实现了 AccessibilityService.Callback接口,所以调用到了IAccessibilityServiceClientWrapper.executeMessage 方法中。IAccessibilityServiceClientWrapper 对象的创建是在 onBind 生命周期中。它接收一个 AccessibilityService.Callback 对象作为 IAccessibilityServiceClientWrapper 的构造参数:

    @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);
            }
        ...
    }); 
    

    IAccessibilityServiceClientWrapper 中的 executeMessage中,根据不同的 Handler 消息调用了 AccessibilityService.Callback 的对应方法:

    public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub implements HandlerCaller.Callback {
        private final HandlerCaller mCaller;
    
        public IAccessibilityServiceClientWrapper(Context context, Looper looper,
            Callbacks callback) {
            // ...
            mCaller = new HandlerCaller(context, looper, this, true);
        }
    
        @Override
        public void executeMessage(Message message) {
            switch (message.what) {
                case DO_ON_ACCESSIBILITY_EVENT: {
                    // ...
                    mCallback.onAccessibilityEvent(event);
                    return;
                }
                case DO_ON_INTERRUPT: {
                    // ...
                    mCallback.onInterrupt();
                    return;
                }
                default :
                    Log.w(LOG_TAG, "Unknown message type " + message.what);
            }   
        }
    } 
    

    而刚才传入的 AccessibilityService.Callback 方法的实现中,调用了AccessibilityServiceonAccessibilityEvent 方法:

     @Override
            public void onAccessibilityEvent(AccessibilityEvent event) {
                AccessibilityService.this.onAccessibilityEvent(event);
            } 
    

    这样整个调用链就清晰了:

    • 外部通过 Binder 机制调用到 AccessibilityService 的内部 Binder 代理实现IAccessibilityServiceClientWrapper对象
    • IAccessibilityServiceClientWrapper对象内部通过 Handler 机制切换到主线程执行 AccessibilityService.Callback 中对应的方法。
    • AccessibilityService.Callback 中的方法调用到了AccessibilityService 对应的生命周期方法。

    接下来关注一下一些重要的事件接收方法。

    onAccessibilityEvent

    Handler 中的 DO_ON_ACCESSIBILITY_EVENT 事件会调用到 onAccessibilityEvent 。 在 executeMessage(Message message) 方法中的逻辑是:

    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(mContext).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;
    } 
    
    • 取出 message.obj 转换为 AccessibilityEvent ,并根据 message.arg1检查 Service 是否想要处理这个事件。
    • 检查 AccessibilityEvent对象是否为 null,为空直接 return
    • AccessibilityEvent对象通过 AccessibilityInteractionClient 加入到 AccessibilityCache 缓存中,然后根据 service 是否要处理事件和AccessibilityInteractionClient连接状态,决定是否要将事件发送给 AccessibilityService
    • 最后回收事件对象。

    这里的AccessibilityInteractionClient连接状态检查时通过 mConnectionId 属性来判断的,在IAccessibilityServiceClientWrapper 的 init 时被赋值,init 也是通过 Handler 传递来的消息切换到主线程进行的:

    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(mContext).addConnection(
                    mConnectionId, connection);
            mCallback.init(mConnectionId, windowToken);
            mCallback.onServiceConnected();
        } else {
            AccessibilityInteractionClient.getInstance(mContext).removeConnection(
                    mConnectionId);
            mConnectionId = AccessibilityInteractionClient.NO_ID;
            AccessibilityInteractionClient.getInstance(mContext).clearCache();
            mCallback.init(AccessibilityInteractionClient.NO_ID, null);
        }
        return;
    } 
    
    

    onIntercept

    onAccessibilityEvent 事件一样都是通过 Handler 机制进行处理的:

    case DO_ON_INTERRUPT: {
        if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
            mCallback.onInterrupt();
        }
        return;
    } 
    
    

    只检查了与AccessibilityInteractionClient 的连接状态。

    AccessibilityService 事件的外部来源

    经过上面的分析,我们知道外部通过 binder 机制触发了 AccessibilityService 的事件监听方法,那么它们来自哪里呢? 接下来从 AccessibilityServiceInfo 开始,分析系统事件是如何传递到无障碍服务的。

    AccessibilityServiceInfo

    AccessibilityServiceInfo 用来描述 AccessibilityService 。系统根据这个类中的信息,将 AccessibilityEvents 通知给一个 AccessibilityService。

    AccessibilityServiceInfo 中定义了一些属性,用来控制无障碍服务的一些权限和能力。我们在 AndroidManifest.xml 中为无障碍服务指定的meta-data 标签中,指定的配置文件中的配置,和AccessibilityServiceInfo 中的属性一一对应。

    AccessibilityServiceInfo 的引用:

    查看 AccessibilityServiceInfo 的引用栈,发现有很多地方都用到了这个类,关于无障碍的重点看 AccessibilityManagerServiceAccessibilityManager 这一套逻辑。 从名称上看,无障碍功能提供了类似 AMS 一样的系统服务,并通过一个 Manager 类来进行调用。

    AccessibilityManager

    AccessibilityManager 是一个系统服务管理器,用来分发 AccessibilityEvent 事件。当用户界面中发生一些值得注意的事件时,例如焦点变化和 Activity 启动等,会生成这些事件。

    AccessibilityManager 内部有一个看起来与发送消息有关的方法:

    public void sendAccessibilityEvent(AccessibilityEvent event) {
            final IAccessibilityManager service;
            final int userId;
            final AccessibilityEvent dispatchedEvent;
            synchronized (mLock) {
                service = getServiceLocked();
                if (service == null) return;
                event.setEventTime(SystemClock.uptimeMillis());
                if (event.getAction() == 0) {
                    event.setAction(mPerformingAction);
                }
                if (mAccessibilityPolicy != null) {
                    dispatchedEvent = mAccessibilityPolicy.onAccessibilityEvent(event, mIsEnabled, mRelevantEventTypes);
                    if (dispatchedEvent == null) return;
                } else {
                    dispatchedEvent = event;
                }
                if (!isEnabled()) {
                    Looper myLooper = Looper.myLooper();
                    if (myLooper == Looper.getMainLooper()) {
                        throw new IllegalStateException("Accessibility off. Did you forget to check that?");
                    } else {
                        // 当不是在主线程(mainLooper)运行时,调用检查无障碍开启状态可能会异常。因此直接抛出异常
                        Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
                        return;
                    }
                }
                userId = mUserId;
            }
            try {
                final long identityToken = Binder.clearCallingIdentity();
                try {
                    service.sendAccessibilityEvent(dispatchedEvent, userId);
                } finally {
                    Binder.restoreCallingIdentity(identityToken);
                }
                if (DEBUG) {
                    Log.i(LOG_TAG, dispatchedEvent + " sent");
                }
            } catch (RemoteException re) {
                Log.e(LOG_TAG, "Error during sending " + dispatchedEvent + " ", re);
            } finally {
                if (event != dispatchedEvent) {
                    event.recycle();
                }
                dispatchedEvent.recycle();
            }
        } 
    
    

    这个方法是用来发送一个 AccessibilityEvent 事件的,简化里面的逻辑:

    - 加锁
    - getServiceLocked() 获取 service 对象,service 获取不到直接 return
    - event 设置一个时间,然后设置 action
    - 检查 AccessibilityPolicy 对象是否为 null
      - 不为空,dispatchedEvent 根据 AccessibilityPolicy 的 onAccessibilityEvent(event) 赋值,赋值后仍为空直接 return
      - 为空, dispatchedEvent = event
    - 检查系统是否开启无障碍功能
    - 解锁
    - try
      - try
        - service.sendAccessibilityEvent(dispatchedEvent, userId); 通过 AccessibilityManagerService 的 sendAccessibilityEvent 发送事件
      - finally
        - Binder.restoreCallingIdentity(identityToken); 
    - finally
      - 回收 event 和 dispatchedEvent 对象 
    
    

    这里 getServiceLocked() 内部调用了 tryConnectToServiceLocked 方法:

     private void tryConnectToServiceLocked(IAccessibilityManager service) {
            if (service == null) {
                IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
                if (iBinder == null) {
                    return;
                }
                service = IAccessibilityManager.Stub.asInterface(iBinder);
            }
              // ...
        } 
    
    

    AccessibilityManagerService 实现了 IAccessibilityManager.Stub ,所以这里的 service 时 AccessibilityManagerService。 这里调用了 service.sendAccessibilityEvent(dispatchedEvent, userId);

    通过 Binder 机制,调用到的是 AccessibilityManagerService 里的 sendAccessibilityEvent 方法

    AccessibilityManager.sendAccessibilityEvent 的调用位置有很多,其中比较显眼的是在 ViewRootImpl 中的,因为 ViewRootImpl 是 View 添加到 Window 的重要实现类。sendAccessibilityEvent 在 ViewRootImpl 的内部类中存在调用:

     @Override
        public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
            // ...
            final int eventType = event.getEventType();
            final View source = getSourceForAccessibilityEvent(event);
            switch (eventType) {
                case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
                    if (source != null) {
                        AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
                        if (provider != null) {
                            final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(event.getSourceNodeId());
                            final AccessibilityNodeInfo node;
                            node = provider.createAccessibilityNodeInfo(virtualNodeId);
                            setAccessibilityFocus(source, node);
                        }
                    }
                } break;
                case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
                    if (source != null && source.getAccessibilityNodeProvider() != null) {
                        setAccessibilityFocus(null, null);
                    }
                } break;
                case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
                    handleWindowContentChangedEvent(event);
                } break;
            }
            mAccessibilityManager.sendAccessibilityEvent(event);
            return true;
        } 
    
    

    这是一个 override 方法,它的定义在接口 ViewParent 中。 这个方法的调用栈很多:

    可以跟到 View 中存在的同名方法 :

    public void sendAccessibilityEvent(int eventType) {
        if (mAccessibilityDelegate != null) {
            mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
        } else {
            sendAccessibilityEventInternal(eventType);
        }
    } 
    
    

    它在 View 中的调用:

    可以看出,常见的 View 的事件,包括:点击、长按、焦点变化等,都会调用 sendAccessibilityEvent 方法。

    ViewRootImpl 中, AccessibilityManager 也存在很多处调用逻辑:

    可以看出在 Android 在 View 体系中,提供了很多对无障碍能力的支持。所有的 View 的事件都会被系统的无障碍服务捕获到。

    回到调用逻辑,AccessibilityManager 内部调用到的是 AccessibilityManagerService 里的 sendAccessibilityEvent 方法。下面介绍 AccessibilityManagerService 里面的流程。

    AccessibilityManagerService

    sendAccessibilityEvent 伪代码逻辑:

    synchronized {
        1. 解析配置文件中的属性,并对其进行配置
        2. 设置配置的包名范围
    }
    if (dispatchEvent) {
        3. 确保接收此事件的 client 能够获取 window 当前的状态,因为 Window Manager 可能会出于性能原因延迟计算,通过配置 shouldComputeWindows = true/false
        if (shouldComputeWindows) {
            4. 获取 WindowManagerInternal wm 
            5. wm.computeWindowsForAccessibility(displayId);
        }
        synchoronized {
            notifyAccessibilityServicesDelayedLocked(event, false)
            notifyAccessibilityServicesDelayedLocked(event, true)
            mUiAutomationManager.sendAccessibilityEventLocked(event);
        }
    }
    ... 
    
    

    最后的关键三行代码中,调用了两个方法:

    notifyAccessibilityServicesDelayedLocked :

     private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, boolean isDefault) {
            try {
                AccessibilityUserState state = getCurrentUserStateLocked();
                for (int i = 0, count = state.mBoundServices.size(); i < count; i++) {
                    AccessibilityServiceConnection service = state.mBoundServices.get(i);
                    if (service.mIsDefault == isDefault) {
                        service.notifyAccessibilityEvent(event);
                    }
                }
            } catch (IndexOutOfBoundsException oobe) {}
        } 
    
    

    AccessibilityManagerService 从这个方法中,调用 AccessibilityServiceConnection 的同名方法notifyAccessibilityEvent

    这个意思是,先通知 service.mIsDefault = false 的无障碍服务连接发送事件,然后再通知 等于 true 的无障碍服务连接发送事件。

    mUiAutomationManager.sendAccessibilityEventLocked(event)

    mUiAutomationManager 的类型是 UiAutomationManager,它的 sendAccessibilityEventLocked 方法实现是:

    void sendAccessibilityEventLocked(AccessibilityEvent event) {
        if (mUiAutomationService != null) {
            mUiAutomationService.notifyAccessibilityEvent(event);
        }
    } 
    
    

    mUiAutomationService 的类型是 UiAutomationService,它是 UiAutomationManager 的内部类,继承自 AbstractAccessibilityServiceConnection,内部操作都是切换到主线程进行的,notifyAccessibilityEvent 方法的实现在父类中,后续会和上面的 AccessibilityServiceConnection 一起说明。

    AccessibilityServiceConnection

    此类用来表示一个无障碍服务。 它存储着服务管理所需的所有每个服务数据,提供用于启动/停止服务的 API,并负责在服务管理的数据结构中添加/删除服务。 该类还公开了配置接口,该接口在绑定后立即传递给它所代表的服务。 它还用作服务的连接。

    AccessibilityServiceConnectionUiAutomationService 一样,继承自 AbstractAccessibilityServiceConnection

    它们的 notifyAccessibilityEvent 方法,在 AbstractAccessibilityServiceConnection 中:

     public void notifyAccessibilityEvent(AccessibilityEvent event) {
            synchronized (mLock) {
                ...
                // copy 一个副本,因为在调度期间,如果接收的服务没有访问窗口内容的权限,则可能会修改并删除事件。
                AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
                Message message;
                if ((mNotificationTimeout > 0)
                        && (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)) {
                    // 最多允许一个待处理事件
                    final AccessibilityEvent oldEvent = mPendingEvents.get(eventType);
                    mPendingEvents.put(eventType, newEvent);
                    if (oldEvent != null) {
                        mEventDispatchHandler.removeMessages(eventType);
                        oldEvent.recycle();
                    }
                    message = mEventDispatchHandler.obtainMessage(eventType);
                } else {
                    // 发送所有消息,绕过 mPendingEvents
                    message = mEventDispatchHandler.obtainMessage(eventType, newEvent);
                }
                message.arg1 = serviceWantsEvent ? 1 : 0;
    
                mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout);
            }
        } 
    
    

    这个方法中,通过一个 Handler 来处理和发送消息。

     mEventDispatchHandler = new Handler(mainHandler.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);
                }
            }; 
    
    

    内部调用 notifyAccessibilityEventInternal

    private void notifyAccessibilityEventInternal(int eventType, AccessibilityEvent event, boolean serviceWantsEvent) {
        IAccessibilityServiceClient listener;
    
        synchronized (mLock) {
            listener = mServiceInterface;
            // 如果在消息分发无障碍事件时 service die 或 关闭,listener 可能为空
            if (listener == null) return;
            // 我们有两种通知事件的方式,节流和非节流。 如果我们不进行节流,那么消息会随事件一起出现,我们会毫不费力地处理这些事件。
            if (event == null) {
                // 我们正在限制事件,所以只要它为空,我们就会在 mPendingEvents 中发送这种类型的事件。 由于竞争条件,它只能为空:
                //   1) 一个 binder 线程调用 notifyAccessibilityServiceDelayedLocked,它发布一条用于调度事件的消息并将该事件存储在 mPendingEvents 中。
                //   2) 消息由服务线程上的处理程序从队列中拉出,此方法即将获取锁。
                //   3) 另一个 binder 线程在 notifyAccessibilityEvent 中获取锁
                //   4) notifyAccessibilityEvent 回收该方法即将处理的事件,替换为新的,并发布第二条消息
                //   5) 此方法抓取新事件,对其进行处理,然后将其从 mPendingEvents 中删除
                //   6) (4) 中发送的第二条消息到达,但事件已在 (5) 中删除。
                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);
            
        } catch (RemoteException re) {} finally {
            event.recycle();
        }
    } 
    
    

    备注中说明了消息处理和分发逻辑,但我们这里只需要关注最后的:

    listener.onAccessibilityEvent(event, serviceWantsEvent); 
    
    

    这里的 listener 是一个 IAccessibilityServiceClient ,是个 AIDL 文件。这个 AIDL 在我们的 AccessibilityService 中有实现类!

    调用到这个 IAccessibilityServiceClient , 就能调用到 AccessibilityService 中的代码了。

    至此无障碍服务的调用,从系统的 View 和其他事件位置,经过AccessibilityManager.notifyAccessibilityEvent 用 Binder 机制调用AccessibilityManagerService.notifyAccessibilityEvent ;然后AccessibilityManagerService 内部通过调用 AccessibilityServiceConnection.notifyAccessibilityEvent 来调用我们可以实现的 AccessibilityService 中接收事件。

    相关文章

      网友评论

          本文标题:Android AccessibilityService 事件分

          本文链接:https://www.haomeiwen.com/subject/jmpagrtx.html