Android 9.0原始碼學習-AccessibilityManager
Android Accessibility是為了幫助殘障人士更好使用手機開發出來一個模組,比如螢幕閱讀器,手勢等等,當然現在已經被玩壞了,各種外掛,比如微信搶紅包的外掛,也是基於Accessibility寫出來的。
Android developer有關於 ofollow,noindex">Accessibility 的介紹(需要科學上網),我自己也基於這個有一篇筆記 Android-Accessibility(Android 8.0以上) 。
Accessibility Architecture
先拿一個具體的例子來看,這是一個搶紅包的外掛,把WeChat稱作 Target APP ,就是被監控的APP,當跳出來一個紅包,觸發了一個 AccessibilityEvent ,system_server中的 AccessibilityManagerService 將AccessibilityEvent分發給有 AccessibilityService 的APP,稱為 Accessibility APP ,這個AccessibilityService受到這個AccessibilityEvent後,會找到這個頁面的Open Button,模擬點選。(Target APP和Accessibility APP是我看別的部落格這麼取的)

在這裡插入圖片描述
Core Class
剛才舉得例子是表象,那麼程式內部,這個過程其實就是三個類之間的互動,當然實際不止這麼簡單啦,這個後面再講,現在只要記住這三個是核心的類就好了。( 以下都會用縮寫代替 )
- AccessibilityManager( AM ):Send AccessibilityEvent
- AccessibilityServiceManager( AMS ):Dispatch
- AccessibilityService( AS ):Response

在這裡插入圖片描述
File List
File Name | File Path |
---|---|
View.java | /frameworks/base/core/java/android/view/View.java |
ViewRootImpl.java | /frameworks/base/core/java/android/view/ViewRootImpl.java |
AccessibilityManager.java | /frameworks/base/core/java/android/view/accessibility/AccessibilityManager.java |
AccessibilityManagerService.java | /frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java |
AbstractAccessibilityServiceConnection.java | /frameworks/base/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java |
AccessibilityServiceConnection.java | /frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java |
AccessibilityManagerService.java | /frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java |
AccessibilityService.java | /frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java |
Accessibility Flow
Accessibility Flow主要有下面幾個Flow了:
- AMS繫結AS
- AM與AMS聯絡
- AccessibilityEvent Dispatch
-
Response to AccessibilityEvent
在這裡插入圖片描述
AMS繫結AS

在這裡插入圖片描述
上圖是AMS與AS聯絡的flow,下面一步一步的來說。
Step1:什麼時候AMS會繫結AS?
- Settings->Accessibility->enable(
enableAccessibilityServiceLocked()
) - Settings->Accessibility->disable(
disableAccessibilityServiceLocked()
) - Some RegisterBroadcastReceivers (
registerBroadcastReceivers()
)onSomePackagesChanged() onPackageUpdateFinished() onHandleForceStop() onPackageRemoved()
- Others State Change
當用戶在設定->無障礙裡面選擇了開啟或關閉一個輔助功能,會導致一些系統狀態會變化;Accessibility APP的安裝狀態會以BroadcastReceivers的方式會通知狀態改變;還有其他的一些狀態改變。這些變化最終會呼叫到AMS的 onUserStateChangedLocked()
方法。

