当前位置: 首页 > java >正文

Android15 AndroidV冻结和解冻的场景

摘要

cgroup 最初由 Google 工程师 Paul Menage 和 Rohit Seth 在 2006 年提出,是一种细粒度资源控制的Linux内核机制。于 2007 年合并到 Linux 内核主线中。然而 Google 原生系统直到 Android 11 或更高版本上才支持 CACHE 应用的 CPU 冻结功能。当应用切换到后台并且没有其他活动时,系统会在一定时间内通过状态判断,将进程 ID 迁移到冻结的 cgroup 节点上,实现冻结 CACHE 应用。这项功能可以减少活跃缓存应用在后台存在时所消耗的 CPU 资源,从而达到节电的目的。当应用再次切换到前台时,系统会将该应用的进程解冻,以实现快速启动。

0

0

一、冻结场景

主要看哪些场景调用到 freezeAppAsyncInternalLSP 函数

冻结场景

触发条件

执行逻辑

操作示例/命令

Adj更新触发冻结

进程的OOM_ADJ值≥CACHED_APP_MIN_ADJ(900)且持续10秒无交互

系统通过CachedAppOptimizer.freezeAppAsyncLSP延迟10秒执行冻结

自动触发,无需手动操作

广播发送方冻结

发送方进程(callerApp)满足isProcessFreezable条件(adj≥900且非豁免)

1. 检查广播发送数量阈值

2. 调用forceFreezeAppAsyncLSP立即冻结

代码逻辑:BroadcastQueueModernImpl.enqueueBroadcastLocked()

ADB命令手动冻结

通过am freeze命令指定目标进程

1. 解析包名/PID

2. 调用runFreeze(pw, true)写入cgroup.freeze=1

adb shell am freeze com.tencent.mp

adb shell am freeze --pid 9432

1.1 Adj 更新触发大于900判断,10秒后执行冻结

冻结流程:前台进程 --> 可感知进程--> 服务进程 --> Cached进程->10秒后->cgroup freeze子系统冻结

CachedAppOptimizer.freezeAppAsyncLSP(ProcessRecord app, @UptimeMillisLong long delayMillis) // 10 秒后冻结->CachedAppOptimizer.freezeAppAsyncInternalLSP(ProcessRecord app, @UptimeMillisLong long delayMillis, boolean force)ActivityManagerService.updateOomAdjLocked→OomAdjuster.updateOomAdjLocked→→OomAdjuster.updateOomAdjLSP→→→OomAdjusterModernImpl.performUpdateOomAdjLSP→→→→OomAdjusterModernImpl.performUpdateOomAdjPendingTargetsLocked→→→→→OomAdjusterModernImpl.partialUpdateLSP→→→→→→OomAdjuster.postUpdateOomAdjInnerLSP→→→→→→→OomAdjuster.updateAndTrimProcessLSP→→→→→→→→OomAdjuster.applyOomAdjLSP→→→→→→→→→OomAdjuster.updateAppFreezeStateLSP adj大于900进程(FREEZER_CUTOFF_ADJ)→→→→→→→→→→CachedAppOptimizer.freezeAppAsyncLSP→→→→→→→→→→→CachedAppOptimizer.freezeAppAsyncInternalLSP 防抖超时阈值(默认10秒)→→→→→→→→→→→→SET_FROZEN_PROCESS_MSG 等10秒(默认10秒防抖)→→→→→→→→→→→→→CachedAppOptimizer.freezeBinder 先冻结Binder再冻结进程的原则→→→→→→→→→→→→→CachedAppOptimizer.freezeProcess 通过cgroup v2 freezer子系统实现→→→→→→→→→→→→→→Process.setProcessFrozen(pid, proc.uid, true) 内核级冻结→→→→→→→→→→→→→→→android_os_Process.android_os_Process_setProcessFrozen(jint pid, jint uid, jboolean freeze)→→→→→→→→→→→→→→→→processgroup.SetProcessProfiles→→→→→→→→→→→→→→→→→TaskProfiles.SetProcessProfiles()→→→→→→→→→→→→→→→→→→TaskProfiles.ExecuteForProcess()→→→→→→→→→→→→→→→→→→→TaskProfiles.ExecuteForProcess()写冻结的文件节点:/sys/fs/cgroup/uid_1000/pid_1234/cgroup.freeze,例如1

1.2 广播发送方若可被冻结,则立刻冻结

防止广播发送方滥用后台资源

当广播发送方进程(callerApp)处于缓存状态(adj≥900)时,若其频繁发送广播,可能通过跨进程通信(Binder)持续占用CPU资源。冻结发送方进程可强制中断其广播发送能力,避免后台进程通过广播保活或消耗系统资源

​​​​​​​

