基于Android 12的force-stop流程分析
Force-stop可以强制结束一个package进程及其相关的信息。可以通过adb命令和设置中的入口来使用。如果想结束进程,主要的adb命令有以下几种:
(1)adb shell kill -9 <进程号>
这种方式只会结束一个进程
(2)adb shell am force-stop <包名>
这种方式会结束package进程及其相关的进程,也就是说可能不止结束一个进程。
(3)adb shell pm clear <包名>
这种方法不仅会停止APP进程,而且会清除这个APP进程产生的所有数据。
这里,只分析force-stop的流程
从adb shell am命令来开始分析,force-stop是一个am的命令,所以可以从ActivityManagerShellCommand.java来开始分析,这个文件里定义了各种am命令会执行的原生方法,我们可以通过这个类来很快的判断am命令最终调用的方法。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
case "force-stop":
return runForceStop(pw);
int runForceStop(PrintWriter pw) throws RemoteException {
int userId = UserHandle.USER_ALL;//当不指定userId时,则默认为UserHandle.USER_ALL
String opt;
while ((opt = getNextOption()) != null) {
if (opt.equals("--user")) {
//是否需要指定userId
userId = UserHandle.parseUserArg(getNextArgRequired());
} else {
getErrPrintWriter().println("Error: Unknown option: " + opt);
return -1;
}
}
mInterface.forceStopPackage(getNextArgRequired(), userId);//binder访问AMS中的方法
return 0;
}
adb shell force-stop命令有两种使用方法:
(1)adb shell force-stop <包名>
(2)adb shell force-stop --user <包名> :停止某个userId下某个包名的进程信息,指定userId
无论哪种方法,都是通过binder调用了AMS的forceStopPackage()方法
AMS
@Override
public void forceStopPackage(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
//force-stop需要FORCE_STOP_PACKAGES权限
String msg = "Permission Denial: forceStopPackage() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES;
Slog.w(TAG, msg);
throw new SecurityException(msg);//缺少权限的情况下需要抛出异常
}
final int callingPid = Binder.getCallingPid();
userId = mUserController.handleIncomingUser(callingPid, Binder.getCallingUid(),
userId, true, ALLOW_FULL_ONLY, "forceStopPackage", null);
final long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
synchronized(this) {
int[] users = userId == UserHandle.USER_ALL
? mUserController.getUsers() : new int[] {
userId };
for (int user : users) {
//遍历所有用户,可能涉及到多用户
if (getPackageManagerInternal().isPackageStateProtected(
packageName, user)) {
//如果package的状态是受保护的,那么不能被force-stop
Slog.w(TAG, "Ignoring request to force stop protected package "
+ packageName + " u" + user);
return;
}
int pkgUid = -1;
try {
pkgUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING,
user);
} catch (RemoteException e) {
}
if (pkgUid == -1) {
Slog.w(TAG, "Invalid packageName: " + packageName);
continue;
}
try {
pm.setPackageStoppedState(packageName, true, user);//将包的状态设置为stopped
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ packageName + ": " + e);
}
if (mUserController.isUserRunning(user, 0)) {
forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);//主要实现方法
finishForceStopPackageLocked(packageName, pkgUid);//结束force-stop之后的广播发送
}
}
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
setPackageStoppedState()方法会将包的状态设置为stopped(被force-stop或者没有被启动过的三方应用都是stopped状态),这个状态下所有广播都无法被接收,除非带有标记FLAG_INCLUDE_STOPPED_PACKAGES的广播。系统默认的广播几乎都是不带有该标志,也就意味着一般情况下被force-stop的应用是无法通过广播来拉起进程的,但是我们可以通过给广播添加FLAG_INCLUDE_STOPPED_PACKAGES标识来让stopped状态下的应用接收到此广播。
finishForceStopPackageLocked()方法,主要是发送ACTION_PACKAGE_RESTARTED广播,这个广播的作用是用户重新开始一个包,包的所有进程将被杀死,所有与其联系的运行时间状态应该被移除,包括包名(重新开始包程序不能接收到这个广播)
主要来看下forceStopPackageLocked()方法
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@GuardedBy("this")
private void forceStopPackageLocked(final String packageName, int uid, String reason) {
forceStopPackageLocked(packageName, UserHandle.getAppId(uid), false,
false, true, false, false, UserHandle.getUserId(uid), reason);
}
@GuardedBy("this")
f