在這裡插入圖片描述
AccessibilityManagerService.java -- enableAccessibilityServiceLocked()
/** 2333* Enables accessibility service specified by {@param componentName} for the {@param userId}. 2334*/ 2335private void enableAccessibilityServiceLocked(ComponentName componentName, int userId) { 2336final SettingStringHelper setting = 2337new SettingStringHelper( 2338mContext.getContentResolver(), 2339Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 2340userId); 2341setting.write(ComponentNameSet.add(setting.read(), componentName)); 2342 2343UserState userState = getUserStateLocked(userId); 2344if (userState.mEnabledServices.add(componentName)) { 2345onUserStateChangedLocked(userState); 2346} 2347}
AccessibilityManagerService.java -- disableAccessibilityServiceLocked()
/** 2350* Disables accessibility service specified by {@param componentName} for the {@param userId}. 2351*/ 2352private void disableAccessibilityServiceLocked(ComponentName componentName, int userId) { 2353final SettingsStringUtil.SettingStringHelper setting = 2354new SettingStringHelper( 2355mContext.getContentResolver(), 2356Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 2357userId); 2358setting.write(ComponentNameSet.remove(setting.read(), componentName)); 2359 2360UserState userState = getUserStateLocked(userId); 2361if (userState.mEnabledServices.remove(componentName)) { 2362onUserStateChangedLocked(userState); 2363} 2364}
AccessibilityManagerService.java -- registerBroadcastReceivers()
private void registerBroadcastReceivers() { 324PackageMonitor monitor = new PackageMonitor() { 325@Override 326public void onSomePackagesChanged() { 327synchronized (mLock) { 328// Only the profile parent can install accessibility services. 329// Therefore we ignore packages from linked profiles. 330if (getChangingUserId() != mCurrentUserId) { 331return; 332} 333// We will update when the automation service dies. 334UserState userState = getCurrentUserStateLocked(); 335// We have to reload the installed services since some services may 336// have different attributes, resolve info (does not support equals), 337// etc. Remove them then to force reload. 338userState.mInstalledServices.clear(); 339if (readConfigurationForUserStateLocked(userState)) { 340onUserStateChangedLocked(userState); 341} 342} 343} 344 345@Override 346public void onPackageUpdateFinished(String packageName, int uid) { 347// Unbind all services from this package, and then update the user state to 348// re-bind new versions of them. 349synchronized (mLock) { 350final int userId = getChangingUserId(); 351if (userId != mCurrentUserId) { 352return; 353} 354UserState userState = getUserStateLocked(userId); 355boolean unboundAService = false; 356for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { 357AccessibilityServiceConnection boundService = 358userState.mBoundServices.get(i); 359String servicePkg = boundService.mComponentName.getPackageName(); 360if (servicePkg.equals(packageName)) { 361boundService.unbindLocked(); 362unboundAService = true; 363} 364} 365if (unboundAService) { 366onUserStateChangedLocked(userState); 367} 368} 369} 370 371@Override 372public void onPackageRemoved(String packageName, int uid) { 373synchronized (mLock) { 374final int userId = getChangingUserId(); 375// Only the profile parent can install accessibility services. 376// Therefore we ignore packages from linked profiles. 377if (userId != mCurrentUserId) { 378return; 379} 380UserState userState = getUserStateLocked(userId); 381Iterator<ComponentName> it = userState.mEnabledServices.iterator(); 382while (it.hasNext()) { 383ComponentName comp = it.next(); 384String compPkg = comp.getPackageName(); 385if (compPkg.equals(packageName)) { 386it.remove(); 387// Update the enabled services setting. 388persistComponentNamesToSettingLocked( 389Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 390userState.mEnabledServices, userId); 391// Update the touch exploration granted services setting. 392userState.mTouchExplorationGrantedServices.remove(comp); 393persistComponentNamesToSettingLocked( 394Settings.Secure. 395TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, 396userState.mTouchExplorationGrantedServices, userId); 397onUserStateChangedLocked(userState); 398return; 399} 400} 401} 402} 403 404@Override 405public boolean onHandleForceStop(Intent intent, String[] packages, 406int uid, boolean doit) { 407synchronized (mLock) { 408final int userId = getChangingUserId(); 409// Only the profile parent can install accessibility services. 410// Therefore we ignore packages from linked profiles. 411if (userId != mCurrentUserId) { 412return false; 413} 414UserState userState = getUserStateLocked(userId); 415Iterator<ComponentName> it = userState.mEnabledServices.iterator(); 416while (it.hasNext()) { 417ComponentName comp = it.next(); 418String compPkg = comp.getPackageName(); 419for (String pkg : packages) { 420if (compPkg.equals(pkg)) { 421if (!doit) { 422return true; 423} 424it.remove(); 425persistComponentNamesToSettingLocked( 426Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 427userState.mEnabledServices, userId); 428onUserStateChangedLocked(userState); 429} 430} 431} 432return false; 433} 434} 435}; 436 437// package changes 438monitor.register(mContext, null,UserHandle.ALL, true); 439 440// user change and unlock 441IntentFilter intentFilter = new IntentFilter(); 442intentFilter.addAction(Intent.ACTION_USER_SWITCHED); 443intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); 444intentFilter.addAction(Intent.ACTION_USER_REMOVED); 445intentFilter.addAction(Intent.ACTION_USER_PRESENT); 446intentFilter.addAction(Intent.ACTION_SETTING_RESTORED); 447 448mContext.registerReceiverAsUser(new BroadcastReceiver() { 449@Override 450public void onReceive(Context context, Intent intent) { 451String action = intent.getAction(); 452if (Intent.ACTION_USER_SWITCHED.equals(action)) { 453switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); 454} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { 455unlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); 456} else if (Intent.ACTION_USER_REMOVED.equals(action)) { 457removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); 458} else if (Intent.ACTION_USER_PRESENT.equals(action)) { 459// We will update when the automation service dies. 460synchronized (mLock) { 461UserState userState = getCurrentUserStateLocked(); 462if (readConfigurationForUserStateLocked(userState)) { 463onUserStateChangedLocked(userState); 464} 465} 466} else if (Intent.ACTION_SETTING_RESTORED.equals(action)) { 467final String which = intent.getStringExtra(Intent.EXTRA_SETTING_NAME); 468if (Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES.equals(which)) { 469synchronized (mLock) { 470restoreEnabledAccessibilityServicesLocked( 471intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE), 472intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)); 473} 474} 475} 476} 477}, UserHandle.ALL, intentFilter, null, null); 478}
這些狀態的變化都會呼叫到AMS的 onUserStateChangedLocked()
。
在 onUserStateChangedLocked()
中,我們關注 updateServicesLocked(userState)
這個函式,其他的函式是一些特定狀態的更新。
AccessibilityManagerService.java -- onUserStateChangedLocked()
/** 1765* Called when any property of the user state has changed. 1766* 1767* @param userState the new user state 1768*/ 1769private void onUserStateChangedLocked(UserState userState) { 1770// TODO: Remove this hack 1771mInitialized = true; 1772updateLegacyCapabilitiesLocked(userState); 1773updateServicesLocked(userState); 1774updateAccessibilityShortcutLocked(userState); 1775updateWindowsForAccessibilityCallbackLocked(userState); 1776updateAccessibilityFocusBehaviorLocked(userState); 1777updateFilterKeyEventsLocked(userState); 1778updateTouchExplorationLocked(userState); 1779updatePerformGesturesLocked(userState); 1780updateDisplayDaltonizerLocked(userState); 1781updateDisplayInversionLocked(userState); 1782updateMagnificationLocked(userState); 1783updateSoftKeyboardShowModeLocked(userState); 1784scheduleUpdateFingerprintGestureHandling(userState); 1785scheduleUpdateInputFilter(userState); 1786scheduleUpdateClientsIfNeededLocked(userState); 1787updateRelevantEventsLocked(userState); 1788updateAccessibilityButtonTargetsLocked(userState); 1789}
AccessibilityManagerService.java -- updateServicesLocked(userState)
1541private void updateServicesLocked(UserState userState) { 1542Map<ComponentName, AccessibilityServiceConnection> componentNameToServiceMap = 1543userState.mComponentNameToServiceMap; 1544boolean isUnlockingOrUnlocked = LocalServices.getService(UserManagerInternal.class) 1545.isUserUnlockingOrUnlocked(userState.mUserId); 1546 1547for (int i = 0, count = userState.mInstalledServices.size(); i < count; i++) { 1548AccessibilityServiceInfo installedService = userState.mInstalledServices.get(i); 1549ComponentName componentName = ComponentName.unflattenFromString( 1550installedService.getId()); 1551 1552AccessibilityServiceConnection service = componentNameToServiceMap.get(componentName); 1553 1554// Ignore non-encryption-aware services until user is unlocked 1555if (!isUnlockingOrUnlocked && !installedService.isDirectBootAware()) { 1556Slog.d(LOG_TAG, "Ignoring non-encryption-aware service " + componentName); 1557continue; 1558} 1559 1560// Wait for the binding if it is in process. 1561if (userState.mBindingServices.contains(componentName)) { 1562continue; 1563} 1564if (userState.mEnabledServices.contains(componentName) 1565&& !mUiAutomationManager.suppressingAccessibilityServicesLocked()) { 1566if (service == null) { 1567service = new AccessibilityServiceConnection(userState, mContext, componentName, 1568installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy, 1569this, mWindowManagerService, mGlobalActionPerformer); 1570} else if (userState.mBoundServices.contains(service)) { 1571continue; 1572} 1573service.bindLocked(); 1574} else { 1575if (service != null) { 1576service.unbindLocked(); 1577} 1578} 1579} 1580 1581final int count = userState.mBoundServices.size(); 1582mTempIntArray.clear(); 1583for (int i = 0; i < count; i++) { 1584final ResolveInfo resolveInfo = 1585userState.mBoundServices.get(i).mAccessibilityServiceInfo.getResolveInfo(); 1586if (resolveInfo != null) { 1587mTempIntArray.add(resolveInfo.serviceInfo.applicationInfo.uid); 1588} 1589} 1590// Calling out with lock held, but to a lower-level service 1591final AudioManagerInternal audioManager = 1592LocalServices.getService(AudioManagerInternal.class); 1593if (audioManager != null) { 1594audioManager.setAccessibilityServiceUids(mTempIntArray); 1595} 1596updateAccessibilityEnabledSetting(userState); 1597}
這個函式做了很多判斷,我畫了一個流程圖,首先要知道在AMS中,有這麼幾個List,看名字就知道每個List是什麼意思了。
mInstalledServices中,AccessibilityServiceInfo代表一個AccessibilityService的一些資訊。
mEnabledServices中,ComponentName包含了className和packageName,ComponentName資訊可以通過AccessibilityServiceInfo得到。
mComponentNameToServiceMap中,儲存了ComponentName與AccessibilityServiceConnection的對應關係,每一個AccessibilityServiceConnection對應一個連線的AccessibilityService。
3650public final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>(); 3651 3652public final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap = 3653new HashMap<>(); 3654 3655public final List<AccessibilityServiceInfo> mInstalledServices = 3656new ArrayList<>(); 3657 3658private final Set<ComponentName> mBindingServices = new HashSet<>(); 3659 3660public final Set<ComponentName> mEnabledServices = new HashSet<>();
(1)先遍歷mInstalledServices這個List,它會先判斷,這個AccessibilityService是否綁定了,如果綁定了,處理下一個;
(2)如果沒有綁定了,會判斷這個AccessibilityService是否處於enable狀態,怎麼判斷是否處於enable狀態呢?可以在mEnabledServices這個HashSet中查詢。
(2.1)如果是處於enable狀態,會在mComponentNameToServiceMap中通過ComponentName查詢對應的AccessibilityServiceConnection是否為空(也就是流程圖中的service)
(2.1.1)如果為空,會根據ComponentName,還有其他的一些資訊new一個AccessibilityServiceConnection物件,然後呼叫裡面的bindLocked()去繫結AccessibilityService。
(2.1.2)如果不為空,則處理下一個
(2.2)如果處於disable狀態,會在mComponentNameToServiceMap中通過ComponentName查詢對應的AccessibilityServiceConnection是否不為空(也就是流程圖中的service)
(2.2.1)如果為空,則處理下一個
(2.2.2)如果不為空,會呼叫AccessibilityServiceConnection的unbindLocked()

在這裡插入圖片描述
Step2:繫結過程
AccessibilityServiceConnection.java -- bindLocked()
91public void bindLocked() { 92UserState userState = mUserStateWeakReference.get(); 93if (userState == null) return; 94final long identity = Binder.clearCallingIdentity(); 95try { 96int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE; 97if (userState.mBindInstantServiceAllowed) { 98flags |= Context.BIND_ALLOW_INSTANT; 99} 100if (mService == null && mContext.bindServiceAsUser( 101mIntent, this, flags, new UserHandle(userState.mUserId))) { 102userState.getBindingServicesLocked().add(mComponentName); 103} 104} finally { 105Binder.restoreCallingIdentity(identity); 106} 107}
進而呼叫到Context中的 bindServiceAsUser()
,它傳入了Intent資訊,這個Intent包含了ComponentName等資訊,進而綁定了AS。接下來的過程其實就相當於是跨程序的IPC(也就是Binder了)。AS會通過 onBind(Intent intent)
這個函式返回一個IAccessibilityServiceClientWrapper物件給AccessibilityServiceConnection,這個物件就是AS的本地Binder,AccessibilityServiceConnection通過這個本地Binder去和AS通訊。
return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks();
AccessibilityServiceConnection會在 onServiceConnected(ComponentName componentName, IBinder service)
的IBinder引數中傳入IAccessibilityServiceClientWrapper,然後通過 IAccessibilityServiceClient.Stub.asInterface(service)
生成IAccessibilityServiceClient型別代理物件mServiceInterface,這個代理物件包含了AS的Callbacks函式,AMS通過這個代理物件去呼叫AS中的方法。
141public void onServiceConnected(ComponentName componentName, IBinder service) { 142synchronized (mLock) { 143if (mService != service) { 144if (mService != null) { 145mService.unlinkToDeath(this, 0); 146} 147mService = service; 148try { 149mService.linkToDeath(this, 0); 150} catch (RemoteException re) { 151Slog.e(LOG_TAG, "Failed registering death link"); 152binderDied(); 153return; 154} 155} 156mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service); 157UserState userState = mUserStateWeakReference.get(); 158if (userState == null) return; 159userState.addServiceLocked(this); 160mSystemSupport.onClientChange(false); 161// Initialize the service on the main handler after we're done setting up for 162// the new configuration (for example, initializing the input filter). 163mMainHandler.sendMessage(obtainMessage( 164AccessibilityServiceConnection::initializeService, this)); 165} 166}
AccessibilityService.java -- Callbacks{}
AS中的Callbacks函式介面
373/** 374* Interface used by IAccessibilityServiceWrapper to call the service from its main thread. 375* @hide 376*/ 377public interface Callbacks { 378void onAccessibilityEvent(AccessibilityEvent event); 379void onInterrupt(); 380void onServiceConnected(); 381void init(int connectionId, IBinder windowToken); 382boolean onGesture(int gestureId); 383boolean onKeyEvent(KeyEvent event); 384void onMagnificationChanged(@NonNull Region region, 385float scale, float centerX, float centerY); 386void onSoftKeyboardShowModeChanged(int showMode); 387void onPerformGestureResult(int sequence, boolean completedSuccessfully); 388void onFingerprintCapturingGesturesChanged(boolean active); 389void onFingerprintGesture(int gesture); 390void onAccessibilityButtonClicked(); 391void onAccessibilityButtonAvailabilityChanged(boolean available); 392}
下面的圖可能清晰一點。

在這裡插入圖片描述
以上就是AMS和AS連線的過程。
AM與AMS聯絡
其實也是一個Binder的過程啦,AM通過IAccessibilityManager(AMS的本地Binder)與AMS跨程序通訊。AMS通過IAccessibilityManagerClient(AM的本地Binder)與AM通訊。

在這裡插入圖片描述
來看程式碼,當Target APP觸發一個AccessibilityEvent(這個等會詳細說),它會new一個AM的例項。
321public static AccessibilityManager getInstance(Context context) { 322synchronized (sInstanceSync) { 323if (sInstance == null) { 324final int userId; 325if (Binder.getCallingUid() == Process.SYSTEM_UID 326|| context.checkCallingOrSelfPermission( 327Manifest.permission.INTERACT_ACROSS_USERS) 328== PackageManager.PERMISSION_GRANTED 329|| context.checkCallingOrSelfPermission( 330Manifest.permission.INTERACT_ACROSS_USERS_FULL) 331== PackageManager.PERMISSION_GRANTED) { 332userId = UserHandle.USER_CURRENT; 333} else { 334userId = context.getUserId(); 335} 336sInstance = new AccessibilityManager(context, null, userId); 337} 338} 339return sInstance; 340}
AccessibilityManager的建構函式,然後會呼叫 tryConnectToServiceLocked()
函式。
AccessibilityManager.java -- AccessibilityManager()
351public AccessibilityManager(Context context, IAccessibilityManager service, int userId) { 352// Constructor can't be chained because we can't create an instance of an inner class 353// before calling another constructor. 354mCallback = new MyCallback(); 355mHandler = new Handler(context.getMainLooper(), mCallback); 356mUserId = userId; 357synchronized (mLock) { 358tryConnectToServiceLocked(service); 359} 360}
在 tryConnectToServiceLocked()
中,不僅會得到AMS的本地Binder(函式中的service),會通過 addClient(mClient, mUserId)
這個函式把自己的資訊註冊進去。
AccessibilityManager.java -- tryConnectToServiceLocked()
1115private void tryConnectToServiceLocked(IAccessibilityManager service) { 1116if (service == null) { 1117IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); 1118if (iBinder == null) { 1119return; 1120} 1121service = IAccessibilityManager.Stub.asInterface(iBinder); 1122} 1123 1124try { 1125final long userStateAndRelevantEvents = service.addClient(mClient, mUserId); 1126setStateLocked(IntPair.first(userStateAndRelevantEvents)); 1127mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents); 1128mService = service; 1129} catch (RemoteException re) { 1130Log.e(LOG_TAG, "AccessibilityManagerService is dead", re); 1131} 1132}
mClient
是一個 IAccessibilityManagerClient
型別的本地Binder
276private final IAccessibilityManagerClient.Stub mClient = 277new IAccessibilityManagerClient.Stub()
下面的圖也許更清楚一點,但總感覺哪裡有點不對勁。

在這裡插入圖片描述
AccessibilityEvent Dispatch
整個過程從事件觸發一直到響應,走過了很長的路,因此我把它分成三個部分:
- AccessibilityEvent Trigger
- AccessibilityEvent Dispatch
- AccessibilityEvent Response

在這裡插入圖片描述
AccessibilityEvent Trigger
這一部分,是當有一個AccessibilityEvent觸發後,怎麼到的AMS裡面。先來看看AccessibilityEvent哪些型別。這些型別包括很多種,比如點選,手勢,焦點等等。

在這裡插入圖片描述
當用戶的一些操作比如click,觸發了Event(這時還不是AccessibilityEvent),會呼叫 sendAccessibilityEvent()
。
View.java -- performClick()
6590public boolean performClick() { 6591// We still need to call this method to handle the cases where performClick() was called 6592// externally, instead of through performClickInternal() 6593notifyAutofillManagerOnClick(); 6594 6595final boolean result; 6596final ListenerInfo li = mListenerInfo; 6597if (li != null && li.mOnClickListener != null) { 6598playSoundEffect(SoundEffectConstants.CLICK); 6599if (ViewDebugManager.DEBUG_TOUCH) { 6600Log.d(VIEW_LOG_TAG, "(View)performClick, listener = " + li.mOnClickListener 6601+ ",this = " + this); 6602} 6603li.mOnClickListener.onClick(this); 6604result = true; 6605} else { 6606result = false; 6607} 6608 6609sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); 6610 6611notifyEnterOrExitForAutoFillIfNeeded(true); 6612 6613return result; 6614}
再來看 sendAccessibilityEvent()
裡面做了什麼,這個AccessibilityDelegate我不知道具體是什麼意思,可看 官網解釋 ,這裡可以暫時先不用管它,然後可以看到它呼叫了 sendAccessibilityEventInternal()
。
View.java -- sendAccessibilityEvent()
7374public void sendAccessibilityEvent(int eventType) { 7375if (mAccessibilityDelegate != null) { 7376mAccessibilityDelegate.sendAccessibilityEvent(this, eventType); 7377} else { 7378sendAccessibilityEventInternal(eventType); 7379} 7380}
在 sendAccessibilityEventInternal()
中,建立了一個AM的例項, 在AM與AMS聯絡 這一節講到,建立例項的時候會與AMS通過Binder連線,然後這裡會判斷是否enable狀態,再呼叫 sendAccessibilityEventUnchecked()
函式。
View.java -- sendAccessibilityEventInternal()
7409public void sendAccessibilityEventInternal(int eventType) { 7410if (AccessibilityManager.getInstance(mContext).isEnabled()) { 7411sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType)); 7412} 7413}
在 sendAccessibilityEventUnchecked()
中又會呼叫 sendAccessibilityEventUncheckedInternal()
。
View.java -- sendAccessibilityEventUnchecked()
7430public void sendAccessibilityEventUnchecked(AccessibilityEvent event) { 7431if (mAccessibilityDelegate != null) { 7432mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event); 7433} else { 7434sendAccessibilityEventUncheckedInternal(event); 7435} 7436}
在 sendAccessibilityEventUncheckedInternal()
中,會呼叫 onInitializeAccessibilityEvent()
初始化一些event資訊,比如className/packageName/source等,然後會呼叫 getParent().requestSendAccessibilityEvent(this, event)
將event分發給ParentView。
View.java -- sendAccessibilityEventUncheckedInternal()
7445public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) { 7446// Panes disappearing are relevant even if though the view is no longer visible. 7447boolean isWindowStateChanged = 7448(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 7449boolean isWindowDisappearedEvent = isWindowStateChanged && ((event.getContentChangeTypes() 7450& AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED) != 0); 7451if (!isShown() && !isWindowDisappearedEvent) { 7452return; 7453} 7454onInitializeAccessibilityEvent(event); 7455// Only a subset of accessibility events populates text content. 7456if ((event.getEventType() & POPULATING_ACCESSIBILITY_EVENT_TYPES) != 0) { 7457dispatchPopulateAccessibilityEvent(event); 7458} 7459// In the beginning we called #isShown(), so we know that getParent() is not null. 7460ViewParent parent = getParent(); 7461if (parent != null) { 7462getParent().requestSendAccessibilityEvent(this, event); 7463} 7464}
這裡不管ParentView是哪一個,最終會到View層次中的頂層,也就是ViewRootImpl的 requestSendAccessibilityEvent()
。這裡,會對一些特殊Type的AccessibilityEvent做特殊處理,最終是呼叫到 mAccessibilityManager.sendAccessibilityEvent(event)
,也就是到了AM。
ViewRootImpl.java -- requestSendAccessibilityEvent()
7788public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { 7789if (mView == null || mStopped || mPausedForTransition) { 7790return false; 7791} 7792 7793// Immediately flush pending content changed event (if any) to preserve event order 7794if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED 7795&& mSendWindowContentChangedAccessibilityEvent != null 7796&& mSendWindowContentChangedAccessibilityEvent.mSource != null) { 7797mSendWindowContentChangedAccessibilityEvent.removeCallbacksAndRun(); 7798} 7799 7800// Intercept accessibility focus events fired by virtual nodes to keep 7801// track of accessibility focus position in such nodes. 7802final int eventType = event.getEventType(); 7803switch (eventType) { 7804case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { 7805final long sourceNodeId = event.getSourceNodeId(); 7806final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId( 7807sourceNodeId); 7808View source = mView.findViewByAccessibilityId(accessibilityViewId); 7809if (source != null) { 7810AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider(); 7811if (provider != null) { 7812final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId( 7813sourceNodeId); 7814final AccessibilityNodeInfo node; 7815node = provider.createAccessibilityNodeInfo(virtualNodeId); 7816setAccessibilityFocus(source, node); 7817} 7818} 7819} break; 7820case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: { 7821final long sourceNodeId = event.getSourceNodeId(); 7822final int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId( 7823sourceNodeId); 7824View source = mView.findViewByAccessibilityId(accessibilityViewId); 7825if (source != null) { 7826AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider(); 7827if (provider != null) { 7828setAccessibilityFocus(null, null); 7829} 7830} 7831} break; 7832 7833 7834case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: { 7835handleWindowContentChangedEvent(event); 7836} break; 7837} 7838mAccessibilityManager.sendAccessibilityEvent(event); 7839return true; 7840}
在AM和AMS的聯絡一節中講到,AM會用AMS的本地Binder IAccessibilityManager
去和AMS通訊,在AM的 sendAccessibilityEvent()
可以看到定義了一個 IAccessibilityManager
型別的service,通過 getServiceLocked()
獲取本地Binder,然後通過 service.sendAccessibilityEvent(dispatchedEvent, userId)
去呼叫AMS的sendAccessibilityEvent方法。到這裡Trigger的部分就結束了,然後是AMS的分發過程。
AccessibilityManager.java -- sendAccessibilityEvent()
456public void sendAccessibilityEvent(AccessibilityEvent event) { 457final IAccessibilityManager service; 458final int userId; 459final AccessibilityEvent dispatchedEvent; 460synchronized (mLock) { 461service = getServiceLocked(); 462if (service == null) { 463return; 464} 465event.setEventTime(SystemClock.uptimeMillis()); 466if (mAccessibilityPolicy != null) { 467dispatchedEvent = mAccessibilityPolicy.onAccessibilityEvent(event, 468mIsEnabled, mRelevantEventTypes); 469if (dispatchedEvent == null) { 470return; 471} 472} else { 473dispatchedEvent = event; 474} 475if (!isEnabled()) { 476Looper myLooper = Looper.myLooper(); 477if (myLooper == Looper.getMainLooper()) { 478throw new IllegalStateException( 479"Accessibility off. Did you forget to check that?"); 480} else { 481// If we're not running on the thread with the main looper, it's possible for 482// the state of accessibility to change between checking isEnabled and 483// calling this method. So just log the error rather than throwing the 484// exception. 485Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled"); 486return; 487} 488} 489if ((dispatchedEvent.getEventType() & mRelevantEventTypes) == 0) { 490if (DEBUG) { 491Log.i(LOG_TAG, "Not dispatching irrelevant event: " + dispatchedEvent 492+ " that is not among " 493+ AccessibilityEvent.eventTypeToString(mRelevantEventTypes)); 494} 495return; 496} 497userId = mUserId; 498} 499try { 500// it is possible that this manager is in the same process as the service but 501// client using it is called through Binder from another process. Example: MMS 502// app adds a SMS notification and the NotificationManagerService calls this method 503long identityToken = Binder.clearCallingIdentity(); 504try { 505service.sendAccessibilityEvent(dispatchedEvent, userId); 506} finally { 507Binder.restoreCallingIdentity(identityToken); 508} 509if (DEBUG) { 510Log.i(LOG_TAG, dispatchedEvent + " sent"); 511} 512} catch (RemoteException re) { 513Log.e(LOG_TAG, "Error during sending " + dispatchedEvent + " ", re); 514} finally { 515if (event != dispatchedEvent) { 516event.recycle(); 517} 518dispatchedEvent.recycle(); 519} 520}
AccessibilityEvent Dispatch
在AMS的 sendAccessibilityEvent()
中,會呼叫 notifyAccessibilityServicesDelayedLocked()
。
AccessibilityManagerService.java -- sendAccessibilityEvent()
519public void sendAccessibilityEvent(AccessibilityEvent event, int userId) { 520boolean dispatchEvent = false; 521 522synchronized (mLock) { 523if (event.getWindowId() == 524AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID) { 525// The replacer window isn't shown to services. Move its events into the pip. 526AccessibilityWindowInfo pip = mSecurityPolicy.getPictureInPictureWindow(); 527if (pip != null) { 528int pipId = pip.getId(); 529event.setWindowId(pipId); 530} 531} 532 533// We treat calls from a profile as if made by its parent as profiles 534// share the accessibility state of the parent. The call below 535// performs the current profile parent resolution. 536final int resolvedUserId = mSecurityPolicy 537.resolveCallingUserIdEnforcingPermissionsLocked(userId); 538 539// Make sure the reported package is one the caller has access to. 540event.setPackageName(mSecurityPolicy.resolveValidReportedPackageLocked( 541event.getPackageName(), UserHandle.getCallingAppId(), resolvedUserId)); 542 543// This method does nothing for a background user. 544if (resolvedUserId == mCurrentUserId) { 545if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) { 546mSecurityPolicy.updateActiveAndAccessibilityFocusedWindowLocked( 547event.getWindowId(), event.getSourceNodeId(), 548event.getEventType(), event.getAction()); 549mSecurityPolicy.updateEventSourceLocked(event); 550dispatchEvent = true; 551} 552if (mHasInputFilter && mInputFilter != null) { 553mMainHandler.sendMessage(obtainMessage( 554AccessibilityManagerService::sendAccessibilityEventToInputFilter, 555this, AccessibilityEvent.obtain(event))); 556} 557} 558} 559 560if (dispatchEvent) { 561// Make sure clients receiving this event will be able to get the 562// current state of the windows as the window manager may be delaying 563// the computation for performance reasons. 564if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED 565&& mWindowsForAccessibilityCallback != null) { 566WindowManagerInternal wm = LocalServices.getService(WindowManagerInternal.class); 567wm.computeWindowsForAccessibility(); 568} 569synchronized (mLock) { 570notifyAccessibilityServicesDelayedLocked(event, false); 571notifyAccessibilityServicesDelayedLocked(event, true); 572mUiAutomationManager.sendAccessibilityEventLocked(event); 573} 574} 575 576if (OWN_PROCESS_ID != Binder.getCallingPid()) { 577event.recycle(); 578} 579}
在 AMS繫結AS 這一節講到,AMS會維護一個繫結AS的List( mBoundServices
),List中每一個AccessibilityServiceConnection對應一個繫結的AS,因此遍歷mBoundServices,然後去到AccessibilityServiceConnection的 notifyAccessibilityEvent()
函式。
AccessibilityManagerService.java -- notifyAccessibilityServicesDelayedLocked()
1378private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, 1379boolean isDefault) { 1380try { 1381UserState state = getCurrentUserStateLocked(); 1382for (int i = 0, count = state.mBoundServices.size(); i < count; i++) { 1383AccessibilityServiceConnection service = state.mBoundServices.get(i); 1384 1385if (service.mIsDefault == isDefault) { 1386service.notifyAccessibilityEvent(event); 1387} 1388} 1389} catch (IndexOutOfBoundsException oobe) { 1390// An out of bounds exception can happen if services are going away 1391// as the for loop is running. If that happens, just bail because 1392// there are no more services to notify. 1393} 1394}
AccessibilityServiceConnection是繼承AbstractAccessibilityServiceConnection的,這裡 notifyAccessibilityEvent()
會發送一個message。
AbstractAccessibilityServiceConnection.java -- notifyAccessibilityEvent()
967public void notifyAccessibilityEvent(AccessibilityEvent event) { 968synchronized (mLock) { 969final int eventType = event.getEventType(); 970 971final boolean serviceWantsEvent = wantsEventLocked(event); 972final boolean requiredForCacheConsistency = mUsesAccessibilityCache 973&& ((AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK & eventType) != 0); 974if (!serviceWantsEvent && !requiredForCacheConsistency) { 975return; 976} 977 978// Make a copy since during dispatch it is possible the event to 979// be modified to remove its source if the receiving service does 980// not have permission to access the window content. 981AccessibilityEvent newEvent = AccessibilityEvent.obtain(event); 982Message message; 983if ((mNotificationTimeout > 0) 984&& (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)) { 985// Allow at most one pending event 986final AccessibilityEvent oldEvent = mPendingEvents.get(eventType); 987mPendingEvents.put(eventType, newEvent); 988if (oldEvent != null) { 989mEventDispatchHandler.removeMessages(eventType); 990oldEvent.recycle(); 991} 992message = mEventDispatchHandler.obtainMessage(eventType); 993} else { 994// Send all messages, bypassing mPendingEvents 995message = mEventDispatchHandler.obtainMessage(eventType, newEvent); 996} 997message.arg1 = serviceWantsEvent ? 1 : 0; 998 999mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout); 1000} 1001}
在AbstractAccessibilityServiceConnection的建構函式中,有對訊息的處理,它最終會呼叫 notifyAccessibilityEventInternal()
。
AbstractAccessibilityServiceConnection.java -- AbstractAccessibilityServiceConnection()
241public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName, 242AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler, 243Object lock, SecurityPolicy securityPolicy, SystemSupport systemSupport, 244WindowManagerInternal windowManagerInternal, 245GlobalActionPerformer globalActionPerfomer) { 246mContext = context; 247mWindowManagerService = windowManagerInternal; 248mId = id; 249mComponentName = componentName; 250mAccessibilityServiceInfo = accessibilityServiceInfo; 251mLock = lock; 252mSecurityPolicy = securityPolicy; 253mGlobalActionPerformer = globalActionPerfomer; 254mSystemSupport = systemSupport; 255mInvocationHandler = new InvocationHandler(mainHandler.getLooper()); 256mEventDispatchHandler = new Handler(mainHandler.getLooper()) { 257@Override 258public void handleMessage(Message message) { 259final int eventType =message.what; 260AccessibilityEvent event = (AccessibilityEvent) message.obj; 261boolean serviceWantsEvent = message.arg1 != 0; 262notifyAccessibilityEventInternal(eventType, event, serviceWantsEvent); 263} 264}; 265setDynamicallyConfigurableProperties(accessibilityServiceInfo); 266}
在 notifyAccessibilityEventInternal()
中, listener
是AS的本地Binder(IAccessibilityServiceClient型別),最終是回撥到了AS的 onAccessibilityEvent()
。到這裡Dispatch的部分就結束了。
AbstractAccessibilityServiceConnection.java -- notifyAccessibilityEventInternal()
1040private void notifyAccessibilityEventInternal( 1041int eventType, 1042AccessibilityEvent event, 1043boolean serviceWantsEvent) { 1044IAccessibilityServiceClient listener; 1045 1046synchronized (mLock) { 1047listener = mServiceInterface; 1048 1049// If the service died/was disabled while the message for dispatching 1050// the accessibility event was propagating the listener may be null. 1051if (listener == null) { 1052return; 1053} 1054 1055// There are two ways we notify for events, throttled AND non-throttled. If we 1056// are not throttling, then messages come with events, which we handle with 1057// minimal fuss. 1058if (event == null) { 1059// We are throttling events, so we'll send the event for this type in 1060// mPendingEvents as long as it it's null. It can only null due to a race 1061// condition: 1062// 1063//1) A binder thread calls notifyAccessibilityServiceDelayedLocked 1064//which posts a message for dispatching an event and stores the event 1065//in mPendingEvents. 1066//2) The message is pulled from the queue by the handler on the service 1067//thread and this method is just about to acquire the lock. 1068//3) Another binder thread acquires the lock in notifyAccessibilityEvent 1069//4) notifyAccessibilityEvent recycles the event that this method was about 1070//to process, replaces it with a new one, and posts a second message 1071//5) This method grabs the new event, processes it, and removes it from 1072//mPendingEvents 1073//6) The second message dispatched in (4) arrives, but the event has been 1074//remvoved in (5). 1075event = mPendingEvents.get(eventType); 1076if (event == null) { 1077return; 1078} 1079mPendingEvents.remove(eventType); 1080} 1081if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) { 1082event.setConnectionId(mId); 1083} else { 1084event.setSource((View) null); 1085} 1086event.setSealed(true); 1087} 1088 1089try { 1090listener.onAccessibilityEvent(event, serviceWantsEvent); 1091if (DEBUG) { 1092Slog.i(LOG_TAG, "Event " + event + " sent to " + listener); 1093} 1094} catch (RemoteException re) { 1095Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re); 1096} finally { 1097event.recycle(); 1098} 1099}
Accessibility Response
至於這裡AS的 onAccessibilityEvent()
就看實際需求寫了,官網給了一個簡單的例子:
這裡根據AccessibilityEvent的型別來判斷,然後case語句,每一步去做什麼。

在這裡插入圖片描述
那如果要回到View去操作怎麼辦呢?比如WeChat的例子,我還要回到View中去模擬點選Open按鈕怎麼辦呢?
其實這裡我沒有具體去深入研究過,不過我找到兩篇部落格,有興趣可以看一看。簡略的說來就是Dispatch的逆過程了。