1.背景概述
为了解决应用在后台占用cpu资源的问题,将进程迁移到冻结的 cgroup 来冻结缓存的应用,可以减少活跃缓存应用存在的活跃和空闲 CPU 消耗,平衡了资源管理与用户体验,改善性能和功耗。
|
版本 |
默认状态 |
冻结延迟 |
核心改进 |
技术实现 |
|
Android 11 (2020年) |
默认关闭(需手动开启) |
10 分钟 |
• 初版功能引入,基于 cgroup freezer • 支持 adj ≥ 900的缓存应用冻结 |
• 通过 /sys/fs/cgroup///cgroup.freeze写入 1/0 控制冻结 |
|
Android 12 (2021年) |
默认开启 |
10 分钟 |
• 稳定性优化 • 修复冻结唤醒问题 |
• 沿用 cgroup v1,优化 Binder 兼容性 |
|
Android 13 (2022年) |
默认开启 |
10 分钟 |
• Binder 驱动支持冻结状态 • 避免跨进程通信阻塞,优化大量的ANR问题 |
• 新增 freezeBinder()接口,对进程的 binder 先进行冻结,这一步禁掉该进程对同步binder请求的接收和处理,以及对异步binder请求的处理,因为之前如果随意的冻结应用,会导致一些应用后台的跨进程行为异常,例如在binder通信期间。 |
|
Android 14 (2023年) |
默认开启 |
10 秒 (可动态调整) |
• 延迟大幅缩短 • 联动内存压缩(zRAM) • 优化广播合并机制 |
Android 14还针对广播进行了优化。为了配合应用的冻结机制,系统调整了应用在进入缓存状态后接收context-registered广播的方式。这些优化使得Android 14能够进一步减少应用的冷启动时间。在8GB设备上,App的冷启动时间减少了20%,而在12GB设备上更是减少了30%以上,为用户带来了更快的启动速度。同时,Android 14还对ART编译器进行了优化,使得代码大小在不影响性能的前提下平均减少了9.3%,从而有效地减少了内存占用。 |
|
Android 15 (2024年) |
默认开启 |
0-10 秒 (动态调整) |
• cgroup v2 + AI 预测 • 异步 Binder 队列 • 与内存压缩深度联动 |
• 废弃 enableFreezer,完全集成至 CachedAppOptimizer |
|
Android 16 (2025年) |
默认开启 |
0-10 秒 |
新增 普通模式Cache进程冻结 激进模式Home进程冻结(默认关闭) |
待研究内核是否变化 |
2. 功能解释
2.1 冻结的原理
冻结流程:前台进程 --> 可感知进程--> 服务进程 --> 普通模式adj 900 激进模式adj 600->10秒后->cgroup freeze子系统冻结,Freeze通过冻结Cache进程来释放它们占用的系统资源。它们的优先级(adj)通过OomAdjuster来进行更新计算。
2.2 冻结的作用
1.进程的执行被暂停:冻结的进程会被暂停,其所有线程的执行将被停止,包括应用程序的主线程以及任何后台线程。
2.资源释放:冻结进程占用的资源,例如 CPU会被释放。这些资源将被系统重新分配给其他需要执行的进程或系统服务。
3.电池节省:冻结进程不会在后台运行,因此可以节省设备的电池消耗。对于后台的应用程序,冻结可以降低其电池使用量,延长设备的电池寿命。
4.系统稳定性:通过冻结不活跃或低优先级的进程,可以避免它们竞争系统资源,从而提高系统的稳定性和响应能力。
5.快速恢复:冻结的进程可以快速恢复其执行状态。当需要重新激活进程时,系统可以迅速将其恢复到之前的运行状态,而无需重新启动或加载应用程序。
2.3 怎样手动开启关闭
开发者选项 “Suspend execution for cached apps” 暂停执行已缓存的应用
里面有三个选项,设备默认设置 Device default, 已启用Enabled 和已停用Disabled

通过adb命令:
adb shell settings put global cached_apps_freezer <enabled|disabled|default>
/*** Whether the app freezer is enabled on this device.* The value of "enabled" enables the app freezer, "disabled" disables it and* "device_default" will let the system decide whether to enable the freezer or not* @hide*/@Readablepublic static final String CACHED_APPS_FREEZER_ENABLED = "cached_apps_freezer";
2.4 怎样查看冻结的状态
查看日志(一般日志会有相应含义的字符)
adb logcat -b all | grep -iE "freezing|froze"

adb root
adb remount
adb shell ps -A | grep -Ei "do_freezer_trap.+.com"
检查/sys/fs/cgroup/uid_xxx/cgroup.freeze文件
adb shell cat /sys/fs/cgroup/uid_{应用UID}/cgroup.freeze 0代表没有被冻结。1代表被冻结
PS查看进程状态和WCHAN

备注:
1.在内核版本 4.14的时候,进程被冻结显示的是D状态, 高版本中已经不会对进程切到 D 状态,而是保留 S 态,然后在 S 态的基础上做 freeze 的处理
2.WCHAN 表示当前线程在内核上正在执行的函数名
3. 冻结的流程
3.1 冻结的初始化
冻结流程:前台进程 --> 可感知进程--> 服务进程 --> 普通模式adj 900 激进模式adj 600->10秒后->cgroup freeze子系统冻结,Freeze通过冻结Cache进程来释放它们占用的系统资源。它们的优先级(adj)通过OomAdjuster来进行更新计算。
当然Freeze初始化也是在OomAdjuster中哈。

