产品文档 前端技术 后端技术 编程语言 数据库 人工智能 大数据云计算 运维技术 操作系统 数据结构与算法 Java C++语言 Python PHP

关于clientversion的信息

首页>>技术文档>>Java

  本文作者

  作者:张朝旭

  链接:

  https://juejin.im/post/5b27bfc56fb9a00e373bd232

  本文由作者授权发布。

  1

  本文需要解决的问题

之前本人做了一个项目clientversion,需要用到AccessibilityService这个系统提供的拓展服务。这个服务本意是作为Android系统的一个辅助功能clientversion,去帮助残疾人更好地使用手机。但是由于它的一些特性clientversion,给很多项目的实现提供了一个新的思路,例如之前大名鼎鼎的微信抢红包插件,本质上就是使用了这个服务。我研究AccessibilityService的目的是解决以下几个我在使用过程中所思考的问题:

AccessibilityService这个Service跟一般的Service有什么区别clientversion

AccessibilityService是如何做到监控并捕捉用户行为的?

AccessibilityService是如何做到查找控件,执行点击等操作的?

  2

  初步分析

本文基于Android 7.1的源码对AccessibilityService进行分析。为了更好地理解和分析代码,我写了一个demo,如果想学习具体的使用方法,可以参考Google官方文档AccessibilityService。本文不做AccessibilityService的具体使用教程。

  创建AccessibilityService

  publicclassMyAccessibilityServiceextendsAccessibilityService{

  privatestaticfinalString TAG = "MyAccessibilityService";

  @Override

  publicvoidonCreate(){

  super.onCreate();

  Log.i(TAG, "onCreate");

  }

  @Override

  publicvoidonAccessibilityEvent(AccessibilityEvent event){

  inteventType = event.getEventType();

  switch(eventType) {

  caseAccessibilityEvent.TYPE_VIEW_CLICKED:

  // 捕获到点击事件

  Log.i(TAG, "capture click event!");

  AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();

  if(nodeInfo != null) {

  // 查找text为Test!的控件

  List<AccessibilityNodeInfo> button = nodeInfo.findAccessibilityNodeInfosByText( "Test!");

  nodeInfo.recycle();

  for(AccessibilityNodeInfo item : button) {

  Log.i(TAG, "long-click button!");

  // 执行长按操作

  item.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);

  }

  }

  break;

  default:

  break;

  }

  }

  @Override

  publicvoidonInterrupt(){

  Log.i(TAG, "onInterrupt");

  }

  }

  可以看到,当我们捕获到click 事件的时候,会将其换成longclick 事件。

  AccessibilityService配置

res/xml/accessibility_service_config.xml

  <?xml version="1.0" encoding="utf-8"?>

  <accessibility-servicexmlns:android="https://schemas.android.com/apk/res/android"

  android:accessibilityEventTypes="typeAllMask"

  android:accessibilityFeedbackType="feedbackSpoken"

  android:accessibilityFlags="flagRetrieveInteractiveWindows|flagRequestFilterKeyEvents"

  android:canRequestFilterKeyEvents="true"

  android:canRetrieveWindowContent="true"

  android:deion="@string/app_name"

  android:notificationTimeout="100"

  android:packageNames="com.xu.accessibilitydemo"/>

在manifest中进行注册

  <service

  android:name=".MyAccessibilityService"

  android:enabled="true"

  android:exported="true"

  android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">

  <intent-filter>

  <actionandroid:name="android.accessibilityservice.AccessibilityService"/>

  </intent-filter>

  <meta-data

  android:name="android.accessibilityservice"

  android:resource="@xml/accessibility_service_config"/>

  </service>

创建一个text为Test!的button控件,设置监听方法:

  publicclassMainActivityextendsAppCompatActivity{

  privatestaticfinalString TAG = "MainActivity";

  privateButton button;

  @Override

  protectedvoidonCreate(Bundle savedInstanceState){

  super.onCreate(savedInstanceState);

  setContentView(R.layout.activity_main);

  button = findViewById(R.id.button);

  button.setOnLongClickListener( newView.OnLongClickListener() {

  @Override

  publicbooleanonLongClick(View v){

  Log.i(TAG, "onLongClick");

  returnfalse;

  }

  });

  }

  }

开启AccessibilityService

开启AccessibilityService有两种方法,一种是在代码中开启,另一种是手动开启,具体开启位置为设置--无障碍中开启。

  运行应用,点击text为Test!的按钮

会出现以下的日志:

  

具体解释:点击按钮即产生TYPE_VIEW_CLICKED事件 --> 被AcceesibilityService捕获 --> 捕获后执行长按按钮操作 --> 执行长按回调方法。

