返回顶部
首页 > 资讯 > 移动开发 >Android AccessibilityService 事件分发原理分析总结
  • 676
分享到

Android AccessibilityService 事件分发原理分析总结

2024-04-02 19:04:59 676人浏览 泡泡鱼
摘要

目录AccessibilityService 监听事件的调用逻辑onAccessibilityEventonInterceptAccessibilityService 事件的外部来源

前言:

在了解了无障碍服务基础使用之后,我们来探究一下 AccessibilityService 的事件接收方法回调的时机和它深层次的实现逻辑。

AccessibilityService 监听事件的调用逻辑

AccessibilityService 有很多用来接收外部调用事件变化的方法,这些方法封装在内部接口 Callbacks 中:

public interface Callbacks {
    void onAccessibilityEvent(AccessibilityEvent event);
    void onInterrupt();
    void onServiceConnected();
    void init(int connectionId, IBinder windowToken);
    boolean onGesture(AccessibilityGestureEvent gestureInfo);
    boolean onKeyEvent(KeyEvent event);
    void onMagnificationChanged(int displayId, @NonNull Region region, float scale, float centerX, float centerY);
    void onSofTKEyboardShowModeChanged(int showMode);
    void onPerfORMGestureResult(int sequence, boolean completedSuccessfully);
    void onFingerprintCapturingGesturesChanged(boolean active);
    void onFingerprintGesture(int gesture);
    void onAccessibilityButtonClicked(int displayId);
    void onAccessibilityButtonAvailabilityChanged(boolean available);
    void onSysteMactionsChanged();
}

以最常用的 onAccessibilityEvent 为例,介绍一下调用流程。

onAccessibilityEvent 在 AccessibilityService 中,AccessibilityService 在 onBind 生命周期中,返回了一个IAccessibilityServiceClientWrapper 对象,它是一个 Binder ,所以外部实际上通过 Binder 机制跨进程调用到无障碍服务的。

外部通过 Binder 调用到 Service 具体的实现方法的调用栈是:

- frameworks/base/core/java/Android/accessibilityservice/AccessibilityService.java#IAccessibilityServiceClientWrapper#onAccessibilityEvent
- frameworks/base/core/java/com/android/internal/os/HandlerCaller.java#sendMessage
- frameworks/base/core/java/com/android/internal/os/HandlerCaller.java#Callback#executeMessage
- frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#IAccessibilityServiceClientWrapper#executeMessage
- frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#Callbacks#onAccessibilityEvent
- frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java#onAccessibilityEvent

首先是外部调用到 IAccessibilityServiceClientWrapper 的 onAccessibilityEvent 方法:

// IAccessibilityServiceClientWrapper
public void onAccessibilityEvent(AccessibilityEvent event, boolean serviceWantsEvent) {
    Message message = mCaller.obtainMessageBO(
            DO_ON_ACCESSIBILITY_EVENT, serviceWantsEvent, event);
    mCaller.sendMessage(message);
}

在这个方法中通过 HandlerCaller 切换到主线程,然后发送了一个消息。

这里的 HandlerCaller 的源码是:

public class HandlerCaller {
    final Looper mMainLooper;
    final Handler mH;
    final Callback mCallback;
    class MyHandler extends Handler {
        MyHandler(Looper looper, boolean async) {
            super(looper, null, async);
        }
        @Override
        public void handleMessage(Message msg) {
            mCallback.executeMessage(msg);
        }
    }
    public interface Callback {
        public void executeMessage(Message msg);
    }
    public HandlerCaller(Context context, Looper looper, Callback callback,
            boolean asyncHandler) {
        mMainLooper = looper != null ? looper : context.getMainLooper();
        mH = new MyHandler(mMainLooper, asyncHandler);
        mCallback = callback;
    }
    ...
}

从它的源码中可以看出,这是一个向主线程发消息的 Handler 。 在主线程中执行它的内部类 Callback 的 executeMessage 方法。

IAccessibilityServiceClientWrapper 实现了 AccessibilityService.Callback接口,所以调用到了IAccessibilityServiceClientWrapper.executeMessage 方法中。IAccessibilityServiceClientWrapper 对象的创建是在 onBind 生命周期中。它接收一个 AccessibilityService.Callback 对象作为 IAccessibilityServiceClientWrapper 的构造参数:

@Override
public final IBinder onBind(Intent intent) {
    return new IAccessibilityServiceClientWrapper(this, getMainLooper(), 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);
        }
	...
});

IAccessibilityServiceClientWrapper 中的 executeMessage中,根据不同的 Handler 消息调用了 AccessibilityService.Callback 的对应方法:

public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub implements HandlerCaller.Callback {
    private final HandlerCaller mCaller;
    public IAccessibilityServiceClientWrapper(Context context, Looper looper,
        Callbacks callback) {
        // ...
        mCaller = new HandlerCaller(context, looper, this, true);
    }
    @Override
    public void executeMessage(Message message) {
        switch (message.what) {
            case DO_ON_ACCESSIBILITY_EVENT: {
                // ...
                mCallback.onAccessibilityEvent(event);
                return;
            }
            case DO_ON_INTERRUPT: {
                // ...
                mCallback.onInterrupt();
                return;
            }
            default :
                Log.w(LOG_TAG, "Unknown message type " + message.what);
        }   
    }
}

而刚才传入的 AccessibilityService.Callback 方法的实现中,调用了AccessibilityService 的 onAccessibilityEvent 方法:

        @Override
        public void onAccessibilityEvent(AccessibilityEvent event) {
            AccessibilityService.this.onAccessibilityEvent(event);
        }

这样整个调用链就清晰了:

  • 外部通过 Binder 机制调用到 AccessibilityService 的内部 Binder 代理实现IAccessibilityServiceClientWrapper对象
  • IAccessibilityServiceClientWrapper对象内部通过 Handler 机制切换到主线程执行 AccessibilityService.Callback 中对应的方法。
  • AccessibilityService.Callback 中的方法调用到了AccessibilityService 对应的生命周期方法。

接下来关注一下一些重要的事件接收方法。

onAccessibilityEvent

Handler 中的 DO_ON_ACCESSIBILITY_EVENT 事件会调用到 onAccessibilityEvent 。 在 executeMessage(Message message) 方法中的逻辑是:

case DO_ON_ACCESSIBILITY_EVENT: {
    AccessibilityEvent event = (AccessibilityEvent) message.obj;
    boolean serviceWantsEvent = message.arg1 != 0;
    if (event != null) {
        // Send the event to AccessibilityCache via AccessibilityInteractionClient
        AccessibilityInteractionClient.getInstance(mContext).onAccessibilityEvent(
                event);
        if (serviceWantsEvent
                && (mConnectionId != AccessibilityInteractionClient.NO_ID)) {
            // Send the event to AccessibilityService
            mCallback.onAccessibilityEvent(event);
        }
        // Make sure the event is recycled.
        try {
            event.recycle();
        } catch (IllegalStateException ise) {
            
        }
    }
    return;
}
  • 取出 message.obj 转换为 AccessibilityEvent ,并根据 message.arg1检查 Service 是否想要处理这个事件。
  • 检查 AccessibilityEvent对象是否为 null,为空直接 return
  • 将 AccessibilityEvent对象通过 AccessibilityInteractionClient 加入到 AccessibilityCache 缓存中,然后根据 service 是否要处理事件和AccessibilityInteractionClient连接状态,决定是否要将事件发送给 AccessibilityService
  • 最后回收事件对象。

这里的AccessibilityInteractionClient连接状态检查时通过 mConnectionId 属性来判断的,在IAccessibilityServiceClientWrapper 的 init 时被赋值,init 也是通过 Handler 传递来的消息切换到主线程进行的:

case DO_INIT: {
    mConnectionId = message.arg1;
    SomeArgs args = (SomeArgs) message.obj;
    IAccessibilityServiceConnection connection =
            (IAccessibilityServiceConnection) args.arg1;
    IBinder windowToken = (IBinder) args.arg2;
    args.recycle();
    if (connection != null) {
        AccessibilityInteractionClient.getInstance(mContext).addConnection(
                mConnectionId, connection);
        mCallback.init(mConnectionId, windowToken);
        mCallback.onServiceConnected();
    } else {
        AccessibilityInteractionClient.getInstance(mContext).removeConnection(
                mConnectionId);
        mConnectionId = AccessibilityInteractionClient.NO_ID;
        AccessibilityInteractionClient.getInstance(mContext).clearCache();
        mCallback.init(AccessibilityInteractionClient.NO_ID, null);
    }
    return;
}

