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
为例,介绍一下调用流程。
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核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。
需要的直接点击文末小卡片可以领取哦我免费分享给你,以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持,需要的自己领取)
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 用户权限 )