Android 16系统源码_无障碍辅助(二)Android 的无障碍框架

前言

上一篇文章我们通过一个简单案例,初步认识了无障碍服务,本篇文章我们将会结合具体的系统源码来对无障碍服务进行一下深入分析。

组件介绍

核心组件

Android 无障碍框架(Accessibility Framework)的源码位于 AOSP(Android Open Source Project)中的frameworks/base/core/java/android/accessibilityservice/仓库中。
该框架主要有以下三个核心组件构成:

  • AccessibilityService:这是无障碍服务的核心类,开发者可以通过继承该类来创建自定义的无障碍服务。AccessibilityService提供了与系统交互的接口,允许开发者监听和处理来自系统的无障碍事件(如屏幕内容变化、焦点变化等),并执行相应的操作(如点击、滚动等)。
  • AccessibilityServiceInfo:该类用于配置无障碍服务的元信息,包括服务的描述、事件类型、反馈类型、设置界面等。开发者可以通过AccessibilityServiceInfo来定义服务的行为和功能,例如指定服务需要监听的事件类型(如TYPE_VIEW_CLICKED或TYPE_WINDOW_CONTENT_CHANGED)。
  • AccessibilityEvent:该类表示系统触发的无障碍事件,包含了与事件相关的详细信息,例如事件类型、触发事件的视图信息、文本内容等。开发者可以通过解析AccessibilityEvent来获取用户界面上的变化,并根据需要作出响应。

非核心组件

除了上述三个核心组件外,Android 无障碍框架还包含以下重要类和接口:

  • AccessibilityNodeInfo:该类表示屏幕上的一个节点(如按钮、文本框等),提供了对节点属性的访问和操作接口。开发者可以通过AccessibilityNodeInfo获取节点的文本、位置、状态等信息,并执行点击、长按等操作。
  • AccessibilityManager:这是一个系统服务,用于管理无障碍服务的状态和配置。开发者可以通过AccessibilityManager检查无障碍功能是否启用、获取已启用的无障碍服务列表等。
  • GestureDescription:该类用于描述复杂的手势操作(如滑动、多点触控等),开发者可以通过GestureDescription构建手势并将其发送到系统,以模拟用户的操作。
  • AccessibilityButtonController:该类用于控制设备上的无障碍按钮(如导航栏中的无障碍按钮),开发者可以通过它监听按钮的点击事件并执行相应的逻辑。
  • AccessibilityWindowInfo:该类表示屏幕上的一个窗口,提供了窗口的位置、层级、类型等信息。开发者可以通过AccessibilityWindowInfo获取当前屏幕上的窗口结构,并与其他无障碍功能结合使用。

源码分析

IAccessibilityServiceClientWrapper

所有应用自定义的无障碍服务都需要继承AccessibilityService这个抽象类,并需要重写onAccessibilityEvent 方法。AccessibilityService 由系统自动启动和管理,开发者无需要手动调用 startService() 或者 bindService() 方法。AccessibilityService 中的 onBinder 方法在被绑定时调用,返回的是一个 IAccessibilityServiceClientWrapper 匿名内部类。该匿名内部类提供了如下的一些无障碍 AIDL 方法。当服务绑定成功后,外部类通过回调的方式执行 onAccessibilityEvent 方法,从而实现开发者实现的无障碍功能。