onIntercept

onAccessibilityEvent 事件一样都是通过 Handler 机制进行处理的:

case DO_ON_INTERRUPT: {
    if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
        mCallback.onInterrupt();
    }
    return;
}

只检查了与AccessibilityInteractionClient 的连接状态。

AccessibilityService 事件的外部来源

经过上面的分析,我们知道外部通过 binder 机制触发了 AccessibilityService 的事件监听方法,那么它们来自哪里呢? 接下来从 AccessibilityServiceInfo 开始,分析系统事件是如何传递到无障碍服务的。

AccessibilityServiceInfo

AccessibilityServiceInfo 用来描述 AccessibilityService 。系统根据这个类中的信息,将 AccessibilityEvents 通知给一个 AccessibilityService。

AccessibilityServiceInfo 中定义了一些属性,用来控制无障碍服务的一些权限和能力。我们在 AndroidManifest.xml 中为无障碍服务指定的meta-data 标签中,指定的配置文件中的配置,和AccessibilityServiceInfo 中的属性一一对应。

AccessibilityServiceInfo 的引用:

查看 AccessibilityServiceInfo 的引用栈,发现有很多地方都用到了这个类,关于无障碍的重点看 AccessibilityManagerService 和 AccessibilityManager 这一套逻辑。 从名称上看,无障碍功能提供了类似 AMS 一样的系统服务,并通过一个 Manager 类来进行调用。

AccessibilityManager

AccessibilityManager 是一个系统服务管理器,用来分发 AccessibilityEvent 事件。当用户界面中发生一些值得注意的事件时,例如焦点变化和 Activity 启动等,会生成这些事件。

AccessibilityManager 内部有一个看起来与发送消息有关的方法:

public void sendAccessibilityEvent(AccessibilityEvent event) {
        final IAccessibilityManager service;
        final int userId;
        final AccessibilityEvent dispatchedEvent;
        synchronized (mLock) {
            service = getServiceLocked();
            if (service == null) return;
            event.setEventTime(SystemClock.uptimeMillis());
            if (event.getAction() == 0) {
                event.setAction(mPerformingAction);
            }
            if (mAccessibilityPolicy != null) {
                dispatchedEvent = mAccessibilityPolicy.onAccessibilityEvent(event, mIsEnabled, mRelevantEventTypes);
                if (dispatchedEvent == null) return;
            } else {
                dispatchedEvent = event;
            }
            if (!isEnabled()) {
                Looper myLooper = Looper.myLooper();
                if (myLooper == Looper.getMainLooper()) {
                    throw new IllegalStateException("Accessibility off. Did you forget to check that?");
                } else {
                    // 当不是在主线程(mainLooper)运行时,调用检查无障碍开启状态可能会异常。因此直接抛出异常
                    Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
                    return;
                }
            }
            userId = mUserId;
        }
        try {
            final long identityToken = Binder.clearCallingIdentity();
            try {
                service.sendAccessibilityEvent(dispatchedEvent, userId);
            } finally {
                Binder.restoreCallingIdentity(identityToken);
            }
            if (DEBUG) {
                Log.i(LOG_TAG, dispatchedEvent + " sent");
            }
        } catch (RemoteException re) {
            Log.e(LOG_TAG, "Error during sending " + dispatchedEvent + " ", re);
        } finally {
            if (event != dispatchedEvent) {
                event.recycle();
            }
            dispatchedEvent.recycle();
        }
    }

这个方法是用来发送一个 AccessibilityEvent 事件的,简化里面的逻辑:

- 加
- getServiceLocked() 获取 service 对象,service 获取不到直接 return
- event 设置一个时间,然后设置 action
- 检查 AccessibilityPolicy 对象是否为 null
  - 不为空,dispatchedEvent 根据 AccessibilityPolicy 的 onAccessibilityEvent(event) 赋值,赋值后仍为空直接 return
  - 为空, dispatchedEvent = event
- 检查系统是否开启无障碍功能
- 解锁
- try
  - try
    - service.sendAccessibilityEvent(dispatchedEvent, userId); 通过 AccessibilityManagerService 的 sendAccessibilityEvent 发送事件
  - finally
    - Binder.restoreCallingIdentity(identityToken); 
- finally
  - 回收 event 和 dispatchedEvent 对象