BroadcastQueueModernImpl.enqueueBroadcastLocked->->CachedAppOptimizer.freezeAppAsyncImmediateLSP(ProcessRecord app) // 立即冻结->->->CachedAppOptimizer.freezeAppAsyncInternalLSP(ProcessRecord app, @UptimeMillisLong long delayMillis, boolean force)/** * 处理广播入队逻辑,并对后台进程的广播发送行为进行限制 *  * 核心设计原则: * 1. 选择性冻结广播发送方(callerApp)而非接收方,原因: *    - 发送方通常是后台进程(adj≥900),冻结可防止其滥用广播保活 *    - 接收方多为用户交互进程(adj≤700),冻结会导致功能中断 *    - 符合Android非对称优先级策略:牺牲后台进程保前台体验 *  * 2. 异步冻结机制确保系统稳定性: *    - freezeAppAsyncImmediateLSP的延迟执行(默认0秒)避免误杀临时性广播 *    - 超过MAX_FROZEN_OUTGOING_BROADCASTS阈值时立即终止进程 */@Overridepublic void enqueueBroadcastLocked(@NonNull BroadcastRecord r) {    // TODO: Apply delivery group policies and FLAG_REPLACE_PENDING to collapse the    // outgoing broadcasts.    // TODO: Add traces/logs for the enqueueing outgoing broadcasts logic.    /* 后台进程广播限制逻辑     * 触发条件:     * 1. 系统启用广播延迟策略(Flags.deferOutgoingBroadcasts)     * 2. 发送方为可冻结进程(adj≥900且非豁免状态)     */    if (Flags.deferOutgoingBroadcasts() && isProcessFreezable(r.callerApp)) {        // 获取或创建进程专属的广播队列        final BroadcastProcessQueue queue = getOrCreateProcessQueue(                r.callerApp.processName, r.callerApp.uid);        /* 广播流量控制         * 当单个后台进程发送广播超过阈值时(防DoS攻击):         * 1. 记录异常日志(noisy=true触发Log.wtf)         * 2. 立即终止进程而非冻结,避免资源持续泄漏         *          * 典型阈值:MAX_FROZEN_OUTGOING_BROADCASTS=5(机型可配置)         */        if (queue.getOutgoingBroadcastCount() >= mConstants.MAX_FROZEN_OUTGOING_BROADCASTS) {            r.callerApp.killLocked("Too many outgoing broadcasts in cached state",                    ApplicationExitInfo.REASON_OTHER,                    ApplicationExitInfo.SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED,                    true /* noisy */);            return;        }        // 记录广播到进程历史(用于ANR分析和状态恢复)        queue.enqueueOutgoingBroadcast(r);        mHistory.onBroadcastFrozenLocked(r);        /* 执行异步冻结         * 关键特性:         * 1. 延迟10秒执行,允许短时突发广播         * 2. 通过Binder冻结+内存压缩双重优化         * 3. 与OomAdjuster协同更新adj值         */        mService.mOomAdjuster.mCachedAppOptimizer.freezeAppAsyncImmediateLSP(r.callerApp);        return;    }}

1.3 adb shell 命令冻结

冻结命令

基础命令格式:adb shell am freeze[options]

:目标应用的包名(如 com.example.app)或进程ID(如 1234)

options]:可选参数(如 --user 0指定用户)

例如

adb shell am freeze com.tencent.mp

adb shell am freeze --pid 9432

0

ActivityManagerShellCommand.runFreeze->CachedAppOptimizer.forceFreezeAppAsyncLSP(ProcessRecord app)/** * 执行进程冻结/解冻操作的命令行接口实现 *  * @param pw 输出流,用于打印操作结果 * @param freeze 操作类型:true=冻结,false=解冻 * @return 执行状态:0=成功,-1=失败 * @throws RemoteException 远程调用异常 *  * 关键流程说明: * 1. 解析命令行参数(如--sticky标记) * 2. 获取目标进程记录(ProcessRecord) * 3. 执行冻结/解冻操作并输出结果 */@NeverCompile  // 标记该方法不参与编译时优化int runFreeze(PrintWriter pw, boolean freeze) throws RemoteException {    // 解析命令行附加参数(如--sticky持久化标记)    String freezerOpt = getNextOption();    boolean isSticky = false;    if (freezerOpt != null) {        isSticky = freezerOpt.equals("--sticky");    }    // 通过shell命令获取目标进程信息    ProcessRecord proc = getProcessFromShell();    if (proc == null) {        return -1;  // 进程不存在或权限不足    }    // 打印操作日志(包含进程名、PID和sticky状态)    pw.print(freeze ? "Freezing" : "Unfreezing");    pw.print(" process " + proc.processName);    pw.println(" (" + proc.mPid + ") sticky=" + isSticky);    /* 核心冻结/解冻逻辑(需双重锁保护)     * 同步块说明:     * 1. mInternal锁:保证AMS服务状态一致性     * 2. mProcLock锁:防止进程状态并发修改     */    synchronized (mInternal) {        synchronized (mInternal.mProcLock) {            // 设置持久化标记(控制解冻后是否保持低优先级)            proc.mOptRecord.setFreezeSticky(isSticky);            if (freeze) {                /* 强制异步冻结                 * 内部流程:                 * 1. 延迟10ms执行(避免锁竞争)                 * 2. 调用Binder驱动冻结IPC通信                 * 3. 写入cgroup.freeze=1触发内核冻结                 */                mInternal.mOomAdjuster.mCachedAppOptimizer.forceFreezeAppAsyncLSP(proc);            } else {                /* 内部解冻                 * 参数说明:                 * @param proc 目标进程                 * @param reason 解冻原因(0=手动触发)                 * @param immediate 是否跳过延迟立即执行                 */                mInternal.mOomAdjuster.mCachedAppOptimizer.unfreezeAppInternalLSP(proc, 0, true);            }        }    }    return 0;  // 操作成功}

二、解冻场景

场景分类

触发条件

解冻原因常量

解冻持续时间

典型命令/场景

广播机制

warm进程接收广播