为什么AcceesibilityService能捕获并执行其clientversion他操作呢,接下来我将对源码进行解析~

  3

  源码解析

  3.1 AccessibilityService内部逻辑

  AccessibilityService.java

  publicabstractclassAccessibilityServiceextendsService{

  // 省略代码

  publicabstractvoidonAccessibilityEvent(AccessibilityEvent event);

  publicabstractvoidonInterrupt();

  @Override

  publicfinalIBinder onBind(Intent intent){

  returnnewIAccessibilityServiceClientWrapper( this, getMainLooper(), newCallbacks() {

  @Override

  publicvoidonServiceConnected(){

  AccessibilityService. this.dispatchServiceConnected();

  }

  @Override

  publicvoidonInterrupt(){

  AccessibilityService. this.onInterrupt();

  }

  @Override

  publicvoidonAccessibilityEvent(AccessibilityEvent event){

  AccessibilityService. this.onAccessibilityEvent(event);

  }

  @Override

  publicvoidinit(intconnectionId, IBinder windowToken){

  mConnectionId = connectionId;

  mWindowToken = windowToken;

  // The client may have already obtained the window manager, so

  // update the default token on whatever manager we gave them.

关于clientversion的信息

  finalWindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);

  wm.setDefaultToken(windowToken);

  }

  @Override

  publicbooleanonGesture(intgestureId){

  returnAccessibilityService. this.onGesture(gestureId);

  }

  @Override

  publicbooleanonKeyEvent(KeyEvent event){

  returnAccessibilityService. this.onKeyEvent(event);

  }

  @Override

  publicvoidonMagnificationChanged(@NonNull Region region,

  floatscale, floatcenterX, floatcenterY){

  AccessibilityService. this.onMagnificationChanged(region, scale, centerX, centerY);

  }

  @Override

  publicvoidonSoftKeyboardShowModeChanged(intshowMode){

  AccessibilityService. this.onSoftKeyboardShowModeChanged(showMode);

  }

  @Override

  publicvoidonPerformGestureResult(intsequence, booleancompletedSuccessfully){

  AccessibilityService. this.onPerformGestureResult(sequence, completedSuccessfully);

  }

  });

  }

  }

  分析:

AccessibilityService是一个抽象类,继承于Service,提供两个抽象方法 onAccessibilityEvent() 和 onInterrupt();

虽然是抽象类,但是实现了最重要的 onBind() 方法,在其中创建了一个IAccessibilityServiceClientWrapper对象,实现Callbacks接口中的抽象方法。

  IAccessibilityServiceClientWrapper

  // 以分析onAccessibilityEvent为例,省略部分代码

  publicstaticclassIAccessibilityServiceClientWrapperextendsIAccessibilityServiceClient.Stub

  implementsHandlerCaller.Callback{

  privatefinalHandlerCaller mCaller;

  privatefinalCallbacks mCallback;

  privateintmConnectionId;

  publicIAccessibilityServiceClientWrapper(Context context, Looper looper,

  Callbacks callback){

  mCallback = callback;

  mCaller = newHandlerCaller(context, looper, this, true/*asyncHandler*/);

  }

  publicvoidinit(IAccessibilityServiceConnection connection, intconnectionId,

  IBinder windowToken){

  Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,

  connection, windowToken);

  mCaller.sendMessage(message);

  }

  // 省略部分代码

  publicvoidonAccessibilityEvent(AccessibilityEvent event){

  Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event);

  mCaller.sendMessage(message);

  }

  @Override

  publicvoidexecuteMessage(Message message){

  switch(message.what) {

  caseDO_ON_ACCESSIBILITY_EVENT: {

  AccessibilityEvent event = (AccessibilityEvent) message.obj;

  if(event != null) {

  AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);

  mCallback.onAccessibilityEvent(event);

  // Make sure the event is recycled.

  try{

  event.recycle();

  } catch(IllegalStateException ise) {

  /* ignore - best effort */

  }

  }

  } return;

  // ...

  }

  }

  }

  分析:

1. IAccessibilityServiceClientWrapper继承于IAccessibilityServiceClient类,它是一个aidl接口,同时注意到它是继承于IAccessibilityServiceClient.Stub类,可以大概猜测到,AccessibilityService为一个远程Service,使用到跨进程通信技术,后面我还会继续分析这个;

2. IAccessibilityServiceClientWrapper的类构造方法中,有两个比较重要的参数,一个是looper,另一个是Callbacks callback。Looper不用说,而Callbacks接口定义了很多方法,代码如下:

  publicinterfaceCallbacks{

  publicvoidonAccessibilityEvent(AccessibilityEvent event);

  publicvoidonInterrupt();

  publicvoidonServiceConnected();

  publicvoidinit(intconnectionId, IBinder windowToken);

  publicbooleanonGesture(intgestureId);

  publicbooleanonKeyEvent(KeyEvent event);

  publicvoidonMagnificationChanged(@NonNull Region region,

  floatscale, floatcenterX, floatcenterY);

  publicvoidonSoftKeyboardShowModeChanged(intshowMode);

  publicvoidonPerformGestureResult(intsequence, booleancompletedSuccessfully);

  }

