作为Android程序员,经常会遇到如下的异常日志:
AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.qiku.activitylifecycletest/com.qiku.activitylifecycletest.MainActivity}: java.lang.SecurityException: Permission Denial: killBackgroundProcesses() from pid=4752, uid=10125 requires android.permission.KILL_BACKGROUND_PROCESSES
AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2808)
AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2886)
AndroidRuntime: at android.app.ActivityThread.-wrap11(Unknown Source:0)
AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1603)
AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106)
AndroidRuntime: at android.os.Looper.loop(Looper.java:164)
AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6538)
AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:453)
AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:833)
AndroidRuntime: Caused by: java.lang.SecurityException: Permission Denial: killBackgroundProcesses() from pid=4752, uid=10125 requires android.permission.KILL_BACKGROUND_PROCESSES
AndroidRuntime: at android.os.Parcel.readException(Parcel.java:2004)
AndroidRuntime: at android.os.Parcel.readException(Parcel.java:1950)
AndroidRuntime: at android.app.IActivityManager$Stub$Proxy.killBackgroundProcesses(IActivityManager.java:6426)
AndroidRuntime: at android.app.ActivityManager.killBackgroundProcesses(ActivityManager.java:3739)
AndroidRuntime: at com.qiku.activitylifecycletest.MainActivity.onCreate(MainActivity.java:36)
AndroidRuntime: at android.app.Activity.performCreate(Activity.java:7000)
AndroidRuntime: at android.app.Activity.performCreate(Activity.java:6991)
AndroidRuntime: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1215)
AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2761)
导致该异常的代码如下:
ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
am.killBackgroundProcesses("com.tencent.mm");
该异常会导致app crash。关键信息如下
Caused by: java.lang.SecurityException: Permission Denial:killBackgroundProcesses() from pid=4752, uid=10125 requires android.permission.KILL_BACKGROUND_PROCESSES
意思就是当前进程调用ActivityManager的killBackgroundProcesses
,而没有android.permission.KILL_BACKGROUND_PROCESSES
这个权限,所以导致crash。
我们想找到这个抛异常的地方,然后就在aosp的源码中寻找。因为调用的是ActivityManager
的方法,所以,凭借经验,我们应该知道,调用killBackgroundProcesses
这个方法,会通过binder调到ActivityManagerService
中,并且只有系统服务才能鉴定app是否有权限。所以,在ActivityManagerService
的killBackgroundProcesses
方法中,找到了抛异常的地方。
@Override
public void killBackgroundProcesses(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED &&
checkCallingPermission(android.Manifest.permission.RESTART_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: killBackgroundProcesses() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, true, ALLOW_FULL_ONLY, "killBackgroundProcesses", null);
那么问题来了:
抛异常的位置位于ActivityManagerService
中,是运行在system_server
进程中的,
为什么system_server
进程没有崩溃,反而导致app进程崩溃呢?
AndroidRuntime: Caused by: java.lang.SecurityException: Permission Denial: killBackgroundProcesses() from pid=4752, uid=10125 requires android.permission.KILL_BACKGROUND_PROCESSES
AndroidRuntime: at android.os.Parcel.readException(Parcel.java:2004)
AndroidRuntime: at android.os.Parcel.readException(Parcel.java:1950)
AndroidRuntime: at android.app.IActivityManager$Stub$Proxy.killBackgroundProcesses(IActivityManager.java:6426)
AndroidRuntime: at android.app.ActivityManager.killBackgroundProcesses(ActivityManager.java:3739)
AndroidRuntime: at com.qiku.activitylifecycletest.MainActivity.onCreate(MainActivity.java:36)
可以看到在Activity
的onCreate
中,调用了ActivityManager.killBackgroundProcesses
ActivityManager.killBackgroundProcesses
又调用了```killBackgroundProcesses`
根据Android中实现java层的binder调用的套路,IActivityManager是一个aidl文件,该文件经过aidl处理,生成IAcitivityManager.java
文件,IAcitivityManager.java
文件中存在一个IActivityManager
接口,定义了app可以调用ams的方法,IActivityManager
中有一个抽象类Stub
,该Stub
类实现IActivityManager
接口,并继承Binder
:
public interface IActivityManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements android.app.IActivityManager {
private static final java.lang.String DESCRIPTOR = "android.app.IActivityManager";
并且Stub
中又有一个叫做Proxy
的内部类,该Proxy
实现IActivityManager
private static class Proxy implements android.app.IActivityManager {
private android.os.IBinder mRemote;
Proxy用于app端使用,当app端调ActivityManager的方法的时候,就会调到IActivityManager$Stub$Proxy
我们看一下IActivityManager$Stub$Proxy
的killBackgroundProcesses
方法:
public void killBackgroundProcesses(java.lang.String packageName, int userId) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(packageName);
_data.writeInt(userId);
mRemote.transact(Stub.TRANSACTION_killBackgroundProcesses, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
mRemote
是一个IBinder
对象,专门用来调用远程进程的。这个方法中把所有的参数收集到一个名为_data
的Parcel
对象中,并且创建一个叫_reply
的空Parcel
对象,然后调用mRemote.transact
进行binder调用。调用完这个方法后,app中的当前线程就暂停了,然后ActivityManagerService
开始执行,等ActivityManagerService
执行完之后,app中的当前线程继续执行,这时候_reply
里面存放的就是ActivityManagerService
的执行结果。
mRemote.transact
之后,继续调用_reply.readException
,app就是从这里崩溃的:
AndroidRuntime: Caused by: java.lang.SecurityException: Permission Denial: killBackgroundProcesses() from pid=4752, uid=10125 requires android.permission.KILL_BACKGROUND_PROCESSES
AndroidRuntime: at android.os.Parcel.readException(Parcel.java:2004)
AndroidRuntime: at android.os.Parcel.readException(Parcel.java:1950)
我们看一下Parcel
的readException
实现:
public final void readException() {
int code = readExceptionCode();
if (code != 0) {
String msg = readString();
readException(code, msg);
}
}
public final int readExceptionCode() {
int code = readInt();
if (code == EX_HAS_REPLY_HEADER) {
int headerSize = readInt();
if (headerSize == 0) {
Log.e(TAG, "Unexpected zero-sized Parcel reply header.");
} else {
// Currently the only thing in the header is StrictMode stacks,
// but discussions around event/RPC tracing suggest we might
// put that here too. If so, switch on sub-header tags here.
// But for now, just parse out the StrictMode stuff.
StrictMode.readAndHandleBinderCallViolations(this);
}
// And fat response headers are currently only used when
// there are no exceptions, so return no error:
return 0;
}
return code;
}
public final void readException(int code, String msg) {
switch (code) {
case EX_PARCELABLE:
if (readInt() > 0) {
SneakyThrow.sneakyThrow(
(Exception) readParcelable(Parcelable.class.getClassLoader()));
} else {
throw new RuntimeException(msg + " [missing Parcelable]");
}
case EX_SECURITY:
throw new SecurityException(msg);
case EX_BAD_PARCELABLE:
throw new BadParcelableException(msg);
case EX_ILLEGAL_ARGUMENT:
throw new IllegalArgumentException(msg);
case EX_NULL_POINTER:
throw new NullPointerException(msg);
case EX_ILLEGAL_STATE:
throw new IllegalStateException(msg);
case EX_NETWORK_MAIN_THREAD:
throw new NetworkOnMainThreadException();
case EX_UNSUPPORTED_OPERATION:
throw new UnsupportedOperationException(msg);
case EX_SERVICE_SPECIFIC:
throw new ServiceSpecificException(readInt(), msg);
}
throw new RuntimeException("Unknown exception code: " + code
+ " msg " + msg);
}
可以看到,其实是从Parcel
中读了一个code,读了一个msg字符串,然后根据code和msg抛出了对应的异常。
所以我们可以认为,ActivityManagerService
在抛出异常后,把异常的信息(code和msg)放到了Parcel
对象中,传递到了app进程中,app进程读到ActivityManagerService
中传过来的异常信息后,又构建了对应的Exception
对象,然后throw这个Exception
对象,导致app进程崩溃。
所以总结一下,异常是可以通过binder来传递的,只是不是直接传递,而是传递异常类型(code)和异常相关的描述信息(msg)。binder调用端会解析这些信息创建和抛出异常。
下面我们看一下ActivityManagerService
端是怎样传递这个异常的。我们需要了解一些java服务端Binder的执行流程。
binder是Android中的进程间通信方式,它是有内核中的binder驱动支持的。客户端调用binder,会从java层一直通过jni调用到native层,然后调到kernel层。kernel层的binder驱动在接到binder请求后,会确定要把这个请求分派给哪个进程的哪个线程去执行。这个流程会从kernel层调到native层,然后native层通过jni反调到java层。具体过程这里不深究,感兴趣的请自己读下源码。
我们直接给出服务端native通过jni调java层的代码,这里是ActivityManagerService
在java层执行的起点,该代码实现在frameworks/base/core/jni/android_util_Binder.cpp
中:
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
{
JNIEnv* env = javavm_to_jnienv(mVM);
ALOGV("onTransact() on %p calling object %p in env %p vm %p\n", this, mObject, env, mVM);
IPCThreadState* thread_state = IPCThreadState::self();
const int32_t strict_policy_before = thread_state->getStrictModePolicy();
//printf("Transact from %p to Java code sending: ", this);
//data.print();
//printf("\n");
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);
首先看gBinderOffsets
const char* const kBinderPathName = "android/os/Binder";
static int int_register_android_os_Binder(JNIEnv* env)
{
jclass clazz = FindClassOrDie(env, kBinderPathName);
gBinderOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");
gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
return RegisterMethodsOrDie(
env, kBinderPathName,
gBinderMethods, NELEM(gBinderMethods));
}
这里gBinderOffsets.mClass
表示的是java层的android.os.Binder
类。
gBinderOffsets.mExecTransact
表示的是android.os.Binder
的execTransact
方法
gBinderOffsets.mObject
表示的是当前的Binder服务对象。在这个例子中,就是ActivityManagerService
实例,
因为ActivityManagerService
继承自IActivityManager.Stub
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
而IActivityManager.Stub
又继承自android.os.Binder
。所以这里mObject
就是ActivityManagerService
的实例。
所以
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);
里面的CallBooleanMethod
就是通过jni调用java层的ActivityManagerService
的execTransact
方法。
因为ActivityManagerService
中没有定义这个方法,所以会调到父类android.os.Binder
的execTransact
方法:
// Entry point from android_util_Binder.cpp's onTransact
private boolean execTransact(int code, long dataObj, long replyObj,
int flags) {
Parcel data = Parcel.obtain(dataObj);
Parcel reply = Parcel.obtain(replyObj);
// theoretically, we should call transact, which will call onTransact,
// but all that does is rewind it, and we just got these from an IPC,
// so we'll just call it directly.
boolean res;
// Log any exceptions as warnings, don't silently suppress them.
// If the call was FLAG_ONEWAY then these exceptions disappear into the ether.
final boolean tracingEnabled = Binder.isTracingEnabled();
try {
if (tracingEnabled) {
Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":" + code);
}
res = onTransact(code, data, reply, flags);
} catch (RemoteException|RuntimeException e) {
if (LOG_RUNTIME_EXCEPTION) {
Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
}
if ((flags & FLAG_ONEWAY) != 0) {
if (e instanceof RemoteException) {
Log.w(TAG, "Binder call failed.", e);
} else {
Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
}
} else {
reply.setDataPosition(0);
reply.writeException(e);
}
res = true;
} finally {
if (tracingEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
}
}
checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
reply.recycle();
data.recycle();
// Just in case -- we are done with the IPC, so there should be no more strict
// mode violations that have gathered for this thread. Either they have been
// parceled and are now in transport off to the caller, or we are returning back
// to the main transaction loop to wait for another incoming transaction. Either
// way, strict mode begone!
StrictMode.clearGatheredViolations();
return res;
}
该方法中的data,就是app在调binder的时候传来的参数,reply就是要返回给app的数据。
首先调用的是onTransact
方法,onTransact
方法会调到子类IActivityManager.Stub
中:
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_openContentUri: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
......
......
该方法会通过传入的code,定位到killBackgroundProcesses
方法:
case TRANSACTION_killBackgroundProcesses: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
int _arg1;
_arg1 = data.readInt();
this.killBackgroundProcesses(_arg0, _arg1);
reply.writeNoException();
return true;
}
这里的this是ActivityManagerService
对象,所以会调用到ActivityManagerService
的killBackgroundProcesses
方法。
该方法就是抛出异常的地方:
@Override
public void killBackgroundProcesses(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED &&
checkCallingPermission(android.Manifest.permission.RESTART_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: killBackgroundProcesses() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, true, ALLOW_FULL_ONLY, "killBackgroundProcesses", null);
long callingId = Binder.clearCallingIdentity();
那么这个异常是怎样处理的呢?我们会到android.os.Binder
的execTransact
方法,可以看到,它在调onTransact
的时候,使用了try-catch
:
try {
if (tracingEnabled) {
Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":" + code);
}
res = onTransact(code, data, reply, flags);
} catch (RemoteException|RuntimeException e) {
if (LOG_RUNTIME_EXCEPTION) {
Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
}
if ((flags & FLAG_ONEWAY) != 0) {
if (e instanceof RemoteException) {
Log.w(TAG, "Binder call failed.", e);
} else {
Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
}
} else {
reply.setDataPosition(0);
reply.writeException(e);
}
res = true;
} finally {
在ActivityManagerService
的killBackgroundProcesses
方法中抛出的SecurityException
,正好被这里的catch捕获,所以并不会导致ActivityManagerService
崩溃,然后又把这个异常通过reply.writeException
写入到reply
中:
public final void writeException(Exception e) {
int code = 0;
if (e instanceof Parcelable
&& (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) {
// We only send Parcelable exceptions that are in the
// BootClassLoader to ensure that the receiver can unpack them
code = EX_PARCELABLE;
} else if (e instanceof SecurityException) {
code = EX_SECURITY;
} else if (e instanceof BadParcelableException) {
code = EX_BAD_PARCELABLE;
} else if (e instanceof IllegalArgumentException) {
code = EX_ILLEGAL_ARGUMENT;
} else if (e instanceof NullPointerException) {
code = EX_NULL_POINTER;
} else if (e instanceof IllegalStateException) {
code = EX_ILLEGAL_STATE;
} else if (e instanceof NetworkOnMainThreadException) {
code = EX_NETWORK_MAIN_THREAD;
} else if (e instanceof UnsupportedOperationException) {
code = EX_UNSUPPORTED_OPERATION;
} else if (e instanceof ServiceSpecificException) {
code = EX_SERVICE_SPECIFIC;
}
writeInt(code);
StrictMode.clearGatheredViolations();
if (code == 0) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new RuntimeException(e);
}
writeString(e.getMessage());
switch (code) {
case EX_SERVICE_SPECIFIC:
writeInt(((ServiceSpecificException) e).errorCode);
break;
case EX_PARCELABLE:
// Write parceled exception prefixed by length
final int sizePosition = dataPosition();
writeInt(0);
writeParcelable((Parcelable) e, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
final int payloadPosition = dataPosition();
setDataPosition(sizePosition);
writeInt(payloadPosition - sizePosition);
setDataPosition(payloadPosition);
break;
}
}
可以看到,往reply
中写异常的时候,写入的是一个code和异常的msg字符串。
这个reply
会通过binder返回给app端,所以导致了app端崩溃。
1 Android中Java层的Binder机制,是支持传递异常的。从Parcel
中有readException
和writeException
方法,就可以看出来。
2 传递异常的方式是将异常的code和msg放到Parcel
中进行传递
3 调用端会检查返回的Parcel
中有没有异常code和msg,如果有的话,就读出这些信息,创建并抛出异常
4 虽然本文分析Binder传递异常的流程,但还是会涉及到很多关于Binder的其他知识,这些知识点没有深入分析,感兴趣的同学自行分析源码。