这里 getServiceLocked() 内部调用了 tryConnectToServiceLocked 方法:

    private void tryConnectToServiceLocked(IAccessibilityManager service) {
        if (service == null) {
            IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
            if (iBinder == null) {
                return;
            }
            service = IAccessibilityManager.Stub.asInterface(iBinder);
        }
		  // ...
    }

而 AccessibilityManagerService 实现了 IAccessibilityManager.Stub ,所以这里的 service 时 AccessibilityManagerService。 这里调用了 service.sendAccessibilityEvent(dispatchedEvent, userId);

通过 Binder 机制,调用到的是 AccessibilityManagerService 里的 sendAccessibilityEvent 方法。

AccessibilityManager.sendAccessibilityEvent 的调用位置有很多,其中比较显眼的是在 ViewRootImpl 中的,因为 ViewRootImpl 是 View 添加到 Window 的重要实现类。sendAccessibilityEvent 在 ViewRootImpl 的内部类中存在调用:

    @Override
    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;
        }
        mAccessibilityManager.sendAccessibilityEvent(event);
        return true;
    }

这是一个 override 方法,它的定义在接口 ViewParent 中。 这个方法的调用栈很多:

可以跟到 View 中存在的同名方法 :

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

它在 View 中的调用:

可以看出,常见的 View 的事件,包括:点击、长按、焦点变化等,都会调用sendAccessibilityEvent方法。

在 ViewRootImpl中,AccessibilityManager也存在很多处调用逻辑:

可以看出在 Android 在 View 体系中,提供了很多对无障碍能力的支持。所有的 View 的事件都会被系统的无障碍服务捕获到。

回到调用逻辑,AccessibilityManager内部调用到的是AccessibilityManagerService里的sendAccessibilityEvent方法。下面介绍 AccessibilityManagerService里面的流程。

AccessibilityManagerService

sendAccessibilityEvent伪代码逻辑:

synchronized {
	1. 解析配置文件中的属性,并对其进行配置
	2. 设置配置的包名范围
}
if (dispatchEvent) {
	3. 确保接收此事件的 client 能够获取 window 当前的状态,因为 Window Manager 可能会出于性能原因延迟计算,通过配置 shouldComputewindows = true/false
	if (shouldComputeWindows) {
		4. 获取 WindowManagerInternal wm 
		5. wm.computeWindowsForAccessibility(displayId);
	}
	synchoronized {
		notifyAccessibilityServicesDelayedLocked(event, false)
		notifyAccessibilityServicesDelayedLocked(event, true)
		mUiAutomationManager.sendAccessibilityEventLocked(event);
	}
}
...

最后的关键三行代码中,调用了两个方法:

notifyAccessibilityServicesDelayedLocked :

    private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, boolean isDefault) {
        try {
            AccessibilityUserState state = getCurrentUserStateLocked();
            for (int i = 0, count = state.mBoundServices.size(); i < count; i++) {
                AccessibilityServiceConnection service = state.mBoundServices.get(i);
                if (service.mIsDefault == isDefault) {
                    service.notifyAccessibilityEvent(event);
                }
            }
        } catch (IndexOutOfBoundsException oobe) {}
    }

AccessibilityManagerService从这个方法中,调用 AccessibilityServiceConnection的同名方法notifyAccessibilityEvent 。

这个意思是,先通知service.mIsDefault = false的无障碍服务连接发送事件,然后再通知 等于 true 的无障碍服务连接发送事件。

mUiAutomationManager.sendAccessibilityEventLocked(event)

mUiAutomationManager的类型是UiAutomationManager,它的sendAccessibilityEventLocked方法实现是:

void sendAccessibilityEventLocked(AccessibilityEvent event) {
    if (mUiAutomationService != null) {
        mUiAutomationService.notifyAccessibilityEvent(event);
    }
}

mUiAutomationService的类型是 UiAutomationService,它是UiAutomationManager的内部类,继承自AbstractAccessibilityServiceConnection,内部操作都是切换到主线程进行的,notifyAccessibilityEvent方法的实现在父类中,后续会和上面的AccessibilityServiceConnection一起说明。

AccessibilityServiceConnection