3. IAccessibilityServiceClientWrapper同时也实现了HandlerCaller.Callback接口,HandlerCaller类通过命名也可以知道,它内部含有一个Handler实例,所以可以把它当做一个Handler,而处理信息的方法就是HandlerCaller.Callback#executeMessage(msg)方法

4. 代码有点绕,故简单总结一下流程:

  AccessibilityEvent产生

  -> Binder驱动

  ->IAccessibilityServiceClientWrapper #onAccessibilityEvent(AccessibilityEvent)

  -> HandlerCaller #sendMessage(message); // message中包括AccessibilityEvent

  -> IAccessibilityServiceClientWrapper #executeMessage();

  -> Callbacks #onAccessibilityEvent(event);

  -> AccessibilityService.this.onAccessibilityEvent(event);

到这里解决了我们的第一个问题:AccessibilityService同样继承于Service类,它属于远程服务类,是Android系统提供的一种服务,可以绑定此服务,用于捕捉界面的一些特定事件。

  3.2 AccessibilityService外部逻辑

前面分析了接收到AccessibilityEvent之后的代码逻辑,那么,这些AccessibilityEvent是怎样产生的呢,而且,在回调执行之后是怎么做到点击等操作的(如demo所示)?我们接下来继续分析相关的源码~

我们从demo作为例子开始入手,首先我们知道,一个点击事件的产生,实际代码逻辑是在View#onTouchEvent() -> View#performClick()中:

  publicbooleanperformClick(){

  finalbooleanresult;

  finalListenerInfo li = mListenerInfo;

  if(li != null&& li.mOnClickListener != null) {

  playSoundEffect(SoundEffectConstants.CLICK);

  li.mOnClickListener.onClick( this);

  result = true;

  } else{

  result = false;

  }

  // !!!

  sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

  returnresult;

  }

这里找到一个重点方法sendAccessibilityEvent(),继续跟进去,最后走到View#sendAccessibilityEventUncheckedInternal()方法:

  publicvoidsendAccessibilityEventUncheckedInternal(AccessibilityEvent event){

  // 省略一堆代码

  // In the beginning we called #isShown(), so we know that getParent() is not null.

  getParent().requestSendAccessibilityEvent( this, event);

  }

这里的getParent()会返回一个实现ViewParent接口的对象。我们可以简单理解为,它会让View的父类执行requestSendAccessibilityEvent()方法,而View的父类一般为ViewGroup,我们查看ViewGroup#requestSendAccessibilityEvent()方法:

  @Override

  publicbooleanrequestSendAccessibilityEvent(View child, AccessibilityEvent event){

  ViewParent parent = mParent;

  // 省略一堆代码

  returnparent.requestSendAccessibilityEvent(this, event);

  }

这里涉及到一个变量mParent,我们要找到这个mParent变量是在哪里被赋值的。首先我们在View类中找到一个相关的方法View#assignParent():

  voidassignParent(ViewParent parent){

  if(mParent == null) {

  mParent = parent;

  } elseif(parent == null) {

  mParent = null;

  } else{

  thrownewRuntimeException( "view "+ this+ " being added, but"+ " it already has a parent");

  }

  }

但是View类中并没有调用此方法,猜测是View的父类进行调用。通过对源码进行搜索,发现最后是在ViewRootImpl#setView()中进行调用,赋值的是this即ViewRootImpl本身。直接跳到ViewRootImpl#requestSendAccessibilityEvent()方法:

  @Override

  publicbooleanrequestSendAccessibilityEvent(View child, AccessibilityEvent event){

  // ... 省略一堆堆代码

关于clientversion的信息

  // !!!

  mAccessibilityManager.sendAccessibilityEvent(event);

  returntrue;

  }

重点:

AccessibilityManager#sendAccessibilityEvent(event)

  publicvoidsendAccessibilityEvent(AccessibilityEvent event){

  finalIAccessibilityManager service;

  finalintuserId;

  synchronized(mLock) {

  service = getServiceLocked();

  userId = mUserId;

  }

  booleandoRecycle = false;

  try{

  event.setEventTime(SystemClock.uptimeMillis());

  // !!!

  doRecycle = service.sendAccessibilityEvent(event, userId);

  Binder.restoreCallingIdentity(identityToken);

  } catch(RemoteException re) {

  Log.e(LOG_TAG, "Error during sending "+ event + " ", re);

  }

  }

  privateIAccessibilityManager getServiceLocked(){

  if(mService == null) {

  tryConnectToServiceLocked( null);

  }

  returnmService;

  }

  privatevoidtryConnectToServiceLocked(IAccessibilityManager service){

  if(service == null) {

  IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);

  if(iBinder == null) {

  return;

  }

  service = IAccessibilityManager.Stub.asInterface(iBinder);

  }

  try{

  finalintstateFlags = service.addClient(mClient, mUserId);

  setStateLocked(stateFlags);

  mService = service;

  } catch(RemoteException re) {

  Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);

  }

  }

