Freezer原理
Android按照优先级将一般的APP从高到低分为: 前台进程 --> 可感知进程–> 服务进程 --> Cached进程。
Freezer通过冻住cached进程,来迫使这些进程让出CPU,以达到优化系统资源使用的目的。
Cached进程是怎么判定的呢?
由于android的进程的adj是会根据不同的运行状态,会动态计算的。当adj大于等于CACHED_APP_MIN_ADJ(900)
整个流程是比较清晰的,在fwk的系统服务中,会计算ADJ的变化,当满足条件后就会调用cgroup hal提供的freeze的接口,设置crashed进程的状态为freeze 
Freeze实现
下面列出下整个流程最主要的几部分代码
1)Framework层
当进程发生了以下变化后,AMS就会触发OomAdjuster.java 中的 updateOomAdjLocked 函数。
     static final String OOM_ADJ_REASON_METHOD = "updateOomAdj";   //触发ADJ重新计算的条件
    static final String OOM_ADJ_REASON_NONE = OOM_ADJ_REASON_METHOD + "_meh";
    static final String OOM_ADJ_REASON_ACTIVITY = OOM_ADJ_REASON_METHOD + "_activityChange";
    static final String OOM_ADJ_REASON_FINISH_RECEIVER = OOM_ADJ_REASON_METHOD + "_finishReceiver";
    static final String OOM_ADJ_REASON_START_RECEIVER = OOM_ADJ_REASON_METHOD + "_startReceiver";
    static final String OOM_ADJ_REASON_BIND_SERVICE = OOM_ADJ_REASON_METHOD + "_bindService";
    static final String OOM_ADJ_REASON_UNBIND_SERVICE = OOM_ADJ_REASON_METHOD + "_unbindService";
    static final String OOM_ADJ_REASON_START_SERVICE = OOM_ADJ_REASON_METHOD + "_startService";
    static final String OOM_ADJ_REASON_GET_PROVIDER = OOM_ADJ_REASON_METHOD + "_getProvider";
    static final String OOM_ADJ_REASON_REMOVE_PROVIDER = OOM_ADJ_REASON_METHOD + "_removeProvider";
    static final String OOM_ADJ_REASON_UI_VISIBILITY = OOM_ADJ_REASON_METHOD + "_uiVisibility";
    static final String OOM_ADJ_REASON_ALLOWLIST = OOM_ADJ_REASON_METHOD + "_allowlistChange";
    static final String OOM_ADJ_REASON_PROCESS_BEGIN = OOM_ADJ_REASON_METHOD + "_processBegin";
    static final String OOM_ADJ_REASON_PROCESS_END = OOM_ADJ_REASON_METHOD + "_processEnd";
updateOomAdjLocked -》 updateOomAdjLSP -》 performUpdateOomAdjLSP 基本的传参和判断,不展开了。
    @GuardedBy({"mService", "mProcLock"})
    private void updateOomAdjInnerLSP(String oomAdjReason, final ProcessRecord topApp,
            ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles,
            boolean startProfiling) {
	......
      if (size > 0) {
            mAdjSeq--;
            // Update these reachable processes
            updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, containsCycle, false);	//更新新的ADJ
        } else if (state.getCurRawAdj() == ProcessList.UNKNOWN_ADJ) {
            // In case the app goes from non-cached to cached but it doesn't have other reachable
            // processes, its adj could be still unknown as of now, assign one.
            processes.add(app);
            assignCachedAdjIfNecessary(processes);
            applyOomAdjLSP(app, false, SystemClock.uptimeMillis(),		//如果
                    SystemClock.elapsedRealtime());
        }
        mTmpProcessList.clear();
        mService.mOomAdjProfiler.oomAdjEnded();
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        return true;
        ......
    }
    @GuardedBy({"mService", "mProcLock"})
    private void updateAppFreezeStateLSP(ProcessRecord app) {
        if (!mCachedAppOptimizer.useFreezer()) {
            return;
        }
        if (app.mOptRecord.isFreezeExempt()) {
            return;
        }
        final ProcessCachedOptimizerRecord opt = app.mOptRecord;
        // if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze
        if (opt.isFrozen() && opt.shouldNotFreeze()) {
            mCachedAppOptimizer.unfreezeAppLSP(app);
            return;
        }
        final ProcessStateRecord state = app.mState;
        // Use current adjustment when freezing, set adjustment when unfreezing.
        if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && !opt.isFrozen()	//根据当前的ADJ,frozen的状态等,设置进入还是退出frozen状态
                && !opt.shouldNotFreeze()) {
            mCachedAppOptimizer.freezeAppAsyncLSP(app);
        } else if (state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ) {
            mCachedAppOptimizer.unfreezeAppLSP(app);
        }
    }
  | 
