目录
前言:
在了解了无障碍服务基础使用之后,我们来探究一下 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
中接收事件。