UNFREEZE_REASON_START_RECEIVER

默认10秒

广播分发时自动触发

广播配置TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED标记

UNFREEZE_REASON_START_RECEIVER

按options配置时长

高优先级推送广播

有序广播结果回传

UNFREEZE_REASON_FINISH_RECEIVER

固定10秒

scheduleResultTo()调用

广播队列标记runningOomAdjusted=true

UNFREEZE_REASON_START_RECEIVER

默认10秒

notifyStartedRunning()触发

UI资源清理

进程处于IMPORTANT_BACKGROUND状态且hasPendingUiClean=true

UNFREEZE_REASON_TRIM_MEMORY

按内存压力动态调整

Activity.onStop()时触发

AMS机制

未完成Binder事务或系统ping检查

UNFREEZE_REASON_PING

默认10秒

waitForApplicationBarrier()调用

Binder异常

Binder冻结失败或冻结后仍有事务

UNFREEZE_REASON_BINDER

立即解冻不重冻

handleBinderFreezerFailure()触发

文件锁冲突

冻结进程持有文件锁阻塞前台进程

UNFREEZE_REASON_FILE_LOCKS

永久解冻直到锁释放

内核回调onBlockingFileLock()

ADB命令

手动执行解冻命令

UNFREEZE_REASON_SHELL

永久解冻

adb shell am unfreeze com.tencent.mp

主要看 unfreezeAppInternalLSP 的调用关系​​​​​​​

解冻场景CachedAppOptimizer.enableFreezer(boolean enable) 开发者选项的冻结功能停用按钮触发解冻场景CachedAppOptimizer.unfreezeTemporarily(ProcessRecord app, @UnfreezeReason int reason, long delayMillis) CachedAppOptimizer.handleBinderFreezerFailure(final ProcessRecord proc, final String reason)CachedAppOptimizer.onBlockingFileLock(IntArray pids)ActivityManagerShellCommand.runFreeze("Unfreezing") adb shell 命令解冻场景->CachedAppOptimizer.unfreezeAppLSP(ProcessRecord app, @UnfreezeReason int reason)->->CachedAppOptimizer.unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason, boolean force)

2.1 开发者选项的冻结功能停用按钮触发解冻场景

具体冻结机制开关对应 Settings.Global.CACHED_APPS_FREEZER_ENABLED 数值

adb shell settings get global cached_apps_freezer_enabled

adb shell settings put global cached_apps_freezer_enabled

0

