目录
android底层Input事件传递流程一文从源码角度介绍了android底层输入事件的来源与分发,那么本文就专门介绍下无障碍服务的事件来源与分发。本文基于android9.0源码进行分析,从AccessibilityService绑定到系统服务以及系统服务发送事件消息给AccessibilityService两个过程来分析。
1、服务绑定
在无障碍服务和悬浮框一文中介绍了应用层实现无障碍服务的一个例子。我们知道应用层要实现无障碍服务首先要创建一个AccessibilityService的子类,并在这个子类中实现抽象方法就可以实现见监听、判断、操作系统事件的功能了。 AccessibilityService是一个可用绑定的服务,它的onBind方法在被绑定时调用,看下具体实现,返回了一个IAccessibilityServiceClientWrapper匿名内部类,该内部类提供了外部可用绑定调用的无障碍服务的方法aidl方法。
/frameworks/base/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
/**
* Top-level interface to an accessibility service component.
*
* @hide
*/
oneway interface IAccessibilityServiceClient {
void init(in IAccessibilityServiceConnection connection, int connectionId, IBinder windowToken);
void onAccessibilityEvent(in AccessibilityEvent event, in boolean serviceWantsEvent);
void onInterrupt();
void onGesture(int gesture);
void clearAccessibilityCache();
void onKeyEvent(in KeyEvent event, int sequence);
void onMagnificationChanged(in Region region, float scale, float centerX, float centerY);
void onSoftKeyboardShowModeChanged(int showMode);
void onPerformGestureResult(int sequence, boolean completedSuccessfully);
void onFingerprintCapturingGesturesChanged(boolean capturing);
void onFingerprintGesture(int gesture);
void onAccessibilityButtonClicked();
void onAccessibilityButtonAvailabilityChanged(boolean available);
}
服务绑定成功后外部可以调用该匿名内部类aidl方法,执行相关操作后最终通过Callbacks访问无障碍服务的相关方法。
//AccessibilityService.java
/**
* Implement to return the implementation of the internal accessibility
* service interface.
*/
@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);
}
@Override
public void init(int connectionId, 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.
final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);
wm.setDefaultToken(windowToken);
}
@Override
public boolean onGesture(int gestureId) {
return AccessibilityService.this.onGesture(gestureId);
}
@Override
public boolean onKeyEvent(KeyEvent event) {
return AccessibilityService.this.onKeyEvent(event);
}
@Override
public void onMagnificationChanged(int displayId, @NonNull Region region,
float scale, float centerX, float centerY) {
AccessibilityService.this.onMagnificationChanged(displayId, region, scale,
centerX, centerY);
}
@Override
public void onSoftKeyboardShowModeChanged(int showMode) {
AccessibilityService.this.onSoftKeyboardShowModeChanged(showMode);
}
@Override
public void onPerformGestureResult(int sequence, boolean completedSuccessfully) {
AccessibilityService.this.onPerformGestureResult(sequence, completedSuccessfully);
}
@Override
public void onFingerprintCapturingGesturesChanged(boolean active) {
AccessibilityService.this.onFingerprintCapturingGesturesChanged(active);
}
@Override
public void onFingerprintGesture(int gesture) {
AccessibilityService.this.onFingerprintGesture(gesture);
}
@Override
public void onAccessibilityButtonClicked() {
AccessibilityService.this.onAccessibilityButtonClicked();
}
@Override
public void onAccessibilityButtonAvailabilityChanged(boolean available) {
AccessibilityService.this.onAccessibilityButtonAvailabilityChanged(available);
}
});
}
系统事件怎么通知给应用层的AccessibilityService子类呢?这就需要让它们产生联系,先分析下AccessibilityService绑定到系统服务的过程吧。
AccessibilityService对应的系统服务是AccessibilityManagerService,AccessibilityManagerService在SystemServer.java中启动。
//SystemServer.java
private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS =
"com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
……
private void startOtherServices() {
……
traceBeginAndSlog("StartAccessibilityManagerService");
try {
mSystemServiceManager.startService(ACCESSIBILITY_MANAGER_SERVICE_CLASS); //Lifecycle是AccessibilityManagerService的静态内部类,启动Lifecycle
} catch (Throwable e) {
reportWtf("starting Accessibility Manager", e);
}
traceEnd();
……
}
这里首先启动的是AccessibilityManagerService的静态内部类Lifecycle,看下Lifecycle的实现,Lifecycle主要做了两件事,首先创建了AccessibilityManagerService实例,其次,在onStart方法中调用publishBinderService方法,publishBinderService方法具体实现在/frameworks/base/services/core/java/com/android/server/SystemService.java中,publishBinderService方法的作用就是将系统服务添加到ServiceManager中,ServiceManager类似网络路由器作用,系统服务一般都会注册到ServiceManager中,后续需要的时候就从ServiceManager中取就行,那么对于本文就是方便后续系统以及应用通过ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)找到AccessibilityManagerService。
//AccessibilityManagerService.java
public static final class Lifecycle extends SystemService {
private final AccessibilityManagerService mService;
public Lifecycle(Context context) {
super(context);
mService = new AccessibilityManagerService(context); //创建AccessibilityManagerService实例
}
@Override
public void onStart() {
publishBinderService(Context.ACCESSIBILITY_SERVICE, mService);//将AccessibilityManagerService添加到路由器ServiceManager中
}
@Override
public void onBootPhase(int phase) {
mService.onBootPhase(phase);
}
}
///frameworks/base/services/core/java/com/android/server/SystemService.java
protected final void publishBinderService(String name, IBinder service,
boolean allowIsolated, int dumpPriority) {
ServiceManager.addService(name, service, allowIsolated, dumpPriority);
}
再看下AccessibilityManagerService的构造函数做了什么,构造函数中创建了包管理、窗口管理、用户管理等成员变量,最后两行方法都是通过观察着模式注册了对于某些事件变化的通知,这两个方法比较重要。registerBroadcastReceivers()注册了应用变化(卸载、更新、安装、强行停止)的监听以及切换用户、上锁、解锁等操作的监听,AccessibilityContentObserver注册了contentResolver相关数据被识别的监听。
//AccessibilityManagerService.java
public AccessibilityManagerService(Context context) {
mContext = context;
mPackageManager = mContext.getPackageManager();
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mSecurityPolicy = new SecurityPolicy();
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mMainHandler = new MainHandler(mContext.getMainLooper());
mGlobalActionPerformer = new GlobalActionPerformer(mContext, mWindowManagerService);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
registerBroadcastReceivers();
new AccessibilityContentObserver(mMainHandler).register(
context.getContentResolver());
}
这里我们简单说下应用变化的监听吧,当通过PackageMonitor监听到应用有变化时先去获取当前用户下的UserState,然后判断UserState 是否变化如果变化就去更新数据。
//AccessibilityManagerService.java
private void registerBroadcastReceivers() {
PackageMonitor monitor = new PackageMonitor() {
@Override
public void onSomePackagesChanged() { //应用有变化
synchronized (mLock) {
// Only the profile parent can install accessibility services.
// Therefore we ignore packages from linked profiles.
if (getChangingUserId() != mCurrentUserId) {
return;
}
// We will update when the automation service dies.
UserState userState = getCurrentUserStateLocked(); //所有无障碍服务都保存在UserState中
// We have to reload the installed services since some services may
// have different attributes, resolve info (does not support equals),
// etc. Remove them then to force reload.
userState.mInstalledServices.clear(); //先清除已安装的无障碍的服务,后面再强制加载
if (readConfigurationForUserStateLocked(userState)) { //UserState是否有变化
onUserStateChangedLocked(userState); //UserState有变化就需要更新数据
}
}
}
……
}
onUserStateChangedLocked(userState)方法经常用到,只要有相关界面以及配置的变化都会调用更新数据。UserState是AccessibilityManagerService的内部类,里面包含了系统中无障碍服务相关信息,其中有三个变量经常使用。
//AccessibilityManagerService$UserState
public final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>();//已经绑定的无障碍服务
public final List<AccessibilityServiceInfo> mInstalledServices =
new ArrayList<>(); //系统上安装的无障碍服务
private final Set<ComponentName> mBindingServices = new HashSet<>(); //绑定断开以及绑定中的无障碍服务
public final Set<ComponentName> mEnabledServices = new HashSet<>(); //可用的无障碍服务
……
/**
* Make sure a services disconnected but still 'on' state is reflected in UserState
* There are three states to a service here: off, bound, and binding.
* This drops a service from a bound state, to the binding state.
* The binding state describes the situation where a service is on, but not bound.
*
* @param serviceConnection The service.
*/
public void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) {
removeServiceLocked(serviceConnection);
mBindingServices.add(serviceConnection.getComponentName());
}
onUserStateChangedLocked(userState)方法中主要看下updateServicesLocked方法,updateServicesLocked会遍历所有已安装的无障碍服务,过滤掉上锁情况以及绑定断开的服务,从可用的无障碍服务中挑选出来并进行绑定,具体的绑定操作需要看下service.bindLocked()。
//AccessibilityManagerService.java
/**
* Called when any property of the user state has changed.
*
* @param userState the new user state
*/
private void onUserStateChangedLocked(UserState userState) {
// TODO: Remove this hack
mInitialized = true;
updateLegacyCapabilitiesLocked(userState);
updateServicesLocked(userState); //更新无障碍服务信息
……
}
……
private void updateServicesLocked(UserState userState) {
Map<ComponentName, AccessibilityServiceConnection> componentNameToServiceMap =
userState.mComponentNameToServiceMap;
boolean isUnlockingOrUnlocked = LocalServices.getService(UserManagerInternal.class)
.isUserUnlockingOrUnlocked(userState.mUserId);
for (int i = 0, count = userState.mInstalledServices.size(); i < count; i++) { //遍历所有已安装的无障碍服务
AccessibilityServiceInfo installedService = userState.mInstalledServices.get(i);
ComponentName componentName = ComponentName.unflattenFromString(
installedService.getId());
AccessibilityServiceConnection service = componentNameToServiceMap.get(componentName);
// Ignore non-encryption-aware services until user is unlocked
if (!isUnlockingOrUnlocked && !installedService.isDirectBootAware()) {
Slog.d(LOG_TAG, "Ignoring non-encryption-aware service " + componentName);
continue;
}
// Wait for the binding if it is in process.
if (userState.mBindingServices.contains(componentName)) {//mBindingServices表示服务断开的集合
continue;
}
if (userState.mEnabledServices.contains(componentName)
&& !mUiAutomationManager.suppressingAccessibilityServicesLocked()) {
if (service == null) {
service = new AccessibilityServiceConnection(userState, mContext, componentName,
installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
this, mWindowManagerService, mGlobalActionPerformer);
} else if (userState.mBoundServices.contains(service)) {
continue;
}
service.bindLocked(); //开始绑定无障碍服务
} else {
if (service != null) {
service.unbindLocked();
}
}
}
……
}
service.bindLocked()调用的是AccessibilityServiceConnection中的方法,最终通过调用bindServiceAsUser绑定特定的无障碍服务,绑定成功后将mComponentName添加到mBindingServices中。这里我就很奇怪,前面说到AccessibilityManagerService$UserState.serviceDisconnectedLocked方法会添加serviceConnection.getComponentName()到mBindingServices,现在绑定成功也添加到mBindingServices,真不知道mBindingServices到底装什么的,如果有哪位大神知道请赐教!!!!!!!!!!!!
//AccessibilityServiceConnection.java
public void bindLocked() {
UserState userState = mUserStateWeakReference.get();
if (userState == null) return;
final long identity = Binder.clearCallingIdentity();
try {
int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS;
if (userState.getBindInstantServiceAllowed()) {
flags |= Context.BIND_ALLOW_INSTANT;
}
if (mService == null && mContext.bindServiceAsUser(
mIntent, this, flags, new UserHandle(userState.mUserId))) {
userState.getBindingServicesLocked().add(mComponentName);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
bindServiceAsUser中第二个参数ServiceConnection传如的是AccessibilityServiceConnection自身this,那么看下onServiceConnected方法吧,onServiceConnected中将本次绑定成功的服务加入到mBoundServices集合中,最后在主线程初始化initializeService方法。initializeService方法从正在绑定的服务集合mBindingServices取出本次绑定成功的服务并去掉,获取到本次成功的服务绑定对象后调用它的init方法并将AccessibilityServiceConnection实例和本次连接的标志mId传过去,这就使得AccessibilityService中onBind方法的IAccessibilityServiceClientWrapper匿名内部类获得了对于本次连接的回调信息和接口。
//AccessibilityServiceConnection.java
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
……
mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
UserState userState = mUserStateWeakReference.get();
if (userState == null) return;
userState.addServiceLocked(this); //将绑定成功的无障碍服务添加到mBoundServices变量中
mSystemSupport.onClientChangeLocked(false);
// Initialize the service on the main handler after we're done setting up for
// the new configuration (for example, initializing the input filter).
mMainHandler.sendMessage(obtainMessage(
AccessibilityServiceConnection::initializeService, this));//绑定成功的初始化
}
}
private void initializeService() {
IAccessibilityServiceClient serviceInterface = null;
synchronized (mLock) {
UserState userState = mUserStateWeakReference.get();
if (userState == null) return;
Set<ComponentName> bindingServices = userState.getBindingServicesLocked();
if (bindingServices.contains(mComponentName) || mWasConnectedAndDied) {
bindingServices.remove(mComponentName); //从正在绑定的服务中去掉绑定成功的服务
mWasConnectedAndDied = false;
serviceInterface = mServiceInterface; //从绑定成功的服务集合中去掉本次成功的服务
}
……
}
if (serviceInterface == null) {
binderDied();
return;
}
try {
serviceInterface.init(this, mId, mOverlayWindowToken); //调用无障碍服务端的init方法
} catch (RemoteException re) {
Slog.w(LOG_TAG, "Error while setting connection for service: "
+ serviceInterface, re);
binderDied();
}
}
至此,AccessibilityManagerService启动以及绑定应用层无障碍服务的过程就分析完了,AccessibilityManagerService可以通过binder通信调用到无障碍服务中的方法了,下面就分析下无障碍事件是怎么达到AccessibilityManagerService并最终输出到无障碍服务里面的过程。
2、无障碍事件产生与分发
一般来说,事件都是通过窗口界面触发的,那么View类中应该就有相关事件,以获取焦点的requestFocus()为例梳理一下整个流程吧。requestFocus()经过一系列的方法调用会调用到onFocusChanged方法,在该方法中有AccessibilityEvent相关方法,感觉应该是这里。跟进sendAccessibilityEvent方法看下,无障碍服务代理一般用不到,忽略掉,看下sendAccessibilityEventInternal方法。
//View.java
protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction,
@Nullable Rect previouslyFocusedRect) {
if (gainFocus) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
} else {
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
// Here we check whether we still need the default focus highlight, and switch it on/off.
switchDefaultFocusHighlight();
……
}
public void sendAccessibilityEvent(int eventType) {
if (mAccessibilityDelegate != null) {
mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
} else {
sendAccessibilityEventInternal(eventType);
}
}
sendAccessibilityEventInternal方法后面又是一些列的方法调用,这里就不一一列出了,最终会走到sendAccessibilityEventUncheckedInternal方。在sendAccessibilityEventUncheckedInternal方法中先对事件做个判断,窗口不可见并且不是消失事件则返回。然后开始初始化无障碍事件,初始化实际上就是设置事件源及其类名、包名,并设置可用性、描述等属性等属性,如果是焦点事件以及选中事件还需要特殊设置一些属性。最后调用父View请求发送事件,getParent()从何而来呢?最终发现在View.java中只有assignParent方法对mParent进行了赋值,那么View.assignParent哪里会调用呢?通过查找源码发现,ViewGroup.java的addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout)会调用,ViewRootImpl.java的setView(View view, WindowManager.LayoutParams attrs, View panelParentView)方法也会调用,并且通过层层getParent()调用,前者也会调用后者,所以最终就走到了ViewRootImpl.requestSendAccessibilityEvent方法。
//View.java
public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) {
// Panes disappearing are relevant even if though the view is no longer visible.
boolean isWindowStateChanged =
(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
boolean isWindowDisappearedEvent = isWindowStateChanged && ((event.getContentChangeTypes()
& AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED) != 0);
if (!isShown() && !isWindowDisappearedEvent) { //窗口不可见并且不是消失事件则返回
return;
}
onInitializeAccessibilityEvent(event); //初始化无障碍事件
// Only a subset of accessibility events populates text content.
if ((event.getEventType() & POPULATING_ACCESSIBILITY_EVENT_TYPES) != 0) {
dispatchPopulateAccessibilityEvent(event);
}
// In the beginning we called #isShown(), so we know that getParent() is not null.
ViewParent parent = getParent();
if (parent != null) {
getParent().requestSendAccessibilityEvent(this, event); //请求发送无障碍事件
}
}
public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
event.setSource(this);//设置事件源
event.setClassName(getAccessibilityClassName()); //事件源View名称
event.setPackageName(getContext().getPackageName()); //事件源所在的包名
event.setEnabled(isEnabled()); //是否可用
event.setContentDescription(mContentDescription); //添加描述
switch (event.getEventType()) {
case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
ArrayList<View> focusablesTempList = (mAttachInfo != null)
? mAttachInfo.mTempArrayList : new ArrayList<View>();
getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD, FOCUSABLES_ALL);
event.setItemCount(focusablesTempList.size()); //设置可用访问的事件源数量
event.setCurrentItemIndex(focusablesTempList.indexOf(this)); //设置当前选中的事件源序号
if (mAttachInfo != null) {
focusablesTempList.clear();
}
} break;
case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
CharSequence text = getIterableTextForAccessibility();
if (text != null && text.length() > 0) {
event.setFromIndex(getAccessibilitySelectionStart()); //设置选中的起点
event.setToIndex(getAccessibilitySelectionEnd());//设置选中的重点
event.setItemCount(text.length()); //设置事件源选中的文字长度
}
} break;
}
}
看下ViewRootImpl.requestSendAccessibilityEvent方法的具体实现吧,先是判断如果view为空或者不可见返回false,然后针对焦点事件则去更新相应的焦点具体节点,最后调用了AccessibilityManager的sendAccessibilityEvent方法。之前一直好奇AccessibilityManager和AccessibilityManagerService有什么联系,现在知道了:应用层层通过AccessibilityManager和AccessibilityManagerService通信。
//ViewRootImpl.java
@Override
public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
if (mView == null || mStopped || mPausedForTransition) { //如果view为空或者不可见返回false
return false;
}
// Immediately flush pending content changed event (if any) to preserve event order
if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
&& mSendWindowContentChangedAccessibilityEvent != null
&& mSendWindowContentChangedAccessibilityEvent.mSource != null) {
mSendWindowContentChangedAccessibilityEvent.removeCallbacksAndRun();
}
// Intercept accessibility focus events fired by virtual nodes to keep
// track of accessibility focus position in such nodes.
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;
}
这里需要注意下,一般android开发者都是查看android studio下载的android源码的,但是在android studio直接点击mAccessibilityManager.sendAccessibilityEvent(event)方法却发现是一个空实现,玩我把。
网页版源码查找下AccessibilityManager.java这个类,如下图,找到了两个,进入查看得知是/frameworks/base/core/java/android/view/accessibility/目录下的sendAccessibilityEvent方法是不是空实现,但是/frameworks/layoutlib/bridge/src/android/view/accessibility/目录下的sendAccessibilityEvent方法却是空实现,于是我知道了android studio下载的源码就是/frameworks/layoutlib/bridge/src/android/view/accessibility/路径的,这个不靠谱,还是看/frameworks/base/core/java/android/view/accessibility/目录下的方法吧。
现在流程走到了/frameworks/base/core/java/android/view/accessibility/目录下AccessibilityManager.sendAccessibilityEvent方法了,sendAccessibilityEvent主要做了两件事:1、通过getServiceLocked()方法找到AccessibilityManagerService;2、调用service.sendAccessibilityEvent将流程传入到AccessibilityManagerService里面。getServiceLocked()方法又调用了tryConnectToServiceLocked(IAccessibilityManager service)方法,在tryConnectToServiceLocked方法内部调用ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)获得了AccessibilityManagerService实例,并且调用了AccessibilityManagerService的addClient方法。
///frameworks/base/core/java/android/view/accessibility/AccessibilityManager.java
public void sendAccessibilityEvent(AccessibilityEvent event) {
final IAccessibilityManager service;
final int userId;
final AccessibilityEvent dispatchedEvent;
synchronized (mLock) {
service = getServiceLocked(); //找到AccessibilityManagerService
if (service == null) {
return;
}
event.setEventTime(SystemClock.uptimeMillis());//设置事件时间
……
try {
// it is possible that this manager is in the same process as the service but
// client using it is called through Binder from another process. Example: MMS
// app adds a SMS notification and the NotificationManagerService calls this method
long identityToken = Binder.clearCallingIdentity();
try {
service.sendAccessibilityEvent(dispatchedEvent, userId);//调用AccessibilityManagerService的sendAccessibilityEvent方法
} 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();
}
}
……
private void tryConnectToServiceLocked(IAccessibilityManager service) {
if (service == null) {
IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);//从路由器中获得了AccessibilityManagerService实例
if (iBinder == null) {
return;
}
service = IAccessibilityManager.Stub.asInterface(iBinder);//转为binder接口
}
try {
final long userStateAndRelevantEvents = service.addClient(mClient, mUserId);//添加服务客户端
setStateLocked(IntPair.first(userStateAndRelevantEvents));
mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents);
mService = service;
} catch (RemoteException re) {
Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
}
}
看下AccessibilityManagerService.sendAccessibilityEvent的实现,省略的部分是事件的判断以及过滤操作,暂时忽略,最终会调用notifyAccessibilityServicesDelayedLocked方法。notifyAccessibilityServicesDelayedLocked又调用了AccessibilityServiceConnection.java继承自其父类的notifyAccessibilityEvent方法,该方法经过一系列的调用走到了notifyAccessibilityEventInternal方法AbstractAccessibilityServiceConnection. notifyAccessibilityEventInternal方法。
//AccessibilityManagerService.java
public void sendAccessibilityEvent(AccessibilityEvent event, int userId) {
……
if (dispatchEvent) {
// Make sure clients receiving this event will be able to get the
// current state of the windows as the window manager may be delaying
// the computation for performance reasons.
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
&& mWindowsForAccessibilityCallback != null) {
WindowManagerInternal wm = LocalServices.getService(WindowManagerInternal.class);
wm.computeWindowsForAccessibility();
}
synchronized (mLock) {
notifyAccessibilityServicesDelayedLocked(event, false);
notifyAccessibilityServicesDelayedLocked(event, true);
mUiAutomationManager.sendAccessibilityEventLocked(event);
}
}
if (OWN_PROCESS_ID != Binder.getCallingPid()) {
event.recycle();
}
}
……
private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
boolean isDefault) {
try {
UserState 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); //AccessibilityServiceConnection的notifyAccessibilityEvent方法被调用
}
}
} 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.
}
}
notifyAccessibilityEventInternal方法里面调用了listener.onAccessibilityEvent方法,listener是一个 mServiceInterface,那么mServiceInterface是什么呢?在上一节绑定服务中我们提到过AccessibilityServiceConnection.bindLocked()绑定有应用层的无障碍服务,绑定成功后AccessibilityServiceConnection的onServiceConnected方法会被调用,然后调用了initializeService()方法,在initializeService()方法中将无障碍服务的绑定信息赋值给了mServiceInterface,所以这里的mServiceInterface保存有IAccessibilityServiceClientWrapper信息,然后通过aidl调用到里面的onAccessibilityEvent方法,这样流程就进入到了AccessibilityService.java里面。
//AbstractAccessibilityServiceConnection.java
private void notifyAccessibilityEventInternal(
int eventType,
AccessibilityEvent event,
boolean serviceWantsEvent) {
IAccessibilityServiceClient listener;
synchronized (mLock) {
listener = mServiceInterface;
……
}
try {
listener.onAccessibilityEvent(event, serviceWantsEvent); //调用onAccessibilityEvent的onAccessibilityEvent方法
if (DEBUG) {
Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
}
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re);
} finally {
event.recycle();
}
}
看下IAccessibilityServiceClientWrapper.onAccessibilityEvent的实现,通过给HandlerCaller发送消息,处理后通过HandlerCaller.Callback回调了executeMessage方法执行到mCallback.onAccessibilityEvent(event),我们知道在AccessibilityService的onBind(Intent intent)方法中创建了一个匿名Callbacks()给IAccessibilityServiceClientWrapper,所以这里mCallback就指向onBind返回给发中的回调函数。
//AccessibilityService$IAccessibilityServiceClientWrapper
public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
implements HandlerCaller.Callback {
……
public IAccessibilityServiceClientWrapper(Context context, Looper looper,
Callbacks callback) {
mCallback = callback;
mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
}
public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {
Message message = mCaller.obtainMessageBO( //发送类型为DO_ON_ACCESSIBILITY_EVENT的handler消息
DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event);
mCaller.sendMessage(message);
}
……
}
public void executeMessage(Message message) {
switch (message.what) {
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().onAccessibilityEvent(event);
if (serviceWantsEvent
&& (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
// Send the event to AccessibilityService
mCallback.onAccessibilityEvent(event);//回调AccessibilityService接口方法
}
// Make sure the event is recycled.
try {
event.recycle();
} catch (IllegalStateException ise) {
/* ignore - best effort */
}
}
} return;
……
}
最终AccessibilityService.this.onAccessibilityEvent(event)被调用,onAccessibilityEvent是一个抽象方法,最后到到了应用层AccessibilityService的子类实现方法。
//AccessibilityService.java
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); //被调用
}
}
……
到此,事件从View.java中产生,中间经过ViewRootImpl.java、AccessibilityManager.java、AccessibilityManagerService.java,AccessibilityServiceConnection.java、IAccessibilityServiceClientWrapper,最终传递给AccessibilityService的子类。
3、无障碍拦截
既然已经知道了无障碍服务获取事件的流程,对于想不被无障碍服务操作的view,可以打断中间流程来阻止事件接收,比如重写sendAccessibilityEvent让事件无法继续传递;或者调用setAccessibilityDelegate方法重写无障碍回调从而屏蔽无障碍模式
EditText editText = findViewById(R.id.search_src_text);
editText.setAccessibilityDelegate(new View.AccessibilityDelegate(){
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
Log.d(TAG, "editText performAccessibilityAction action="+action);
//return super.performAccessibilityAction(host, action, args);
return false;
}
});