public abstract class AccessibilityService extends Service {
    @Override
    public final IBinder onBind(Intent intent) {
        return new IAccessibilityServiceClientWrapper(this, getMainExecutor(), 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.
                if (mWindowManager != null) {
                    final WindowManagerImpl wm = (WindowManagerImpl) mWindowManager;
                    wm.setDefaultToken(mWindowToken);
                }
            }

            @Override
            public boolean onGesture(AccessibilityGestureEvent gestureEvent) {
                return AccessibilityService.this.onGesture(gestureEvent);
            }

            @Override
            public boolean onKeyEvent(KeyEvent event) {
                return AccessibilityService.this.onKeyEvent(event);
            }

            @Override
            public void onMagnificationChanged(int displayId, @NonNull Region region,
                    MagnificationConfig config) {
                AccessibilityService.this.onMagnificationChanged(displayId, region, config);
            }

            @Override
            public void onMotionEvent(MotionEvent event) {
                AccessibilityService.this.sendMotionEventToCallback(event);
            }

            @Override
            public void onTouchStateChanged(int displayId, int state) {
                AccessibilityService.this.onTouchStateChanged(displayId, state);
            }

            @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(int displayId) {
                AccessibilityService.this.onAccessibilityButtonClicked(displayId);
            }

            @Override
            public void onAccessibilityButtonAvailabilityChanged(boolean available) {
                AccessibilityService.this.onAccessibilityButtonAvailabilityChanged(available);
            }

            @Override
            public void onSystemActionsChanged() {
                AccessibilityService.this.onSystemActionsChanged();
            }

            @Override
            public void createImeSession(IAccessibilityInputMethodSessionCallback callback) {
                if (mInputMethod != null) {
                    mInputMethod.createImeSession(callback);
                }
            }

            @Override
            public void startInput(@Nullable RemoteAccessibilityInputConnection connection,
                    @NonNull EditorInfo editorInfo, boolean restarting) {
                if (mInputMethod != null) {
                    if (restarting) {
                        mInputMethod.restartInput(connection, editorInfo);
                    } else {
                        mInputMethod.startInput(connection, editorInfo);
                    }
                }
            }
        });
    }
}

启动流程

SystemServer 是 Android 系统的核心进程,负责启动和管理所有系统服务(比如:ActivityManagerService、WindowsManagerService 等)。在 SystemServer 启动过程中,会初始化 AccessibilityManagerService,并将其注册为系统服务。

// SystemServer.java
public final class SystemServer implements Dumpable {
    // ...
    
    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
        // ...
        t.traceBegin("StartAccessibilityManagerService");
        try {
            mSystemServiceManager.startService(AccessibilityManagerService.Lifecycle.class);
        } catch (Throwable e) {
            reportWtf("starting Accessibility Manager", e);
        }
        t.traceEnd();
        // ...
    }
    
    // ...
    
    /**
     * Publish the service so it is accessible to other services and apps.
     */
    protected final void publishBinderService(@NonNull String name, @NonNull IBinder service) {
        publishBinderService(name, service, false);
    }
    
    protected final void publishBinderService(String name, IBinder service,
            boolean allowIsolated, int dumpPriority) {
        ServiceManager.addService(name, service, allowIsolated, dumpPriority);
    }
    
    // ...
}

// AccessibilityManagerService.java
public static final class Lifecycle extends SystemService {
    private final AccessibilityManagerService mService;

    public Lifecycle(Context context) {
        super(context);
        mService = new AccessibilityManagerService(context);
    }

    @Override
    public void onStart() {
        LocalServices.addService(AccessibilityManagerInternal.class,
                new LocalServiceImpl(mService));
        publishBinderService(Context.ACCESSIBILITY_SERVICE, mService);
    }

    @Override
    public void onBootPhase(int phase) {
        mService.onBootPhase(phase);
    }
}

在 Android 系统启动过程中,SystemServer 通过 startOtherServices 方法启动一系列系统服务。其中,AccessibilityManagerService 的启动是通过 SystemServiceManager 的 startService 方法完成的。具体来说,startService 方法接收一个 AccessibilityManagerService.Lifecycle.class 类对象作为参数,该对象用于管理 AccessibilityManagerService 的生命周期。

当 AccessibilityManagerService 启动后,其 onStart 方法会被调用。在该方法中,AccessibilityManagerService 通过 publishBinderService 方法将自己注册并发布到 ServiceManager 中。publishBinderService 方法会将 AccessibilityManagerService 的 Binder 对象注册到 ServiceManager,使得其他组件可以通过 ServiceManager 获取 AccessibilityManagerService 的实例。