->CachedAppOptimizer.enableFreezer(boolean enable)->->CachedAppOptimizer.unfreezeAppLSP(ProcessRecord app, @UnfreezeReason int reason)->->->CachedAppOptimizer.unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason, boolean force)/** * 动态更新冻结功能开关状态,并初始化相关资源 *  * 核心功能: * 1. 从DeviceConfig和全局设置中读取冻结功能开关状态 * 2. 根据配置初始化冻结参数(如防抖时间、豁免列表) * 3. 异步启动冻结管理线程(避免持有AMS主锁) *  * 线程安全: * - 通过@GuardedBy确保方法在mPhenotypeFlagLock保护下执行 * - 使用Handler异步处理资源初始化 */@GuardedBy("mPhenotypeFlagLock")private void updateUseFreezer() {    // 从Settings.Global读取强制覆盖配置(最高优先级)    final String configOverride = Settings.Global.getString(        mAm.mContext.getContentResolver(),        Settings.Global.CACHED_APPS_FREEZER_ENABLED    );    /* 配置决策树(三级优先级):     * 1. 强制禁用(Settings.Global="disabled")     * 2. 强制启用(Settings.Global="enabled")或DeviceConfig允许     * 3. 默认禁用     */    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               )) {        // 检查硬件支持并更新子配置        mUseFreezer = isFreezerSupported(); // 检测cgroup freezer和内核支持        updateFreezerDebounceTimeout();      // 更新防抖时间(默认10秒)        updateFreezerExemptInstPkg();        // 更新安装器包名豁免列表    } else {        mUseFreezer = false; // 默认关闭    }    // 通过Handler异步执行资源初始化(避免同步持锁)    final boolean useFreezer = mUseFreezer;    mAm.mHandler.post(() -> {        if (useFreezer) {            Slog.d(TAG_AM, "Freezer enabled");            enableFreezer(true); // 实际启用冻结功能            /* 初始化冻结管理线程             * - 单例线程,负责执行延迟冻结/解冻操作             * - 绑定到SYSTEM线程组(提高调度优先级)             */            if (!mCachedAppOptimizerThread.isAlive()) {                mCachedAppOptimizerThread.start();            }            // 初始化冻结任务处理器(含延迟队列)            if (mFreezeHandler == null) {                mFreezeHandler = new FreezeHandler();            }            // 设置线程调度策略(提升为系统关键线程)            Process.setThreadGroupAndCpuset(                mCachedAppOptimizerThread.getThreadId(),                Process.THREAD_GROUP_SYSTEM            );        } else {            Slog.d(TAG_AM, "Freezer disabled");            enableFreezer(false); // 关闭所有冻结功能        }    });}

2.2 临时解冻场景

2.2.1 广播机制中的临时解冻场景

warm进程收广播,触发临时解冻

广播配置豁免TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED标记,触发临时解冻

确保传递有序广播正常,触发临时解冻

广播队列标记为runningOomAdjusted=true ,触发临时解冻​​​​​​​

->BroadcastQueueModernImpl.updateRunningListLocked warm进程收广播,触发解冻->BroadcastQueueModernImpl.dispatchReceivers 广播配置豁免TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED标记,触发解冻->BroadcastQueueModernImpl.scheduleResultTo 确保传递有序广播正常,触发解冻->BroadcastQueueModernImpl.notifyStartedRunning 广播队列标记为runningOomAdjusted=true ,触发解冻->->CachedAppOptimizer.unfreezeTemporarily(ProcessRecord app, @UnfreezeReason int reason, long delayMillis) ->->->CachedAppOptimizer.unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason, boolean force)/** * 更新广播队列运行状态,管理进程解冻/冻结状态 *  * 核心逻辑:将符合条件的runnable队列提升为running状态 * 重点关注:解冻条件出现在处理warm进程时 */@GuardedBy("mService")private void updateRunningListLocked() {    // ... [资源计算和初始化部分省略] ...    // 遍历所有可运行(runnable)的广播队列    BroadcastProcessQueue queue = mRunnableHead;    while (queue != null && avail > 0) {        BroadcastProcessQueue nextQueue = queue.runnableAtNext;        // --- 触发解冻的核心条件判断 ---        if (queue.isProcessWarm()) {  // 检查是否为warm进程(已缓存但未冻结)            /* 解冻条件:             * 1. 进程处于warm状态(adj=900-999)             * 2. 队列有待处理的广播(isRunnable=true)             * 3. 未超过MAX_RUNNING_PROCESS_QUEUES限制             */            mService.mOomAdjuster.unfreezeTemporarily(queue.app,                    CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER);            // 解冻后二次检查进程状态(可能因解冻失败被杀死)            if (!queue.isProcessWarm()) {                continue; // 进程已死亡则跳过            }            // 执行广播投递(warm进程快速响应)            scheduleReceiverWarmLocked(queue);        }         else {             // 冷启动处理(非冻结进程,无需解冻)            scheduleReceiverColdLocked(queue);        }        // ... [后续处理逻辑省略] ...    }    // ... [状态更新和清理逻辑省略] ...}/** * 分发广播到目标接收器,处理ANR计时和进程解冻逻辑 *  * @param queue 目标进程的广播队列 * @param r 待分发的广播记录 * @param index 接收器在列表中的位置 * @return 是否为阻塞式分发(需等待接收器完成) *  * 核心逻辑: * 1. 设置ANR超时监控(非豁免广播) * 2. 处理后台启动权限 * 3. 根据广播配置触发进程解冻 */@GuardedBy("mService")private boolean dispatchReceivers(@NonNull BroadcastProcessQueue queue,        @NonNull BroadcastRecord r, int index) throws BroadcastRetryException {    // --- 1. ANR监控设置 ---    if (mService.mProcessesReady && !r.timeoutExempt && !r.isAssumedDelivered(index)) {        startDeliveryTimeoutLocked(queue, r.isForeground() ? mFgConstants.TIMEOUT : mBgConstants.TIMEOUT);    }    // --- 2. 后台启动权限处理 ---    if (r.mBackgroundStartPrivileges.allowsAny()) {        app.addOrUpdateBackgroundStartPrivileges(r, r.mBackgroundStartPrivileges);        // ... [设置超时回调省略] ...    }    // --- 3. 解冻条件判断(重点)---    if (r.options != null && r.options.getTemporaryAppAllowlistDuration() > 0) {        if (r.options.getTemporaryAppAllowlistType() ==                 PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED) {            /* 触发解冻的核心条件:             * 1. 广播携带临时白名单配置(options不为null)             * 2. 白名单类型为APP_FREEZING_DELAYED(延迟冻结类型)             * 3. 白名单持续时间>0(getTemporaryAppAllowlistDuration)             *              * 解冻参数:             * - 目标进程:queue.app             * - 原因:UNFREEZE_REASON_START_RECEIVER             * - 持续时间:白名单配置的时长             */            mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(                app,                CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER,                r.options.getTemporaryAppAllowlistDuration() // 解冻持续时间            );        } else {            // 其他白名单类型走常规处理            mService.tempAllowlistUidLocked(...);        }    }    // --- 4. 广播分发执行 ---    try {        if (receiver instanceof BroadcastFilter) {            thread.scheduleRegisteredReceiver(...);         } else {            thread.scheduleReceiver(...); // 动态注册接收器        }        return true;    } catch (RemoteException e) {        app.killLocked(...); // 进程通信失败时终止        throw new BroadcastRetryException(e);    }}/** * 为有序广播安排最终结果回传(发送给resultTo指定的接收器) *  * 触发条件: * 1. 广播是有序广播(resultTo != null) * 2. 发送方进程仍处于warm状态(adj=900-999) * 3. 接收方进程存活(thread != null) *  * 关键操作: * - 临时解冻接收方进程确保结果能送达 * - 处理跨进程身份共享 * - 清理结果接收器引用防止内存泄漏 */@GuardedBy("mService")private void scheduleResultTo(@NonNull BroadcastRecord r) {    // 1. 基础检查:非有序广播直接返回    if (r.resultTo == null) return;    // 2. 获取接收方进程状态    final ProcessRecord app = r.resultToApp;    final IApplicationThread thread = (app != null) ? app.getOnewayThread() : null;    if (thread != null) {        /* 核心解冻条件(同时满足才会解冻):         * - 接收方进程处于冻结状态(isFrozen=true)         * - 需要传递有序广播结果(resultTo非空)         * - 接收方Binder通道存活(thread != null)         *          * 解冻参数:         * - 原因:UNFREEZE_REASON_FINISH_RECEIVER(完成接收)         * - 默认持续时间:10秒(见CachedAppOptimizer.TEMP_UNFREEZE_DURATION_MS)         */        mService.mOomAdjuster.unfreezeTemporarily(                app, CachedAppOptimizer.UNFREEZE_REASON_FINISH_RECEIVER);        // 3. 处理跨进程身份共享(如ContentProvider调用链)        if (r.shareIdentity && app.uid != r.callingUid) {            mService.mPackageManagerInt.grantImplicitAccess(...);        }        // 4. 执行结果回传        try {            thread.scheduleRegisteredReceiver(...);        } catch (RemoteException e) {            // 进程通信失败时终止目标进程            app.killLocked(...);        }    }    // 5. 清理结果接收器引用(防内存泄漏)    r.resultTo = null;}/** * 通知系统其他部分广播队列已开始运行(用于内部状态维护) *  * 核心操作: * 1. 更新进程接收器计数 * 2. 调整进程LRU位置(非受限进程) * 3. 临时解冻目标进程 * 4. 强制更新进程状态(若需要) */@GuardedBy("mService")private void notifyStartedRunning(@NonNull BroadcastProcessQueue queue) {    if (queue.app != null) {        // 1. 增加当前接收器计数(用于ANR检测和状态跟踪)        queue.app.mReceivers.incrementCurReceivers();        /* 2. 调整LRU位置(除非进程处于后台受限状态)         * 受限状态示例:省电模式下的后台进程         */        if (mService.mInternal.getRestrictionLevel(queue.uid)                 < ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET) {            mService.updateLruProcessLocked(queue.app, false, null);        }        /* --- 核心解冻条件 ---         * 触发解冻需同时满足:         * - 目标进程处于冻结状态(isFrozen=true)         * - 进程需要处理广播(queue.runningOomAdjusted=true)         * - 进程未被标记为豁免冻结(!app.mOptRecord.isFreezeExempt())         *          * 解冻参数:         * - 原因:UNFREEZE_REASON_START_RECEIVER(广播接收启动)         * - 默认持续时间:10秒(见CachedAppOptimizer.TEMP_UNFREEZE_DURATION_MS)         */        mService.mOomAdjuster.unfreezeTemporarily(            queue.app,            CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER        );        // 3. 强制提升进程状态至RECEIVER级别(adj=700)        if (queue.runningOomAdjusted) {            queue.app.mState.forceProcessStateUpTo(                ActivityManager.PROCESS_STATE_RECEIVER            );            mService.enqueueOomAdjTargetLocked(queue.app);        }    }}

2.2.2 进程存在未清理的UI资源的场景触发临时解冻

进程处于重要后台状态(PROCESS_STATE_IMPORTANT_BACKGROUND及以上)或系统无UI状态)且进程有未清理的UI资源标记(hasPendingUiClean),触发解冻。

