Android之辅助服务下篇————AccessibilityServic源码分析
一.前言
在上一篇博客中,我介绍了辅助服务的大致使用。这一篇我们来看看AccessibilityServic的原理。
通过上篇,我们知道将AccessibilityServic配置完成后。之后的使用可以分为下面三个步骤
- onAccessibilityEvent接收事件(屏幕变化,点击事件)
- 通过控件文字或者id在当前屏幕里寻找对应的控件
- 调用控件的performAction进行相应的操作
所以我们源码分析也是着重于上面的三个流程
- 点击事件如何分发到onAccessibilityEvent
- findAccessibilityNodeInfosByText如果通过控件文字找到对应的控件
- performAction如何模拟操作控件
二.接收AccessibilityEvent事件
1.View对点击事件的分发
我们以View的点击事件发送到AccessibilityEvent为例。
从View的事件分发onTouchEvent为起点
所在类:View.java
onTouchEvent
public boolean onTouchEvent(MotionEvent event) {
....
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
if ((viewFlags & TOOLTIP) == TOOLTIP) {
handleTooltipUp();
}
if (!clickable) {
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
}
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
....
return false;
}
点击事件是在 performClickInternal()中,我们继续追踪
所在类:View.java
private boolean performClickInternal() {
// Must notify autofill manager before performing the click actions to avoid scenarios where
// the app has a click listener that changes the state of views the autofill service might
// be interested on.
notifyAutofillManagerOnClick();
return performClick();
}
public boolean performClick() {
// We still need to call this method to handle the cases where performClick() was called
// externally, instead of through performClickInternal()
notifyAutofillManagerOnClick();
final boolean result;
final ListenerInfo 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);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
很明显在sendAccessibilityEvent中将点击事件进行了传递
所在类:View.java
public void sendAccessibilityEvent(int eventType) {
if (mAccessibilityDelegate != null) {
mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
} else {
sendAccessibilityEventInternal(eventType);//见下
}
}
public void sendAccessibilityEventInternal(int eventType) {
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType));//见下
}
}
public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
if (mAccessibilityDelegate != null) {
mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event);
} else {
sendAccessibilityEventUncheckedInternal(event);//见下
}
}
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);//重点
}
}
在这里我们看到直接调用了getParent().requestSendAccessibilityEvent(this, event);,对这个事件进行分发。即调用了View的父类进行实现,这里其实有点像双亲委托模型。我们都知道View的最终父类是ViewRootImpl,我们直接ViewRootImpl中去看requestSendAccessibilityEvent的实现
所在类:ViewRootImpl
@Override
public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
if (mView == null || mStopped || mPausedForTransition) {
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();
switch (eventType) {
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
final long sourceNodeId = event.getSourceNodeId();
final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
sourceNodeId);
View source = mView.findViewByAccessibilityId(accessibilityViewId);
if (source != null) {
AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
if (provider != null) {
final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
sourceNodeId);
final AccessibilityNodeInfo node;
node = provider.createAccessibilityNodeInfo(virtualNodeId);
setAccessibilityFocus(source, node);
}
}
} break;
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
final long sourceNodeId = event.getSourceNodeId();
final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
sourceNodeId);
View source = mView.findViewByAccessibilityId(accessibilityViewId);
if (source != null) {
AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
if (provider != null) {
setAccessibilityFocus(null, null);
}
}
} break;
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
handleWindowContentChangedEvent(event);
} break;
}
mAccessibilityManager.sendAccessibilityEvent(event);//重点
return true;
}
最后在requestSendAccessibilityEvent里面调用mAccessibilityManager.sendAccessibilityEvent继续将点击事件进行分发。
小结:
2.AccessibilityManager
从AS跳转到AccessibilityManager.sendAccessibilityEvent,发现sendAccessibilityEvent是一个空实现,不应该啊,然后我就去打开Source Insight 4.0,在里面阅读AccessibilityManager的相关源码.
源码版本:Android8.1
目录:frameworks\base\core\java\android\view\accessibility\AccessibilityManager.java
public void sendAccessibilityEvent(AccessibilityEvent event) {
final IAccessibilityManager service;
final int userId;
synchronized (mLock) {
service = getServiceLocked();//关键代码
if (service == null) {
return;
}
if (!mIsEnabled) {
Looper myLooper = Looper.myLooper();
if (myLooper == Looper.getMainLooper()) {
throw new IllegalStateException(
"Accessibility off. Did you forget to check that?");
} else {
// If we're not running on the thread with the main looper, it's possible for
// the state of accessibility to change between checking isEnabled and
// calling this method. So just log the error rather than throwing the
// exception.
Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
return;
}
}
if ((event.getEventType() & mRelevantEventTypes) == 0) {
if (DEBUG) {
Log.i(LOG_TAG, "Not dispatching irrelevant event: " + event
+ " that is not among "
+ AccessibilityEvent.eventTypeToString(mRelevantEventTypes));
}
return;
}
userId = mUserId;
}
try {
event.setEventTime(SystemClock.uptimeMillis());
// 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();
service.sendAccessibilityEvent(event, userId);//关键代码
Binder.restoreCallingIdentity(identityToken);
if (DEBUG) {
Log.i(LOG_TAG, event + " sent");
}
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error during sending " + event + " ", re);
} finally {
event.recycle();
}
}
从上的代码可以看出通过 service.sendAccessibilityEvent,将AccessibilityEvent进行下一步传递。那么我们来分析一下,service是什么类型的对象。
Service的来源
目录:frameworks\base\core\java\android\view\accessibility\AccessibilityManager.java
private IAccessibilityManager getServiceLocked() {
if (mService == null) {
tryConnectToServiceLocked(null);//往下追踪
}
return mService;
}
private void tryConnectToServiceLocked(IAccessibilityManager service) {
if (service == null) {
IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
if (iBinder == null) {
return;
}
service = IAccessibilityManager.Stub.asInterface(iBinder);
}
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);
}
}
public static final String ACCESSIBILITY_SERVICE = "accessibility";
追踪发现 mService其实是一个binder代理类,代表了accessibility的服务。
所以我们直接看AccessibilityManagerService中sendAccessibilityEvent的实现
目录: frameworks\base\services\accessibility\java\com\android\server\accessibility\AccessibilityManagerService.java
@Override
public void sendAccessibilityEvent(AccessibilityEvent event, int userId) {
boolean dispatchEvent = false;
synchronized (mLock) {
....
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);
}
}
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++) { //遍历
Service service = state.mBoundServices.get(i);
if (service.mIsDefault == isDefault) {
if (doesServiceWantEventLocked(service, event)) {
service.notifyAccessibilityEvent(event, true);
} else if (service.mUsesAccessibilityCache
&& (AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK
& event.getEventType()) != 0) {
service.notifyAccessibilityEvent(event, false);
}
}
}
} 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.
}
}
在notifyAccessibilityServicesDelayedLocked方法中,对所有存储在mBoundServices的Service进行遍历,并调用Service.notifyAccessibilityEvent。这里的Service是AccessibilityManagerService中的一个内部类。
先看看Service的定义
class Service extends IAccessibilityServiceConnection.Stub
implements ServiceConnection, DeathRecipient, KeyEventDispatcher.KeyEventFilter,
FingerprintGestureDispatcher.FingerprintGestureClient {
....
}
- IAccessibilityServiceConnection.Stub Aidl接口
- ServiceConnection bander连接成功后的回调
- aidl的内容可以参考之前博客:Android之IPC2————AIDL
目录: frameworks\base\services\accessibility\java\com\android\server\accessibility\AccessibilityManagerService.java#Service
public void notifyAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {
synchronized (mLock) {
final int eventType = event.getEventType();
// Make a copy since during dispatch it is possible the event to
// be modified to remove its source if the receiving service does
// not have permission to access the window content.
AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
Message message;
if ((mNotificationTimeout > 0)
&& (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)) {
// Allow at most one pending event
final AccessibilityEvent oldEvent = mPendingEvents.get(eventType);
mPendingEvents.put(eventType, newEvent);
if (oldEvent != null) {
mEventDispatchHandler.removeMessages(eventType);
oldEvent.recycle();
}
message = mEventDispatchHandler.obtainMessage(eventType);
} else {
// Send all messages, bypassing mPendingEvents
message = mEventDispatchHandler.obtainMessage(eventType, newEvent);
}
message.arg1 = serviceWantsEvent ? 1 : 0;
mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout);//在Handler里进行处理
}
}
public Handler mEventDispatchHandler = new Handler(mMainHandler.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);
}
};
/**
* Notifies an accessibility service client for a scheduled event given the event type.
*
* @param eventType The type of the event to dispatch.
*/
private void notifyAccessibilityEventInternal(
int eventType,
AccessibilityEvent event,
boolean serviceWantsEvent) {
IAccessibilityServiceClient listener;
synchronized (mLock) {
listener = mServiceInterface;
if (listener == null) {
return;
}
if (event == null) {
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);//重点
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();
}
}
继续最终追踪一下mServiceInterface的来源。
通过全局搜索,发现在onServiceConnected里面,完成的mServiceInterface,即binder连接成功后的回调
目录: frameworks\base\services\accessibility\java\com\android\server\accessibility\AccessibilityManagerService.java#Service
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
synchronized (mLock) {
if (mService != service) {
if (mService != null) {
mService.unlinkToDeath(this, 0);
}
mService = service;
try {
mService.linkToDeath(this, 0);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Failed registering death link");
binderDied();
return;
}
}
mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service); //被赋值
UserState userState = getUserStateLocked(mUserId);
addServiceLocked(this, userState);
if (userState.mBindingServices.contains(mComponentName) || mWasConnectedAndDied) {
userState.mBindingServices.remove(mComponentName);
mWasConnectedAndDied = false;
onUserStateChangedLocked(userState);
// Initialize the service on the main handler after we're done setting up for
// the new configuration (for example, initializing the input filter).
mMainHandler.obtainMessage(MainHandler.MSG_INIT_SERVICE, this).sendToTarget();
} else {
binderDied();
}
}
}
我们注意一下,mServiceInterface的类型是IAccessibilityServiceClient。这个后面会用。
小结:
3.AccessibilityService
前面分析从View到IAccessibilityServiceClient的过程,但是对于IAccessibilityServiceClient的实现。我一直没有找到,然后就回过同从AccessibilityService 重新分析。
AccessibilityService是继承的Service类,只不过它实现了onBind
目录: frameworks\base\core\java\android\accessibilityservice\AccessibilityService.java
public abstract class AccessibilityService extends Service {
....
/**
* 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(@NonNull Region region,
float scale, float centerX, float centerY) {
AccessibilityService.this.onMagnificationChanged(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);
}
});
}
....
}
继续看看IAccessibilityServiceClientWrapper这个内部类
目录: frameworks\base\core\java\android\accessibilityservice\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 init(IAccessibilityServiceConnection connection, int connectionId,
IBinder windowToken) {
Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,
connection, windowToken);
mCaller.sendMessage(message);
}
public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {
Message message = mCaller.obtainMessageBO(
DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event);
mCaller.sendMessage(message);
}
....
@Override
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);
}
// Make sure the event is recycled.
try {
event.recycle();
} catch (IllegalStateException ise) {
/* ignore - best effort */
}
}
} return;
case DO_ON_INTERRUPT: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
mCallback.onInterrupt();
}
} return;
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().addConnection(mConnectionId,
connection);
mCallback.init(mConnectionId, windowToken);
mCallback.onServiceConnected();
} else {
AccessibilityInteractionClient.getInstance().removeConnection(
mConnectionId);
mConnectionId = AccessibilityInteractionClient.NO_ID;
AccessibilityInteractionClient.getInstance().clearCache();
mCallback.init(AccessibilityInteractionClient.NO_ID, null);
}
} return;
case DO_ON_GESTURE: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
final int gestureId = message.arg1;
mCallback.onGesture(gestureId);
}
} return;
case DO_CLEAR_ACCESSIBILITY_CACHE: {
AccessibilityInteractionClient.getInstance().clearCache();
} return;
case DO_ON_KEY_EVENT: {
KeyEvent event = (KeyEvent) message.obj;
try {
IAccessibilityServiceConnection connection = AccessibilityInteractionClient
.getInstance().getConnection(mConnectionId);
if (connection != null) {
final boolean result = mCallback.onKeyEvent(event);
final int sequence = message.arg1;
try {
connection.setOnKeyEventResult(result, sequence);
} catch (RemoteException re) {
/* ignore */
}
}
} finally {
// Make sure the event is recycled.
try {
event.recycle();
} catch (IllegalStateException ise) {
/* ignore - best effort */
}
}
} return;
case DO_ON_MAGNIFICATION_CHANGED: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
final SomeArgs args = (SomeArgs) message.obj;
final Region region = (Region) args.arg1;
final float scale = (float) args.arg2;
final float centerX = (float) args.arg3;
final float centerY = (float) args.arg4;
mCallback.onMagnificationChanged(region, scale, centerX, centerY);
}
} return;
case DO_ON_SOFT_KEYBOARD_SHOW_MODE_CHANGED: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
final int showMode = (int) message.arg1;
mCallback.onSoftKeyboardShowModeChanged(showMode);
}
} return;
case DO_GESTURE_COMPLETE: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
final boolean successfully = message.arg2 == 1;
mCallback.onPerformGestureResult(message.arg1, successfully);
}
} return;
case DO_ON_FINGERPRINT_ACTIVE_CHANGED: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
mCallback.onFingerprintCapturingGesturesChanged(message.arg1 == 1);
}
} return;
case DO_ON_FINGERPRINT_GESTURE: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
mCallback.onFingerprintGesture(message.arg1);
}
} return;
case (DO_ACCESSIBILITY_BUTTON_CLICKED): {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
mCallback.onAccessibilityButtonClicked();
}
} return;
case (DO_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED): {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
final boolean available = (message.arg1 != 0);
mCallback.onAccessibilityButtonAvailabilityChanged(available);
}
} return;
default :
Log.w(LOG_TAG, "Unknown message type " + message.what);
}
}
}
在这里我们找到了IAccessibilityServiceClient.Stub。
所以可知,在AccessibilityService中,当AccessibilityManagerService和建立连接后,获得onBind返回的IAccessibilityServiceClientWrapper,在onServiceConnected回调中,将IAccessibilityServiceClientWrapper赋值给我们上面说的mServiceInterface。
三.findAccessibilityNodeInfosByText
1.AccessibilityNodeInfo
上面分析了点击事件是如何通知给AccessibilityService,现在让我们来看看AccessibilityService是如何选择的对应的控件的,以findAccessibilityNodeInfosByText为例
目录:frameworks\base\core\java\android\view\accessibility\AccessibilityNodeInfo.java
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
enforceSealed();
if (!canPerformRequestOverConnection(mSourceNodeId)) {
return Collections.emptyList();
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,
text);
}
继续追踪AccessibilityInteractionClient.findAccessibilityNodeInfosByText
目录:frameworks\base\core\java\android\view\accessibility\AccessibilityInteractionClient.java
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(int connectionId,
int accessibilityWindowId, long accessibilityNodeId, String text) {
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final long identityToken = Binder.clearCallingIdentity();
final boolean success = connection.findAccessibilityNodeInfosByText(
accessibilityWindowId, accessibilityNodeId, text, interactionId, this,
Thread.currentThread().getId()); //重点
Binder.restoreCallingIdentity(identityToken);
if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
if (infos != null) {
finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
return infos;
}
}
} else {
if (DEBUG) {
Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
}
}
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while calling remote"
+ " findAccessibilityNodeInfosByViewText", re);
}
return Collections.emptyList();
}
这里出现了IAccessibilityServiceConnection类型,他是在哪定义的呢?其实我们在上一节中,有分析过这个内容,它其实就是AccessibilityManagerService中的内部类Service。
上面这个过程也是通过Binder完成的IPC
2.AccessibilityManagerService
我们继续分析AccessibilityManagerService中的内部类Service中的实现
class Service extends IAccessibilityServiceConnection.Stub
implements ServiceConnection, DeathRecipient, KeyEventDispatcher.KeyEventFilter,
FingerprintGestureDispatcher.FingerprintGestureClient {
}
目录" frameworks\base\services\accessibility\java\com\android\server\accessibility\AccessibilityManagerService.java#Service
@Override
public boolean findAccessibilityNodeInfosByText(int accessibilityWindowId,
long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
Region partialInteractiveRegion = Region.obtain();
MagnificationSpec spec;
synchronized (mLock) {
mUsesAccessibilityCache = true;
if (!isCalledForCurrentUserLocked()) {
return false;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
return false;
} else {
connection = getConnectionLocked(resolvedWindowId);
if (connection == null) {
return false;
}
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
}
final int interrogatingPid = Binder.getCallingPid();
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
try {
connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
partialInteractiveRegion, interactionId, callback, mFetchFlags,
interrogatingPid, interrogatingTid, spec); //重点代码
return true;
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()");
}
} finally {
Binder.restoreCallingIdentity(identityToken);
// Recycle if passed to another process.
if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
partialInteractiveRegion.recycle();
}
}
return false;
}
这里又出现一个connection,类型是IAccessibilityInteractionConnection。这个我找了半天,后来通过上面一节内容。我猜测是在ViewRootImpl中,最后搜索了一些。果然是在ViewRootImpl中实现的
3.ViewRootImpl
我们继续来看ViewRootImpl中的实现
目录:\frameworks\base\core\java\android\view\ViewRootImpl.java
AccessibilityInteractionConnection内部类定义
static final class AccessibilityInteractionConnection
extends IAccessibilityInteractionConnection.Stub {
....
}
findAccessibilityNodeInfosByText的实现
@Override
public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text,
interactiveRegion, interactionId, callback, flags, interrogatingPid,
interrogatingTid, spec);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
callback.setFindAccessibilityNodeInfosResult(null, interactionId);
} catch (RemoteException re) {
/* best effort - ignore */
}
}
}
viewRootImpl.getAccessibilityInteractionController() 返回的是AccessibilityInteractionController,我们看看
目录:frameworks\base\core\java\android\view\AccessibilityInteractionController.java
public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
String text, Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
long interrogatingTid, MagnificationSpec spec) {
Message message = mHandler.obtainMessage();
message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT;
message.arg1 = flags;
SomeArgs args = SomeArgs.obtain();
args.arg1 = text;
args.arg2 = callback;
args.arg3 = spec;
args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
args.argi3 = interactionId;
args.arg4 = interactiveRegion;
message.obj = args;
scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS); //追踪
}
private void scheduleMessage(Message message, int interrogatingPid, long interrogatingTid,
boolean ignoreRequestPreparers) {
if (ignoreRequestPreparers
|| !holdOffMessageIfNeeded(message, interrogatingPid, interrogatingTid)) {
// If the interrogation is performed by the same thread as the main UI
// thread in this process, set the message as a static reference so
// after this call completes the same thread but in the interrogating
// client can handle the message to generate the result.
if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
AccessibilityInteractionClient.getInstanceForThread(
interrogatingTid).setSameThreadMessage(message);
} else {
mHandler.sendMessage(message); //去handler的实现类
}
}
}
private class PrivateHandler extends Handler {
private static final int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
private static final int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3;
private static final int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
private static final int MSG_FIND_FOCUS = 5;
private static final int MSG_FOCUS_SEARCH = 6;
private static final int MSG_PREPARE_FOR_EXTRA_DATA_REQUEST = 7;
private static final int MSG_APP_PREPARATION_FINISHED = 8;
private static final int MSG_APP_PREPARATION_TIMEOUT = 9;
public PrivateHandler(Looper looper) {
super(looper);
}
@Override
public String getMessageName(Message message) {
final int type = message.what;
switch (type) {
case MSG_PERFORM_ACCESSIBILITY_ACTION:
return "MSG_PERFORM_ACCESSIBILITY_ACTION";
case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID:
return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID";
case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID:
return "MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID";
case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT:
return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT";
case MSG_FIND_FOCUS:
return "MSG_FIND_FOCUS";
case MSG_FOCUS_SEARCH:
return "MSG_FOCUS_SEARCH";
case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST:
return "MSG_PREPARE_FOR_EXTRA_DATA_REQUEST";
case MSG_APP_PREPARATION_FINISHED:
return "MSG_APP_PREPARATION_FINISHED";
case MSG_APP_PREPARATION_TIMEOUT:
return "MSG_APP_PREPARATION_TIMEOUT";
default:
throw new IllegalArgumentException("Unknown message type: " + type);
}
}
@Override
public void handleMessage(Message message) {
final int type = message.what;
switch (type) {
case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
} break;
case MSG_PERFORM_ACCESSIBILITY_ACTION: {
performAccessibilityActionUiThread(message);
} break;
case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: {
findAccessibilityNodeInfosByViewIdUiThread(message);
} break;
case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: {
findAccessibilityNodeInfosByTextUiThread(message);
} break;
case MSG_FIND_FOCUS: {
findFocusUiThread(message);
} break;
case MSG_FOCUS_SEARCH: {
focusSearchUiThread(message);
} break;
case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST: {
prepareForExtraDataRequestUiThread(message);
} break;
case MSG_APP_PREPARATION_FINISHED: {
requestPreparerDoneUiThread(message);
} break;
case MSG_APP_PREPARATION_TIMEOUT: {
requestPreparerTimeoutUiThread();
} break;
default:
throw new IllegalArgumentException("Unknown message type: " + type);
}
}
}
//重点代码
private void findAccessibilityNodeInfosByTextUiThread(Message message) {
final int flags = message.arg1;
SomeArgs args = (SomeArgs) message.obj;
final String text = (String) args.arg1;
final IAccessibilityInteractionConnectionCallback callback =
(IAccessibilityInteractionConnectionCallback) args.arg2;
final MagnificationSpec spec = (MagnificationSpec) args.arg3;
final int accessibilityViewId = args.argi1;
final int virtualDescendantId = args.argi2;
final int interactionId = args.argi3;
final Region interactiveRegion = (Region) args.arg4;
args.recycle();
List<AccessibilityNodeInfo> infos = null;
try {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
root = mViewRootImpl.mView;
}
if (root != null && isShown(root)) {
AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
if (provider != null) {
infos = provider.findAccessibilityNodeInfosByText(text,
virtualDescendantId);
} else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
ArrayList<View> foundViews = mTempArrayList;
foundViews.clear();
//重点代码
root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
| View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
| View.FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS);
if (!foundViews.isEmpty()) {
infos = mTempAccessibilityNodeInfoList;
infos.clear();
final int viewCount = foundViews.size();
for (int i = 0; i < viewCount; i++) {
View foundView = foundViews.get(i);
if (isShown(foundView)) {
provider = foundView.getAccessibilityNodeProvider();
if (provider != null) {
List<AccessibilityNodeInfo> infosFromProvider =
provider.findAccessibilityNodeInfosByText(text,
AccessibilityNodeProvider.HOST_VIEW_ID);
if (infosFromProvider != null) {
infos.addAll(infosFromProvider);
}
} else {
infos.add(foundView.createAccessibilityNodeInfo());
}
}
}
}
}
}
} finally {
updateInfosForViewportAndReturnFindNodeResult(
infos, callback, interactionId, spec, interactiveRegion);
}
}
在最后 root.findViewsWithText。root的来源是 root = mViewRootImpl.mView。我们知道mViewRootImpl.mView其实是通过setView来赋值的。一般都是ViewGrop类型。我们直接去看ViewGrup的findViewsWithText
目录:frameworks\base\core\java\android\view\ViewGroup.java
@Override
public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) {
super.findViewsWithText(outViews, text, flags);
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < childrenCount; i++) {
View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
&& (child.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
child.findViewsWithText(outViews, text, flags);
}
}
}
继续看看View的实现
目录:frameworks\base\core\java\android\view\View.java
public void findViewsWithText(ArrayList<View> outViews, CharSequence searched,
@FindViewFlags int flags) {
if (getAccessibilityNodeProvider() != null) {
if ((flags & FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS) != 0) {
outViews.add(this);
}
} else if ((flags & FIND_VIEWS_WITH_CONTENT_DESCRIPTION) != 0
&& (searched != null && searched.length() > 0)
&& (mContentDescription != null && mContentDescription.length() > 0)) {
String searchedLowerCase = searched.toString().toLowerCase();
String contentDescriptionLowerCase = mContentDescription.toString().toLowerCase();
if (contentDescriptionLowerCase.contains(searchedLowerCase)) {
outViews.add(this);
}
}
}
至此我们可以看出,最终会调用View的findViewsWithText,将符合条件的View放入到 outViews集合中。
4.小结
时序图:
四.performAction
点击事件和上面查找控件的流程很想,着重分析一下AccessibilityInteractionController之后的流程
1.AccessibilityInteractionController
从performAccessibilityActionClientThread开始分析
目录:frameworks\base\core\java\android\view\AccessibilityInteractionController.java
public void performAccessibilityActionClientThread(long accessibilityNodeId, int action,
Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
long interrogatingTid) {
Message message = mHandler.obtainMessage();
message.what = PrivateHandler.MSG_PERFORM_ACCESSIBILITY_ACTION;
message.arg1 = flags;
message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
SomeArgs args = SomeArgs.obtain();
args.argi1 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
args.argi2 = action;
args.argi3 = interactionId;
args.arg1 = callback;
args.arg2 = arguments;
message.obj = args;
scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
}
//Hander的handleMessage处理
@Override
public void handleMessage(Message message) {
final int type = message.what;
switch (type) {
case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
findAccessibilityNodeInfoByAccessibilityIdUiThread(message);
} break;
case MSG_PERFORM_ACCESSIBILITY_ACTION: {
performAccessibilityActionUiThread(message);//这里
} break;
case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: {
findAccessibilityNodeInfosByViewIdUiThread(message);
} break;
case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: {
findAccessibilityNodeInfosByTextUiThread(message);
} break;
case MSG_FIND_FOCUS: {
findFocusUiThread(message);
} break;
case MSG_FOCUS_SEARCH: {
focusSearchUiThread(message);
} break;
case MSG_PREPARE_FOR_EXTRA_DATA_REQUEST: {
prepareForExtraDataRequestUiThread(message);
} break;
case MSG_APP_PREPARATION_FINISHED: {
requestPreparerDoneUiThread(message);
} break;
case MSG_APP_PREPARATION_TIMEOUT: {
requestPreparerTimeoutUiThread();
} break;
default:
throw new IllegalArgumentException("Unknown message type: " + type);
}
}
}
private void performAccessibilityActionUiThread(Message message) {
final int flags = message.arg1;
final int accessibilityViewId = message.arg2;
SomeArgs args = (SomeArgs) message.obj;
final int virtualDescendantId = args.argi1;
final int action = args.argi2;
final int interactionId = args.argi3;
final IAccessibilityInteractionConnectionCallback callback =
(IAccessibilityInteractionConnectionCallback) args.arg1;
Bundle arguments = (Bundle) args.arg2;
args.recycle();
boolean succeeded = false;
try {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null ||
mViewRootImpl.mStopped || mViewRootImpl.mPausedForTransition) {
return;
}
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View target = null;
if (accessibilityViewId != AccessibilityNodeInfo.ROOT_ITEM_ID) {
target = findViewByAccessibilityId(accessibilityViewId);
} else {
target = mViewRootImpl.mView;
}
if (target != null && isShown(target)) {
if (action == R.id.accessibilityActionClickOnClickableSpan) {
// Handle this hidden action separately
succeeded = handleClickableSpanActionUiThread(
target, virtualDescendantId, arguments);
} else {
AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
if (provider != null) {
succeeded = provider.performAction(virtualDescendantId, action,
arguments);
} else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
succeeded = target.performAccessibilityAction(action, arguments); //这里
}
}
}
} finally {
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
callback.setPerformAccessibilityActionResult(succeeded, interactionId);
} catch (RemoteException re) {
/* ignore - the other side will time out */
}
}
}
我们可以看到最终通过target.performAccessibilityAction调用到View的相关方法里
2.View
目录:
public boolean performAccessibilityAction(int action, Bundle arguments) {
if (mAccessibilityDelegate != null) {
return mAccessibilityDelegate.performAccessibilityAction(this, action, arguments);
} else {
return performAccessibilityActionInternal(action, arguments);
}
}
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
if (isNestedScrollingEnabled()
&& (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
|| action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
|| action == R.id.accessibilityActionScrollUp
|| action == R.id.accessibilityActionScrollLeft
|| action == R.id.accessibilityActionScrollDown
|| action == R.id.accessibilityActionScrollRight)) {
if (dispatchNestedPrePerformAccessibilityAction(action, arguments)) {
return true;
}
}
switch (action) {
//点击事件
case AccessibilityNodeInfo.ACTION_CLICK: {
if (isClickable()) {
performClick();
return true;
}
} break;
case AccessibilityNodeInfo.ACTION_LONG_CLICK: {
if (isLongClickable()) {
performLongClick();
return true;
}
} break;
case AccessibilityNodeInfo.ACTION_FOCUS: {
if (!hasFocus()) {
// Get out of touch mode since accessibility
// wants to move focus around.
getViewRootImpl().ensureTouchMode(false);
return requestFocus();
}
} break;
case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
if (hasFocus()) {
clearFocus();
return !isFocused();
}
} break;
case AccessibilityNodeInfo.ACTION_SELECT: {
if (!isSelected()) {
setSelected(true);
return isSelected();
}
} break;
case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {
if (isSelected()) {
setSelected(false);
return !isSelected();
}
} break;
case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: {
if (!isAccessibilityFocused()) {
return requestAccessibilityFocus();
}
} break;
case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
if (isAccessibilityFocused()) {
clearAccessibilityFocus();
return true;
}
} break;
case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: {
if (arguments != null) {
final int granularity = arguments.getInt(
AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
final boolean extendSelection = arguments.getBoolean(
AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN);
return traverseAtGranularity(granularity, true, extendSelection);
}
} break;
case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: {
if (arguments != null) {
final int granularity = arguments.getInt(
AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
final boolean extendSelection = arguments.getBoolean(
AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN);
return traverseAtGranularity(granularity, false, extendSelection);
}
} break;
case AccessibilityNodeInfo.ACTION_SET_SELECTION: {
CharSequence text = getIterableTextForAccessibility();
if (text == null) {
return false;
}
final int start = (arguments != null) ? arguments.getInt(
AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, -1) : -1;
final int end = (arguments != null) ? arguments.getInt(
AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, -1) : -1;
// Only cursor position can be specified (selection length == 0)
if ((getAccessibilitySelectionStart() != start
|| getAccessibilitySelectionEnd() != end)
&& (start == end)) {
setAccessibilitySelection(start, end);
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
return true;
}
} break;
case R.id.accessibilityActionShowOnScreen: {
if (mAttachInfo != null) {
final Rect r = mAttachInfo.mTmpInvalRect;
getDrawingRect(r);
return requestRectangleOnScreen(r, true);
}
} break;
case R.id.accessibilityActionContextClick: {
if (isContextClickable()) {
performContextClick();
return true;
}
} break;
}
return false;
}
public boolean performClick() {
final boolean result;
final ListenerInfo 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);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
至此AccessibilityServic所有内容基本都分析完了