一旦 AccessibilityManagerService 成功发布到 ServiceManager,后续需要使用该服务的组件(如系统应用或其他服务)可以直接通过 ServiceManager 的 getService 方法获取其 Binder 对象,并通过该 Binder 对象与 AccessibilityManagerService 进行交互。这种设计使得系统服务的注册和获取更加高效和统一,同时也为系统组件的协作提供了便利。

AccessibilityManagerService

AccessibilityManagerService 的初始化过程如下。

public AccessibilityManagerService(Context context) {
    super(PermissionEnforcer.fromContext(context));
    mContext = context;
    mPowerManager = context.getSystemService(PowerManager.class);
    mUserManager = context.getSystemService(UserManager.class);
    mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
    mTraceManager = AccessibilityTraceManager.getInstance(
            mWindowManagerService.getAccessibilityController(), this, mLock);
    mMainHandler = new MainHandler(mContext.getMainLooper());
    mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
    mPackageManager = mContext.getPackageManager();
    // ...
    mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
            mWindowManagerService, this, mSecurityPolicy, this, mTraceManager);
    mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
    // ...
	mProxyManager = new ProxyManager(mLock, mA11yWindowManager, mContext, mMainHandler,
            mUiAutomationManager, this);
    // ...
    mUmi = LocalServices.getService(UserManagerInternal.class);	
    
    // ...
    init();
}

private void init() {
    mSecurityPolicy.setAccessibilityWindowManager(mA11yWindowManager);
    registerBroadcastReceivers();
    new AccessibilityContentObserver(mMainHandler).register(
            mContext.getContentResolver());
    disableAccessibilityMenuToMigrateIfNeeded();
}

在 AccessibilityManagerService 构造函数中,通过 getSystemService() 获取到了各种系统服务,比如 PowerManager、UserManager、WindowManager 等等。在 init() 方法中,registerBroadcastReceivers() 方法注册了系统广播的监听:

private void registerBroadcastReceivers() {
    // package changes
    mPackageMonitor = new ManagerPackageMonitor(this);
    mPackageMonitor.register(mContext, null,  UserHandle.ALL, true);

    // user change and unlock
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
    intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
    intentFilter.addAction(Intent.ACTION_USER_REMOVED);
    intentFilter.addAction(Intent.ACTION_SETTING_RESTORED);

    Handler receiverHandler =
            Flags.managerAvoidReceiverTimeout() ? BackgroundThread.getHandler() : null;
    mContext.registerReceiverAsUser(new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // ...
            String action = intent.getAction();
            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
            } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
                unlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
            } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
                removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
            } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
                // ...
        }
    }, UserHandle.ALL, intentFilter, null, receiverHandler);

    final IntentFilter filter = new IntentFilter();
    filter.addAction(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED);
    final BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            setNonA11yToolNotificationToMatchSafetyCenter();
        }
    };
    mContext.registerReceiverAsUser(
            receiver, UserHandle.ALL, filter, null, mMainHandler,
            Context.RECEIVER_EXPORTED);
    // ...
    }
}

registerBroadcastReceivers() 方法中初始化了一个 ManagerPackageMonitor 对象。ManagerPackageMonitor 继承 PackageMonitor 对象。PackageMonitor是 Android 中的一个工具类,用于监听应用程序包(APK)的安装、更新、删除等操作,用于监控系统中应用程序包的变化,并在发生这些变化时执行相应的操作。

public static class ManagerPackageMonitor extends PackageMonitor {}

当系统的应用信息发生变化时,ManagerPackageMonitor 中的 onSomePackagesChanged() 方法会被回调执行,最终执行到 onSomePackagesChangedLocked() 方法。onSomePackagesChangedLocked() 方法对已安装的无障碍服务进行一次强制加载,然后调用 onUserStateChangedLocked() 方法。

