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

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


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


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

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

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

java 复制代码
private void startOtherServices() {
    // ... 大量其他服务的初始化 ...

    // 获取 WindowManagerService,它在 AMS 之前初始化
    inputManager = new InputManagerService(context);
    wm = WindowManagerService.main(context, inputManager,
            mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
            !mFirstBoot, mOnlyCore, new PhoneWindowManager());

    // ... 其他服务 ...

    // 将已初始化的服务注册到 ServiceManager
    ServiceManager.addService(Context.WINDOW_SERVICE, wm);
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

    // ...

    // 创建 AccessibilityManagerService
    accessibility = new AccessibilityManagerService(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

java 复制代码
public AccessibilityManagerService(Context context) {
    mContext = context;

    // 创建 PackageManager 引用,用于后续查询已安装的无障碍服务
    mPackageManager = mContext.getPackageManager();

    // 创建 PowerManager 引用,放大镜功能需要用 WakeLock
    mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);

    // 初始化全局锁对象 ------ 几乎所有对内部状态的修改都需持有此锁
    mLock = new Object();

    // 当前操作用户 ID,初始化为当前活跃用户
    mCurrentUserId = ActivityManager.getCurrentUser();

    // 创建主线程 Handler,AMS 中大量异步操作依赖它
    mMainHandler = new MainHandler(mContext.getMainLooper());

    // 向 WMS 注册无障碍焦点相关的 WindowManagerInternal
    // 通过 LocalServices 获取引用,不直接持有 WMS

    // 初始化 UserState ------ 每个用户独立的状态存储
    mUserStates = new SparseArray<>();

    // 读取 Secure Settings 初始化当前用户的配置
    UserState userState = new UserState(mCurrentUserId);
    mUserStates.put(mCurrentUserId, userState);
    readConfigurationForUserStateLocked(userState);

    // 注册 Settings.Secure 内容观察者
    registerSettingsContentObservers(mCurrentUserId);

    // 注册系统广播接收器
    registerBroadcastReceivers();
}

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

2.1 系统服务引用

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

2.2 匿名 Binder:mService

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

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

private final IAccessibilityManager.Stub mService = new IAccessibilityManager.Stub() {

    @Override
    public void sendAccessibilityEvent(AccessibilityEvent event, int userId) {
        // 第四篇重点:事件的跨进程入口
    }

    @Override
    public boolean sendFingerprintGesture(int keyCode) {
        // 指纹手势的无障碍处理
    }

    @Override
    public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
        // 查询已安装的无障碍服务列表
    }

    @Override
    public List<AccessibilityServiceInfo> getEnabledAccessibilityServices(int feedbackType) {
        // 查询已启用的无障碍服务列表
    }

    @Override
    public void interrupt(int userId) {
        // 中断所有无障碍服务的当前操作
    }

    @Override
    public int addClient(IAccessibilityManagerClient client, int userId) {
        // 客户端注册回调,用于状态变化通知
    }

    @Override
    public void registerUiTestAutomationService(
            IBinder owner, IAccessibilityServiceClient client,
            AccessibilityServiceInfo info, int flags) {
        // UiAutomation 测试框架专用的注册入口
    }

    // ... 更多接口方法,涵盖事件投递、服务管理、状态查询等
};

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

java 复制代码
@Override
public IBinder onBind(Intent intent) {
    return mService;
}

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

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

复制代码
Binder 线程 → 获取 mLock → 切 mMainHandler → 执行业务逻辑

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

2.3 mMainHandler:主线程消息处理

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

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

private final class MainHandler extends Handler {
    public static final int MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER = 1;
    public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 2;
    public static final int MSG_SEND_STATE_TO_CLIENTS = 3;
    public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 4;
    public static final int MSG_ANNOUNCE_NEW_USER_IF_NEEDED = 5;
    public static final int MSG_CLEAR_ACCESSIBILITY_FOCUS = 6;

    public MainHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER:
                // 将事件发送到 InputFilter 链,供 TouchExplorer 等处理
                notifyAccessibilityServicesDelayedLocked(...);
                break;
            case MSG_SEND_KEY_EVENT_TO_INPUT_FILTER:
                // 发送按键事件到 InputFilter 链
                break;
            case MSG_SEND_STATE_TO_CLIENTS:
                // 通知所有已注册客户端无障碍状态变化
                break;
            case MSG_CLEAR_ACCESSIBILITY_CACHE:
                // 通知所有 AccessibilityService 清除内部缓存
                break;
            case MSG_ANNOUNCE_NEW_USER_IF_NEEDED:
                // 多用户切换时的语音提示
                break;
            case MSG_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

java 复制代码
private class UserState {
    // 用户 ID
    public final int mUserId;

    // ===== 持久化配置项(来自 Settings.Secure)=====

