Android 12.0进程保活白名单功能实现

ops/2024/10/24 4:26:35/

        在Android 12.0系统中,实现进程保活白名单功能是为了确保某些重要的应用程序即使进入后台也能长时间保持运行状态,不被系统自动杀死。这一功能的实现涉及多个核心类和文件,以下是具体的实现步骤和核心功能分析:  

一、实现步骤 

1.1 在IActivityManager.aidl中增加接口:

        需要在IActivityManager.aidl文件中增加与进程白名单相关的接口,例如void addWhiteListApp(String packageName)用于添加应用到白名单,void removeWhiteListApp(String packageName)用于从白名单中移除应用。

1.2 在ActivityManager.java中提供接口给应用调用:

        在ActivityManager.java文件中,需要实现上述接口,使得应用可以通过ActivityManager对象调用这些接口来操作进程白名单。

1.3 在ActivityManagerService.java中实现接口逻辑:

        在ActivityManagerService.java文件中,需要实现接口的具体逻辑,包括将应用添加到白名单或从白名单中移除,以及确保在系统杀进程时不杀死白名单中的进程。

1.4 在ActivityStackSupervisor和OomAdjuster.java中处理白名单进程:

        在ActivityStackSupervisor.java和OomAdjuster.java文件中,需要添加对白名单进程的处理逻辑。例如,在OomAdjuster.java中,可以修改进程的OOM(Out Of Memory)调整策略,以确保白名单中的进程在内存不足时不会被优先杀死。

二、涉及文件类路径

      frameworks/base/core/java/android/app/IActivityManager.aidlframeworks/base/core/java/android/app/ActivityManager.javaframeworks/base/services/core/java/com/android/server/am/ActivityManagerService.javaframeworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.javaframeworks/base/services/core/java/com/android/server/am/OomAdjuster.java