@VisibleForTesting
public static class ManagerPackageMonitor extends PackageMonitor {
    private final AccessibilityManagerService mManagerService;
    public ManagerPackageMonitor(AccessibilityManagerService managerService) {
        super(/* supportsPackageRestartQuery = */ true);
        mManagerService = managerService;
    }

    @Override
    public void onSomePackagesChanged() {
        //... 
        mManagerService.onSomePackagesChangedLocked(parsedAccessibilityServiceInfos,
                parsedAccessibilityShortcutInfos);
    }
    
    // ...
    
    private void onSomePackagesChangedLocked(
        @Nullable List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos,
        @Nullable List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos) {
        final AccessibilityUserState userState = getCurrentUserStateLocked();
        // 重新加载已安装的服务,因为某些服务的属性或解析信息(不支持 equals 方法)可能已发生变化。
        // 移除它们以强制重新加载。
        userState.mInstalledServices.clear();
        if (readConfigurationForUserStateLocked(userState,
                    parsedAccessibilityServiceInfos, parsedAccessibilityShortcutInfos)) {
            onUserStateChangedLocked(userState);
        }
    }
}

onUserStateChangedLocked() 方法会调用 updateServicesLocked() 更新无障碍的服务信息:

/**
 * Called when any property of the user state has changed.
 */
private void onUserStateChangedLocked(AccessibilityUserState userState, boolean forceUpdate) {
    // ...
    mInitialized = true;
    updateLegacyCapabilitiesLocked(userState);
    updateServicesLocked(userState); // Here!
    updateWindowsForAccessibilityCallbackLocked(userState);
    // ...
}

在 updateServicesLocked() 方法中,首先创建了一个临时容器对象用于保存已安装的无障碍服务,然后遍历所已经安装的无障碍服务,将满足如下的要求的服务都过滤掉:

  1. 忽略非加密感知的服务,直到用户解锁设备。在 Android 中,设备加密后,某些服务可能无法在用户解锁设备之前正常运行。因此,系统会暂时忽略这些服务,直到用户解锁设备。
  2. 跳过该组件,因为它可能正在运行或已崩溃。组件可能处于不稳定的状态(如正在运行但无响应,或已崩溃)。为了避免进一步的问题,系统会跳过这些组件。
  3. 跳过被设备管理员策略禁止启用的服务。某些服务可能被设备管理员策略明确禁止启用。系统会跳过这些服务,以遵守设备管理员策略。

最后留下的是满足要求的无障碍服务,然后调用这些服务 AccessibilityServiceConnection 的 bindLocked() 方法绑定服务:

private void updateServicesLocked(AccessibilityUserState userState) {
    Map<ComponentName, AccessibilityServiceConnection> componentNameToServiceMap =
            userState.mComponentNameToServiceMap;
    boolean isUnlockingOrUnlocked = mUmi.isUserUnlockingOrUnlocked(userState.mUserId);

    // 临时变量存储已安装的服务
    mTempComponentNameSet.clear();
    for (int i = 0, count = userState.mInstalledServices.size(); i < count; i++) {
        AccessibilityServiceInfo installedService = userState.mInstalledServices.get(i);
        ComponentName componentName = ComponentName.unflattenFromString(
                installedService.getId());
        mTempComponentNameSet.add(componentName);

        AccessibilityServiceConnection service = componentNameToServiceMap.get(componentName);

        // 忽略非加密感知的服务,直到用户解锁设备
        if (!isUnlockingOrUnlocked && !installedService.isDirectBootAware()) {
            Slog.d(LOG_TAG, "Ignoring non-encryption-aware service " + componentName);
            continue;
        }

        // 跳过该组件,因为它可能正在运行或已崩溃
        if (userState.getBindingServicesLocked().contains(componentName)
                || userState.getCrashedServicesLocked().contains(componentName)) {
            continue;
        }
        if (userState.mEnabledServices.contains(componentName)
                && !mUiAutomationManager.suppressingAccessibilityServicesLocked()) {
            // 跳过被设备管理员策略禁止启用的服务
            if (!isAccessibilityTargetAllowed(componentName.getPackageName(),
                    installedService.getResolveInfo().serviceInfo.applicationInfo.uid,
                    userState.mUserId)) {
                Slog.d(LOG_TAG, "Skipping enabling service disallowed by device admin policy: "
                        + componentName);
                disableAccessibilityServiceLocked(componentName, userState.mUserId);
                continue;
            }
            if (service == null) {
                service = new AccessibilityServiceConnection(userState, mContext, componentName,
                        installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
                        this, getTraceManager(), mWindowManagerService,
                        getSystemActionPerformer(), mA11yWindowManager,
                        mActivityTaskManagerService);
            } else if (userState.mBoundServices.contains(service)) {
                continue;
            }
            service.bindLocked(); // 绑定服务
        } else {
            if (service != null) {
                service.unbindLocked();
                removeShortcutTargetForUnboundServiceLocked(userState, service);
            }
        }
    }
    // ...
}

