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, thistrue);
    }

    @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 的引用:

Android AccessibilityService 事件分发原理
AD40F11D-24B2-4F00-9250-2AC887D8B20B.png

查看 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 == nullreturn;
            event.setEventTime(SystemClock.uptimeMillis());
            if (event.getAction() == 0) {
                event.setAction(mPerformingAction);
            }
            if (mAccessibilityPolicy != null) {
                dispatchedEvent = mAccessibilityPolicy.onAccessibilityEvent(event, mIsEnabled, mRelevantEventTypes);
                if (dispatchedEvent == nullreturn;
            } 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(nullnull);
                }
            } break;
            case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
                handleWindowContentChangedEvent(event);
            } break;
        }
        mAccessibilityManager.sendAccessibilityEvent(event);
        return true;
    }

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

Android AccessibilityService 事件分发原理
5566C7DD-3DD6-4714-AB4F-5ADAF5657EA3.png

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

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

它在 View 中的调用:

Android AccessibilityService 事件分发原理
2ABB5F0D-E1BA-4370-83EA-BD3458AFD806.png

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

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

Android AccessibilityService 事件分发原理
B4D472AE-0236-4D69-BAA7-E1805584663F.png

可以看出在 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 == nullreturn;
        // 我们有两种通知事件的方式,节流和非节流。 如果我们不进行节流,那么消息会随事件一起出现,我们会毫不费力地处理这些事件。
        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.bmabk.com/index.php/post/60181.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!