Android SystemServer 系列专题【AttentionManagerService】

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;@VisibleForTestingAttentionCallbackInternalImpl 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;}@VisibleForTestingvoid 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++;         // 生成唯一请求IDmLastActedOnNextScreenDimming = 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.asClass=com.google.android.apps.miphone.aiai.attention.service.AiAiAttentionService

我们接着来研究一下为什么是google as:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/96174.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/96174.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nginx 反向代理使用变量的坑

nginx采用反向代理的时候使用变量的坑 正常情况&#xff1a; location ~ ^/prod-api(?<rest>/.*)?$ {# 假设 $mes_backend 形如: http://127.0.0.1:16889proxy_pass $mes_backend$rest$is_args$args;proxy_http_version 1.1;proxy_set_header Host $host;…

Origin绘制径向条形图|科研论文图表教程

数据排列格式截图&#xff0c;请查看每张图↘右下角水印 目录 数据排列格式截图&#xff0c;请查看每张图↘右下角水印 本 期 导 读 No.1 理解图形 1 定义 2 特点 3 适用场景 No.2 画图教程 1 导入数据&#xff0c;绘制图形 2 设置绘图细节 本 期 导 读 径…

MySQL InnoDB 的 MVCC 机制

前言 多版本并发控制&#xff08;MVCC&#xff09;是 MySQL InnoDB 存储引擎实现高性能事务的核心机制。它通过创建数据快照&#xff0c;使得读写操作可以无锁并发&#xff0c;极大地提升了数据库的并发性能。本文将深入探讨 MVCC 的工作原理、实现细节以及它与事务隔离级别的紧…

景区负氧离子气象站:引领绿色旅游,畅吸清新每一刻

在绿色旅游成为消费主流的今天&#xff0c;游客对 “清新空气” 的需求不再是模糊的期待&#xff0c;而是可感知、可选择的具体体验。景区负氧离子气象站的出现&#xff0c;正以科技之力重塑绿色旅游格局&#xff0c;让 “畅吸清新每一刻” 从口号变为触手可及的现实&#xff0…

Pytorch笔记一之 cpu模型保存、加载与推理

Pytorch笔记一之 cpu模型保存、加载与推理 1.保存模型 首先&#xff0c;在加载模型之前&#xff0c;我们需要了解如何保存模型。PyTorch 提供了两种保存模型的方法&#xff1a;保存整个模型和仅保存模型的状态字典&#xff08;state dict&#xff09;。推荐使用第二种方式&…

当AI在代码车间组装模块:初级开发者的创意反成「核心算法」

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录当AI在…

技术视界 | 跨域机器人通信与智能系统:打破壁垒的开源探索

8 月 16 日&#xff0c;在 OpenLoong 社区举办的第九期线下分享会上&#xff0c;国家地方共建人形机器人创新中心的软件开发负责人 Amadeus 博士带来了一场主题为“跨域机器人通信与智能系统&#xff1a;打破行业壁垒的创新方案”的演讲。深入探讨了当前机器人领域的一个关键痛…

Android入门到实战(八):从发现页到详情页——跳转、传值与RecyclerView多类型布局

一. 引言在上一篇文章里&#xff0c;我们从零开始实现了 App 的 发现页面&#xff0c;通过网络请求获取数据&#xff0c;并使用 RecyclerView 展示了剧集列表。但光有发现页还不够&#xff0c;用户在点击一部剧时&#xff0c;自然希望进入到一个更详细的页面&#xff0c;去查看…

【工具】41K star!网页一键变桌面应用

项目中遇到了一个需要将现有的 web 页面打包成一个 桌面应用 的需求。 最一开始想到的是 Electron&#xff0c;但是它还需要一些开发工作并且打包后的应用体积比较大&#xff0c;调研后发现了开源工具 Pake。 它能让你用最轻量的方式&#xff0c;把任何网页一键打包成跨平台桌…

浪潮CD1000-移动云电脑-RK3528芯片-2+32G-安卓9-2种开启ADB ROOT刷机教程方法

