三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Android 7系统无障碍服务(二)AccessibilityManagerService 启动与初始化

Android 7系统无障碍服务(二)AccessibilityManagerService 启动与初始化

系列目录:第一篇:全景图与架构概览 | 第二篇:AccessibilityManagerService 启动与初始化 | 第三篇:无障碍服务的注册与绑定 | 第四篇:AccessibilityEvent 的产生与投递 | 第五篇:AccessibilityNodeInfo 的构建与查询 | 第六篇:输入事件拦截与 TouchExplorer | 第七篇:手势分发与 KeyEvent 处理 | 第八篇:放大镜与辅助功能特性


上一篇我们从宏观上梳理了无障碍服务的五层架构、核心类关系和三条主干数据流。本篇聚焦于整个无障碍模块的"中枢"——AccessibilityManagerService(以下简称 AMS),深入分析它从 SystemServer 启动到完成初始化的全过程。


一、启动入口:SystemServer.startOtherServices()

AMS 的创建发生在 SystemServer 的startOtherServices()方法中,与 ActivityManagerService、WindowManagerService 等核心服务一样,属于系统启动的中后期阶段。

源码路径frameworks/base/services/java/com/android/server/SystemServer.java

privatevoidstartOtherServices(){// ... 大量其他服务的初始化 ...// 获取 WindowManagerService,它在 AMS 之前初始化inputManager=newInputManagerService(context);wm=WindowManagerService.main(context,inputManager,mFactoryTestMode!=FactoryTest.FACTORY_TEST_LOW_LEVEL,!mFirstBoot,mOnlyCore,newPhoneWindowManager());// ... 其他服务 ...// 将已初始化的服务注册到 ServiceManagerServiceManager.addService(Context.WINDOW_SERVICE,wm);ServiceManager.addService(Context.INPUT_SERVICE,inputManager);// ...// 创建 AccessibilityManagerServiceaccessibility=newAccessibilityManagerService(context);// 注册到 ServiceManager,使客户端可以通过 Binder 获取ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,accessibility);}

调用链路很简洁:

SystemServer.startOtherServices() → new AccessibilityManagerService(context) → ServiceManager.addService(Context.ACCESSIBILITY_SERVICE, accessibility)

这里有两个值得注意的细节:

  1. AMS 的创建时机在 WMS 之后,但 WMS 实例并没有直接传递给 AMS 的构造函数。AMS 的构造函数仅接收Context,后续需要 WMS 时通过LocalServices机制获取WindowManagerInternal——这是一种更晚绑定、更低耦合的设计。

  2. AMS 注册的服务名是Context.ACCESSIBILITY_SERVICE,也就是字符串"accessibility"。客户端通过Context.getSystemService(Context.ACCESSIBILITY_SERVICE)拿到的就是 AMS 的 Binder 代理——实际上是IAccessibilityManager.Stub的代理对象。


二、AMS 构造函数:内部状态的初始化

AMS 的构造函数完成了全部"基础设施"的搭建。

源码路径frameworks/base/services/core/java/com/android/server/accessibility/AccessibilityManagerService.java

publicAccessibilityManagerService(Contextcontext){mContext=context;// 创建 PackageManager 引用,用于后续查询已安装的无障碍服务mPackageManager=mContext.getPackageManager();// 创建 PowerManager 引用,放大镜功能需要用 WakeLockmPowerManager=(PowerManager)mContext.getSystemService(Context.POWER_SERVICE);// 初始化全局锁对象 —— 几乎所有对内部状态的修改都需持有此锁mLock=newObject();// 当前操作用户 ID,初始化为当前活跃用户mCurrentUserId=ActivityManager.getCurrentUser();// 创建主线程 Handler,AMS 中大量异步操作依赖它mMainHandler=newMainHandler(mContext.getMainLooper());// 向 WMS 注册无障碍焦点相关的 WindowManagerInternal// 通过 LocalServices 获取引用,不直接持有 WMS// 初始化 UserState —— 每个用户独立的状态存储mUserStates=newSparseArray<>();// 读取 Secure Settings 初始化当前用户的配置UserStateuserState=newUserState(mCurrentUserId);mUserStates.put(mCurrentUserId,userState);readConfigurationForUserStateLocked(userState);// 注册 Settings.Secure 内容观察者registerSettingsContentObservers(mCurrentUserId);// 注册系统广播接收器registerBroadcastReceivers();}

构造函数中的初始化可以归为以下几类:

2.1 系统服务引用

变量类型用途
mPackageManagerPackageManager查询已安装无障碍服务列表、解析 ServiceInfo
mPowerManagerPowerManager放大镜功能需要防止屏幕超时(WakeLock)
mLockObject全局同步锁,保护内部状态的一致性

2.2 匿名 Binder:mService

mService是 AMS 内部最核心的服务端 Binder,一个匿名IAccessibilityManager.Stub实现。所有来自AccessibilityManager(客户端)和AccessibilityService的跨进程调用,都通过这个 Binder 进入 AMS。

// 源码路径:frameworks/base/services/core/java/com/android/server/accessibility/AccessibilityManagerService.javaprivatefinalIAccessibilityManager.StubmService=newIAccessibilityManager.Stub(){@OverridepublicvoidsendAccessibilityEvent(AccessibilityEventevent,intuserId){// 第四篇重点:事件的跨进程入口}@OverridepublicbooleansendFingerprintGesture(intkeyCode){// 指纹手势的无障碍处理}@OverridepublicList<AccessibilityServiceInfo>getInstalledAccessibilityServiceList(intuserId){// 查询已安装的无障碍服务列表}@OverridepublicList<AccessibilityServiceInfo>getEnabledAccessibilityServices(intfeedbackType){// 查询已启用的无障碍服务列表}@Overridepublicvoidinterrupt(intuserId){// 中断所有无障碍服务的当前操作}@OverridepublicintaddClient(IAccessibilityManagerClientclient,intuserId){// 客户端注册回调,用于状态变化通知}@OverridepublicvoidregisterUiTestAutomationService(IBinderowner,IAccessibilityServiceClientclient,AccessibilityServiceInfoinfo,intflags){// UiAutomation 测试框架专用的注册入口}// ... 更多接口方法,涵盖事件投递、服务管理、状态查询等};

这个匿名 Binder 通过onBind()暴露给 ServiceManager:

@OverridepublicIBinderonBind(Intentintent){returnmService;}

ServiceManager.addService(Context.ACCESSIBILITY_SERVICE, accessibility)执行时,系统会将mService注册到 ServiceManager,使所有进程都能通过 Binder 查询到它。

mService中的每个方法几乎都遵循同一个调用模式:

Binder 线程 → 获取 mLock → 切 mMainHandler → 执行业务逻辑

这种"Binder 线程加锁 + Handler 切主线程"的双重保护,确保了多线程环境下内部状态的一致性。

2.3 mMainHandler:主线程消息处理

AMS 内部定义了一个MainHandler,是Handler的子类,承载了大量延时任务和异步回调的分发:

// 源码路径:frameworks/base/services/core/java/com/android/server/accessibility/AccessibilityManagerService.javaprivatefinalclassMainHandlerextendsHandler{publicstaticfinalintMSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER=1;publicstaticfinalintMSG_SEND_KEY_EVENT_TO_INPUT_FILTER=2;publicstaticfinalintMSG_SEND_STATE_TO_CLIENTS=3;publicstaticfinalintMSG_CLEAR_ACCESSIBILITY_CACHE=4;publicstaticfinalintMSG_ANNOUNCE_NEW_USER_IF_NEEDED=5;publicstaticfinalintMSG_CLEAR_ACCESSIBILITY_FOCUS=6;publicMainHandler(Looperlooper){super(looper);}@OverridepublicvoidhandleMessage(Messagemsg){switch(msg.what){caseMSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER:// 将事件发送到 InputFilter 链,供 TouchExplorer 等处理notifyAccessibilityServicesDelayedLocked(...);break;caseMSG_SEND_KEY_EVENT_TO_INPUT_FILTER:// 发送按键事件到 InputFilter 链break;caseMSG_SEND_STATE_TO_CLIENTS:// 通知所有已注册客户端无障碍状态变化break;caseMSG_CLEAR_ACCESSIBILITY_CACHE:// 通知所有 AccessibilityService 清除内部缓存break;caseMSG_ANNOUNCE_NEW_USER_IF_NEEDED:// 多用户切换时的语音提示break;caseMSG_CLEAR_ACCESSIBILITY_FOCUS:// 清除无障碍焦点break;}}}

这些消息类型覆盖了事件投递(第四篇)、状态广播、缓存清理(第五篇)等后续各篇涉及的核心流程。理解 MainHandler 的消息模型,是理解整个 AMS 异步机制的基石。


三、UserState:多用户状态隔离

Android 7 支持多用户(Multi-User),每个用户拥有独立的无障碍服务配置和运行时状态。AMS 通过UserState内部类实现用户级隔离,以SparseArray<UserState>存储所有用户状态,键为用户 ID。

源码路径frameworks/base/services/core/java/com/android/server/accessibility/AccessibilityManagerService.java

privateclassUserState{// 用户 IDpublicfinalintmUserId;// ===== 持久化配置项(来自 Settings.Secure)=====// 已启用的无障碍服务列表(ComponentName 字符串,冒号分隔)publicStringmEnabledServicesStr;// 触摸探索模式是否允许publicbooleanmTouchExplorationGranted;// 放大镜是否开启publicbooleanmMagnificationEnabled;// 无障碍快捷方式是否开启publicbooleanmAccessibilityShortcutEnabled;// 无障碍快捷方式对应的目标服务 ComponentNamepublicStringmAccessibilityShortcutTarget;// 高对比度文字是否开启publicbooleanmHighTextContrastEnabled;// 字体缩放比例publicfloatmFontScale;// ===== 运行时状态 =====// 已绑定并正在运行的无障碍服务列表publicfinalList<Service>mBoundServices=newArrayList<>();// 正在绑定中的服务(ComponentName 集合)publicfinalSet<ComponentName>mBindingServices=newHashSet<>();// 已安装的无障碍服务信息缓存publicList<AccessibilityServiceInfo>mInstalledServices;// 无障碍是否全局开启publicbooleanmEnabled;// 当前有无障碍焦点的窗口 IDpublicintmAccessibilityFocusWindowId=-1;// 当前有无障碍焦点的节点 IDpubliclongmAccessibilityFocusNodeId=AccessibilityNodeInfo.UNDEFINED_ITEM_ID;// ContentObserver 引用(切换用户时用于注销)publicContentObservermContentObserver;// 广播接收器列表publicfinalList<BroadcastReceiver>mReceivers=newArrayList<>();}

多用户切换时,AMS 的switchUser()方法会:

  1. 在当前 UserState 上调用unbindAllServicesLocked()停止所有服务
  2. 切换到目标 UserState
  3. 在目标 UserState 上调用bindServicesLocked()重新绑定该用户启用的服务

mUserStatesSparseArray结构确保了不同用户间的状态完全隔离。


四、Settings.Secure 配置项读取与初始化

无障碍服务的大部分开关和配置都通过Settings.Secure持久化存储。AMS 在初始化 UserState 时,需要一次性读取所有相关配置并缓存到内存。

4.1 关键 Secure 配置项一览

配置项 Key类型含义
ACCESSIBILITY_ENABLEDint (0/1)无障碍功能全局开关
ENABLED_ACCESSIBILITY_SERVICESString已启用的无障碍服务列表,格式pkg/ServiceCls:pkg2/ServiceCls
TOUCH_EXPLORATION_ENABLEDint (0/1)触摸探索模式开关
ACCESSIBILITY_SHORTCUT_ENABLEDint (0/1)无障碍快捷方式开关
ACCESSIBILITY_SHORTCUT_TARGET_SERVICEString快捷方式目标服务 ComponentName
ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLEDint (0/1)放大镜开关
ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLEDint (0/1)高对比度文字开关
FONT_SCALEfloat字体缩放比例
ACCESSIBILITY_SCRIPT_INJECTIONint (0/1)允许服务注入网页脚本

4.2 readConfigurationForUserStateLocked()

这个方法在初始化时被调用,将所有 Secure 配置一次性读取并缓存到 UserState:

privatevoidreadConfigurationForUserStateLocked(UserStateuserState){// 全局开关userState.mEnabled=(Settings.Secure.getIntForUser(mContext.getContentResolver(),Settings.Secure.ACCESSIBILITY_ENABLED,0,userState.mUserId)==1);// 已启用的服务列表字符串userState.mEnabledServicesStr=Settings.Secure.getStringForUser(mContext.getContentResolver(),Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,userState.mUserId);// 触摸探索userState.mTouchExplorationGranted=(Settings.Secure.getIntForUser(mContext.getContentResolver(),Settings.Secure.TOUCH_EXPLORATION_ENABLED,0,userState.mUserId)==1);// 放大镜userState.mMagnificationEnabled=(Settings.Secure.getIntForUser(mContext.getContentResolver(),Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,0,userState.mUserId)==1);// 高对比度文字userState.mHighTextContrastEnabled=(Settings.Secure.getIntForUser(mContext.getContentResolver(),Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,0,userState.mUserId)==1);// ... 其他配置项 ...}

后续所有状态判断都直接使用内存缓存,而不是每次都查询 ContentProvider,这是一个典型的性能优化策略。


五、ContentObserver:配置变化的实时监听

仅仅读取初始值是不够的——用户在"设置 → 无障碍"中修改任何开关,AMS 都必须立即响应。这是通过ContentObserver实现的。

源码路径frameworks/base/services/core/java/com/android/server/accessibility/AccessibilityManagerService.java

privatevoidregisterSettingsContentObservers(intuserId){UserStateuserState=getUserStateLocked(userId);ContentObserverobserver=newContentObserver(mMainHandler){@OverridepublicvoidonChange(booleanselfChange,Uriuri){synchronized(mLock){UserStateuserState=getCurrentUserStateLocked();// 无障碍全局开关变化 → 更新状态并重新绑定/解绑服务if(userState.mEnabled!=isAccessibilityEnabled()){userState.mEnabled=isAccessibilityEnabled();if(userState.mEnabled){bindServicesLocked(userState);// 关→开:绑定服务}else{unbindAllServicesLocked(userState);// 开→关:解绑全部}notifyClientsOfStateChangeLocked(userState);}// 已启用服务列表变化 → 重新绑定StringnewServicesStr=Settings.Secure.getStringForUser(mContext.getContentResolver(),Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,userId);if(!newServicesStr.equals(userState.mEnabledServicesStr)){userState.mEnabledServicesStr=newServicesStr;unbindAllServicesLocked(userState);bindServicesLocked(userState);}// 触摸探索开关变化 → 重新配置 InputFilter// 放大镜开关变化 → 重新配置 MagnificationController// ...}}};ContentResolverresolver=mContext.getContentResolver();// 为每个关键 URI 注册监听resolver.registerContentObserver(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_ENABLED),false,observer,userId);resolver.registerContentObserver(Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES),false,observer,userId);resolver.registerContentObserver(Settings.Secure.getUriFor(Settings.Secure.TOUCH_EXPLORATION_ENABLED),false,observer,userId);// ... 更多 URI 注册userState.mContentObserver=observer;}

这里有三个关键设计细节:

  1. **回调在 m
← 返回列表