从源码角度看AccessibilityService

简介

AccessibilityService的设计初衷是为了辅助有身体缺陷的群体使用Android应用,它的设计贯穿着Android的控件树View, ViewGroup, ViewRootImpl体系。借助于system_server进程的中转,能够注册Accessibility事件的客户端可以具备通过system_server提供的Accessibility服务来实现监听、操作其它应用视图的功能。这个功能十分强大,可以模拟用户的行为去操作其它APP,常常被用在自动化测试、微信抢红包、自动回复等功能实现中。

写这个的初衷有二:

  1. 之前已经完成了Android View控件树的绘制、事件分发的源码分析,知识储备足够
  2. 最近接触到了一些自动化方面的项目,并且对使用无障碍服务实现的自动微信抢红包功能原理十分好奇

整体图

类图

  • AccessibilityService: APP端直接继承的类,本质上是Service,通过onBind获取匿名Binder对象实现通信
  • IAccessibilityServiceClientWrapper: 用于和system_server通信的匿名Binder服务
  • AccessibilityInteractionClient: 本质上是个binder服务,用于获取Node信息
  • AccessibilityManagerService: 运行在system_server的实名binder服务,是整体的管理类
  • Service: AccessibilityManagerService的内部类,用于响应AccessibilityInteractionClient的binder通信请求
  • AccessibilityInteractionConnection: 运行在被监测的APP端,提供查找、点击视图等服务
  • AccessibilityManager: 运行在各个APP端,用于发送视图变化事件
  • AccessibilityInteractionController: 具体视图查找、点击服务的中间控制器
  • AccessibilityNodeProvider: 由客户端实现的视图节点内容提供者,最终操作的实现者

整体设计图

实例代码

public class AutoDismissService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (event == null) {
            return;
        }

        // 自动将android系统弹出的其它crash dialog取消
        dismissAppErrorDialogIfExists(event);
    }

    private void dismissAppErrorDialogIfExists(AccessibilityEvent event) {
        // WINDOW视图变化才进行对应操作
        if ((event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
                && event.getPackageName().equals("android")) {
          // 查找带有"OK"字符的可点击Node
          AccessibilityNodeInfo nodeInfo = findViewByText("OK", true);
         if (nodeInfo != null) {
               // 查找到后执行点击操作
            performViewClick(nodeInfo);
          }
    }

    public AccessibilityNodeInfo findViewByText(String text, boolean clickable) {
         // 获取当前窗口父节点
        AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
        if (accessibilityNodeInfo == null) {
            return null;
        }
        // 获取到满足字符要求的节点
        List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text);
        if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
            for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
                if (nodeInfo != null && (nodeInfo.isClickable() == clickable)) {
                    return nodeInfo;
                }
            }
        }
        return null;
    }

    public void performViewClick(AccessibilityNodeInfo nodeInfo) {
        if (nodeInfo == null) {
            return;
        }
        // 由下至上进行查询,直到寻找到可点击的节点
        while (nodeInfo != null) {
            if (nodeInfo.isClickable()) {
                nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                break;
            }
            nodeInfo = nodeInfo.getParent();
        }
    }
}

以上是一个典型的实现Accessibility功能的JAVA代码,主要涉及三点功能:

  1. 当系统中有应用视图变化后,onAccessibilityEvent 方法会自动被system_server调用
  2. 通过AccessibilityService的getRootInActiveWindowfindAccessibilityNodeInfosByText方法,可以获取到节点信息
  3. 通过AccessibilityNodeInfo的performAction方法,最终会在被监听APP中执行对应操作

本篇文章将会围绕着这三点主要功能进行源码分析

源码分析

常见 AccessibilityEvent 事件种类

序号 种类名称 触发时机
1 TYPE_VIEW_CLICKED 可点击的组件被点击
2 TYPE_VIEW_LONG_CLICKED 可点击的组件被长按
3 TYPE_VIEW_SELECTED 组件被选中
4 TYPE_VIEW_FOCUSED 组件获取到了焦点
5 TYPE_VIEW_TEXT_CHANGED 组件中的文本发生变化
6 TYPE_VIEW_SCROLLED 组件被滑动
7 TYPE_WINDOW_STATE_CHANGED dialog等被打开
8 TYPE_NOTIFICATION_STATE_CHANGED 通知弹出
9 TYPE_WINDOW_CONTENT_CHANGED 组件树发生了变化

onAccessibilityEvent 触发流程

这里以TextView.setText触发事件变化流程为例进行分析

TextView.setText

应用组件状态发生变化

frameworks/base/core/java/android/widget/TextView.java

