一.概述
1.1 引言
话说Android开源系统拥有着App不计其数,百家争鸣,都想在这“大争之世”寻得系统存活的一席之地。然则系统资源有限,如若都割据为王,再强劲的CPU也会忙不过来,再庞大的内存终会消耗殆尽,再大容量的电池续航终会昙花一现。
面对芸芸众生,无尽变数,系统以不变应万变,一招绝杀神技forceStop腾空出世,此处以adb指令的方式为例来说说其内部机理:am force-stop pkgName
am force-stop --user 2 pkgName //只杀用户userId=2的相关信息
force-stop命令杀掉所有用户空间下的包名pkgName相关的信息,也可以通过--user来指定用户Id。 当执行上述am指令时,则会触发调用Am.java的main()方法,接下来从main方法开始说起。
1.2 Am.main
[-> Am.java]public static void main(String[] args) {
(new Am()).run(args); //【见小节1.3】
}
1.3 Am.run
[-> Am.java]public void run(String[] args) {
...
mArgs = args;
mNextArg = 0;
mCurArgData = null;
onRun(); //【见小节1.4】
...
}
1.4 Am.onRun
[-> Am.java]public void onRun() throws Exception {
//获取的是Binder proxy对象AMP
mAm = ActivityManagerNative.getDefault();
String op = nextArgRequired();
if (op.equals("start")) {
...
} else if (op.equals("force-stop")) {
runForceStop(); //【见小节1.5】
}
...
}
1.5 Am.runForceStop
[-> Am.java]private void runForceStop() throws Exception {
int userId = UserHandle.USER_ALL;
String opt;
// 当指定用户时,则解析相应userId
while ((opt=nextOption()) != null) {
if (opt.equals("--user")) {
userId = parseUserArg(nextArgRequired());
}
}
//【见小节1.6】
mAm.forceStopPackage(nextArgRequired(), userId);
}
当不指定userId时,则默认为UserHandle.USER_ALL。
1.6 AMP.forceStopPackage
[-> ActivityManagerNative.java ::AMP]public void forceStopPackage(String packageName, int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeString(packageName);
data.writeInt(userId);
//【见小节1.7】
mRemote.transact(FORCE_STOP_PACKAGE_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
1.7 AMN.onTransact
[-> ActivityManagerNative.java]public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
case FORCE_STOP_PACKAGE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String packageName = data.readString();
int userId = data.readInt();
//【见小节2.1】
forceStopPackage(packageName, userId);
reply.writeNoException();
return true;
}
...
}
}
AMP.forceStopPackage来运行在执行adb时所创建的进程,经过Binder Driver后,进入system_server进程的一个binder线程来执行AMN.forceStopPackage,从这开始的操作(包括当前操作)便都运行在system_server系统进程。
1.8 小节
进程绝杀技force-stop,并非任意app可直接调用, 否则App间可以相互停止对方,则岂非天下大乱。该方法的存在便是供系统差遣。一般地,点击home弹出的清理用户最近使用app采取的策略便是force-stop.
至于force-stop的触发方式,除了adb的方式,还可通过获取ActivityManager再调用其方法forceStopPackage(),不过这是@hide隐藏方法,同样是需要具有FORCE_STOP_PACKAGES权限。虽然第三方普通app不能直接调用,但对于深入理解Android,还是很有必要知道系统是如何彻底清理进程的过程。接下来,进入AMS来深入探查force-stop的内部机理。
二. force-stop内部机理
2.1 AMS.forceStopPackage
[-> ActivityManagerService.java]public void forceStopPackage(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
//需要权限permission.FORCE_STOP_PACKAGES
throw new SecurityException();
}
final int callingPid = Binder.getCallingPid();
userId = handleIncomingUser(callingPid, Binder.getCallingUid(),
userId, true, ALLOW_FULL_ONLY, "forceStopPackage", null);
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
synchronized(this) {
int[] users = userId == UserHandle.USER_ALL
? getUsersLocked() : new int[] { userId };
for (int user : users) {
int pkgUid = -1;
//根据包名和userId来查询相应的uid
pkgUid = pm.getPackageUid(packageName, user);
//设置包的状态为stopped
pm.setPackageStoppedState(packageName, true, user);
if (isUserRunningLocked(user, false)) {
//【见流程2.2】
forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
}
}
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
这里有一个过程非常重要,那就是setPackageStoppedState()将包的状态设置为stopped,那么所有广播都无法接收,除非带有标记FLAG_INCLUDE_STOPPED_PACKAGES的广播,系统默认的广播几乎都是不带有该标志,也就意味着被force-stop的应用是无法通过建立手机网络状态或者亮灭的广播来拉起进程。
当使用force stop方式来结束进程时, reason一般都是"from pid " + callingPid. 当然也有另外,那就是AMS.clearApplicationUserData方法调用forceStopPackageLocked的reason为"clear data".
2.2 AMS.forceStopPackageLockedprivate void forceStopPackageLocked(final String packageName, int uid, String reason) {
//[见流程2.3]
forceStopPackageLocked(packageName, UserHandle.getAppId(uid), false,
false, true, false, false, UserHandle.getUserId(uid), reason);
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
Uri.fromParts("package", packageName, null));
//系统启动完毕后,则mProcessesReady=