​​​​​​​

->AppProfiler.scheduleTrimMemoryLSP->->CachedAppOptimizer.unfreezeTemporarily(ProcessRecord app, @UnfreezeReason int reason, long delayMillis) ->->->CachedAppOptimizer.unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason, boolean force)/** * 检查并触发UI隐藏时的内存修剪(需持有mService和mProcLock锁) *  * 解冻条件: * 1. 进程处于重要后台状态(PROCESS_STATE_IMPORTANT_BACKGROUND及以上)或系统无UI状态 * 2. 进程有未清理的UI资源标记(hasPendingUiClean) *  * 注:该方法通常由ActivityStack调用,当Activity进入onStop时触发 */@GuardedBy({"mService", "mProcLock"})private void trimMemoryUiHiddenIfNecessaryLSP(ProcessRecord app) {    // 复合条件判断:进程状态+UI标记    if ((app.mState.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND            || app.mState.isSystemNoUi()) && app.mProfile.hasPendingUiClean()) {        /* 触发UI隐藏级别的内存修剪         * 使用TRIM_MEMORY_UI_HIDDEN(级别20):         * - 释放Activity中View树/Texture等资源         * - 通知Glide等图片库清除缓存         */        scheduleTrimMemoryLSP(app, ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN,                "Trimming memory of bg-ui ");        // 清除UI待清理标记(避免重复处理)        app.mProfile.setPendingUiClean(false);    }}/** * 执行内存修剪调度(含解冻逻辑) *  * 解冻核心条件: * 1. 新请求的内存修剪级别高于当前记录值(getTrimMemoryLevel < level) * 2. 进程Binder通信通道存活(getThread != null) * 3. 进程当前处于冻结状态(由unfreezeTemporarily内部检查) *  * @param level 内存修剪级别(参见ComponentCallbacks2常量) * @param msg   调试日志前缀 */@GuardedBy({"mService", "mProcLock"})private void scheduleTrimMemoryLSP(ProcessRecord app, int level, String msg) {    IApplicationThread thread;    // 双重条件检查(内存级别+进程存活)    if (app.mProfile.getTrimMemoryLevel() < level && (thread = app.getThread()) != null) {        try {            // 调试日志输出            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {                Slog.v(TAG_OOM_ADJ, msg + app.processName + " to " + level);            }            /* 关键解冻操作:             * 1. 临时提升进程优先级(adj=VISIBLE_APP_ADJ)             * 2. 清除cgroup.freeze标记             * 3. 设置10秒冻结延迟(TEMP_UNFREEZE_DURATION_MS)             */            mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app,                    CachedAppOptimizer.UNFREEZE_REASON_TRIM_MEMORY);            // 通过Binder跨进程调用执行内存修剪            thread.scheduleTrimMemory(level);        } catch (RemoteException e) {            // 忽略进程已死亡的异常(Binder通道断开)        }    }}

什么时候会解冻一个冻结的APP?

当同时满足以下两个情况时:

1.这个APP现在"不活跃"了:

要么已经退到后台(比如你按了Home键)

要么是系统服务且当前不需要显示界面(比如后台运行的天气服务)

2.这个APP之前用过界面资源还没收拾干净:

比如还留着没释放的图片缓存、界面元素等

2.2.3 AMS机制中的临时解冻场景

进程有未完成的Binder事务或系统需要确保进程能立即响应ping,触发临时解冻场景​​​​​​​

->ActivityManagerService.waitForApplicationBarrier->->CachedAppOptimizer.unfreezeTemporarily(ProcessRecord app, @UnfreezeReason int reason, long delayMillis) ->->->CachedAppOptimizer.unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason, boolean force)/** * 等待所有正在运行的应用程序处理完挂起的跨进程事件(Binder调用) *  * 典型场景:系统执行关键操作(如OTA升级、内存压缩)前,确保所有进程完成当前任务 */void waitForApplicationBarrier(@NonNull PrintWriter pw) {    // 同步工具:用于等待所有进程响应    final CountDownLatch finishedLatch = new CountDownLatch(1);    // 计数器:记录发送的ping请求数和返回的pong响应数    final AtomicInteger pingCount = new AtomicInteger(0);    final AtomicInteger pongCount = new AtomicInteger(0);    // pong回调:当进程完成事件处理时触发    final RemoteCallback pongCallback = new RemoteCallback((result) -> {        if (pongCount.incrementAndGet() == pingCount.get()) {            finishedLatch.countDown(); // 所有响应到达时解除阻塞        }    });    // 先发一个哨兵ping(防止空进程导致立即完成)    pingCount.incrementAndGet();    /* 阶段1:向所有进程发送ping请求(需持有AMS和进程锁) */    synchronized (ActivityManagerService.this) {        synchronized (mProcLock) {            // 遍历所有运行中的进程            final ArrayMap<String, SparseArray<ProcessRecord>> pmap =                    mProcessList.getProcessNamesLOSP().getMap();            for (int iProc = 0; iProc < pmap.size(); iProc++) {                final SparseArray<ProcessRecord> apps = pmap.valueAt(iProc);                for (int iApp = 0; iApp < apps.size(); iApp++) {                    final ProcessRecord app = apps.valueAt(iApp);                    final IApplicationThread thread = app.getOnewayThread();                    if (thread != null) {                        /* 关键解冻操作(触发条件):                         * 1. 进程当前处于冻结状态(isFrozen=true)                         * 2. 进程有未完成的Binder事务(outstanding_txns>0)                         * 3. 系统需要确保进程能立即响应ping                         */                        mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app,                                CachedAppOptimizer.UNFREEZE_REASON_PING);                        pingCount.incrementAndGet(); // 计数+1                        try {                            thread.schedulePing(pongCallback); // 发送ping指令                        } catch (RemoteException ignored) {                            // 进程已死亡时模拟响应                            pongCallback.sendResult(null);                        }                    }                }            }        }    }    /* 阶段2:发送哨兵pong并等待响应 */    pongCallback.sendResult(null); // 触发最终检查    // 最多等待30秒(每秒打印进度)    for (int i = 0; i < 30; i++) {        try {            if (finishedLatch.await(1, TimeUnit.SECONDS)) {                pw.println("Finished application barriers!");                return; // 所有进程响应完成            } else {                pw.println("Waiting... " + pongCount.get() + "/" + pingCount.get());            }        } catch (InterruptedException ignored) {        }    }    pw.println("Gave up waiting!"); // 超时放弃}

2.3 Binder冻结不成功或冻结后仍有Binder事务,则触发解冻

Binder冻结不成功或冻结后仍有Binder事务,则触发解冻​​​​​​​

->CachedAppOptimizer.freezeProcess->->CachedAppOptimizer.handleBinderFreezerFailure(final ProcessRecord proc, final String reason)->->->CachedAppOptimizer.unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason, boolean force)/** * 冻结目标进程的核心方法 *  * 关键流程: * 1. 检查进程状态是否允许冻结 * 2. 冻结Binder通信通道 * 3. 通过cgroup freezer冻结进程 * 4. 更新进程和UID的冻结状态 */@GuardedBy({"mAm"})private void freezeProcess(final ProcessRecord proc) {    // 初始信息获取(不持锁)    int pid = proc.getPid();    final String name = proc.processName;    final ProcessCachedOptimizerRecord opt = proc.mOptRecord;    synchronized (mProcLock) {        /* --- 解冻条件检查(提前终止冻结的场景)--- */        // 1. 检查冻结请求是否已被取消        if (!opt.isPendingFreeze()) {            return;        }        opt.setPendingFreeze(false); // 清除待处理标记        // 2. 全局冻结功能被覆盖(调试用途)        if (mFreezerOverride) {            opt.setFreezerOverride(true);            Slog.d(TAG_AM, "Skipping freeze for process " + pid + "(override)");            return;        }        // 3. 进程标记为不应冻结(如持有唤醒锁)        if (opt.shouldNotFreeze()) {            if (DEBUG_FREEZER) Slog.d(TAG_AM, "Skip freeze: process marked shouldNotFreeze");            return;        }        // 4. 进程已冻结或正在退出        if (pid == 0 || opt.isFrozen()) {            if (DEBUG_FREEZER) Slog.d(TAG_AM, "Skip freeze: already frozen or dying");            return;        }        /* --- 执行冻结流程 --- */        Slog.d(TAG_AM, "freezing " + pid + " " + name);        // 阶段1:冻结Binder通信(防止事务丢失)        try {            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"freezeBinder:" + name);            if (freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS) != 0) {                // Binder事务未完成时触发解冻(解冻条件1)                handleBinderFreezerFailure(proc, "outstanding txns");                return;            }        } catch (RuntimeException e) {            // Binder冻结失败时杀死进程(解冻条件2)            mFreezeHandler.post(() -> killProcessForFreezeFailure(proc));            return;        } finally {            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);        }        // 阶段2:cgroup freezer冻结        try {            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"setProcessFrozen:" + name);            Process.setProcessFrozen(pid, proc.uid, true); // 写入cgroup.freeze=1            opt.setFrozen(true); // 更新内存状态            mFrozenProcesses.put(pid, proc); // 加入全局冻结列表            // 解冻条件3:UID下所有进程冻结时标记UID冻结            if (proc.getUidRecord().areAllProcessesFrozen()) {                proc.getUidRecord().setFrozen(true);            }        } catch (Exception e) {            Slog.w(TAG_AM, "Freeze failed for " + pid); // 解冻条件4:内核操作异常        } finally {            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);        }    }    // 阶段3:冻结后检查(防竞态条件)    if (opt.isFrozen()) {        try {            // 解冻条件5:冻结后仍有Binder事务(需解冻处理)            if ((getBinderFreezeInfo(pid) & TXNS_PENDING_WHILE_FROZEN) != 0) {                synchronized (mProcLock) {                    handleBinderFreezerFailure(proc, "new pending txns");                }            }        } catch (RuntimeException e) {            // 解冻条件6:状态检查异常时解冻            mFreezeHandler.post(() -> killProcessForFreezeFailure(proc));        }    }}

2.4 冻结进程持有文件锁并阻塞其他进程时,强制解冻该进程

冻结进程持有文件锁并阻塞其他进程时,强制解冻该进程

0

->CachedAppOptimizer.onBlockingFileLock(IntArray pids)->->CachedAppOptimizer.unfreezeAppLSP->->->CachedAppOptimizer.unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason, boolean force)/** * 处理进程因文件锁阻塞的回调(由内核触发) *  * 核心逻辑:当冻结进程持有文件锁并阻塞其他进程时,强制解冻该进程 *  * @param pids 包含两组PID的数组: *             - pids[0]: 持有文件锁的冻结进程PID *             - pids[1..N]: 被该锁阻塞的进程PID列表 */@GuardedBy({"mAm"})@Overridepublic void onBlockingFileLock(IntArray pids) {    if (DEBUG_FREEZER) {        Slog.d(TAG_AM, "Blocking file lock found: " + pids);    }    // 双重锁保护(AMS全局锁 + 进程锁)    synchronized (mAm) {        synchronized (mProcLock) {            int pid = pids.get(0); // 获取持有锁的冻结进程PID            ProcessRecord app = mFrozenProcesses.get(pid); // 从冻结进程表中查找            if (app != null) {                /* 遍历所有被阻塞的进程,检查是否需要解冻:                 * 1. 被阻塞进程必须是高优先级(adj < FREEZER_CUTOFF_ADJ)                 * 2. 至少存在一个符合条件的被阻塞进程时触发解冻                 */                for (int i = 1; i < pids.size(); i++) {                    int blocked = pids.get(i);                    ProcessRecord pr = mAm.mPidsSelfLocked.get(blocked); // 获取被阻塞进程记录                    if (pr != null && pr.mState.getCurAdj() < ProcessList.FREEZER_CUTOFF_ADJ) {                        Slog.d(TAG_AM, app.processName + " (" + pid + ") blocks "                                 + pr.processName + " (" + blocked + ")");                        /* 关键解冻条件:                         * 1. 冻结进程持有文件锁(内核检测到flock/fcntl锁)                         * 2. 被阻塞进程是前台/可见进程(adj < 900)                         * 3. 解冻原因标识:UNFREEZE_REASON_FILE_LOCKS                         */                        unfreezeAppLSP(app, UNFREEZE_REASON_FILE_LOCKS);                        break; // 解冻后立即终止检查                    }                }            }        }    }}

2.5 adb shell解冻命令场景

基础命令格式:adb shell am unfreeze[options]

:目标应用的包名(如 com.example.app)或进程ID(如 1234)

options]:可选参数(如 --user 0指定用户)