三、具体实现

  3.1 app进程保活白名单功能实现的核心功能实现和分析

   3.1.1在lActivityManager.aidl@中新增进程白名单的接口

       void killUidForPermissionChange(int appId, int userId, String reason);boolean enableAppFreezer(in boolean enable);+    List<String> getWhiteAppProcessList();+    void setWhiteAppProcessList(in List<String> whiteAppProcessList);

 3.1.2在ActivityManager.java中增加白名单接口给app调用

       public List<String> getWhiteAppProcessList() {try {return getService().getWhiteAppProcessList();} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}public void setWhiteAppProcessList(List<String> whiteAppProcessList) {try {getService().setWhiteAppList(whiteAppProcessList);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

在ActivityManagerjava中增加这两个关于app进程白名单的接口,通过调用ActivityManagerService.java的白名单接口来设置app进程白名单实现保活app进程。

3.2 ActivityManagerService.java中实现lActivityManager.aidl的白名单接口

  1.实现保活白名单接口public class ActivityManagerService extends IActivityManager.Stubimplements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {private List<String> mWhiteAppProcessList = new ArrayList<String>();public void setWindowManager(WindowManagerService wm) {synchronized (this) {mWindowManager = wm;mWmInternal = LocalServices.getService(WindowManagerInternal.class);mActivityTaskManager.setWindowManager(wm);}}public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {mUsageStatsService = usageStatsManager;mActivityTaskManager.setUsageStatsManager(usageStatsManager);}//add core startprivate List<String> mWhiteAppProcessList;@Overridepublic List<String> getWhiteAppProcessList() {return mWhiteAppProcessList;}@Overridepublic void setWhiteAppProcessList(List<String> whiteAppProcessList) {this.mWhiteAppProcessList = whiteAppProcessList;}//add core end

从AcivityManagerService中可以看出继承了IActivityManager.Stub所以说就是IActivityManager的子类所以需要实现IAcivityManager定义的保活白名单接口接下来看下关于杀进程的相关方法.

        /*** Main function for removing an existing process from the activity manager* as a result of that process going away.  Clears out all connections* to the process.*/@GuardedBy("this")final void handleAppDiedLocked(ProcessRecord app,boolean restarting, boolean allowRestart) {int pid = app.pid;boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,false /*replacingPid*/);if (!kept && !restarting) {removeLruProcessLocked(app);if (pid > 0) {ProcessList.remove(pid);}}if (mProfileData.getProfileProc() == app) {clearProfilerLocked();}mAtmInternal.handleAppDied(app.getWindowProcessController(), restarting, () -> {Slog.w(TAG, "Crash of app " + app.processName+ " running instrumentation " + app.getActiveInstrumentation().mClass);Bundle info = new Bundle();info.putString("shortMsg", "Process crashed.");finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);});}

从handleAppDiedLocked的注释中可以看出在ActivityManagerService.java中主要是调用handleAppDiedLocked处理杀掉进程的功能的,而在cleanUpApplicationRecordLocked中主要处理杀进程的工作接下来看cleanUpApplicationRecordLocked相关进程处理的方法。

        @GuardedBy("this")final boolean cleanUpApplicationRecordLocked(ProcessRecord app,boolean restarting, boolean allowRestart, int index, boolean replacingPid) {if (index >= 0) {removeLruProcessLocked(app);ProcessList.remove(app.pid);}mProcessesToGc.remove(app);mPendingPssProcesses.remove(app);ProcessList.abortNextPssTime(app.procStateMemTracker);// Dismiss any open dialogs.app.getDialogController().clearAllErrorDialogs();app.setCrashing(false);app.setNotResponding(false);app.resetPackageList(mProcessStats);app.unlinkDeathRecipient();app.makeInactive(mProcessStats);app.waitingToKill = null;app.forcingToImportant = null;updateProcessForegroundLocked(app, false, 0, false);app.setHasForegroundActivities(false);app.hasShownUi = false;app.treatLikeActivity = false;app.hasAboveClient = false;app.setHasClientActivities(false);mServices.killServicesLocked(app, allowRestart);boolean restart = false;// Remove published content providers.for (int i = app.pubProviders.size() - 1; i >= 0; i--) {ContentProviderRecord cpr = app.pubProviders.valueAt(i);if (cpr.proc != app) {// If the hosting process record isn't really us, bail outcontinue;}final boolean alwaysRemove = app.bad || !allowRestart;final boolean inLaunching = removeDyingProviderLocked(app, cpr, alwaysRemove);if (!alwaysRemove && inLaunching && cpr.hasConnectionOrHandle()) {// We left the provider in the launching list, need to// restart it.restart = true;}cpr.provider = null;cpr.setProcess(null);}app.pubProviders.clear();// Take care of any launching providers waiting for this process.if (cleanupAppInLaunchingProvidersLocked(app, false)) {mProcessList.noteProcessDiedLocked(app);restart = true;}// Unregister from connected content providers.if (!app.conProviders.isEmpty()) {for (int i = app.conProviders.size() - 1; i >= 0; i--) {ContentProviderConnection conn = app.conProviders.get(i);conn.provider.connections.remove(conn);stopAssociationLocked(app.uid, app.processName, conn.provider.uid,conn.provider.appInfo.longVersionCode, conn.provider.name,conn.provider.info.processName);}app.conProviders.clear();}// At this point there may be remaining entries in mLaunchingProviders// where we were the only one waiting, so they are no longer of use.// Look for these and clean up if found.// XXX Commented out for now.  Trying to figure out a way to reproduce// the actual situation to identify what is actually going on.if (false) {for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {ContentProviderRecord cpr = mLaunchingProviders.get(i);if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) {synchronized (cpr) {cpr.launchingApp = null;cpr.notifyAll();}}}}skipCurrentReceiverLocked(app);// Unregister any receivers.for (int i = app.receivers.size() - 1; i >= 0; i--) {removeReceiverLocked(app.receivers.valueAt(i));}app.receivers.clear();// If the app is undergoing backup, tell the backup manager about itfinal BackupRecord backupTarget = mBackupTargets.get(app.userId);if (backupTarget != null && app.pid == backupTarget.app.pid) {if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "+ backupTarget.appInfo + " died during backup");mHandler.post(new Runnable() {@Overridepublic void run(){try {IBackupManager bm = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));bm.agentDisconnectedForUser(app.userId, app.info.packageName);} catch (RemoteException e) {// can't happen; backup manager is local}}});}for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) {ProcessChangeItem item = mPendingProcessChanges.get(i);if (app.pid > 0 && item.pid == app.pid) {mPendingProcessChanges.remove(i);mAvailProcessChanges.add(item);}}mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED_UI_MSG, app.pid, app.info.uid,null).sendToTarget();// If this is a precede instance of another process instanceallowRestart = true;synchronized (app) {if (app.mSuccessor != null) {// We don't allow restart with this ProcessRecord now,// because we have created a new one already.allowRestart = false;// If it's persistent, add the successor to mPersistentStartingProcessesif (app.isPersistent() && !app.removed) {if (mPersistentStartingProcesses.indexOf(app.mSuccessor) < 0) {mPersistentStartingProcesses.add(app.mSuccessor);}}// clean up the field so the successor's proc starter could proceed.app.mSuccessor.mPrecedence = null;app.mSuccessor = null;// Notify if anyone is waiting for it.app.notifyAll();}}// If the caller is restarting this app, then leave it in its// current lists and let the caller take care of it.if (restarting) {return false;}if (!app.isPersistent() || app.isolated) {if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,"Removing non-persistent process during cleanup: " + app);if (!replacingPid) {mProcessList.removeProcessNameLocked(app.processName, app.uid, app);}mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());} else if (!app.removed) {// This app is persistent, so we need to keep its record around.// If it is not already on the pending app list, add it there// and start a new process for it.if (mPersistentStartingProcesses.indexOf(app) < 0) {mPersistentStartingProcesses.add(app);restart = true;}}.....return false;}

在cleanUpApplicationRecordLocked中的进行进程列表遍历的时候,判断app是否清除的时候根据if (!app.isPersistent() app.isolated)
调用 mAtmlntemnal.clearHeavyWeightProcesslfEquals(app.getWindowProcesscontroler();来杀掉进程所以可以在这里增加判断添加
看是否杀掉进程
具体修改如下:

    --- a/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java+++ b/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java@@ -14079,8 +14079,13 @@ public class ActivityManagerService extends IActivityManager.Stubif (restarting) {return false;}--        if (!app.isPersistent() || app.isolated) {+                List<String> lists=    this.mWhiteAppProcessList;+                boolean iskeepAlive=false;+                if(lists!=null && lists.contains(app.processName)){+                        iskeepAlive=true;+                }+        if ((!app.isPersistent() || app.isolated) && !iskeepAlive) {if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,"Removing non-persistent process during cleanup: " + app);if (!replacingPid) {@@ -14097,6 +14102,7 @@ public class ActivityManagerService extends IActivityManager.Stub// This app is persistent, so we need to keep its record around.// If it is not already on the pending app list, add it there// and start a new process for it.if (mPersistentStartingProcesses.indexOf(app) < 0) {mPersistentStartingProcesses.add(app);restart = true;

3.3 ActivityStackSupervisor.java中关于任务栈中杀进程的相关功能分析

             @Overridepublic void onRecentTaskRemoved(Task task, boolean wasTrimmed, boolean killProcess) {if (wasTrimmed) {// Task was trimmed from the recent tasks list -- remove the active task record as well// since the user won't really be able to go back to itremoveTaskById(task.mTaskId, killProcess, false /* removeFromRecents */,"recent-task-trimmed");}task.removedFromRecents();}boolean removeTaskById(int taskId, boolean killProcess, boolean removeFromRecents,String reason) {final Task task =mRootWindowContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);if (task != null) {removeTask(task, killProcess, removeFromRecents, reason);return true;}Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);return false;}void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason) {if (task.mInRemoveTask) {// Prevent recursion.return;}task.mInRemoveTask = true;try {task.performClearTask(reason);cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents);mService.getLockTaskController().clearLockedTask(task);mService.getTaskChangeNotificationController().notifyTaskStackChanged();if (task.isPersistable) {mService.notifyTaskPersisterLocked(null, true);}} finally {task.mInRemoveTask = false;}}

在AcivitvStackSupervisor.java中,关于移除栈内的app进程,主要是在onRecentTaskRemoved中通过调用removeTaskByld实现的,而removeTaskByld又是调用removeTask实现的,具体是在removeTask中处理的杀进程所以具体实现如下:

          boolean removeTaskById(int taskId, boolean killProcess, boolean removeFromRecents,String reason) {final Task task =mRootWindowContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);if (task != null) {//add code startComponentName component = tr.getBaseIntent().getComponent();if(component!=null){String pkg=component.getPackageName();ActivityManager am = (ActivityManager) mService.mContext.getSystemService(Context.ACTIVITY_SERVICE);List<String> list=am.getWhiteAppProcessList();if(list!=null && list.contains(pkg)){return false;}}//add code endremoveTask(task, killProcess, removeFromRecents, reason);return true;}Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);return false;}

3.4OomAdjuster.java中关于对保活白名单的分析

   +    private boolean isInWhitelist(ProcessRecord pr) {+        String pkgName = pr.info.packageName;+               List<String> mLmKillerBypassPackages = mService.getKeepAliveList();+               if(mLmKillerBypassPackages!=null && mLmKillerBypassPackages.size()>0){+                       for (String token : mLmKillerBypassPackages) {+                               if (pkgName.startsWith(token)) {+                                       return true;+                               }+                       }+               }+        return false;+    }/** Applies the computed oomadj, procstate and sched group values and freezes them in set* */@GuardedBy("mService")private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,long nowElapsed) {boolean success = true;if (app.getCurRawAdj() != app.setRawAdj) {app.setRawAdj = app.getCurRawAdj();}int changes = 0;// don't compact during bootupif (mCachedAppOptimizer.useCompaction() && mService.mBooted) {// Cached and prev/home compactionif (app.curAdj != app.setAdj) {// Perform a minor compaction when a perceptible app becomes the prev/home app// Perform a major compaction when any app enters cached// reminder: here, setAdj is previous state, curAdj is upcoming stateif (app.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ &&(app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||app.curAdj == ProcessList.HOME_APP_ADJ)) {mCachedAppOptimizer.compactAppSome(app);} else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ|| app.setAdj > ProcessList.CACHED_APP_MAX_ADJ)&& app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ&& app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {mCachedAppOptimizer.compactAppFull(app);}} else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE&& app.setAdj < ProcessList.FOREGROUND_APP_ADJ// Because these can fire independent of oom_adj/procstate changes, we need// to throttle the actual dispatch of these requests in addition to the// processing of the requests. As a result, there is throttling both here// and in CachedAppOptimizer.&& mCachedAppOptimizer.shouldCompactPersistent(app, now)) {mCachedAppOptimizer.compactAppPersistent(app);} else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE&& app.getCurProcState()== ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE&& mCachedAppOptimizer.shouldCompactBFGS(app, now)) {mCachedAppOptimizer.compactAppBfgs(app);}}if (app.curAdj != app.setAdj) {// 主要修改部分开始// 注释代码开始/*ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {String msg = "Set " + app.pid + " " + app.processName + " adj "+ app.curAdj + ": " + app.adjType;reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);}app.setAdj = app.curAdj;app.verifiedAdj = ProcessList.INVALID_ADJ;*/// 注释代码结束+            boolean isAppWhiteProcess = false;+            if(isInWhitelist(app) && (app.curAdj > ProcessList.PERSISTENT_SERVICE_ADJ))isAppWhiteProcess = true;+            if(isAppWhiteProcess){+                Slog.d(TAG,"isAppWhiteProcess");+                ProcessList.setOomAdj(app.pid, app.uid, ProcessList.PERSISTENT_SERVICE_ADJ);+                if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {+                    String msg = "Set " + app.pid + " " + app.processName + " adj "+                            + app.curAdj + ": " + app.adjType;+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);+                }+                app.setAdj = ProcessList.PERSISTENT_SERVICE_ADJ;+                app.verifiedAdj = ProcessList.INVALID_ADJ;+            }else{+                ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);+                if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {+                    String msg = "Set " + app.pid + " " + app.processName + " adj "+                            + app.curAdj + ": " + app.adjType;+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);+                }+                app.setAdj = app.curAdj;+                app.verifiedAdj = ProcessList.INVALID_ADJ;+            }}.....return success;}

在上述的OomAdjuster.java的代码中,首选通过isInWhitelist(ProcessRecord pr)判断当前进程是否在白名单之内,然后通过设置app.setAdj和app.verifiedAdj 两个值来确保app不会被杀掉.


http://www.ppmy.cn/ops/128004.html

相关文章

【python Arrow库】一个处理日期和时间的Python库

Arrow库 引言&#xff1a;箭&#xff0c;不仅仅是武器1、安装&#xff1a;搭弓上箭2、基础&#xff1a;箭头的构造3、实战&#xff1a;箭无虚发3.1 案例一&#xff1a;时间比较3.2 案例二&#xff1a;时间格式化3.3 案例三&#xff1a;时区转换 4、结语&#xff1a;箭已离弦 引…

LeetCode15 三数之和 - “贪心+双指针: 基于”两数之和“的拓展题“

Leetcode 15&#xff1a; 三数之和 题目链接 发布在LeetCode上的题解 思路 这道题的思路建立在 167.两数之和 的基础上。先来看看”两数之和“的大概题意&#xff1a; 已知一个非递减的数组&#xff0c;找出满足相加之和等于目标数 target 的两个数&#xff0c;假设每个输…

【linux】网络基础

1. 网络发展 独立模式->网络互联->局域网LAN->广域网WAN 独立模式: 计算机之间相互独立网络互联: 多台计算机连接在一起, 完成数据共享局域网LAN: 计算机数量更多了, 通过交换机和路由器连接在一起广域网WAN: 将远隔千里的计算机都连在一起 2. 认识协议 "协议…

【MySQL 保姆级教学】表结构的操作(4)

表结构的操作 1. 定义和语法2. 创建表 CREATE2.1 创建表的本质2.2 表的存储引擎2.3 表的字符集和校验规则2.4 创建表实例 3. 查看表结构 DESC3.1 作用3.2 示例 4. 修改表结构 ALTER4.1 添加列 ADD4.2 修改列 MODIFY4.3 删除列 DROP4.4 更改列名 CHANGE 5. 修改表名 RENAME6. 删…

CRMEB标准版Mysql修改sql_mode

数据库配置 1.宝塔控制面板-软件商店-MySql-设置 2.点击配置修改&#xff0c;查找sql-mode或sql_mode &#xff08;可使用CtrlF快捷查找&#xff09; 3.复制 NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 然后替换粘贴&#xff0c;保存 注&#xff1a;MySQL8.0版本的 第三步用…

记录一个容易混淆的 Spring Boot 项目配置文件问题

记录一个容易混淆的 Spring Boot 项目配置文件问题 去年&#xff0c;我遇到了这样一个问题&#xff1a; 在这个例子中&#xff0c;由于密码 password 以 0 开头&#xff0c;当它被 Spring Boot 的 bean 读取时&#xff0c;前导的 0 被自动去掉了。这导致程序无法正确读取密码。…

【Flutter】基础入门:自定义Widget

在 Flutter 开发中&#xff0c;除了使用丰富的内置 Widgets 构建界面外&#xff0c;自定义 Widget 是让你的应用更灵活和个性化的重要手段。Flutter 允许你根据需求自定义 StatelessWidget 和 StatefulWidget&#xff0c;以实现复杂的 UI 组件或功能模块。 本教程将通过实例讲…

Git Push(TODO)

最近经常碰到GIT push不上去的问题。到处求人解决也真是尴尬&#xff0c;想自己看看&#xff0c;所以刚刚在github上建了一个仓&#xff0c;试了下。结果如下&#xff1a; 暂时可能还不行&#xff0c;因为数据都是加密的&#xff0c;没法看到具体GIT的交互信息。。。 后面再想办…