此类用来表示一个无障碍服务。 它存储着服务管理所需的所有每个服务数据,提供用于启动/停止服务的 api,并负责在服务管理的数据结构中添加/删除服务。 该类还公开了配置接口,该接口在绑定后立即传递给它所代表的服务。 它还用作服务的连接。

AccessibilityServiceConnectionUiAutomationService 一样,继承自AbstractAccessibilityServiceConnection

它们的notifyAccessibilityEvent方法,在AbstractAccessibilityServiceConnection中:

    public void notifyAccessibilityEvent(AccessibilityEvent event) {
        synchronized (mLock) {
            ...
            // copy 一个副本,因为在调度期间,如果接收的服务没有访问窗口内容的权限,则可能会修改并删除事件。
            AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
            Message message;
            if ((mNotificationTimeout > 0)
                    && (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)) {
                // 最多允许一个待处理事件
                final AccessibilityEvent oldEvent = mPendingEvents.get(eventType);
                mPendingEvents.put(eventType, newEvent);
                if (oldEvent != null) {
                    mEventDispatchHandler.removeMessages(eventType);
                    oldEvent.recycle();
                }
                message = mEventDispatchHandler.obtainMessage(eventType);
            } else {
                // 发送所有消息,绕过 mPendingEvents
                message = mEventDispatchHandler.obtainMessage(eventType, newEvent);
            }
            message.arg1 = serviceWantsEvent ? 1 : 0;

            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 serviceWantsEvent = message.arg1 != 0;
                notifyAccessibilityEventInternal(eventType, event, serviceWantsEvent);
            }
        };

内部调用notifyAccessibilityEventInternal

private void notifyAccessibilityEventInternal(int eventType, AccessibilityEvent event, boolean serviceWantsEvent) {
    IAccessibilityServiceClient listener;
    synchronized (mLock) {
        listener = mServiceInterface;
        // 如果在消息分发无障碍事件时 service die 或 关闭,listener 可能为空
        if (listener == null) return;
        // 我们有两种通知事件的方式,节流和非节流。 如果我们不进行节流,那么消息会随事件一起出现,我们会毫不费力地处理这些事件。
        if (event == null) {
            // 我们正在限制事件,所以只要它为空,我们就会在 mPendingEvents 中发送这种类型的事件。 由于竞争条件,它只能为空:
            //   1) 一个 binder 线程调用 notifyAccessibilityServiceDelayedLocked,它发布一条用于调度事件的消息并将该事件存储在 mPendingEvents 中。
            //   2) 消息由服务线程上的处理程序从队列中拉出,此方法即将获取锁。
            //   3) 另一个 binder 线程在 notifyAccessibilityEvent 中获取锁
            //   4) notifyAccessibilityEvent 回收该方法即将处理的事件,替换为新的,并发布第二条消息
            //   5) 此方法抓取新事件,对其进行处理,然后将其从 mPendingEvents 中删除
            //   6) (4) 中发送的第二条消息到达,但事件已在 (5) 中删除。
            event = mPendingEvents.get(eventType);
            if (event == null) {
                return;
            }
            mPendingEvents.remove(eventType);
        }
        if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) {
            event.setConnectionId(mId);
        } else {
            event.setSource((View) null);
        }
        event.setSealed(true);
    }

    try {
        listener.onAccessibilityEvent(event, serviceWantsEvent);

    } catch (RemoteException re) {} finally {
        event.recycle();
    }
}

备注中说明了消息处理和分发逻辑,但我们这里只需要关注最后的:

listener.onAccessibilityEvent(event, serviceWantsEvent);

这里的 listener 是一个IAccessibilityServiceClient,是个 AIDL 文件。这个 AIDL 在我们的 AccessibilityService 中有实现类!

调用到这个IAccessibilityServiceClient , 就能调用到AccessibilityService中的代码了。

至此无障碍服务的调用,从系统的 View 和其他事件位置,经过AccessibilityManager.notifyAccessibilityEvent用 Binder 机制调用AccessibilityManagerService.notifyAccessibilityEvent ;然后AccessibilityManagerService内部通过调用AccessibilityServiceConnection.notifyAccessibilityEvent来调用我们可以实现的AccessibilityService中接收事件。

到此这篇关于Android AccessibilityService 事件分发原理分析总结的文章就介绍到这了,更多相关Android AccessibilityService 内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Android AccessibilityService 事件分发原理分析总结

本文链接: https://lsjlt.com/news/151681.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作