2)Native层
 void android_os_Process_setProcessFrozen(
        JNIEnv *env, jobject clazz, jint pid, jint uid, jboolean freeze)
{
    bool success = true;
    if (freeze) {
        success = SetProcessProfiles(uid, pid, {"Frozen"});		//根据Frozen的字符串,去 task_profiles.json 查找对应的设备节点,最终设置 /sys/fs/cgroup/<UID_xxx>/<PID_xxx>/cgroup.freeze 
    } else {
        success = SetProcessProfiles(uid, pid, {"Unfrozen"});
    }
    if (!success) {
        signalExceptionForGroupError(env, EINVAL, pid);
    }
}  | 
3)Kernel层
Kernel的设备节点会通过定义write函数,回调到相关的定义函数,这样就能看到具体的实现代码
 {
	.name = "cgroup.freeze",		 //cgroup_freeze_show文件 触发write函数,执行cgroup_freeze_write函数。
	.flags = CFTYPE_NOT_ON_ROOT,
	.seq_show = cgroup_freeze_show,   
	.write = cgroup_freeze_write,    
},
freezer.c 最终会调用到 freezer中的 cgroup_do_freeze 函数
/*
 * Freeze or unfreeze all tasks in the given cgroup.
 */
static void cgroup_do_freeze(struct cgroup *cgrp, bool freeze)
{
	struct css_task_iter it;
	struct task_struct *task;
	lockdep_assert_held(&cgroup_mutex);
	spin_lock_irq(&css_set_lock);
	if (freeze)
		set_bit(CGRP_FREEZE, &cgrp->flags);   //往设备节点写入值
	else
		clear_bit(CGRP_FREEZE, &cgrp->flags);
	spin_unlock_irq(&css_set_lock);
	if (freeze)
		TRACE_CGROUP_PATH(freeze, cgrp);   
	else
		TRACE_CGROUP_PATH(unfreeze, cgrp);
	css_task_iter_start(&cgrp->self, 0, &it);
	while ((task = css_task_iter_next(&it))) {
		/*
		 * Ignore kernel threads here. Freezing cgroups containing
		 * kthreads isn't supported.
		 */
		if (task->flags & PF_KTHREAD)  //忽略 kernel进程
			continue;
		cgroup_freeze_task(task, freeze);  //把状态加入task中
	}
	css_task_iter_end(&it);
	/*
	 * Cgroup state should be revisited here to cover empty leaf cgroups
	 * and cgroups which descendants are already in the desired state.
	 */
	spin_lock_irq(&css_set_lock);
	if (cgrp->nr_descendants == cgrp->freezer.nr_frozen_descendants)
		cgroup_update_frozen(cgrp);
	spin_unlock_irq(&css_set_lock);
} Freeze 项目中遇到问题:1)进程在unfreeze的时候被system 主动kill了 被kill的原因根据根据错误提示很容易找到,相关代码也比较好理解 
 此问题在那些非persisit的进程上遇到过几次,比如多媒体中心。引起的问题也都是因为注册过capservice的回调,在后台的情况下有回调过来。 解决方案:把服务变成前台服务,或者有UI界面退出后,取消注册可能被回调的接口。 2)STR后第一次打开应用会闪退,再次打开恢复 APP退出后台并进入了freezon在状态,是可以接受oneway的消息,但是会堆积在kernel的buffer中,但是在超过50%后,就会分配不出内存。 当再发起oneway消息后binder驱动就会直接返回 BR_DEAD_REPLY。不同的client会有不同的处理方式,systemservice的处理方式大部分均为kill相关进程。 主要代码如下 
 此问题并不会让进程直接crash,而是在后续发起其他binder通信的情况下才会出现,比如startactivty或者sendboardcast之类的消息。 解决方案:解决方案如问题1的方式,退出后需要自行unregiest相关注册,如果一定要监听某些状态,则可以启动一个前台服务来监听相关进程。此种用法也符合google的用法规则 Freezer问题调试命令1)修改进frezon的时间,改为1秒,方便测试frezon后的状态是否Ok 
  | 



