这里有使用到Android Binder机制,看到 Stub.asInterface就了解到这是 Binder 的客户端了,重点为IAccessibilityManager#sendAccessibilityEvent()方法,这里调用的是代理方法,实际代码逻辑在AccessibilityManagerService#sendAccessibilityEvent():

  @Override

  publicbooleansendAccessibilityEvent(AccessibilityEvent event, intuserId){

  synchronized(mLock) {

  if(mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {

  mSecurityPolicy.updateActiveAndAccessibilityFocusedWindowLocked(event.getWindowId(), event.getSourceNodeId(), event.getEventType(), event.getAction());

  mSecurityPolicy.updateEventSourceLocked(event);

  // !!!

  notifyAccessibilityServicesDelayedLocked(event, false);

  notifyAccessibilityServicesDelayedLocked(event, true);

  }

  event.recycle();

  }

  return(OWN_PROCESS_ID != Binder.getCallingPid());

  }

  privatevoidnotifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, booleanisDefault){

  try{

  UserState state = getCurrentUserStateLocked();

  for( inti = 0, count = state.mBoundServices.size(); i < count; i++) {

  Service service = state.mBoundServices.get(i);

  if(service.mIsDefault == isDefault) {

  if(canDispatchEventToServiceLocked(service, event)) {

  service.notifyAccessibilityEvent(event);

  }

  }

  }

  } catch(IndexOutOfBoundsException oobe) {

  // An out of bounds exception can happen if services are going away

  // as the for loop is running. If that happens, just bail because

  // there are no more services to notify.

  }

  }

1.在方法中,最后会调用notifyAccessibilityServicesDelayedLocked()方法,然后将event进行回收;

2. 在notifyAccessibilityServicesDelayedLocked()方法中,会获得所有Bound即绑定的Service,执行notifyAccessibilityEvent()方法,通过跟踪代码逻辑,最后会调用绑定Service的onAccessibilityEvent()方法。绑定的Service是指我们自己实现的继承于AccessibilityService的Service类,当你在设置-无障碍中开启服务之后即将服务绑定到AccessibilityManagerService中。

这样我们解决了第二个问题:AccessibilityService是如何做到监控捕捉用户行为的:(以点击事件为例)

AccessibilityEvent产生:

  View #performClick()

  -> View #sendAccessibilityEventUncheckedInternal()

  -> ViewGroup #requestSendAccessibilityEvent()

  -> ViewRootImpl #requestSendAccessibilityEvent()

  -> AccessibilityManager #sendAccessibilityEvent(event)

  -> AccessibilityManagerService #sendAccessibilityEvent()

  -> AccessibilityManagerService #notifyAccessibilityServicesDelayedLocked()

  -> Service #notifyAccessibilityEvent(event)

AccessibilityEvent处理:

  AccessibilityEvent

  -> Binder驱动

  -> IAccessibilityServiceClientWrapper #onAccessibilityEvent(AccessibilityEvent)

  -> HandlerCaller #sendMessage(message); // message中包括AccessibilityEvent

  -> IAccessibilityServiceClientWrapper #executeMessage();

  -> Callbacks #onAccessibilityEvent(event);

  -> AccessibilityService.this.onAccessibilityEvent(event);

作者原文还有分析如何查找控件以及一些有用代码记录,不过由于文章篇幅限制,可以通过阅读原文查看。

文章代码有点多,以下由鸿洋我来给大家总结下:

当有人问你 AccessibilityService 的原理时?

其实也就是一个 Service 的子类,就像我们平时 bindService 一样,其核心代码就是复写 onBind方法,返回一个Binder对象,其实和我们平时写aidl 很类似,返回的是 IAccessibilityServiceClient.Stub 对象。

记住我们启动了一个远程 Service,等着客户端 bindService。

而当点击我们的 View 的时候,拿到 Event,辗转通过 ServiceManager 拿到AccessibilityManagerService 的代理对象与AccessibilityManagerService交互,而其内部维护的 UserState.mBoundServices为内部类 Service继承自ServiceConnection,内部包含 bindService(accessibilityService)代码,并在 onServiceConnected中通过IAccessibilityServiceClient.Stub.asInterface,这样就看到客户端binder 对象了,这样就可以和我们的服务端binder 交互了。

简言之,你可以认为本质上就是 bindService通信!

核心代码都在AccessibilityManagerService中。

上一篇: 知乎搬运工网站,知乎 搬家

下一篇: win7破解版,win7破解版系统