系列目录 :第一篇:全景图与架构概览 | [第二篇:AccessibilityManagerService 启动与初始化](#第二篇:AccessibilityManagerService 启动与初始化) | 第三篇:无障碍服务的注册与绑定 | [第四篇:AccessibilityEvent 的产生与投递](#第四篇:AccessibilityEvent 的产生与投递) | [第五篇:AccessibilityNodeInfo 的构建与查询](#第五篇:AccessibilityNodeInfo 的构建与查询) | [第六篇:输入事件拦截与 TouchExplorer](#第六篇:输入事件拦截与 TouchExplorer) | [第七篇:手势分发与 KeyEvent 处理](#第七篇:手势分发与 KeyEvent 处理) | 第八篇:放大镜与辅助功能特性
一、为什么单独把「手势分发」与「KeyEvent」拿出来讲
上一篇结尾停在 TouchExplorer.onGestureCompleted(gestureId) 回调到 mAms.onGesture(gestureId) 那一刻。这一篇回答三个问题:
- gestureId 从 AMS 到
AccessibilityService.onGesture()之间,经过了哪些 Binder 边界?- 当用户按下物理键(音量键、Back 键)时,声明了
FLAG_REQUEST_FILTER_KEY_EVENTS的无障碍服务如何在事件到达目标窗口之前「看到」它?performGlobalAction()这个看似简单的 API,在 system_server 里到底触发了什么?
这三个问题对应 AOSP 7 无障碍子系统里两条独立的「输入拦截」通道:
- 触摸通道 :
TouchExplorer→AMS.onGesture→Service.notifyGesture→IAccessibilityServiceClient.onGesture→AccessibilityService.onGesture() - 按键通道 :
KeyboardInterceptor→AMS.notifyKeyEvent→KeyEventDispatcher→IAccessibilityServiceClient.onKeyEvent→AccessibilityService.onKeyEvent()→setOnKeyEventResult
两条通道的设计哲学不同:触摸通道是「单向通知」(系统识别出手势,服务据此执行动作),按键通道是「双向协商」(服务必须明确告诉系统「我处理了 / 没处理」,系统才能决定事件是否继续往后走)。
二、手势分发链路
2.1 AMS 端:onGesture → notifyGestureLocked
TouchExplorer 调用的 mAms.onGesture(gestureId) 是 AccessibilityManagerService 的内部方法(非 Binder 接口)。它在一个同步块里把 gestureId 分发给「最后一个声明了触摸探索且 default 状态匹配」的服务:
源码路径 :frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
java
public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// ...
boolean onGesture(int gestureId) {
synchronized (mLock) {
boolean handled = notifyGestureLocked(gestureId, false);
if (!handled) {
handled = notifyGestureLocked(gestureId, true);
}
return handled;
}
}
private boolean notifyGestureLocked(int gestureId, boolean isDefault) {
// 把 gesture 发给「最后一个能处理的 service」
// 即 mBoundServices 中最后一个满足条件的项
UserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
Service service = state.mBoundServices.get(i);
if (service.mRequestTouchExplorationMode
&& service.mIsDefault == isDefault) {
service.notifyGesture(gestureId);
return true;
}
}
return false;
}
}
关键设计 :「先试
isDefault=false再试isDefault=true」的顺序意味着:如果用户同时启用了「默认 TalkBack」和「自定义辅助 App」,自定义 App 优先拿到手势。这是 AOSP 7 中「后启用的服务优先」策略的体现,源码注释里也明确写了「Ideally, the user should make the call which service handles gestures」。
2.2 Service 内部类:notifyGesture
Service 是 AccessibilityManagerService 的内部类,每个绑定的无障碍服务对应一个 Service 实例。它的 notifyGesture 把 gestureId 包装成 Message 投递到 InvocationHandler:
源码路径 :frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
java
public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// ...
private final class Service {
// ...
private Handler mInvocationHandler;
// ...
public void notifyGesture(int gestureId) {
mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE,
gestureId, 0).sendToTarget();
}
}
}
2.3 InvocationHandler:MSG_ON_GESTURE 的落点
InvocationHandler 是 system_server 端负责把 Service 的消息转成「对服务进程的 Binder 调用」的 Handler。MSG_ON_GESTURE 的处理逻辑是:取当前 Service 的 mServiceInterface(即 IAccessibilityServiceClient.Stub 的远端代理),调用 onGesture(gestureId)。
源码路径 :frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
java
public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// ...
private final class InvocationHandler extends Handler {
public static final int MSG_ON_GESTURE = 1;
public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 2;
@Override
public void handleMessage(Message message) {
final int type = message.what;
switch (type) {
case MSG_ON_GESTURE: {
final int gestureId = message.arg1;
notifyGestureInternal(gestureId);
} break;
// ...
}
}
}
// Service 内部类的方法
private void notifyGestureInternal(int gestureId) {
final IAccessibilityServiceClient listener;
synchronized (mLock) {
listener = mServiceInterface;
}
if (listener != null) {
try {
listener.onGesture(gestureId); // ← Binder 调用到服务进程
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error during sending gesture " + gestureId
+ " to " + mService, re);
}
}
}
}
关键 :
notifyGestureInternal先持mLock取出mServiceInterface的局部引用,再在锁外发起 Binder 调用------这是 AOSP 7 中「持锁取引用、锁外发 IPC」的典型模式,避免 Binder 调用期间阻塞其他线程获取mLock。RemoteException捕获后只打日志,服务死亡由binderDied回调统一处理。
2.4 服务进程端:IAccessibilityServiceClientWrapper
服务进程侧,AccessibilityService 内部有一个 IAccessibilityServiceClientWrapper(继承 IAccessibilityServiceClient.Stub),它收到 onGesture(int gestureId) 后把 gestureId 包装成 Message 投递到 mCaller(即 AccessibilityService 的 MainHandler):
源码路径 :frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java
java
public abstract class AccessibilityService extends Service {
// ...
private class IAccessibilityServiceClientWrapper
extends IAccessibilityServiceClient.Stub {
// ...
@Override
public void onGesture(int gestureId) {
Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId);
mCaller.sendMessage(message);
}
}
}
2.5 MainHandler:DO_ON_GESTURE 的处理
MainHandler 是 AccessibilityService 进程内的主线程 Handler。DO_ON_GESTURE 的处理逻辑是:调用开发者重写的 mCallback.onGesture(gestureId):
源码路径 :frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java
java
public abstract class AccessibilityService extends Service {
// ...
private final class MainHandler extends Handler {
// ...
@Override
public void handleMessage(Message message) {
// ...
switch (message.what) {
case DO_ON_GESTURE: {
final int gestureId = message.arg1;
mCallback.onGesture(gestureId);
} return;
// ...
}
}
}
}
2.6 开发者回调:onGesture
onGesture 是 AccessibilityService 的 protected 方法,默认返回 false。开发者覆写它来执行具体动作:
源码路径 :frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java
java
public abstract class AccessibilityService extends Service {
// ...
/**
* Callback that is called when a gesture is performed on the screen.
* ...
*/
protected boolean onGesture(int gestureId) {
return false;
}
}
关键设计 :
onGesture返回boolean表示「是否已消费该手势」。AOSP 7 中IAccessibilityServiceClient.onGesture的签名没有返回值(void onGesture(int gesture)),所以「消费」语义实际上由 AMS 端通过notifyGestureLocked的「找到即返回」逻辑实现------服务进程返回的 boolean 在 AOSP 7 里并未被系统使用,仅作为 API 契约保留。
2.7 手势 ID 常量
AOSP 7 中 AccessibilityService 定义了 16 个手势 ID 常量,覆盖单指与双指的组合:
源码路径 :frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java
java
public abstract class AccessibilityService extends Service {
// ...
public static final int GESTURE_SWIPE_UP = 1;
public static final int GESTURE_SWIPE_DOWN = 2;
public static final int GESTURE_SWIPE_LEFT = 3;
public static final int GESTURE_SWIPE_RIGHT = 4;
public static final int GESTURE_SWIPE_LEFT_AND_RIGHT = 5;
public static final int GESTURE_SWIPE_RIGHT_AND_LEFT = 6;
public static final int GESTURE_SWIPE_UP_AND_DOWN = 7;
public static final int GESTURE_SWIPE_DOWN_AND_UP = 8;
public static final int GESTURE_SWIPE_LEFT_AND_UP = 9;
public static final int GESTURE_SWIPE_LEFT_AND_DOWN = 10;
public static final int GESTURE_SWIPE_RIGHT_AND_UP = 11;
public static final int GESTURE_SWIPE_RIGHT_AND_DOWN = 12;
public static final int GESTURE_SWIPE_UP_AND_LEFT = 13;
public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14;
public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 15;
public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16;
}
关键 :这些常量与
R.raw.accessibility_gestures手势库中模板的name字段一一对应。AccessibilityGestureDetector.recognizeGesture()中Integer.parseInt(bestPrediction.name)解析出的 int 就是这里的常量。ODM 自定义手势库时必须保证 name 是数字且不与 AOSP 7 的 1-16 冲突。
2.8 完整链路图
TouchExplorer (主线程)
│ onGestureCompleted(gestureId)
│ → mAms.onGesture(gestureId)
▼
AMS.onGesture (system_server, 主线程, 持 mLock)
│ notifyGestureLocked(gestureId, false) → 找到即返回
│ → Service.notifyGesture(gestureId)
│ → mInvocationHandler.send(MSG_ON_GESTURE, gestureId)
▼
AMS.InvocationHandler (system_server, 主线程)
│ service.mServiceInterface.onGesture(gestureId) ← Binder 调用
▼
IAccessibilityServiceClientWrapper (服务进程, Binder 线程池)
│ mCaller.send(DO_ON_GESTURE, gestureId)
▼
MainHandler (服务进程, 主线程)
│ mCallback.onGesture(gestureId)
▼
开发者重写的 AccessibilityService.onGesture(gestureId)
三、KeyEvent 的拦截处理
3.1 触发条件:updateFilterKeyEventsLocked
与手势分发的 FLAG_REQUEST_TOUCH_EXPLORATION_MODE 类似,FLAG_REQUEST_FILTER_KEY_EVENTS 也需要在 UserState 中聚合为 mIsFilterKeyEventsEnabled 布尔量。聚合逻辑在 updateFilterKeyEventsLocked 中:
源码路径 :frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
java
public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// ...
private void updateFilterKeyEventsLocked(UserState userState) {
final int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
Service service = userState.mBoundServices.get(i);
if (service.mRequestFilterKeyEvents
&& (service.mAccessibilityServiceInfo.getCapabilities()
& AccessibilityServiceInfo
.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) != 0) {
userState.mIsFilterKeyEventsEnabled = true;
return;
}
}
userState.mIsFilterKeyEventsEnabled = false;
}
}
关键设计 :这里同时校验了两个条件------服务 XML 声明了
flagRequestFilterKeyEvents(映射到mRequestFilterKeyEvents),且服务通过onServiceConnected设置了CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTScapability。两者缺一不可,这是 AOSP 7 对「能力声明 + 实现确认」双重校验的典型模式。
3.2 进入 InputFilter 链:KeyboardInterceptor
mIsFilterKeyEventsEnabled 为 true 时,AMS.updateInputFilter 会打开 FLAG_FEATURE_FILTER_KEY_EVENTS,AccessibilityInputFilter.enableFeatures 创建 KeyboardInterceptor 并加入链尾:
源码路径 :frameworks/base/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
java
public class KeyboardInterceptor implements EventStreamTransformation {
private EventStreamTransformation mNext;
private AccessibilityManagerService mAms;
public KeyboardInterceptor(AccessibilityManagerService service) {
mAms = service;
}
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
if (mNext != null) {
mNext.onMotionEvent(event, rawEvent, policyFlags);
}
}
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
mAms.notifyKeyEvent(event, policyFlags); // ← 唯一动作:转发给 AMS
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (mNext != null) {
mNext.onAccessibilityEvent(event);
}
}
@Override
public void setNext(EventStreamTransformation next) {
mNext = next;
}
@Override
public void clearEvents(int inputSource) {
if (mNext != null) {
mNext.clearEvents(inputSource);
}
}
@Override
public void onDestroy() {
}
}
关键 :
KeyboardInterceptor是整个EventStreamTransformation链中最简的 handler ------它不修改事件、不消费事件、不维护状态,唯一职责是把KeyEvent转发给AMS.notifyKeyEvent。它之所以被放在链尾,是因为KeyEvent不需要被前面的TouchExplorer、MagnificationGestureHandler等触摸类 handler 看到(它们只处理MotionEvent)。
3.3 AMS 端:notifyKeyEvent
AMS.notifyKeyEvent 在 mLock 内调用 KeyEventDispatcher.notifyKeyEventLocked,把事件分发给所有声明了 CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS 的服务:
源码路径 :frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
java
public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// ...
private KeyEventDispatcher mKeyEventDispatcher;
boolean notifyKeyEvent(KeyEvent event, int policyFlags) {
synchronized (mLock) {
List<Service> boundServices = getCurrentUserStateLocked().mBoundServices;
if (boundServices.isEmpty()) {
return false;
}
return getKeyEventDispatcher().notifyKeyEventLocked(event, policyFlags, boundServices);
}
}
}
3.4 KeyEventDispatcher:双向协商的核心
KeyEventDispatcher 是 AOSP 7 中「按键拦截」的核心类。它的设计哲学是:
- 广播 :把
KeyEvent同时发给所有声明了能力的服务 - 引用计数 :用
referenceCount跟踪「还有多少服务没回报结果」 - 超时 :500ms 内若服务未调用
setOnKeyEventResult,强制视为「未处理」 - 消费判定 :只要有一个服务返回
handled=true,事件即被消费,不再传给InputFilter链尾
源码路径 :frameworks/base/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java
java
public class KeyEventDispatcher {
private static final long ON_KEY_EVENT_TIMEOUT_MILLIS = 500;
private static final int MSG_ON_KEY_EVENT_TIMEOUT = 1;
private static final int MAX_POOL_SIZE = 10;
private final Pool<PendingKeyEvent> mPendingEventPool =
new Pools.SimplePool<>(MAX_POOL_SIZE);
private final Object mLock;
private final Map<Service, ArrayList<PendingKeyEvent>> mPendingEventsMap =
new ArrayMap<>();
private final InputEventConsistencyVerifier mSentEventsVerifier;
private final Handler mHandlerToSendKeyEventsToInputFilter;
private final int mMessageTypeForSendKeyEvent;
private final Handler mKeyEventTimeoutHandler;
private final PowerManager mPowerManager;
public boolean notifyKeyEventLocked(
KeyEvent event, int policyFlags, List<Service> boundServices) {
PendingKeyEvent pendingKeyEvent = null;
KeyEvent localClone = KeyEvent.obtain(event);
for (int i = 0; i < boundServices.size(); i++) {
Service service = boundServices.get(i);
// 只发给声明了能力且设置了 capability 的服务
if (!service.mRequestFilterKeyEvents || (service.mServiceInterface == null)) {
continue;
}
int filterKeyEventBit = service.mAccessibilityServiceInfo.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
if (filterKeyEventBit == 0) {
continue;
}
try {
service.mServiceInterface.onKeyEvent(localClone,
localClone.getSequenceNumber());
} catch (RemoteException re) {
continue;
}
if (pendingKeyEvent == null) {
pendingKeyEvent = obtainPendingEventLocked(localClone, policyFlags);
}
ArrayList<PendingKeyEvent> pendingEventList = mPendingEventsMap.get(service);
if (pendingEventList == null) {
pendingEventList = new ArrayList<>();
mPendingEventsMap.put(service, pendingKeyEvent);
}
pendingEventList.add(pendingKeyEvent);
pendingKeyEvent.referenceCount++;
}
if (pendingKeyEvent == null) {
localClone.recycle();
return false; // 没有任何服务能处理
}
Message message = mKeyEventTimeoutHandler.obtainMessage(
MSG_ON_KEY_EVENT_TIMEOUT, pendingKeyEvent);
mKeyEventTimeoutHandler.sendMessageDelayed(message, ON_KEY_EVENT_TIMEOUT_MILLIS);
return true;
}
public void setOnKeyEventResult(Service service, boolean handled, int sequence) {
synchronized (mLock) {
PendingKeyEvent pendingEvent =
removeEventFromListLocked(mPendingEventsMap.get(service), sequence);
if (pendingEvent != null) {
if (handled && !pendingEvent.handled) {
pendingEvent.handled = handled;
final long identity = Binder.clearCallingIdentity();
try {
mPowerManager.userActivity(pendingEvent.event.getEventTime(),
PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
removeReferenceToPendingEventLocked(pendingEvent);
}
}
}
}
关键设计 :
referenceCount是引用计数,等于该PendingKeyEvent在mPendingEventsMap中出现的次数。每当一个服务调用setOnKeyEventResult或超时,referenceCount--;当referenceCount == 0且handled == false时,事件被重新投递到mHandlerToSendKeyEventsToInputFilter(即AMS.MainHandler的MSG_SEND_KEY_EVENT_TO_INPUT_FILTER),最终由AccessibilityInputFilter.onKeyEvent调sendInputEvent交给WindowManagerService继续分发。
3.5 超时处理:Callback
KeyEventDispatcher 内部的 Callback 处理 MSG_ON_KEY_EVENT_TIMEOUT 消息。超时后强制移除 pendingKeyEvent,若 handled == false 则走「传给 InputFilter」路径:
源码路径 :frameworks/base/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java
java
public class KeyEventDispatcher {
// ...
private boolean removeReferenceToPendingEventLocked(PendingKeyEvent pendingEvent) {
if (--pendingEvent.referenceCount > 0) {
return false;
}
mKeyEventTimeoutHandler.removeMessages(MSG_ON_KEY_EVENT_TIMEOUT, pendingEvent);
if (!pendingEvent.handled) {
// 没有任何服务处理 → 传给 InputFilter 继续分发
if (mSentEventsVerifier != null) {
mSentEventsVerifier.onKeyEvent(pendingEvent.event, 0);
}
int policyFlags = pendingEvent.policyFlags
| WindowManagerPolicy.FLAG_PASS_TO_USER;
mHandlerToSendKeyEventsToInputFilter
.obtainMessage(mMessageTypeForSendKeyEvent, policyFlags, 0,
pendingEvent.event)
.sendToTarget();
} else {
pendingEvent.event.recycle(); // 被消费 → 释放
}
mPendingEventPool.release(pendingEvent);
return true;
}
private class Callback implements Handler.Callback {
@Override
public boolean handleMessage(Message message) {
if (message.what != MSG_ON_KEY_EVENT_TIMEOUT) {
throw new IllegalArgumentException("Unknown message: " + message.what);
}
PendingKeyEvent pendingKeyEvent = (PendingKeyEvent) message.obj;
synchronized (mLock) {
for (ArrayList<PendingKeyEvent> listForService : mPendingEventsMap.values()) {
if (listForService.remove(pendingKeyEvent)) {
if (removeReferenceToPendingEventLocked(pendingKeyEvent)) {
break;
}
}
}
}
return true;
}
}
}
3.6 服务进程端:onKeyEvent 与 setOnKeyEventResult
服务进程侧,IAccessibilityServiceClientWrapper.onKeyEvent 把事件包装成 Message 投递到 MainHandler。MainHandler 的 DO_ON_KEY_EVENT 处理逻辑是:调用开发者重写的 mCallback.onKeyEvent(event),拿到 boolean 结果后通过 connection.setOnKeyEventResult(result, sequence) 回报给 AMS:
源码路径 :frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java
java
public abstract class AccessibilityService extends Service {
// ...
private class IAccessibilityServiceClientWrapper
extends IAccessibilityServiceClient.Stub {
// ...
@Override
public void onKeyEvent(KeyEvent event, int sequence) {
Message message = mCaller.obtainMessageIO(DO_ON_KEY_EVENT, sequence, event);
mCaller.sendMessage(message);
}
}
private final class MainHandler extends Handler {
// ...
@Override
public void handleMessage(Message message) {
// ...
switch (message.what) {
case DO_ON_KEY_EVENT: {
KeyEvent event = (KeyEvent) message.obj;
try {
IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getInstance()
.getConnection(mConnectionId);
if (connection != null) {
final boolean result = mCallback.onKeyEvent(event);
final int sequence = message.arg1;
try {
connection.setOnKeyEventResult(result, sequence);
} catch (RemoteException re) {
/* ignore */
}
}
} finally {
// Make sure the event is recycled.
try {
event.recycle();
} catch (IllegalStateException ise) {
/* ignore - best effort */
}
}
} return;
// ...
}
}
}
/**
* Callback that allows an accessibility service to observe the key events
* before they are passed to the rest of the system. ...
*/
protected boolean onKeyEvent(KeyEvent event) {
return false; // 默认不消费
}
}
关键设计 :
onKeyEvent是同步 的------MainHandler在拿到result后立即调setOnKeyEventResult。但要注意,setOnKeyEventResult是 Binder 调用,它返回后MainHandler才能处理下一条消息。这意味着:
- 服务进程必须快速响应
onKeyEvent,否则 500ms 超时会触发- 多个
KeyEvent会被MainHandler串行处理,sequence号用于在KeyEventDispatcher中定位对应的PendingKeyEvent- 开发者不能 在
onKeyEvent里做耗时操作(如启动 Intent、读数据库),否则会触发超时
3.7 典型应用:音量键映射
许多辅助 App 利用 onKeyEvent 实现按键重映射。例如:
java
@Override
protected boolean onKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) {
performGlobalAction(GLOBAL_ACTION_NOTIFICATIONS);
return true; // 消费事件,不再传给目标窗口
}
return false; // 其他按键放行
}
关键 :
onKeyEvent的「消费」语义是系统级 的------一旦handled=true,KeyEventDispatcher会recycle()掉该事件,WindowManagerService不会收到它。这意味着服务可以「拦截」音量键而不影响系统音量,这是 TalkBack 等屏幕阅读器实现「音量键 + 双击 = 选择当前朗读控件」等交互的基础。
四、全局动作:performGlobalAction
4.1 服务进程端:performGlobalAction
performGlobalAction 是 AccessibilityService 的 final 方法。它通过 IAccessibilityServiceConnection(服务进程 → AMS 的 Binder 接口)把 action 发给 system_server:
源码路径 :frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java
java
public abstract class AccessibilityService extends Service {
// ...
private int mConnectionId;
public final boolean performGlobalAction(int action) {
IAccessibilityServiceConnection connection =
AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
if (connection != null) {
try {
return connection.performGlobalAction(action);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Error while calling performGlobalAction", re);
re.rethrowFromSystemServer();
}
}
return false;
}
}
4.2 AMS 端:Service.performGlobalAction
AMS 端,Service 内部类实现了 IAccessibilityServiceConnection.performGlobalAction。它在 mLock 内校验调用者身份,然后 Binder.clearCallingIdentity 后在主线程执行具体动作:
源码路径 :frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
java
public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// ...
private final class Service {
// ...
@Override
public boolean performGlobalAction(int action) {
synchronized (mLock) {
if (!isCalledForCurrentUserLocked()) {
return false;
}
}
final long identity = Binder.clearCallingIdentity();
try {
mPowerManager.userActivity(SystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0);
switch (action) {
case AccessibilityService.GLOBAL_ACTION_BACK: {
sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
} return true;
case AccessibilityService.GLOBAL_ACTION_HOME: {
sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME);
} return true;
case AccessibilityService.GLOBAL_ACTION_RECENTS: {
return openRecents();
}
case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: {
expandNotifications();
} return true;
case AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS: {
expandQuickSettings();
} return true;
case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: {
showGlobalActions();
} return true;
case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: {
toggleSplitScreen();
} return true;
}
return false;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
}
关键设计 :
Binder.clearCallingIdentity()是关键------它把「服务进程」的调用身份替换为「system_server」自身,使得后续的sendDownAndUpKeyEvents、expandNotifications等调用拥有系统级权限。finally块里的restoreCallingIdentity保证身份恢复,避免污染后续调用。
4.3 全局动作常量
AOSP 7 中 AccessibilityService 定义了 7 个全局动作常量:
源码路径 :frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java
java
public abstract class AccessibilityService extends Service {
// ...
public static final int GLOBAL_ACTION_BACK = 1;
public static final int GLOBAL_ACTION_HOME = 2;
public static final int GLOBAL_ACTION_RECENTS = 3;
public static final int GLOBAL_ACTION_NOTIFICATIONS = 4;
public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5;
public static final int GLOBAL_ACTION_POWER_DIALOG = 6;
public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7;
}
关键 :AOSP 7 的
performGlobalAction只实现了上述 7 个动作。Android 8+ 才加入GLOBAL_ACTION_LOCK_SCREEN(8)与GLOBAL_ACTION_TAKE_SCREENSHOT(9)。如果开发者在 Android 7 设备上调用GLOBAL_ACTION_LOCK_SCREEN,AMS 端的switch会落到return false,调用者拿到的返回值为false。
4.4 各动作的底层实现
| 动作 | AMS 端实现 | 底层机制 |
|---|---|---|
GLOBAL_ACTION_BACK |
sendDownAndUpKeyEvents(KEYCODE_BACK) |
InputManager.injectInputEvent 注入带 FLAG_FROM_SYSTEM 的 down/up 事件 |
GLOBAL_ACTION_HOME |
sendDownAndUpKeyEvents(KEYCODE_HOME) |
同上 |
GLOBAL_ACTION_RECENTS |
openRecents() |
StatusBarManagerInternal.toggleRecentApps() |
GLOBAL_ACTION_NOTIFICATIONS |
expandNotifications() |
StatusBarManager.expandNotificationsPanel() |
GLOBAL_ACTION_QUICK_SETTINGS |
expandQuickSettings() |
StatusBarManager.expandSettingsPanel() |
GLOBAL_ACTION_POWER_DIALOG |
showGlobalActions() |
mWindowManagerService.showGlobalActions() |
GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN |
toggleSplitScreen() |
StatusBarManagerInternal.toggleSplitScreen() |
关键 :
sendDownAndUpKeyEvents通过InputManager.injectInputEvent注入带FLAG_FROM_SYSTEM标志的按键事件,这是「系统级」按键注入的标准方式------注入的事件会被WindowManagerService视为来自系统而非用户,绕过部分权限检查。其余动作则通过StatusBarManager/StatusBarManagerInternal/WindowManagerService的 Binder 接口触发------这意味着全局动作的「系统级」权限边界在StatusBarManager这一层。
五、关键源码文件索引
| 文件 | 角色 | 关键方法 |
|---|---|---|
AccessibilityManagerService.java |
手势/按键/全局动作的 system_server 端入口 | onGesture、notifyGestureLocked、notifyKeyEvent、Service.performGlobalAction、updateFilterKeyEventsLocked |
KeyboardInterceptor.java |
把 KeyEvent 转发给 AMS 的链尾 handler | onKeyEvent |
KeyEventDispatcher.java |
双向协商的核心,引用计数 + 超时 | notifyKeyEventLocked、setOnKeyEventResult、removeReferenceToPendingEventLocked |
AccessibilityService.java |
服务进程端,开发者 API 载体 | onGesture、onKeyEvent、performGlobalAction、IAccessibilityServiceClientWrapper、MainHandler |
IAccessibilityServiceClient.aidl |
手势/按键的 Binder 接口定义 | onGesture(int)、onKeyEvent(KeyEvent, int) |
IAccessibilityServiceConnection.aidl |
全局动作的 Binder 接口定义 | performGlobalAction(int)、setOnKeyEventResult(boolean, int) |
六、小结
两条输入拦截通道的对比:
┌─ 触摸/手势通道 ──────────────────────────────────────────────┐
│ 单向通知:系统识别手势 → 服务执行动作 │
│ │
│ TouchExplorer → AMS.onGesture → Service.notifyGesture │
│ → InvocationHandler → IAccessibilityServiceClient.onGesture│
│ → MainHandler → 开发者 onGesture(gestureId) │
│ │
│ 特点:无返回值,"消费"语义由 AMS 端"找到即返回"实现 │
└──────────────────────────────────────────────────────────────┘
┌─ 按键通道 ───────────────────────────────────────────────────┐
│ 双向协商:服务必须明确"处理/未处理",系统据此决定继续分发 │
│ │
│ KeyboardInterceptor → AMS.notifyKeyEvent │
│ → KeyEventDispatcher.notifyKeyEventLocked │
│ → IAccessibilityServiceClient.onKeyEvent │
│ → MainHandler → 开发者 onKeyEvent(event) → boolean │
│ → setOnKeyEventResult(result, sequence) │
│ → 若 handled=false → 传回 InputFilter 继续分发 │
│ → 若 500ms 超时 → 强制视为未处理 │
└──────────────────────────────────────────────────────────────┘
┌─ 全局动作 ───────────────────────────────────────────────────┐
│ 系统级操作:注入按键 / 调用 StatusBarManager / WMS │
│ │
│ 开发者 performGlobalAction(action) │
│ → IAccessibilityServiceConnection.performGlobalAction │
│ → AMS.Service.performGlobalAction (clearCallingIdentity) │
│ → sendDownAndUpKeyEvents / expandNotifications / 等 │
└──────────────────────────────────────────────────────────────┘
三条路径共同构成了无障碍服务与系统输入系统交互的完整图景:
- 手势:用于「空间导航」(TalkBack 的双指左滑 = 返回)
- 按键:用于「物理输入重映射」(音量键 + 双击 = 选择当前朗读控件)
- 全局动作:用于「系统级操作」(Back、Home、通知栏等)
下一篇作为系列终篇,将分析放大镜(MagnificationController 与 MagnificationGestureHandler)和 MotionEventInjector 等辅助功能特性,补全 AOSP 7 无障碍子系统的最后一块拼图。