例如命令:

adb shell am unfreeze --pid 9432

adb shell am unfreeze com.tencent.mp

adb shell cat /dev/freezer/frozen/cgroup.procs | grep -E "[0-9]{4}" | xargs adb shell ps -A | grep -Ei "com.tencent.mp"

0

ActivityManagerShellCommand.runFreeze("Unfreezing") -> CachedAppOptimizer.unfreezeAppInternalLSP/** * 执行进程冻结/解冻操作的命令行接口实现 *  * @param pw 输出流,用于打印操作结果 * @param freeze 操作类型:true=冻结,false=解冻 * @return 执行状态:0=成功,-1=失败 * @throws RemoteException 远程调用异常 *  * 关键流程说明: * 1. 解析命令行参数(如--sticky标记) * 2. 获取目标进程记录(ProcessRecord) * 3. 执行冻结/解冻操作并输出结果 */@NeverCompile  // 标记该方法不参与编译时优化int runFreeze(PrintWriter pw, boolean freeze) throws RemoteException {    // 解析命令行附加参数(如--sticky持久化标记)    String freezerOpt = getNextOption();    boolean isSticky = false;    if (freezerOpt != null) {        isSticky = freezerOpt.equals("--sticky");    }    // 通过shell命令获取目标进程信息    ProcessRecord proc = getProcessFromShell();    if (proc == null) {        return -1;  // 进程不存在或权限不足    }    // 打印操作日志(包含进程名、PID和sticky状态)    pw.print(freeze ? "Freezing" : "Unfreezing");    pw.print(" process " + proc.processName);    pw.println(" (" + proc.mPid + ") sticky=" + isSticky);    /* 核心冻结/解冻逻辑(需双重锁保护)     * 同步块说明:     * 1. mInternal锁:保证AMS服务状态一致性     * 2. mProcLock锁:防止进程状态并发修改     */    synchronized (mInternal) {        synchronized (mInternal.mProcLock) {            // 设置持久化标记(控制解冻后是否保持低优先级)            proc.mOptRecord.setFreezeSticky(isSticky);            if (freeze) {                /* 强制异步冻结                 * 内部流程:                 * 1. 延迟10ms执行(避免锁竞争)                 * 2. 调用Binder驱动冻结IPC通信                 * 3. 写入cgroup.freeze=1触发内核冻结                 */                mInternal.mOomAdjuster.mCachedAppOptimizer.forceFreezeAppAsyncLSP(proc);            } else {                /* 内部解冻                 * 参数说明:                 * @param proc 目标进程                 * @param reason 解冻原因(0=手动触发)                 * @param immediate 是否跳过延迟立即执行                 */                mInternal.mOomAdjuster.mCachedAppOptimizer.unfreezeAppInternalLSP(proc, 0, true);            }        }    }    return 0;  // 操作成功}