3.1.1 SystemServer.run
OomAdjuster服务启动处startOtherServices。一般在系统偏后阶段执行,例如快稳省的一些策略服务也是放在这里
private void run() {...startOtherServices(t); // OomAdjuster服务启动处。一般在系统偏后阶段执行,例如快稳省的一些策略服务也是放在这里...}
3.1.2 SystemServer.startOtherServices
调用ContentProviderHelper.installSystemProviders来初始化SystemProviders,这里android15和android16一样的。
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {...// 通过ActivityManagerService安装系统级ContentProvider// 这些Provider提供系统级别的数据访问接口(如Settings、配置信息等)mActivityManagerService.getContentProviderHelper().installSystemProviders();...}
3.1.3 ContentProviderHelper.installSystemProviders
mOomAdjuster在 installSystemProviders()中初始化,是为了保证其依赖的系统配置(如进程优先级规则)已由 SettingsProvider加载完成,从而避免竞态条件和配置失效问题。 这是Android系统启动流程中典型的依赖顺序控制设计。
private final ActivityManagerService mService; // AMS实例,管理系统进程生命周期和资源分配// ContentProviderHelper.javapublic final void installSystemProviders() {...mService.mOomAdjuster.initSettings(); // 初始化OOM(Out-Of-Memory)调节器的配置...}
3.1.4 OomAdjuster.initSettings
调用CachedAppOptimizer.init来初始化CachedAppOptimizer
void initSettings() {// 初始化缓存应用优化器(CachedAppOptimizer)// 负责管理后台进程的内存和CPU资源分配策略(如冻结、优先级调整)mCachedAppOptimizer.init();// 初始化缓存进程排序器(CacheOomRanker)// 根据进程的OOM(Out-Of-Memory)优先级和活跃状态,对缓存进程进行动态排序// 使用主线程的Executor确保线程安全mCacheOomRanker.init(ActivityThread.currentApplication().getMainExecutor());}
3.1.4 CachedAppOptimizer.init()
会调用CachedAppOptimizer.updateUseFreezer来初始化进程冻结功能,我们可以看下读取冻结机制开关的逻辑
// 定义测试可见的全局配置URI,用于监听"cached_apps_freezer_enabled"开关状态@VisibleForTestingstatic final Uri CACHED_APP_FREEZER_ENABLED_URI = Settings.Global.getUriFor(Settings.Global.CACHED_APPS_FREEZER_ENABLED);public void init() {...// 注册ContentObserver监听系统设置变化mAm.mContext.getContentResolver().registerContentObserver(CACHED_APP_FREEZER_ENABLED_URI, // 监听的配置项URIfalse, // 不监听子路径变化mSettingsObserver); // 自定义的观察者实例...}// 自定义设置变化观察者(继承ContentObserver)private final class SettingsContentObserver extends ContentObserver {SettingsContentObserver() {// 使用AMS的Handler处理回调,确保线程安全super(mAm.mHandler);}@Overridepublic void onChange(boolean selfChange, Uri uri) {// 当监听到CACHED_APP_FREEZER_ENABLED配置变更时if (CACHED_APP_FREEZER_ENABLED_URI.equals(uri)) {// 加锁防止并发问题synchronized (mPhenotypeFlagLock) {// 更新Freezer功能启用状态updateUseFreezer();}}}}
3.1.5 CachedAppOptimizer.updateUseFreezer
支持如下2种命令进行开关冻结机制
adb shell settings put global cached_apps_freezer动态生效
adb shell device_config put activity_manager_native_boot use_freezer true && adb reboot 重启生效
/*** 根据系统配置更新 Freezer 功能状态:* 1. 优先检查 Settings.Global.CACHED_APPS_FREEZER_ENABLED* 2. 其次检查 DeviceConfig 的 use_freezer 标志* 3. 最终确认内核是否支持 Freezer*/@GuardedBy("mPhenotypeFlagLock")private void updateUseFreezer() {// 读取全局设置项 CACHED_APPS_FREEZER_ENABLEDfinal String configOverride = Settings.Global.getString(mAm.mContext.getContentResolver(),Settings.Global.CACHED_APPS_FREEZER_ENABLED);if ("disabled".equals(configOverride)) {mUseFreezer = false; // 显式禁用} else if ("enabled".equals(configOverride)|| DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {// 检查内核是否支持 FreezermUseFreezer = mFreezer.isFreezerSupported();// 更新冻结延迟时间updateFreezerDebounceTimeout();// 更新豁免安装包列表updateFreezerExemptInstPkg();} else {mUseFreezer = false; // 默认禁用}}
3.1.6 Freezer.isFreezerSupported()
通过数据库开关启动Freeze,还需要判断系统是否支持Freeze才能成功启动
Android 15:纯 Java 实现,依赖 FileReader和 Java 异常处理。
Android 16+:通过 JNI 调用 C++ 实现,直接操作文件描述符和系统调用。
/**@return true 表示支持,false 表示不支持*/* 检查当前系统是否支持 Freezer 机制*public boolean isFreezerSupported() {return nativeIsFreezerSupported(); // 调用 JNI 方法,实际实现在 C++ 层}static const JNINativeMethod sMethods[] = {{"nativeIsFreezerSupported", "()Z", (void*) isFreezerSupported }, // 注册 native 方法};
3.1.8 com_android_server_am_Freezer.cpp.isFreezerSupported()
读取/sys/fs/cgroup/uid_0/pid_*/cgroup.freeze节点下的值,如果节点的值为0或者1,然后调用getBinderFreezeInfo判断驱动是否支持freezer(kernel版本(Linux 内核 ≥ 5.4 + cgroups v2 + Binder 驱动支持)较低的话,默认不会实现BINDER_GET_FROZEN_INFO,就不支持freezer),接着调用isProfileValidForProcess判断知否加载了task_profiles.json文件(进程冻结相关的profiles)
/*** 检查当前系统是否支持 Freezer 机制(冻结/解冻进程)* @return true 表示支持,false 表示不支持*/bool isFreezerSupported(JNIEnv *env, jclass) {// 1. 获取 cgroup.freeze 文件路径(如 /sys/fs/cgroup/freezer/cgroup.freeze)std::string path;if (!cgroupGetAttributePathForTask("FreezerState", getpid(), &path)) {ALOGI("No attribute for FreezerState"); // 内核未启用 cgroup freezer 功能return false;}// 2. 尝试打开 cgroup.freeze 文件base::unique_fd fid(open(path.c_str(), O_RDONLY)); // 只读模式打开if (fid < 0) {ALOGI("Cannot open freezer path \"%s\": %s", path.c_str(), strerror(errno));return false; // 文件打开失败(权限不足或路径错误)}// 3. 读取文件内容(应为 '0' 或 '1')char state;if (::read(fid, &state, 1) != 1) {ALOGI("Failed to read freezer state: %s", strerror(errno));return false; // 读取失败(文件损坏或 IO 错误)}if (state != '1' && state != '0') {ALOGE("Unexpected value in cgroup.freeze: %d", state); // 非法状态值return false;}// 4. 检查 Binder 驱动是否支持 Freezer IPC(关键步骤)uid_t uid = getuid(); // 当前进程的用户 IDpid_t pid = getpid(); // 当前进程的 PIDuint32_t syncReceived = 0, asyncReceived = 0;int error = IPCThreadState::getProcessFreezeInfo(pid, &syncReceived, &asyncReceived);if (error < 0) {ALOGE("Unable to read freezer info: %s", strerror(errno)); // Binder 驱动不支持 Freezerreturn false;}// 5. 验证 Freezer 配置文件(如 /system/etc/task_profiles.json)if (!isProfileValidForProcess("Frozen", uid, pid) ||!isProfileValidForProcess("Unfrozen", uid, pid)) {ALOGE("Missing freezer profiles"); // 缺少冻结/解冻的配置文件return false;}return true; // 所有检查通过,系统支持 Freezer}
3.1.7 冻结机制初始化流程总结
流程总结如下:
SystemServer.run()└─> startOtherServices()└─> ContentProviderHelper.installSystemProviders()└─> OomAdjuster.initSettings()└─> CachedAppOptimizer.init()└─> Freezer.isFreezerSupported()└─> com_android_server_am_Freezer.cpp.isFreezerSupported()├─ 检查 cgroup.freeze├─ 检查 Binder IPC└─ 验证配置文件
Android 版本差异
Android 15:isFreezerSupported()在 Java 层实现。
Android 16+:改为 C++ 实现(com_android_server_am_Freezer.cpp)。
3.2 冻结的触发
冻结流程:前台进程 --> 可感知进程--> 服务进程 --> 普通模式adj 900 激进模式adj 600 ->10秒后->cgroup freeze子系统冻结
ActivityRecord.setState->ProcessRecord.updateProcessInfo->->ActivityManagerService.updateOomAdjLocked->->->ProcessStateController.runUpdate->->->->OomAdjuster.updateOomAdjLocked->->->->->OomAdjuster.updateAppFreezeStateLSP 普通模式adj 900 激进模式adj 600 以上会被进程冻结->->->->->->CachedAppOptimizer.freezeAppAsyncLSP->->->->->->->CachedAppOptimizer.freezeAppAsyncInternalLSP 延时10秒后冻结->->->->->->->->CachedAppOptimizer.freezeProcess->->->->->->->->->Freezer.freezeBinder->com_android_server_am_Freezer.freezeBinder->IPCThreadState. freeze 先冻结binder->->->->->->->->->->Freezer.setProcessFrozen->Process. setProcessFrozen->android_util_Process.android_os_Process_setProcessFrozen 再冻结进程
当进程的优先级(adj)发生变化的时候,会去计算判断,并执行冻结,adj会发生变化的原因一般如下:
/*** 将adj原因代码转换为可读的字符串描述** @param adj调整原因代码,使用@OomAdjReason注解标记的整型值* @return 返回格式为"updateOomAdj_原因描述"的字符串,如"updateOomAdj_activityChange"*/public static final String oomAdjReasonToString(@OomAdjReason int oomReason) {// 定义所有返回字符串的统一前缀final String OOM_ADJ_REASON_METHOD = "updateOomAdj";// 根据不同的调整原因返回对应的描述字符串switch (oomReason) {case OOM_ADJ_REASON_NONE:return OOM_ADJ_REASON_METHOD + "_meh"; // 无特别原因// Activity相关调整case OOM_ADJ_REASON_ACTIVITY:return OOM_ADJ_REASON_METHOD + "_activityChange"; // Activity状态变化// BroadcastReceiver相关case OOM_ADJ_REASON_FINISH_RECEIVER:return OOM_ADJ_REASON_METHOD + "_finishReceiver"; // 结束广播接收器case OOM_ADJ_REASON_START_RECEIVER:return OOM_ADJ_REASON_METHOD + "_startReceiver"; // 启动广播接收器// Service相关case OOM_ADJ_REASON_BIND_SERVICE:return OOM_ADJ_REASON_METHOD + "_bindService"; // 绑定服务case OOM_ADJ_REASON_UNBIND_SERVICE:return OOM_ADJ_REASON_METHOD + "_unbindService"; // 解绑服务case OOM_ADJ_REASON_START_SERVICE:return OOM_ADJ_REASON_METHOD + "_startService"; // 启动服务case OOM_ADJ_REASON_STOP_SERVICE:return OOM_ADJ_REASON_METHOD + "_stopService"; // 停止服务case OOM_ADJ_REASON_EXECUTING_SERVICE:return OOM_ADJ_REASON_METHOD + "_executingService"; // 服务正在执行// ContentProvider相关case OOM_ADJ_REASON_GET_PROVIDER:return OOM_ADJ_REASON_METHOD + "_getProvider"; // 获取ContentProvidercase OOM_ADJ_REASON_REMOVE_PROVIDER:return OOM_ADJ_REASON_METHOD + "_removeProvider"; // 移除ContentProvider// UI相关case OOM_ADJ_REASON_UI_VISIBILITY:return OOM_ADJ_REASON_METHOD + "_uiVisibility"; // UI可见性变化// 系统管理相关case OOM_ADJ_REASON_ALLOWLIST:return OOM_ADJ_REASON_METHOD + "_allowlistChange"; // 白名单变更case OOM_ADJ_REASON_PROCESS_BEGIN:return OOM_ADJ_REASON_METHOD + "_processBegin"; // 进程开始case OOM_ADJ_REASON_PROCESS_END:return OOM_ADJ_REASON_METHOD + "_processEnd"; // 进程结束// 特殊场景case OOM_ADJ_REASON_SHORT_FGS_TIMEOUT:return OOM_ADJ_REASON_METHOD + "_shortFgs"; // 前台服务超时case OOM_ADJ_REASON_SYSTEM_INIT:return OOM_ADJ_REASON_METHOD + "_systemInit"; // 系统初始化case OOM_ADJ_REASON_BACKUP:return OOM_ADJ_REASON_METHOD + "_backup"; // 备份操作case OOM_ADJ_REASON_SHELL:return OOM_ADJ_REASON_METHOD + "_shell"; // shell命令执行case OOM_ADJ_REASON_REMOVE_TASK:return OOM_ADJ_REASON_METHOD + "_removeTask"; // 移除任务case OOM_ADJ_REASON_UID_IDLE:return OOM_ADJ_REASON_METHOD + "_uidIdle"; // UID进入空闲状态case OOM_ADJ_REASON_RESTRICTION_CHANGE:return OOM_ADJ_REASON_METHOD + "_restrictionChange"; // 限制条件变更case OOM_ADJ_REASON_COMPONENT_DISABLED:return OOM_ADJ_REASON_METHOD + "_componentDisabled"; // 组件被禁用case OOM_ADJ_REASON_FOLLOW_UP:return OOM_ADJ_REASON_METHOD + "_followUp"; // 后续处理case OOM_ADJ_REASON_RECONFIGURATION:return OOM_ADJ_REASON_METHOD + "_reconfiguration"; // 重新配置// 默认返回未知原因default:return "_unknown"; // 未知原因}}
从上我们可以看到当进程状态或者组件状态发生变化的时候,就会触发adj的更新(实际冻结策略优化中我们会进一步管理广播和服务拦截策略与冻结策略的融合),这里以Activity的状态变化为例:
3.2.1 ActivityRecord.setState
可以看到Activity的STARTED状态和DESTROYING状态调用了updateProcessInfo方法去更新进程状态,updateOomAdj都是true
/*** 设置Activity状态并触发相关处理** @param state 要设置的新状态(STARTED或DESTROYING)* @param reason 状态变更的原因描述(用于调试)*/void setState(State state, String reason) {switch (state) {case STARTED: // Activity变为可见状态if (app != null) {// 更新进程信息,确保进程状态正确更新为前台状态app.updateProcessInfo(false, // 不更新服务连接活动true, // 标记为Activity变化true, // 更新OOM调整值true // 添加待处理的顶部UID);}// 通过Handler异步通知内容捕获服务Activity已启动mAtmService.mH.post(this::notifyActivityStartedToContentCaptureService);break;case DESTROYING: // Activity正在销毁if (app != null && !app.hasActivities()) {// 当应用不再拥有任何Activity时:// 1. 更新绑定的服务(通知它们客户端可能不再有Activity)// 2. 更新LRU列表和OOM调整值app.updateProcessInfo(true, // 更新服务连接活动false, // 不是Activity变化true, // 更新OOM调整值false // 不添加待处理的顶部UID);}break;}}
3.2.2 ProcessRecord.updateProcessInfo
生命周期变化后,会调用到updateOomAdjLocked来继续更新adj的流程
/*** 更新进程信息并调整进程优先级** @param updateServiceConnectionActivities 是否更新服务连接的活动状态* @param activityChange 是否由Activity状态变化触发此次更新* @param updateOomAdj 是否更新进程的OOM_ADJ值(内存不足时的调整优先级)*/@Overridepublic void updateProcessInfo(boolean updateServiceConnectionActivities, boolean activityChange,boolean updateOomAdj) {// 更新LRU(最近使用)进程列表// 参数说明:// this - 当前进程对象// activityChange - 标记是否因Activity变化触发更新// null - 不关联特定客户端mService.updateLruProcessLocked(this, activityChange, null /* client */);// 如果需要更新OOM_ADJ值(内存不足时的进程优先级)if (updateOomAdj) {// 更新进程的OOM_ADJ值// 参数说明:// this - 当前进程对象// OOM_ADJ_REASON_ACTIVITY - 调整原因为Activity状态变化mService.updateOomAdjLocked(this, OOM_ADJ_REASON_ACTIVITY);}}
3.2.3 ActivityManagerService.updateOomAdjLocked
套娃接口,直接继续next
/*** 更新指定进程及其关联进程的OOM_ADJ值(内存不足时的进程优先级)** @param app 需要更新的目标进程记录(ProcessRecord)* @param oomAdjReason OOM调整原因码(使用@OomAdjReason注解标记)* @return 返回更新操作是否成功(true表示成功)** @GuardedBy("this") 表示该方法需要持有当前对象锁才能调用** 实现说明:* 1. 该方法会更新目标进程及其所有可达关联进程的OOM_ADJ值* 2. 实际工作委托给mProcessStateController执行* 3. 调用前必须确保已持有当前对象锁(同步锁)*/@GuardedBy("this")final boolean updateOomAdjLocked(ProcessRecord app, @OomAdjReason int oomAdjReason) {// 委托给ProcessStateController执行实际更新操作return mProcessStateController.runUpdate(app, oomAdjReason);}
3.2.4 ProcessStateController.runUpdate
套娃接口,直接继续next
/*** 执行单个进程及其关联进程的OOM_ADJ值更新** @param proc 需要更新的目标进程记录(ProcessRecord),不可为null* @param oomAdjReason OOM调整原因码(使用@OomAdjReason注解标记)* @return 返回更新操作是否成功(true表示成功)** 功能说明:* 1. 更新指定进程的OOM_ADJ值(内存不足时的优先级)* 2. 同时会处理通过enqueueUpdateTarget入队的所有关联进程* 3. 实际工作委托给mOomAdjuster组件执行** 调用场景:* - Activity可见性变化时* - 服务绑定状态变更时* - 进程重要性调整时** 线程安全:* - 需要在持有AMS锁的情况下调用* - 内部会处理进程更新队列的线程安全问题*/public boolean runUpdate(@NonNull ProcessRecord proc,@ActivityManagerInternal.OomAdjReason int oomAdjReason) {// 委托给OomAdjuster执行实际的OOM_ADJ更新// proc: 要更新的目标进程// oomAdjReason: 调整原因(如ACTIVITY_CHANGE等)return mOomAdjuster.updateOomAdjLocked(proc, oomAdjReason);}
3.2.5 OomAdjuster.updateOomAdjLocked
经过漫长的函数套娃,adj的变化终于到了 updateAppFreezeStateLSP(app, oomAdjReson, false, oldOomAdj) 更新冻结状态函数了
/**@param oomAdjReason 调整原因描述(如"activityChange")* @GuardedBy("mService") 需持有AMS主锁*/* 更新LRU列表中所有进程的OOM_ADJ值(内存不足优先级)*@GuardedBy("mService")void updateOomAdjLocked(String oomAdjReason) {synchronized (mProcLock) { // 额外获取进程锁updateOomAdjLSP(oomAdjReason);}}/**@GuardedBy({"mService", "mProcLock"}) 需同时持有AMS主锁和进程锁*/* 带双重锁保护的OOM_ADJ更新入口*@GuardedBy({"mService", "mProcLock"})private void updateOomAdjLSP(String oomAdjReason) {mOomAdjUpdateOngoing = true; // 标记更新状态performUpdateOomAdjLSP(oomAdjReason); // 实际处理}/**@param doingAll 是否处理全部进程(true)或仅目标进程(false)*/* 执行OOM_ADJ更新的核心分发逻辑*@GuardedBy({"mService", "mProcLock"})private void performUpdateOomAdjLSP(String oomAdjReason) {postUpdateOomAdjInnerLSP(oomAdjReason, activeUids, now, nowElapsed, oldTime, true);}/**@param activeUids 当前活跃的UID集合* @param now 当前系统时间(用于LRU计算)*/* 更新前的预处理(计算活跃UID、时间戳等)*@GuardedBy({"mService", "mProcLock"})protected void postUpdateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, ActiveUids activeUids,long now, long nowElapsed, long oldTime, boolean doingAll) {updateAndTrimProcessLSP(now, nowElapsed, oldTime, activeUids, oomAdjReason, doingAll);}/**@return 是否成功执行*/* 更新进程优先级并裁剪不活跃进程*@GuardedBy({"mService", "mProcLock"})private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed,final long oldTime, final ActiveUids activeUids) {applyOomAdjLSP(app, true, now, nowElapsed); // 对每个进程应用调整}/**@param app 目标进程记录* @param doingAll 是否批量更新模式*/* 对单个进程应用OOM_ADJ调整*@GuardedBy({"mService", "mProcLock"})private boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,long nowElapsed) {updateAppFreezeStateLSP(app, oomAdjReson, false, oldOomAdj); // 更新冻结状态}
3.2.6 OomAdjuster.updateAppFreezeStateLSP
Android 16 新增配置激进的冻结策略(Flags.prototypeAggressiveFreezing()),调整冻结的adj阈值:
// 普通模式:900 (CACHED_APP_MIN_ADJ) → 仅冻结缓存进程
// 激进模式:600 (HOME_APP_ADJ) → 连桌面应用都可能被冻结
// 相关常量定义static final String KEY_FREEZER_CUTOFF_ADJ = "freezer_cutoff_adj"; // 冻结阈值配置键/*** 冻结的OOM_ADJ阈值,大于此值的进程符合冻结条件* @see #KEY_FREEZER_CUTOFF_ADJ*/public int FREEZER_CUTOFF_ADJ = DEFAULT_FREEZER_CUTOFF_ADJ;// 进程优先级常量public static final int HOME_APP_ADJ = 600; // 桌面应用优先级public static final int CACHED_APP_MIN_ADJ = 900; // 缓存应用最低优先级/*** 默认冻结阈值,根据激进冻结标志决定:* - 开启时使用HOME_APP_ADJ(600)* - 关闭时使用CACHED_APP_MIN_ADJ(900)*/private static final int DEFAULT_FREEZER_CUTOFF_ADJ =Flags.prototypeAggressiveFreezing() ? ProcessList.HOME_APP_ADJ: ProcessList.CACHED_APP_MIN_ADJ;/*** 更新进程冻结状态的核心方法** @param app 目标进程记录* @param oomAdjReason 触发调整的原因(如ACTIVITY_CHANGE)* @param immediate 是否立即执行冻结(true=紧急情况)* @param oldOomAdj 进程之前的OOM_ADJ值(用于状态变化检测)** @GuardedBy 需要同时持有AMS主锁(mService)和进程锁(mProcLock)*/@GuardedBy({"mService", "mProcLock"})void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason,boolean immediate, int oldOomAdj) {// 检查系统是否启用Freezer功能if (!mCachedAppOptimizer.useFreezer()) {return;}// 获取冻结策略决策结果final boolean freezePolicy = getFreezePolicy(app); // 核心决策函数// 冻结/解冻执行逻辑if (freezePolicy) {// 符合冻结条件的情况if (immediate && !opt.isFrozen()) {// 紧急冻结:立即安排冻结操作mCachedAppOptimizer.freezeAppAsyncAtEarliestLSP(app);} else if (!opt.isFrozen() || !opt.isPendingFreeze()) {// 常规冻结:异步安排冻结操作mCachedAppOptimizer.freezeAppAsyncLSP(app);}} else {// 不符合冻结条件的情况if (opt.isFrozen() || opt.isPendingFreeze()) {// 如果进程已冻结或待冻结,则执行解冻mCachedAppOptimizer.unfreezeAppLSP(app,CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));}}}/*** 判断指定进程是否符合冻结条件** @param proc 待检查的进程记录对象* @return true表示应该冻结该进程,false表示不应冻结** 决策逻辑优先级:* 1. 先检查禁止冻结的条件(任一条件满足即返回false)* 2. 再检查允许冻结的条件(需全部满足才返回true)*/boolean getFreezePolicy(ProcessRecord proc) {// ==================== 禁止冻结的条件检查 ====================// 条件1:进程具有CPU时间能力(重要进程)if (Flags.useCpuTimeCapability()) {if ((proc.mState.getCurCapability() & PROCESS_CAPABILITY_CPU_TIME) != 0) {/** 当前进程被标记为需要CPU时间能力(如前台服务、绑定到可见Activity等)* 参见getCpuCapability()的逻辑*/return false;}}// 条件2:进程被显式标记为禁止冻结else if (proc.mOptRecord.shouldNotFreeze()) {/** 特殊情况标记(如正在进行关键操作):* - Binder调用中* - 关键服务生命周期回调*/return false;}// 条件3:进程属于冻结豁免类型if (proc.mOptRecord.isFreezeExempt()) {/** 系统关键进程白名单:* - 系统核心服务(如ActivityManager)* - 持久化进程(persistent)*/return false;}// ==================== 允许冻结的条件检查 ====================// 唯一条件:进程OOM_ADJ值超过冻结阈值if (proc.mState.getCurAdj() >= mConstants.FREEZER_CUTOFF_ADJ) {/** 进程处于低优先级状态:* - 默认阈值CACHED_APP_MIN_ADJ=900* - 激进模式阈值HOME_APP_ADJ=600*/return true;}// 默认情况:不满足任何冻结条件return false;}
3.2.6.1 查看下是否开启激进模式-Flags.prototypeAggressiveFreezing()
Android 新的 Feature Flag 管理系统 flags.aconfig,Soong 构建系统解析 .aconfig文件后自动创建 Flags类
flag {name: "prototype_aggressive_freezing"namespace: "backstage_power"description: "Grant PROCESS_CAPABILITY_CPU_TIME to as many valid states and aggressively freeze other states"bug: "370798593"metadata {purpose: PURPOSE_BUGFIX}}
构建系统会根据 flags.aconfig自动生成对应的 Java 访问类
# 1. 检查当前值adb shell device_config list backstage_poweradb shell device_config get backstage_power prototype_aggressive_freezing# 2. 临时修改并验证adb shell device_config put backstage_power prototype_aggressive_freezing true
实测:默认为 false

3.2.7 CachedAppOptimizer.freezeAppAsyncLSP
freezeAppAsyncLSP里面会post一个10s的message在时间到了的时候调用freezeProcess去冻结进程(延时10s发送进程冻结的消息,在10s内如果收到进程解冻的消息,会把进程冻结消息移除,也就不会执行进程冻结的操作)
/*** 默认的进程冻结防抖时间(单位:毫秒)** 功能说明:防止频繁冻结/解冻操作导致系统抖动* 设计意图:10秒延迟可平衡响应速度与性能开销* 线程安全:常量默认线程安全(final修饰)*/@VisibleForTestingstatic final long DEFAULT_FREEZER_DEBOUNCE_TIMEOUT = 10_000L;/*** 当前生效的冻结防抖时间(单位:毫秒)** 功能说明:运行时动态调整冻结延迟时间* 设计意图:允许通过设备配置覆盖默认值* 线程安全:volatile保证多线程可见性*/@VisibleForTestingvolatile long mFreezerDebounceTimeout = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;/*** 异步冻结应用进程(带默认防抖延迟)** 功能说明:触发进程冻结流程,默认延迟10秒执行* 设计意图:通过防抖机制避免短时间内的重复冻结操作* 线程安全:需持有 mAm(AMS锁) 和 mProcLock(进程锁)** @param app 目标进程记录*/@GuardedBy({"mAm", "mProcLock"})void freezeAppAsyncLSP(ProcessRecord app) {freezeAppAsyncLSP(app, updateEarliestFreezableTime(app, mFreezerDebounceTimeout)); // 延迟10秒}/*** 带自定义延迟的异步冻结** 功能说明:支持指定精确的延迟时间* 设计意图:供特殊场景(如紧急内存回收)绕过默认延迟* 线程安全:需持有 mAm 和 mProcLock** @param app 目标进程记录* @param delayMillis 延迟时间(毫秒)*/@GuardedBy({"mAm", "mProcLock"})private void freezeAppAsyncLSP(ProcessRecord app, @UptimeMillisLong long delayMillis) {freezeAppAsyncInternalLSP(app, delayMillis, false /* force */);}/*** 冻结进程的核心实现** 功能说明:* 1. 通过Handler延迟执行实际冻结操作* 2. 支持强制冻结模式(无视进程状态)** 设计意图:* - 延迟执行:避免阻塞AMS主线程* - force标志:供OOM Killer等紧急场景使用** 线程安全:* - 需持有 mAm 和 mProcLock* - 通过Handler确保跨线程安全** @param app 目标进程记录* @param delayMillis 精确延迟时间(基于uptimeMillis时钟)* @param force 是否强制冻结(即使进程处于活跃状态)*/@GuardedBy({"mAm", "mProcLock"})private void freezeAppAsyncInternalLSP(ProcessRecord app, @UptimeMillisLong long delayMillis,boolean force) {// 使用FreezeHandler发送延迟消息mFreezeHandler.sendMessageDelayed(mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),delayMillis);}adb shell device_config get activity_manager_native_boot freeze_debounce_timeout
总结上面条件就是,通过判断冻结功能是否开启、应用是否属于豁免的应用、应用是否已经被冻结、应用是否不应该被冻结。当做完基础的判断之后,然后看应用当前的 adj 根据是否冻结激进模式条件大于等于 900 (CACHE_APP)或600(Home_APP) 来决定是否冻结应用,然后开启一个延迟10s,如果这个10s内状态都没有变化,就执行冻结流程
tips
1.DEFAULT_FREEZER_DEBOUNCE_TIMEOUT在Android 14之前是十分钟,Android 14上面是十秒**
2.这个时间可以动态的修改: adb shell device_config put activity_manager_native_boot freeze_debounce_timeout 1000 这个是改成1s**
3.冻结的时候会去检查文件锁状态,这是为了防止冻结进程持有文件锁引起死锁。考虑到一些特殊场景下,进程在被冻结的过程中拿住了文件锁,冻结成功后还会再检查一次,发现持有锁就立刻解冻。**
3.2.8 CachedAppOptimizer.freezeProcess
最终都会调用到freezeProcess函数,Android 13以后针对binder 调用进行了优化,对进程的 binder 先进行冻结,这一步禁掉该进程对同步binder请求的接收和处理,以及对异步binder请求的处理,因为之前如果随意的冻结应用,会导致一些应用后台的跨进程行为异常,例如在binder通信期间。
到了Android 13之后,binder驱动已经支持FREEZER状态,当应用调用一个被冻结binder进程后,会直接返回一个错误,这样子就不会阻塞调用方的进程,大大避免了很多ANR问题。
/*** 冻结指定进程的核心方法** 功能说明:* 1. 先冻结进程的Binder通信接口* 2. 再冻结进程的执行状态* 3. 记录冻结操作日志** 关键操作:* - 通过mFreezer.freezeBinder()冻结Binder事务(超时时间见FREEZE_BINDER_TIMEOUT_MS)* - 通过mFreezer.setProcessFrozen()冻结进程状态* - 日志记录进程PID和名称** 异常处理:* - Binder冻结失败时调用handleBinderFreezerFailure()* - 进程冻结失败会抛出SecurityException(由setProcessFrozen内部处理)** 线程安全:* - 需持有ActivityManagerService锁(@GuardedBy("mAm"))* - 禁止在非AMS线程直接调用** @param proc 要冻结的进程记录(包含PID/UID/进程名等关键信息)*/@GuardedBy({"mAm"})private void freezeProcess(final ProcessRecord proc) {final int pid = proc.pid;final String name = proc.processName;Slog.d(TAG_AM, "freezing " + pid + " " + name);// 阶段1:冻结Binder通信try {if (mFreezer.freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS) != 0) {handleBinderFreezerFailure(proc, "outstanding txns");return; // 终止流程}// 阶段2:冻结进程状态mFreezer.setProcessFrozen(pid, proc.uid, true);} catch (SecurityException e) {Slog.e(TAG_AM, "Freeze failed for " + pid, e);}}
3.2.9 Freezer.freezeBinder
freezeBinder最终调用到libbinder里面去,最终判断是否是Android设备,是的话,就通过ioctl,传递BINDER_FREEZE,最终走到内核的 binder 驱动上
Java 层 (Freezer.java)/*** 控制进程的Binder事务冻结状态** 核心功能:* 1. 冻结时:同步刷出待处理事务后阻塞新事务* 2. 解冻时:立即恢复Binder通信能力** 关键特性:* - 同步操作:方法返回时已完成事务刷出(若freeze=true)* - 轻量解冻:解冻操作(freeze=false)无显著延迟* - 事务隔离:冻结后发送到该进程的Binder调用将立即失败** 执行流程:* 1. 通过JNI调用nativeFreezeBinder* 2. 返回0表示成功,-EAGAIN表示有未完成事务** 注意事项:* - 超时设置:timeoutMs需大于典型事务处理时间(建议≥5000ms)* - 线程安全:需在调用线程的Binder线程池上下文执行** @param pid 目标进程ID* @param freeze true=冻结,false=解冻* @param timeoutMs 等待事务完成的超时时间(毫秒)* @return 0=成功, -EAGAIN=有未完成事务, 其他负数=错误码* @throws RuntimeException 当底层操作失败时抛出*/public int freezeBinder(int pid, boolean freeze, int timeoutMs) {return nativeFreezeBinder(pid, freeze, timeoutMs);}JNI 层 (com_android_server_am_Freezer.cpp)/*** Binder冻结的JNI桥接实现** 参数转换:* Java参数 -> C++结构体 -> 内核参数** 异常处理:* 1. 过滤-EAGAIN(预期中的可重试状态)* 2. 其他错误码转换为Java异常** 线程约束:* 必须在Binder线程调用(因访问IPCThreadState)*/static const JNINativeMethod sMethods[] = { {"nativeFreezeBinder", "(IZI)I", (void*) freezeBinder }, // (int,boolean,int)->int};jint freezeBinder(JNIEnv* env, jobject, jint pid, jboolean freeze, jint timeout_ms) { jint retVal = IPCThreadState::freeze(pid, freeze, timeout_ms); // 非EAGAIN错误抛出RuntimeException if (retVal != 0 && retVal != -EAGAIN) { jniThrowException(env, "java/lang/RuntimeException", "Unable to freeze/unfreeze binder"); } return retVal;}Native 层 (IPCThreadState.cpp)/*** 执行实际的Binder冻结操作** 内核交互:* 通过ioctl(BINDER_FREEZE)指令控制内核驱动** 数据结构:* struct binder_freeze_info {* pid_t pid; // 目标进程* bool enable; // 冻结/解冻标志* uint32_t timeout_ms; // 超时时间* };** 返回值处理:* - 0:成功* - -EAGAIN:事务未排空(需重试)* - 其他负数:errno转换的错误码** 底层依赖:* 要求内核配置CONFIG_ANDROID_BINDER_FREEZER*/status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) {struct binder_freeze_info info = {.pid = pid,.enable = enable,.timeout_ms = timeout_ms};#if defined(BINDER_WITH_KERNEL_IPC)// 通过ioctl与Binder驱动交互if (ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0)return -errno; // 转换内核错误码#endifreturn 0;}
最终通过ioctl,传递BINDER_FREEZE,走到内核的 binder 驱动上
3.2.10 Freezer.setProcessFrozen
setProcessFrozen是一个native函数,会调用到android_util_Process的android_os_Process_setProcessFrozen函数,在此函数里会调用cgroup中间抽象层libprocessgroup的API,通过cgroup本身的freezer子系统来实现进程冻结功能
1.Java接口层 (Freezer.java)/*** 控制进程冻结状态的外部接口** 功能说明:* 1. 提供进程冻结/解冻的公共API* 2. 参数校验后调用底层Native实现** 线程安全:* - 需通过ActivityManagerService锁保护(@GuardedBy("mAm"))* - 禁止跨进程直接调用** 典型调用场景:* - 内存不足时冻结后台进程* - 应用进入后台时延迟冻结** @param pid 目标进程ID(必须>0)* @param uid 进程所属用户ID(必须>=0)* @param frozen true=冻结进程,false=解冻进程** @throws SecurityException 当调用者无权限时抛出* @throws IllegalArgumentException 参数非法时抛出*/public void setProcessFrozen(int pid, int uid, boolean frozen) {Process.setProcessFrozen(pid, uid, frozen);}2. JNI桥接层 (Process.java)/*** 进程冻结的Native方法声明** 设计要点:* 1. 标记为@hide禁止普通应用调用* 2. 实际实现在android_os_Process.cpp中** 参数约束:* - pid必须对应存在的进程* - uid必须与进程实际UID匹配** @hide 系统内部API*/public static final native void setProcessFrozen(int pid, int uid, boolean frozen);3. Native实现层 (android_util_Process.cpp)/*** 进程冻结的JNI实现** 核心逻辑:* 1. 参数校验(UID有效性)* 2. 根据freeze参数切换进程的cgroup分组* 3. 错误处理** 关键技术:* - 冻结:将进程移入"Frozen" cgroup* - 解冻:将进程移回"Unfrozen" cgroup** 安全控制:* - 调用者需有android.permission.FREEZE_PROCESS权限* - 禁止冻结系统关键进程(uid<10000)*/static void android_os_Process_setProcessFrozen(JNIEnv *env, jobject clazz, jint pid, jint uid, jboolean freeze){// 参数校验if (uid < 0) {jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException","Invalid uid %d", uid);return;}bool success = false;if (freeze) {// 冻结操作:将进程加入Frozen cgroupsuccess = SetProcessProfiles(uid, pid, {"Frozen"});} else {// 解冻操作:将进程移回Unfrozen cgroupsuccess = SetProcessProfiles(uid, pid, {"Unfrozen"});}// 错误处理if (!success) {signalExceptionForGroupError(env, EINVAL, pid);}}
3.2.11 android系统层冻结流程总结
通过上面的触发流程可以看出,系统通过adj的变化来触发冻结状态的更新。经过一系列判断后,从 Java 层到 Native 层,最终调用到 cgroup 中间抽象层的 API,进而通过 cgroup 实现进程的冻结功能。




1273

被折叠的 条评论
为什么被折叠?



