Android AccessibilityService 事件分发原理分析总结
作者: 自动化BUG制造器
前言:
在了解了无障碍服务基础使用之后,我们来探究一下 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
为例,介绍一下调用流程。
onAccessibilityEvent
在 AccessibilityService
中,AccessibilityService
在 onBind
生命周期中,返回了一个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 。 在主线程中执行它的内部类 Callback
的 executeMessage
方法。
而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
方法的实现中,调用了AccessibilityService
的 onAccessibilityEvent
方法:
@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
的引用栈,发现有很多地方都用到了这个类,关于无障碍的重点看 AccessibilityManagerService
和 AccessibilityManager
这一套逻辑。 从名称上看,无障碍功能提供了类似 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,并负责在服务管理的数据结构中添加/删除服务。 该类还公开了配置接口,该接口在绑定后立即传递给它所代表的服务。 它还用作服务的连接。
AccessibilityServiceConnection
与UiAutomationService
一样,继承自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 事件分发原理分析总结的文章就介绍到这了,更多相关Android AccessibilityService 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!