小结:

1.进程的冻结是通过cgroup的Freezer子系统来实现的,最终的原理是调度进程,不去申请cpu资源。

2.冻结的进程不会释放内存,因为当解冻时,要快速恢复,而无需重新加载或启动

3.冻结机制是针对cache进程

4.正常当进程变为非cache进程时候,就会解冻

5.Android 13以后针对binder 调用进行了优化,对进程的binder先进行冻结,如果向冻住的进程发送同步binder请求会立刻收到错误码BR_FROZEN_REPLY

6.进程进行解冻的时候,会去检查,如果在冻结期间有收到同步binder的请求,就会kill掉这个进程

7.拥有INSTALL_PACKAGES权限的应用,能够豁免被冻结

http://www.xdnf.cn/news/18431.html

相关文章:

  • 学习Linux嵌入式(正点原子imx课程)开发到底是在学什么
  • 【Linux | 网络】多路转接IO之select
  • Python 面向对象编程入门:从思想到属性操作
  • 图(Graph):关系网络的数学抽象
  • 3维模型导入到3Dmax中的修改色彩简单用法----第二讲
  • 零成本加速:EdgeOne免费套餐3分钟接入指南
  • MYSQL库及表的操作
  • 奈飞工厂:算法优化实战 —— 从推荐系统到内容分发
  • Python工程师向项目管理转型的深度分析与学习道路规划
  • 《用餐》,午餐食堂即景小诗分享(手机/小视频/光盘/养生)
  • AI + 云原生 + ITSM 的三重融合:企业数字化转型的新引擎
  • 面试准备革命:面试汪 vs 传统方法,谁更胜一筹?
  • 搭建我的世界mc服务器全流程——阿里云游戏攻略
  • 相似图像处理程序
  • 北京-15k测试-入职甲方金融-上班第二天
  • 哈尔滨云前沿服务器租用类型
  • 高效获取应用程序图标的方法
  • CSS 3D动画,围绕旋转动画Demo
  • 面试可能问到的问题思考-Redis
  • 机器学习7
  • 网络与信息安全有哪些岗位:(5)安全开发工程师
  • Ubuntu22.04配置网络上网
  • Ubuntu Server 安装 gvm 管理 Go 语言开发环境
  • 自然语言处理NLP L4: 高级语言模型——四种泛化平滑方式
  • 【TrOCR】用Transformer和torch库实现TrOCR模型
  • Matplotlib+HTML+JS:打造可交互的动态数据仪表盘
  • 智慧工厂的 “隐形大脑”:边缘计算网关凭什么重构设备连接新逻辑?
  • 详细说明http协议特别是conten-length和chunk编码,并且用linux的命令行演示整个过程
  • Go语言变量声明与初始化详解
  • 一个状态机如何启动/停止另一个状态机