在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.aidl
frameworks/base/core/java/android/app/ActivityManager.java
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
frameworks/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.Stub
implements 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 start
private List<String> mWhiteAppProcessList;
@Override
public List<String> getWhiteAppProcessList() {
return mWhiteAppProcessList;
}
@Override
public 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 out
continue;
}
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 it
final 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() {
@Override
public 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 instance
allowRestart = 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 mPersistentStartingProcesses
if (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.Stub
if (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中关于任务栈中杀进程的相关功能分析
@Override
public 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 it
removeTaskById(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 start
ComponentName 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 end
removeTask(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 bootup
if (mCachedAppOptimizer.useCompaction() && mService.mBooted) {
// Cached and prev/home compaction
if (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 state
if (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不会被杀掉.