AccessibilityServiceConnection 类的解释如下:

这个类表示一个无障碍服务。它存储了服务管理所需的所有与服务相关的数据,提供了启动/停止服务的 API,并负责在服务管理的数据结构中添加/移除该服务。该类还暴露了一个配置接口,该接口会在服务绑定后立即传递给其所代表的服务。它同时还充当服务的连接。

不难看出,AccessibilityServiceConnection 类是一个“服务类”,封装了无障碍功能的相关方法,提供给外部使用。该类的 bindLocked() 方法调用了实现接口类的上下文 mContext 对象的 bindServiceAsUser() 方法来绑定服务。bindServiceAsUser() 方法的行为和 bindService 类似,只不过 bindServiceAsUser() 方法是在指定的用户上绑定服务:

public void bindLocked() {
    AccessibilityUserState userState = mUserStateWeakReference.get();
    if (userState == null) return;
    final long identity = Binder.clearCallingIdentity();
    try {
        int flags = Context.BIND_AUTO_CREATE
                | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
                | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
                | Context.BIND_INCLUDE_CAPABILITIES;
        if (userState.getBindInstantServiceAllowedLocked()) {
            flags |= Context.BIND_ALLOW_INSTANT;
        }
        if (mClientBinder == null
                && mContext.bindServiceAsUser( // 通过 bindServiceAsUser 绑定服务
                        mIntent, this, flags, new UserHandle(userState.mUserId))) {
            userState.getBindingServicesLocked().add(mComponentName);
        }
    } finally {
        Binder.restoreCallingIdentity(identity);
    }
    mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(),
            mAccessibilityServiceInfo.getResolveInfo().serviceInfo.applicationInfo.uid,
            userState.mUserId);
}

当服务被绑定后,就会调用 AccessibilityServiceConnection#onServiceConnected 方法

@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
    AccessibilityUserState userState = mUserStateWeakReference.get();
    if (userState != null) {
        addWindowTokensForAllDisplays();
    }
    synchronized (mLock) {
        if (mClientBinder != service) {
            if (mClientBinder != null) {
                mClientBinder.unlinkToDeath(this, 0);
            }
            mClientBinder = service;
            try {
                mClientBinder.linkToDeath(this, 0);
            } catch (RemoteException re) {
                Slog.e(LOG_TAG, "Failed registering death link");
                binderDied();
                return;
            }
        }
        mClient = IAccessibilityServiceClient.Stub.asInterface(service);
        if (userState == null) return;
        userState.addServiceLocked(this); // 将绑定成功的服务添加到全局 mBoundServices 变量中
        mSystemSupport.onClientChangeLocked(false);
        // 在主线程的 Handler 上初始化服务,确保在完成新配置的设置(例如,初始化输入过滤器)后再执行
        mMainHandler.sendMessage(obtainMessage(
                AccessibilityServiceConnection::initializeService, this)); //调用 initializeService 方法
        if (requestImeApis()) {
            mSystemSupport.requestImeLocked(this);
        }
    }
}

