android accessibilityservice 事件分发原理分析总结-kb88凯时官网登录

来自:网络
时间:2022-12-26
阅读:
免费资源网 - https://freexyz.cn/
目录

前言:

在了解了无障碍服务基础使用之后,我们来探究一下 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,并负责在服务管理的数据结构中添加/删除服务。 该类还公开了配置接口,该接口在绑定后立即传递给它所代表的服务。 它还用作服务的连接。

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中接收事件。

免费资源网 - https://freexyz.cn/
返回顶部
顶部
网站地图