Android SystemServer 系列专题【AttentionManagerService】

发布于:2025-09-07 ⋅ 阅读:(26) ⋅ 点赞:(0)

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: 43

2)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:


网站公告

今日签到

点亮在社区的每一天
去签到