浪潮CD1000-移动云电脑-RK3528芯片-232G-安卓9-2种开启ADB ROOT刷机教程方法 往期文章&#xff1a; 浪潮CD1000-移动云电脑-RK3528芯片-232G-安卓9-开启ADB ROOT破解教程 地址1&#xff1a;浪潮CD1000-移动云电脑-RK3528芯片-232G-开启ADB ROOT破解教程-CSDN博客 中国移动浪潮…

Day23_【机器学习—聚类算法—K-Means聚类 及评估指标SSE、SC、CH】

一、聚类算法概念属于无监督学习算法&#xff0c;即有特征无标签&#xff0c;根据样本之间的相似性&#xff0c;将样本划分到不同的类别中。所谓相似性可以理解为欧氏距离、曼哈顿距离、切比雪夫距离... 。分类按颗粒度分为&#xff1a;粗聚类、细聚类。按实现方法分为&#xf…

android seekbar显示刻度

SeekBar简介 SeekBar是Android中的一个可交互UI组件&#xff0c;允许用户通过拖动滑块在特定范围内选择数值。继承自ProgressBar&#xff0c;但增加了用户手动调节功能&#xff0c;常用于音量控制、亮度调节等场景。 核心属性 android:maxHeight // 背景高度 android:progres…

【高并发内存池】五、页缓存的设计

文章目录Ⅰ. page cache页缓存的结构设计Ⅱ. 完善central cache中的 get_span() 函数Ⅲ. 实现页缓存获取span对象的接口Ⅰ. page cache页缓存的结构设计 ​ 首先页缓存还是一个哈希桶的结构&#xff0c;但是和前两者不同的是&#xff0c;页缓存的哈希桶中存放的是一个或者多个…

Elasticsearch(text和keyword)区别分析

text:全文检索类型,经过分词处理,支持模糊匹配‌ keyword:精确匹配类型,适用于聚合、排序和过滤‌ text 1. 核心属性 ‌analyzer属性‌: 指定用于索引和搜索的分词器 默认使用标准分析器(Standard Analyzer) 示例:"analyzer": "ik_max_word"(中文…

通过tailscale实现一台电脑上vscode通过ssh连接另一台电脑上的VMware Linux 虚拟机

当需要通过一台windows电脑上的vscode来ssh连接另一台电脑上的linux虚拟机进行远程操作&#xff0c;可以通过tailscale来实现。 Linux虚拟机上安装tailscale 由于挂代理下载仍然很慢&#xff0c;而清华镜像源又没有tailscale的软件包&#xff0c;所以可以通过下载 DEB 包安装…

[Upscayl图像增强] docs | 前端 | Electron工具(web->app)

链接&#xff1a;https://upscayl.org/docs&#xff1a;Upscayl Upscayl是一款桌面应用程序&#xff0c;允许用户使用人工智能放大和增强图像。 提供了一个用户友好的图形界面&#xff08;渲染器用户界面&#xff09;&#xff0c;用户可以选择图像或文件夹&#xff0c;从多种AI…

阿里云通义MoE全局均衡技术:突破专家负载失衡的革新之道

MoE模型的基本原理与核心价值 混合专家模型&#xff08;Mixture of Experts&#xff0c;MoE&#xff09;是当前AI大模型领域最重要的架构创新之一&#xff0c;其核心思想是通过多个“专家”网络协同处理输入数据&#xff0c;并由门控网络动态选择或组合各个专家的输出&#xf…

macOS中设置环境变量的各文件及作用域

在 macOS 中&#xff0c;~/.zshrc 和 ~/.bash_profile 是 Shell 的配置文件&#xff0c;用于设置环境变量、命令别名、启动命令等。它们在你每次打开终端时会被自动加载。文件对应 Shell作用~/.zshrcZsh&#xff08;macOS Catalina 及以后默认&#xff09;每次打开新的终端窗口…

【华为培训笔记】OptiX OSN 9600 设备保护专题

OptiX OSN 9600 设备保护专题 1、光层保护 定义 方式 应用

Python开篇撬动未来的万能钥匙 从入门到架构的全链路指南

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 持续学习&#xff0c;不断…