    // 已启用的无障碍服务列表(ComponentName 字符串,冒号分隔)
    public String mEnabledServicesStr;

    // 触摸探索模式是否允许
    public boolean mTouchExplorationGranted;

    // 放大镜是否开启
    public boolean mMagnificationEnabled;

    // 无障碍快捷方式是否开启
    public boolean mAccessibilityShortcutEnabled;

    // 无障碍快捷方式对应的目标服务 ComponentName
    public String mAccessibilityShortcutTarget;

    // 高对比度文字是否开启
    public boolean mHighTextContrastEnabled;

    // 字体缩放比例
    public float mFontScale;

    // ===== 运行时状态 =====

    // 已绑定并正在运行的无障碍服务列表
    public final List<Service> mBoundServices = new ArrayList<>();

    // 正在绑定中的服务(ComponentName 集合)
    public final Set<ComponentName> mBindingServices = new HashSet<>();

    // 已安装的无障碍服务信息缓存
    public List<AccessibilityServiceInfo> mInstalledServices;

    // 无障碍是否全局开启
    public boolean mEnabled;

    // 当前有无障碍焦点的窗口 ID
    public int mAccessibilityFocusWindowId = -1;

    // 当前有无障碍焦点的节点 ID
    public long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;

    // ContentObserver 引用(切换用户时用于注销)
    public ContentObserver mContentObserver;

    // 广播接收器列表
    public final List<BroadcastReceiver> mReceivers = new ArrayList<>();
}

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

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

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


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

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

4.1 关键 Secure 配置项一览

配置项 Key 类型 含义
ACCESSIBILITY_ENABLED int (0/1) 无障碍功能全局开关
ENABLED_ACCESSIBILITY_SERVICES String 已启用的无障碍服务列表,格式 pkg/ServiceCls:pkg2/ServiceCls
TOUCH_EXPLORATION_ENABLED int (0/1) 触摸探索模式开关
ACCESSIBILITY_SHORTCUT_ENABLED int (0/1) 无障碍快捷方式开关
ACCESSIBILITY_SHORTCUT_TARGET_SERVICE String 快捷方式目标服务 ComponentName
ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED int (0/1) 放大镜开关
ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED int (0/1) 高对比度文字开关
FONT_SCALE float 字体缩放比例
ACCESSIBILITY_SCRIPT_INJECTION int (0/1) 允许服务注入网页脚本

4.2 readConfigurationForUserStateLocked()

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

java 复制代码
private void readConfigurationForUserStateLocked(UserState userState) {
    // 全局开关
    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

java 复制代码
private void registerSettingsContentObservers(int userId) {
    UserState userState = getUserStateLocked(userId);

    ContentObserver observer = new ContentObserver(mMainHandler) {
        @Override
        public void onChange(boolean selfChange, Uri uri) {
            synchronized (mLock) {
                UserState userState = getCurrentUserStateLocked();

                // 无障碍全局开关变化 → 更新状态并重新绑定/解绑服务
                if (userState.mEnabled != isAccessibilityEnabled()) {
                    userState.mEnabled = isAccessibilityEnabled();
                    if (userState.mEnabled) {
                        bindServicesLocked(userState);  // 关→开:绑定服务
                    } else {
                        unbindAllServicesLocked(userState);  // 开→关:解绑全部
                    }
                    notifyClientsOfStateChangeLocked(userState);
                }

                // 已启用服务列表变化 → 重新绑定
                String newServicesStr = 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
                // ...
            }
        }
    };

    ContentResolver resolver = 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
相关推荐
阿pin1 小时前
Android随笔-kotlin Flow
android·kotlin·flow
阳光予你1 小时前
App 启动流程:从 Launcher 点击到 Activity 创建
android
小孔龙2 小时前
BufferQueue 对象交接与同步
android
XiaoLeisj2 小时前
Kotlin Flow 常用操作符:数据变换 map、filter、onEach,时间控制 debounce、sample,终端聚合 reduce、fold
android·kotlin·android jetpack·协程·响应式编程·flow
EQ-雪梨蛋花汤2 小时前
Android 3D 开发教程(三):Sceneform-EQR 配置 PBR 材质、光照、相机与实时阴影
android·3d·材质
Dovis(誓平步青云)3 小时前
《 固井工程软件 Cemsol 的数据管理与国产化适配实践》
android·java·开发语言·人工智能
Kapaseker3 小时前
小白都看得懂的 Skill 教程 - 创建第一个 Skill
android·kotlin·vibecoding
zhangphil3 小时前
Android BitmapFactory实现AOSP ContentResolver.loadThumbnail快速取小缩略图,Kotlin
android·kotlin
weixin_440784113 小时前
【OkHttp实现原理】
android·java·okhttp