在 onServiceConnected()方法中会在主线程的 Handler 上以异步的方式调用 AccessibilityServiceConnection 的 initializeService() 方法初始化服务:

private void initializeService() {
    IAccessibilityServiceClient client = null;
    synchronized (mLock) {
        AccessibilityUserState userState = mUserStateWeakReference.get();
        if (userState == null) return;
        final Set<ComponentName> bindingServices = userState.getBindingServicesLocked();
        final Set<ComponentName> crashedServices = userState.getCrashedServicesLocked();
        if (bindingServices.contains(mComponentName)
                || crashedServices.contains(mComponentName)) {
            bindingServices.remove(mComponentName);
            crashedServices.remove(mComponentName);
            mAccessibilityServiceInfo.crashed = false;
            client = mClient;
        }
        // ...
    }
    if (client == null) {
        binderDied();
        return;
    }
    try {
        // ...
        // 调用无障碍端的 init 方法
        client.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); 
    } catch (RemoteException re) {
        Slog.w(LOG_TAG, "Error while setting connection for service: " + client, re);
        binderDied();
    }
}

initializeService() 方法中调用 IAccessibilityServiceClient 的 init() 方法。此时,就完成了整个服务从启动、绑定到初始化的整个流程。

3.3.4 事件的分发

用户的操作(如点击、滑动)或系统状态变化(如:通知、焦点变化)会触发系统生成无障碍事件。这是因为在 View.java 中的相关方法中,无障碍框架早已事先预埋了相关的方法。例如,当 一个 View 被点击时发送一个AccessibilityEvent.TYPE_VIEW_CLICKED 类型的事件:

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;
    }

    // 当执行点击操作时,发送 TYPE_VIEW_CLICKED 事件
    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

    notifyEnterOrExitForAutoFillIfNeeded(true);

    return result;
}

再比如,当 View 的焦点发送变化时,发送一个 AccessibilityEvent.TYPE_VIEW_FOCUSED 类型的事件:

protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction,
        @Nullable Rect previouslyFocusedRect) {
    if (DBG) {
        Log.d(VIEW_LOG_TAG, "onFocusChanged() entered. gainFocus: "
                + gainFocus);
    }
    if (gainFocus) {
        // 当执行焦点变化时,发送 TYPE_VIEW_FOCUSED 事件
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
    }
    // ...
}

我们以 onFocusChanged() 方法中,发送的 AccessibilityEvent.TYPE_VIEW_FOCUSED 为例,跟踪无障碍框架的事件分发流程。

在 View.java 文件中对 sendAccessibilityEvent() 方法的注解大概说了无障碍功能的事件分发流程:

首先调用onInitializeAccessibilityEvent()来填充事件源(即此 View)的信息
然后调用dispatchPopulateAccessibilityEvent()来填充事件源的文本内容(包括其子视图的内容),接着对于事件类型为AccessibilityEvent.TYPE_VIEW_SCROLLED和AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED的事件进行节流处理
最后调用其父视图的ViewParent.requestSendAccessibilityEvent()来请求将事件发送给感兴趣的接收方。如果通过调用setAccessibilityDelegate()指定了AccessibilityDelegate,则由其AccessibilityDelegate.sendAccessibilityEvent()负责处理此调用。
接下来,我们来分析具体的代码调用链关系。先看 sendAccessibilityEvent() 方法的实现:

public void sendAccessibilityEvent(int eventType) {
    if (mAccessibilityDelegate != null) {
        mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
    } else {
        sendAccessibilityEventInternal(eventType);
    }
}

public static class AccessibilityDelegate {
    public void sendAccessibilityEvent(@NonNull View host, int eventType) {
        // 最终还是执行 View#sendAccessibilityEventInternal 方法
        host.sendAccessibilityEventInternal(eventType);  
    }
}

