AttentionManagerService是framework中用来实现屏幕感知的一个系统级服务,他继承于systemserver。我们可以通过dumpsys attention来获取他的一些信息。
如下针对屏幕感知的功能的引入来针对这个服务进行一个介绍。
1、屏幕感知Settings UI实现
屏幕感知的功能在A14上面默认都支持,其入口在设置->显示->屏幕超时->屏幕感知。
根据其描述他会检测到如果你在定制屏幕看,就会阻止屏幕超时息屏。
Settings UI控制代码:AdaptiveSleepPreferenceController.java
如上的开关看起来是在isChecked这里实现,主要判断了一些条件来进行显示,这个开关的key是:adaptive_sleep,为1表示此功能开启
2、屏幕感知FW Client实现
通过搜索ADAPTIVE_SLEEP发现fw只有如下一个地方在使用:
//frameworks/base/services/core/java/com/android/server/power/AttentionDetector.java public class AttentionDetector { private static final String TAG = "AttentionDetector"; private static final boolean DEBUG = false; private Context mContext; private boolean mIsSettingEnabled; @VisibleForTesting AttentionCallbackInternalImpl mCallback; public AttentionDetector(Runnable onUserAttention, Object lock) { mOnUserAttention = onUserAttention; mLock = lock; mRequested = new AtomicBoolean(false); mRequestId = 0; // Device starts with an awake state upon boot. mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; } @VisibleForTesting void updateEnabledFromSettings(Context context) { //判断Settings的屏幕感知功能是否使能 mIsSettingEnabled = Settings.Secure.getIntForUser(context.getContentResolver(), Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1; } //屏幕感知核心功能,PowerManagerService#updateUserActivitySummaryLocked会调用到这里 //即PowerManagerService在进行屏幕唤醒或者息屏以及应用锁持有计算的时候进行调用,因此这里会影响屏幕超时息屏的逻辑 To be called in {@link PowerManagerService#updateUserActivitySummaryLocked} public long updateUserActivity(long nextScreenDimming, long dimDurationMillis) { // 1. 前置条件检查:满足任一条件则直接返回原定的变暗时间 if (nextScreenDimming == mLastActedOnNextScreenDimming // 已处理过相同变暗时间 || !mIsSettingEnabled // 功能未启用 || !isAttentionServiceSupported() // 设备不支持注意力检测服务 || mWindowManager.isKeyguardShowingAndNotOccluded()) { // 当前处于锁屏状态 return nextScreenDimming; } // 2. 计算关键时间节点 final long now = SystemClock.uptimeMillis(); // 当前系统启动时间(毫秒) final long whenToCheck = nextScreenDimming - mPreDimCheckDurationMillis; // 检测触发时间(变暗前提前量) final long whenToStopExtending = mLastUserActivityTime + mMaximumExtensionMillis; // 最长可延时时限 // 3. 时间窗口判断逻辑 if (now < whenToCheck) { // 未到检测时间:返回下次应检测的时间点(DEBUG模式输出日志) if (DEBUG) Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now)); return whenToCheck; } else if (whenToStopExtending < whenToCheck) { // 超过最大延时时长:放弃检测直接允许变暗(安全策略) if (DEBUG) Slog.d(TAG, "Let device sleep to avoid false results..."); return nextScreenDimming; } else if (mRequested.get()) { // 已有未完成的检测请求:等待当前请求完成 if (DEBUG) Slog.d(TAG, "Pending attention callback with ID=" + mCallback.mId + ", wait."); return whenToCheck; } // 4. 发起屏幕感知检测请求 mRequested.set(true); // 标记请求状态 mRequestId++; // 生成唯一请求ID mLastActedOnNextScreenDimming = nextScreenDimming; // 记录当前处理的变暗时间 mCallback = new AttentionCallbackInternalImpl(mRequestId); // 创建回调实例 // 5. 计算实际检测超时时间(取配置值与剩余变暗时间的较小值) mEffectivePostDimTimeoutMillis = Math.min(mRequestedPostDimTimeoutMillis, dimDurationMillis); // 6. 调用系统服务检测用户注意力 Slog.v(TAG, "Checking user attention, ID: " + mRequestId); final boolean sent = mAttentionManager.checkAttention( mPreDimCheckDurationMillis + mEffectivePostDimTimeoutMillis, mCallback); // 7. 请求失败处理 if (!sent) { mRequested.set(false); // 重置请求状态 } return whenToCheck; // 返回建议的下次检测时间 } }综上最核心的方法如下:
1)Attention注意力检查请求
每次从pms那边过来就会请求一次是否有人眼盯着屏幕看,每次请求都有唯一标准ID,可以参考如下日志:
130|OrderPAD_3:/ $ logcat | grep -E "AttentionManagerService|AttentionDetector" 09-04 11:22:26.250 2307 2372 V AttentionDetector: Checking user attention, ID: 40 09-04 11:22:28.258 2307 3160 V AttentionDetector: onSuccess: 0, ID: 40 09-04 11:23:08.249 2307 2372 V AttentionDetector: Checking user attention, ID: 41 09-04 11:23:10.258 2307 2409 V AttentionDetector: onSuccess: 0, ID: 41 09-04 11:23:42.238 2307 2372 V AttentionDetector: Checking user attention, ID: 42 09-04 11:23:44.246 2307 4514 V AttentionDetector: onSuccess: 0, ID: 42 09-04 11:27:38.810 2307 2372 V AttentionDetector: Checking user attention, ID: 43 09-04 11:27:40.831 2307 3177 V AttentionDetector: onSuccess: 0, ID: 432)Attention检查结果返回
//frameworks/base/services/core/java/com/android/server/power/AttentionDetector.java @Override public void onSuccess(int result, long timestamp) { // 1. 日志记录检测结果(VERBOSE级别日志,包含结果码和请求ID) Slog.v(TAG, "onSuccess: " + result + ", ID: " + mId); // 2. 防循环处理:验证请求ID有效性并原子性重置请求状态 // - mId == mRequestId:确保回调匹配当前最新请求(避免旧请求干扰) // - mRequested.getAndSet(false):原子操作标记请求已完成 if (mId == mRequestId && mRequested.getAndSet(false)) { // 3. 同步锁保护关键代码块(防止多线程竞争条件) synchronized (mLock) { // 4. 设备状态检查:若非唤醒状态则直接返回(如已进入休眠) if (mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) { if (DEBUG) Slog.d(TAG, "Device slept before receiving callback."); return; } // 5. 根据检测结果执行分支逻辑 if (result == AttentionService.ATTENTION_SUCCESS_PRESENT) { // 5.1 检测到用户注意力:触发延屏操作(如执行mOnUserAttention回调) mOnUserAttention.run(); } else { // 5.2 未检测到注意力:重置连续延屏计数(统计上报逻辑) resetConsecutiveExtensionCount(); } } } }3、屏幕感知FW Service实现
在此回到attention的检查请求,是直接调用了mAttentionManager.checkAttention,根据经验,其实就是调用到了AttentionManagerService.java如下代码:
//frameworks/base/services/core/java/com/android/server/attention/AttentionManagerService.java boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) { Objects.requireNonNull(callbackInternal); //流程1:服务可用性检查(三级防御式编程) if (!mIsServiceEnabled) { // 设备硬件不支持该服务 Slog.w(LOG_TAG, "Trying to call checkAttention() on an unsupported device."); return false; } if (!isServiceAvailable()) { // 服务进程未就绪 Slog.w(LOG_TAG, "Service is not available at this moment."); return false; } // 摄像头被系统级禁用 if (mPrivacyManager.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA)) { Slog.w(LOG_TAG, "Camera is locked by a toggle."); return false; } // 电源状态检查:屏幕关闭或省电模式下禁止检测 if (!mPowerManager.isInteractive() || mPowerManager.isPowerSaveMode()) { return false; } //流程2: 服务绑定与缓存管理(线程安全),这里其实要去发送intent移交其他组件来实现Attention检查 synchronized (mLock) { freeIfInactiveLocked(); bindLocked(); } // 阻塞等待intent那边组件完全启动 final long now = SystemClock.uptimeMillis(); awaitServiceBinding(Math.min(SERVICE_BINDING_WAIT_MILLIS, timeout)); //流程3:核心业务逻 synchronized (mLock) { final AttentionCheckCache cache = mAttentionCheckCacheBuffer == null ? null : mAttentionCheckCacheBuffer.getLast(); if (cache != null && now < cache.mLastComputed + mStaleAfterMillis) { callbackInternal.onSuccess(cache.mResult, cache.mTimestamp); return true; } //请求限流:同一时间只允许一个未完成的请求 if (mCurrentAttentionCheck != null) { if (!mCurrentAttentionCheck.mIsDispatched || ! mCurrentAttentionCheck.mIsFulfilled) return false; } //创建新检测请求:其实把Attention请求的实现转移给到了前面流程2启动的intent的组件来实现 mCurrentAttentionCheck = new AttentionCheck(callbackInternal, this); if (mService != null) { try { cancelAfterTimeoutLocked(timeout); mService.checkAttention(mCurrentAttentionCheck.mIAttentionCallback); mCurrentAttentionCheck.mIsDispatched = true; } catch (RemoteException e) { Slog.e(LOG_TAG, "Cannot call into the AttentionService"); return false; } } return true; } }AttentionManagerService实现的checkAttention方法来看,貌似他也没有来实现具体逻辑,而是作为一个中间人也去调用了一个mService.checkAttention里面去进行调用,它已经是一个系统级服务了,为什么还存在一个mService呢?先剧透一下,他其实依赖其他com.google.android.as来完成attention注意力感知的逻辑实现。
1)绑定google as服务
在checkAttention方法中通过bindLocked去绑定google as服务,其实就是普通的一个服务绑定,这里传递了mConnection来接收Binder对端,他的定义如下:
google as服务绑定成功,拿到service并赋值给mService,后续的checkAttention通过google as服务区实现
2)调用google as服务
这里发现了两处去调用google as服务的,但是基本实现逻辑基本一致:
- 服务绑定成功的时候触发一次handlePendingCallbackLocked
- PMS间歇式调用checkAttention/如果mService不为null直接请求
如上两种场景其实就是绑定服务后的第一次触发和后面的轮询触发。原理都一致。
3)为什么是google as服务?
从第一小节的bindLocked方法可以看到她并不是隐式的去启动attention服务,而是直接指定了包名:
final Intent serviceIntent = new Intent( AttentionService.SERVICE_INTERFACE).setComponent( mComponentName);这里的mComponentName到底是谁?通过dump出来的答案就是
AttentionServicePackageName=com.google.android.as Resolved component: Component=com.google.android.as Class=com.google.android.apps.miphone.aiai.attention.service.AiAiAttentionService我们接着来研究一下为什么是google as: