AccessibilityService 的 onInterrupt 方法是怎么用的?
Posted bug樱樱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AccessibilityService 的 onInterrupt 方法是怎么用的?相关的知识,希望对你有一定的参考价值。
onInterrupt 是实现自定义无障碍服务必须实现的方法,但官方文档和注释中对此方法的描述十分模糊,从字面上可以理解这是无障碍服务中断的回调,但具体是什么样的场景并没有一个明确的示例。
onInterrupt 在官方文档中查到的相关描述是:
onInterrupt
() - (required) This method is called when the system wants to interrupt the feedback your service is providing, usually in response to a user action such as moving focus to a different control. This method may be called many times over the lifecycle of your service.
(必需)当系统要中断服务正在提供的反馈(通常是为了响应将焦点移到其他控件等用户操作)时,会调用此方法。此方法可能会在服务的整个生命周期内被调用多次。
onInterrupt 在 AccessibilityService 中定义,是一个抽象方法:
public abstract class AccessibilityService extends Service
// ...
public abstract void onInterrupt();
// ...
AccessibilityService#onInterrupt() 的调用在 AccessibilityService 的 onBind 方法中:
@Override
public final IBinder onBind(Intent intent)
return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks()
// ...
@Override
public void onInterrupt()
AccessibilityService.this.onInterrupt();
这里返回了一个 IAccessibilityServiceClientWrapper 对象作为 IBinder,IAccessibilityServiceClientWrapper 构造参数有三个:
public IAccessibilityServiceClientWrapper(Context context, Looper looper,
Callbacks callback)
mCallback = callback;
mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
在构造方法中,通过 Context。和 Looper 构造了一个 HandlerCaller 对象。
而最后的 Callbacks 参数是 AccessibilityService 的内部接口。将其保存到了 mCallback 。
AccessibilityService 中定义 Callbacks 接口:
public interface Callbacks
void onAccessibilityEvent(AccessibilityEvent event);
void onInterrupt();
void onServiceConnected();
void init(int connectionId, IBinder windowToken);
/** The detected gesture information for different displays */
boolean onGesture(AccessibilityGestureEvent gestureInfo);
boolean onKeyEvent(KeyEvent event);
/** Magnification changed callbacks for different displays */
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);
/** Accessbility button clicked callbacks for different displays */
void onAccessibilityButtonClicked(int displayId);
void onAccessibilityButtonAvailabilityChanged(boolean available);
/** This is called when the system action list is changed. */
void onSystemActionsChanged();
AccessibilityService.Callback 接口是 IAccessibilityServiceClientWrapper 用于从其主线程调用 Service 的接口。这里是通过 handler 机制来进行的。
IAccessibilityServiceClientWrapper 在构造时,会构造一个 HandlerCaller :
mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
HandlerCaller 是一个 Handler 的包装器:
public HandlerCaller(Context context, Looper looper, Callback callback,
boolean asyncHandler)
mMainLooper = looper != null ? looper : context.getMainLooper();
mH = new MyHandler(mMainLooper, asyncHandler);
mCallback = callback;
HandlerCaller 内部定义了 Callback 接口:
public interface Callback
public void executeMessage(Message msg);
这个 Callback 由 IAccessibilityServiceClientWrapper 实现,并处理了中断相关类型的消息:
@Override
public void executeMessage(Message message)
switch (message.what)
// ...
case DO_ON_INTERRUPT:
if (mConnectionId != AccessibilityInteractionClient.NO_ID)
mCallback.onInterrupt();
return;
还记得这里 mCallback 吗,就是 IAccessibilityServiceClientWrapper 构造方法中传入的 AccessibilityService 中的 onBind 中构造的匿名对象,从而调用到 AccessibilityService 的 onInterrupt:
@Override
public final IBinder onBind(Intent intent)
return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks()
// ...
@Override
public void onInterrupt()
AccessibilityService.this.onInterrupt();
IAccessibilityServiceClientWrapper 可以作为 IBinder 对象,是因为实现了 IAccessibilityServiceClient.Stub ,IAccessibilityServiceClient 是 AIDL 接口,内部定义了 onInterrupt() 方法,在这里的实现是通过 Handler 发送了 DO_ON_INTERRUPT 类型的消息。
public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
implements HandlerCaller.Callback
public void onInterrupt()
Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
mCaller.sendMessage(message);
发送DO_ON_INTERRUPT
类型的消息,正好回调到了 mCallback.onInterrupt 。而
IAccessibilityServiceClientWrapper 的 onInterrupt 是在哪里调用的呢?
IAccessibilityServiceClientWrapper 的 onInterrupt 方法是 AIDL 实现,说明来自于 Service 调用。查看这个方法的调用,发现来自于 AccessibilityManagerService 。
AccessibilityManagerService 实现了 IAccessibilityManager.Stub:
public class AccessibilityManagerService extends IAccessibilityManager.Stub
implements AbstractAccessibilityServiceConnection.SystemSupport,
AccessibilityUserState.ServiceInfoChangeListener,
AccessibilityWindowManager.AccessibilityEventSender,
AccessibilitySecurityPolicy.AccessibilityUserManager,
SystemActionPerformer.SystemActionsChangedListener
IAccessibilityManager.Stub 中定义了 interrupt 方法,AccessibilityManagerService 实现了这个方法,并在这个方法中调用了 IAccessibilityServiceClientWrapper#onInterrupt():
// in IAccessibilityManager
private static class Proxy implements IAccessibilityManager
// ...
public void interrupt(int userId) throws RemoteException
// in AccessibilityManagerService
@Override
public void interrupt(int userId)
List<IAccessibilityServiceClient> interfacesToInterrupt;
synchronized (mLock)
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
// This method does nothing for a background user.
if (resolvedUserId != mCurrentUserId)
return;
List<AccessibilityServiceConnection> services =
getUserStateLocked(resolvedUserId).mBoundServices;
int numServices = services.size();
interfacesToInterrupt = new ArrayList<>(numServices);
for (int i = 0; i < numServices; i++)
AccessibilityServiceConnection service = services.get(i);
IBinder a11yServiceBinder = service.mService;
IAccessibilityServiceClient a11yServiceInterface = service.mServiceInterface;
if ((a11yServiceBinder != null) && (a11yServiceInterface != null))
interfacesToInterrupt.add(a11yServiceInterface);
for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++)
try
interfacesToInterrupt.get(i).onInterrupt();
catch (RemoteException re)
Slog.e(LOG_TAG, "Error sending interrupt request to "
+ interfacesToInterrupt.get(i), re);
AccessibilityManagerService 的 interrupt 方法的注释,仍然让我无法理解:
请求所有无障碍服务的反馈中断。
Requests feedback interruption from all accessibility services.
那么只能继续跟进哪里调用了 AccessibilityManagerService 的 interrupt 方法。
最后在 frameworks/base/core/java/android/view/accessibility/AccessibilityManager.java # interrupt()
方法发现了调用。
AccessibilityManager#interrupt()
备注是:
Requests feedback interruption from all accessibility services.
请求所有无障碍服务的反馈中断。
到了这里彻底断了调用链(没有任何位置调用该方法)。
但是! 很明显 AccessibilityManager 这个东西是 Framework 层供应用层调用的。就是 Context.getSystemService 可以访问到的 Manager 。
查看 AccessibilityManager 的备注:
AccessibilityManager 是 系统级服务,用作 AccessibilityEvent 的事件调度,并提供用于查询系统无障碍状态的工具方法。
也就是说 ·AccessibilityManager#interrupt()· 可以由开发中手动调用。
手写一个测试代码看看,将下面的代码放在一个点击事件中:
val am = getSystemService(ACCESSIBILITY_SERVICE) as? AccessibilityManager
am?.interrupt()
在无障碍服务的实现类中的 onInterrupt 方法中打印日志:
override fun onInterrupt()
Log.i("BaseA11yService", "onInterrupt")
运行代码,发现的确打印出了日志:
2022-09-26 15:17:34.375 I/BaseA11yService: onUnbind
2022-09-26 15:17:44.094 I/BaseA11yService: onInterrupt
顺便提一下,在设置中关闭无障碍服务,会回调 AccessibilityService 的 onUnBind 。
总结
所以 onInterrupt 这个抽象方法回调,是应用层可以自行实现打点位置的。
整体的调用逻辑是:
// 从上到下为回调顺序
AccessibilityManager.interrupt()
|
AccessibilityManagerService.interrupt() // 本质是 IAccessibilityManager.Stub.interrupt()
|
IAccessibilityServiceClientWrapper.onInterrupt() // 本质是 IAccessibilityServiceClient.Stub.onInterrupt()
|
HandlerCaller.sendMessage(message) // DO_ON_INTERRUPT 类型的 message
|
IAccessibilityServiceClientWrapper.executeMessage(message) // 本质是 HandlerCaller.Callback.executeMessage(message)
|
AccessibilityService.Callbacks.onInterrupt() // AccessibilityService.onBind 中的匿名对象
|
AccessibilityService.onInterrupt() // 无障碍服务自己的抽象方法 onInterrupt
作者:自动化BUG制造器
链接:https://juejin.cn/post/7147602016737427492
更多Android学习资料可点击下方卡片~
以上是关于AccessibilityService 的 onInterrupt 方法是怎么用的?的主要内容,如果未能解决你的问题,请参考以下文章
UiAutomator2.0 - 与AccessibilityService的关联
红包外挂史及AccessibilityService分析与防御
IMS:AccessibilityService辅助服务拦截注入Input事件
IMS:AccessibilityService辅助服务拦截注入Input事件