如果指定了 AccessibilityDelegate 则由 AccessibilityDelegate 负责发送事件,但是不管是谁发送事件,最终都会走到 sendAccessibilityEventInternal() 方法中:

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) {
    // ...
    
    // 初始化事件信息
    onInitializeAccessibilityEvent(event);
    // Only a subset of accessibility events populates text content.
    if ((event.getEventType() & POPULATING_ACCESSIBILITY_EVENT_TYPES) != 0) {
        // 填充事件源的文本内容
        dispatchPopulateAccessibilityEvent(event);
    }
    SendAccessibilityEventThrottle throttle = getThrottleForAccessibilityEvent(event);
    if (throttle != null) {
        throttle.post(event);
    } else if (!isWindowDisappearedEvent && detached) {
        // Views could be attached soon later. Accessibility events during this temporarily
        // detached period should be sent too.
        postDelayed(() -> {
            if (AccessibilityManager.getInstance(mContext).isEnabled() && isShown()) {
                // 发送事件
                requestParentSendAccessibilityEvent(event);
            }
        }, ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
    } else {
        // 发送事件
        requestParentSendAccessibilityEvent(event);
    }
}

private void requestParentSendAccessibilityEvent(AccessibilityEvent event) {
    ViewParent parent = getParent();
    if (parent != null) {
        // 获取 ViewParent 请求发送事件
        getParent().requestSendAccessibilityEvent(this, event);
    }
}

sendAccessibilityEventInternal() 方法调用 sendAccessibilityEventUnchecked() 方法,sendAccessibilityEventUnchecked() 调用 sendAccessibilityEventUncheckedInternal() 方法。在 sendAccessibilityEventUncheckedInternal() 方法中,对事件进行了初始化操作:

public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
    event.setSource(this);
    event.setClassName(getAccessibilityClassName());
    event.setPackageName(getContext().getPackageName());
    event.setEnabled(isEnabled());
    event.setContentDescription(mContentDescription);
    event.setScrollX(getScrollX());
    event.setScrollY(getScrollY());

    switch (event.getEventType()) {
        case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
            ArrayList<View> focusablesTempList = (mAttachInfo != null)
                    ? mAttachInfo.mTempArrayList : new ArrayList<View>();
            getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD, FOCUSABLES_ALL);
            event.setItemCount(focusablesTempList.size());
            event.setCurrentItemIndex(focusablesTempList.indexOf(this));
            if (mAttachInfo != null) {
                focusablesTempList.clear();
            }
        } break;
        case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
            CharSequence text = getIterableTextForAccessibility();
            if (text != null && text.length() > 0) {
                event.setFromIndex(getAccessibilitySelectionStart());
                event.setToIndex(getAccessibilitySelectionEnd());
                event.setItemCount(text.length());
            }
        } break;
    }
}

不难看出,onInitializeAccessibilityEvent() 方法对事件进行了一层简单的封装,比如添加了类、包等信息。初始化完成后,通过 dispatchPopulateAccessibilityEvent() 方法填充文本内容,然后最终走到 ViewParent#requestSendAccessibilityEvent 方法。

ViewParent 是一个接口类,其实现由 ViewRootImpl 完成,因此看一下 ViewRootImpl#requestSendAccessibilityEvent 方法:

public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
    // ...
    final int eventType = event.getEventType();
    final View source = getSourceForAccessibilityEvent(event);
    switch (eventType) {
        case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
            if (source != null) {
                AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
                if (provider != null) {
                    final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
                            event.getSourceNodeId());
                    final AccessibilityNodeInfo node;
                    node = provider.createAccessibilityNodeInfo(virtualNodeId);
                    setAccessibilityFocus(source, node);
                }
            }
        } break;
        case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
            if (source != null && source.getAccessibilityNodeProvider() != null) {
                setAccessibilityFocus(null, null);
            }
        } break;
        case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
            handleWindowContentChangedEvent(event);
        } break;
    }
    //通过 AccessibilityManager 发送事件
    mAccessibilityManager.sendAccessibilityEvent(event);
    return true;
}