private void setText(CharSequence text, BufferType type,
                     boolean notifyBefore, int oldlen) {
    ...
    notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
    ...                     
}

public void notifyViewAccessibilityStateChangedIfNeeded(int changeType) {
    if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
        return;
    }
    if (mSendViewStateChangedAccessibilityEvent == null) {
         // 本质上是一个Runnable,意味着这里的流程会进入异步处理
        mSendViewStateChangedAccessibilityEvent =
                new SendViewStateChangedAccessibilityEvent();
    }
    mSendViewStateChangedAccessibilityEvent.runOrPost(changeType);
}

private class SendViewStateChangedAccessibilityEvent implements Runnable {
    ...

    @Override
    public void run() {
        mPosted = false;
        mPostedWithDelay = false;
        mLastEventTimeMillis = SystemClock.uptimeMillis();
        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
            final AccessibilityEvent event = AccessibilityEvent.obtain();
            event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
            event.setContentChangeTypes(mChangeTypes);
            // 最终TYPE_WINDOW_CONTENT_CHANGED事件在这里异步发送
            sendAccessibilityEventUnchecked(event);
        }
        mChangeTypes = 0;
    }
    ...
}

public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
    if (mAccessibilityDelegate != null) {
        mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event);
    } else {
        sendAccessibilityEventUncheckedInternal(event);
    }
}

public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
    host.sendAccessibilityEventUncheckedInternal(event);
}

public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) {
    if (!isShown()) {
        return;
    }
    ...
    // 此处交由TextView所在父View进行处理,为责任链模式,事件经过层层向上传递,最终交由ViewRootImpl进行处理
    ViewParent parent = getParent();
    if (parent != null) {
        getParent().requestSendAccessibilityEvent(this, event);
    }
}

ViewRootImpl.requestSendAccessibilityEvent

ViewRootImpl将事件派发到system_server

frameworks/base/core/java/android/view/ViewRootImpl.java

@Override
public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
    ...
    // 本地调用到AccessibilityManager进行事件发送
    mAccessibilityManager.sendAccessibilityEvent(event);
   return true;
}

frameworks/base/core/java/android/view/accessibility/AccessibilityManager.java

 public void sendAccessibilityEvent(AccessibilityEvent event) {
      final IAccessibilityManager service;
    final int userId;
    synchronized (mLock) {
         // 获取system_server的Accessibility实名服务
        service = getServiceLocked();
        ...
    }

    try {
        ...
        long identityToken = Binder.clearCallingIdentity();
        // binder call 到服务端,进行事件分发中转
        doRecycle = service.sendAccessibilityEvent(event, userId);
        Binder.restoreCallingIdentity(identityToken);
          ...
    } catch (RemoteException re) {
        Log.e(LOG_TAG, "Error during sending " + event + " ", re);
    } finally {
        ...
    }
 }

AccessibilityManagerService.sendAccessibilityEvent

system_server将事件分发到各个监听组件变化的Service

frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java

```java
// binder call 到服务端,触发事件派发
@Override
public boolean sendAccessibilityEvent(AccessibilityEvent event, int userId) {
synchronized (mLock) {
...
if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {
...
notifyAccessibilityServicesDelayedLocked(event, false);
notifyAccessibilityServicesDelayedLocked(event, true);
}
...
}
return (OWN_PROCESS_ID != Binder.getCallingPid());
}

private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
boolean isDefault) {
try {
UserState state = getCurrentUserStateLocked();
for (int i = 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) {
    ...
}

}

class Service extends IAccessibilityServiceConnection.Stub
implements ServiceConnection, DeathRecipient {
public void notifyAccessibilityEvent(AccessibilityEvent event) {
synchronized (mLock) {
...
if ((mNotificationTimeout > 0)
&& (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)) {
...
// 按照惯例,异步分发到客户端进行派发
message = mEventDispatchHandler.obtainMessage(eventType);
} else {
message = mEventDispatchHandler.obtainMessage(eventType, newEvent);
}

        mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout);
    }
}     

}

public Handler mEventDispatchHandler = new Handler(mMainHandler.getLooper()) {
@Override
public void handleMessage(Message message) {
final int eventType = message.what;
AccessibilityEvent event = (AccessibilityEvent) message.obj;
notifyAccessibilityEventInternal(eventType, event);
}
};

private void notifyAccessibilityEventInternal(int eventType, AccessibilityEvent event) {
IAccessibilityServiceClient listener;
...
// mServiceInterface是通过bind客户端的AccessibilityService,在onServiceConnected连接成功后,获取到binder proxy转化来的,以这种方式实现了system_server与客户端的通信
listener = mServiceInterface;
...
try {

top Created with Sketch.