Android AccessibilityService 事件分发原理

Posted datian1234

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android AccessibilityService 事件分发原理相关的知识,希望对你有一定的参考价值。

在了解了无障碍服务基础使用之后,我们来探究一下 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核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。

需要的直接点击文末小卡片可以领取哦我免费分享给你,以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持,需要的自己领取)

Android学习PDF+架构视频+面试文档+源码笔记

部分资料一览:

  • 330页PDF Android学习核心笔记(内含8大板块)

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

  • Android BAT大厂面试题(有解析)

领取地址:

点击下方卡片免费领取~

以上是关于Android AccessibilityService 事件分发原理的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )

Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )

android 21 是啥版本

Android逆向-Android基础逆向(2-2)

【Android笔记】android Toast

图解Android - Android核心机制