ViewRootImpl#requestSendAccessibilityEvent 对 View 进行简单的判断后,通过 AccessibilityManager 的 sendAccessibilityEvent 方法继续发送事件:

public void sendAccessibilityEvent(AccessibilityEvent event) {
    final IAccessibilityManager service;
    final int userId;
    final AccessibilityEvent dispatchedEvent;
    // ...
    try {
        final long identityToken = Binder.clearCallingIdentity();
        try {
            // AccessibilityManagerService 是 IAccessibilityManager 的具体实现
            service.sendAccessibilityEvent(dispatchedEvent, userId);
        } finally {
            Binder.restoreCallingIdentity(identityToken);
        }
    // ...
   }
}

之前说过,IAccessibilityManager 是一个 AIDL 接口类,通过 Binder 机制远程服务,最终会走到 AccessibilityManagerService 的方法:

// AccessibilityManagerService 
public void sendAccessibilityEvent(AccessibilityEvent event, int userId) {
    if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
        mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent", FLAGS_ACCESSIBILITY_MANAGER,
                "event=" + event + ";userId=" + userId);
    }
    boolean dispatchEvent = false;
    int resolvedUserId;

    synchronized (mLock) {
        // ...
        if (resolvedUserId == mCurrentUserId) {
            if (mSecurityPolicy.canDispatchAccessibilityEventLocked(mCurrentUserId, event)) {
                mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(
                        mCurrentUserId, event.getWindowId(), event.getSourceNodeId(),
                        event.getEventType(), event.getAction());
                mSecurityPolicy.updateEventSourceLocked(event);
                dispatchEvent = true;
            }
           // ...
        }
    }

    if (dispatchEvent) {
        // ...
        synchronized (mLock) {
            dispatchAccessibilityEventLocked(event);
        }
    }

    // ...
}

private void dispatchAccessibilityEventLocked(AccessibilityEvent event) {
    if (mProxyManager.isProxyedDisplay(event.getDisplayId())) {
        mProxyManager.sendAccessibilityEventLocked(event);
    } else {
        notifyAccessibilityServicesDelayedLocked(event, false);
        notifyAccessibilityServicesDelayedLocked(event, true);
    }
    mUiAutomationManager.sendAccessibilityEventLocked(event);
}

在 dispatchAccessibilityEventLocked() 方法中不管是走哪条分支,最终都会执行 AbstractAccessibilityServiceConnection#notifyAccessibilityEvent方法:

public void notifyAccessibilityEvent(AccessibilityEvent event) {
    synchronized (mLock) {
        // ...
        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 = clientWantsEvent ? 1 : 0;
       // 通过 Handler 机制发送消息
        mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout);
    }
}

// ...
// Handler 的实现
mEventDispatchHandler =
            new Handler(mainHandler.getLooper()) {
                @Override
                public void handleMessage(Message message) {
                    final int eventType = message.what;
                    AccessibilityEvent event = (AccessibilityEvent) message.obj;
                    boolean clientWantsEvent = message.arg1 != 0;
                    notifyAccessibilityEventInternal(eventType, event, clientWantsEvent);
                }
            };

// ...
private void notifyAccessibilityEventInternal(
            int eventType, AccessibilityEvent event, boolean clientWantsEvent) {
    IAccessibilityServiceClient client;
    // ...
    try {
        // ... 最终回调 AccessibilityService 中的 onAccessibilityEvent 方法
        client.onAccessibilityEvent(event, clientWantsEvent);
    }
    // ...
}

最终,通过 client 的 onAccessibilityEvent 方法,回调开发者实现的无障碍服务中的 onAccessibilityEvent() 方法。到这里为止,无障碍事件的分发、封装和传递的流程就完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值