小米8 利用IBinder transact获取服务的接口名字,结果出现以下异常:
W/System.err: java.lang.SecurityException
W/System.err: at android.os.BinderProxy.transactNative(Native Method)
W/System.err: at android.os.BinderProxy.transact(BinderProxy.java:482)
W/System.err: at com.gamesec.essential.Essential.getInterfaceName(Essential.java:182)
W/System.err: at com.gamesec.essential.Essential.getRunningServiceInfo(Essential.java:204)
W/System.err: at com.gamesec.essential.Essential.getEssential(Essential.java:134)
W/System.err: at com.gamesec.DataCollector.collectDeviceInfo(DataCollector.java:373)
W/System.err: at com.gamesec.DataCollectorThread.run(DataCollectorThread.java:12)
手机系统为android9.0,所以查看源码 /.core/jni/android_util_Binder.cpp文件
android_os_BinderProxy_transact
[ android_util_Binder.cpp]
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
if (dataObj == NULL) {
jniThrowNullPointerException(env, NULL);
return JNI_FALSE;
}
Parcel* data = parcelForJavaObject(env, dataObj);
if (data == NULL) {
return JNI_FALSE;
}
Parcel* reply = parcelForJavaObject(env, replyObj);
if (reply == NULL && replyObj != NULL) {
return JNI_FALSE;
}
IBinder* target = getBPNativeData(env, obj)->mObject.get();
if (target == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
return JNI_FALSE;
}
ALOGV("Java code calling transact on %p in Java object %p with code %" PRId32 "\n",
target, obj, code);
bool time_binder_calls;
int64_t start_millis;
if (kEnableBinderSample) {
// Only log the binder call duration for things on the Java-level main thread.
// But if we don't
time_binder_calls = should_time_binder_calls();
if (time_binder_calls) {
start_millis = uptimeMillis();
}
}
//printf("Transact from Java code to %p sending: ", target); data->print();
status_t err = target->transact(code, *data, reply, flags);
//if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
if (kEnableBinderSample) {
if (time_binder_calls) {
conditionally_log_binder_call(start_millis, target, code);
}
}
if (err == NO_ERROR) {
return JNI_TRUE;
} else if (err == UNKNOWN_TRANSACTION) {
return JNI_FALSE;
}
signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data->dataSize());
return JNI_FALSE;
}
这里会有异常抛出:
void signalExceptionForError(JNIEnv* env, jobject obj, status_t err, bool canThrowRemoteException, int parcelSize) {
switch (err) {
case UNKNOWN_ERROR:
jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
break;
case NO_MEMORY:
jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
break;
case INVALID_OPERATION:
jniThrowException(env, "java/lang/UnsupportedOperationException", NULL);
break;
case BAD_VALUE:
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
break;
case BAD_INDEX:
jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
break;
case BAD_TYPE:
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
break;
case NAME_NOT_FOUND:
jniThrowException(env, "java/util/NoSuchElementException", NULL);
break;
case PERMISSION_DENIED:
jniThrowException(env, "java/lang/SecurityException", NULL);
break;
case NOT_ENOUGH_DATA:
jniThrowException(env, "android/os/ParcelFormatException", "Not enough data");
break;
case NO_INIT:
jniThrowException(env, "java/lang/RuntimeException", "Not initialized");
break;
case ALREADY_EXISTS:
jniThrowException(env, "java/lang/RuntimeException", "Item already exists");
break;
case DEAD_OBJECT:
jniThrowException(env, canThrowRemoteException
? "android/os/DeadObjectException"
: "java/lang/RuntimeException", NULL);
break;
case UNKNOWN_TRANSACTION:
jniThrowException(env, "java/lang/RuntimeException", "Unknown transaction code");
break;
case FAILED_TRANSACTION: {
ALOGE("!!! FAILED BINDER TRANSACTION !!! (parcel size = %d)", parcelSize);
const char* exceptionToThrow;
char msg[128];
//transaction失败的底层原因有可能很多种,这里无法确定是那种,后续binder driver会进一步完善
if (canThrowRemoteException && parcelSize > 200*1024) {
exceptionToThrow = "android/os/TransactionTooLargeException";
snprintf(msg, sizeof(msg)-1, "data parcel size %d bytes", parcelSize);
} else {
exceptionToThrow = (canThrowRemoteException)
? "android/os/DeadObjectException"
: "java/lang/RuntimeException";
snprintf(msg, sizeof(msg)-1,
"Transaction failed on small parcel; remote process probably died");
}
jniThrowException(env, exceptionToThrow, msg);
} break;
case FDS_NOT_ALLOWED:
jniThrowException(env, "java/lang/RuntimeException",
"Not allowed to write file descriptors here");
break;
case UNEXPECTED_NULL:
jniThrowNullPointerException(env, NULL);
break;
case -EBADF:
jniThrowException(env, "java/lang/RuntimeException",
"Bad file descriptor");
break;
case -ENFILE:
jniThrowException(env, "java/lang/RuntimeException",
"File table overflow");
break;
case -EMFILE:
jniThrowException(env, "java/lang/RuntimeException",
"Too many open files");
break;
case -EFBIG:
jniThrowException(env, "java/lang/RuntimeException",
"File too large");
break;
case -ENOSPC:
jniThrowException(env, "java/lang/RuntimeException",
"No space left on device");
break;
case -ESPIPE:
jniThrowException(env, "java/lang/RuntimeException",
"Illegal seek");
break;
case -EROFS:
jniThrowException(env, "java/lang/RuntimeException",
"Read-only file system");
break;
case -EMLINK:
jniThrowException(env, "java/lang/RuntimeException",
"Too many links");
break;
default:
ALOGE("Unknown binder error code. 0x%" PRIx32, err);
String8 msg;
msg.appendFormat("Unknown binder error code. 0x%" PRIx32, err);
jniThrowException(env, canThrowRemoteException
? "android/os/RemoteException" : "java/lang/RuntimeException", msg.string());
break;
}
}
struct BinderProxyNativeData {
// Both fields are constant and not null once javaObjectForIBinder returns this as
// part of a BinderProxy.
// The native IBinder proxied by this BinderProxy.
sp mObject;
// Death recipients for mObject. Reference counted only because DeathRecipients
// hold a weak reference that can be temporarily promoted.
sp mOrgue; // Death recipients for mObject.
};
...
BinderProxyNativeData* getBPNativeData(JNIEnv* env, jobject obj) {
return (BinderProxyNativeData *) env->GetLongField(obj, gBinderProxyOffsets.mNativeData);
}
...
IBinder* target = getBPNativeData(env, obj)->mObject.get();
status_t err = target->transact(code, *data, reply, flags);
从上述代码可以看到,返回通过IBinder->transact接口返回err结果,IBinder->transact的接口可继续查看:BpBinder.transact和IPC.transact里面的实现,关于BpBinder.transact和IPC.transact的详细信息下面继续介绍:
BpBinder.transact
[ BpBinder.cpp]
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
if (mAlive) {
//[见小节2.6]
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
当binder死亡,则返回err=DEAD_OBJECT,所对应的抛出的异常为DeadObjectException
IPC.transact
[IPCThreadState.cpp]
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err = data.errorCheck(); //错误检查
flags |= TF_ACCEPT_FDS;
if (err == NO_ERROR) {
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err); //返回writeTransactionData的执行结果err
}
if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
err = waitForResponse(reply); //
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
} else {
err = waitForResponse(NULL, NULL);
}
return err; //返回waitForResponse的执行结果err
}
返回值err的来源:
IPC.waitForResponse
[IPCThreadState.cpp]
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err;
while (1) {
// 向Binder驱动写入交互
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
...
switch (cmd) {
case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;
case BR_FAILED_REPLY:
err = FAILED_TRANSACTION;
goto finish;
default:
err = executeCommand(cmd); //[见小节2.7.1]
if (err != NO_ERROR) goto finish;
break;
}
}
...
return err;
}
当收到BR_DEAD_REPLY,则抛出err=DEAD_OBJECT
当收到BR_FAILED_REPLY, 则抛出err=FAILED_TRANSACTION
否则返回的是executeCommand的err
IPC.executeCommand
status_t IPCThreadState::executeCommand(int32_t cmd)
{
BBinder* obj;
RefBase::weakref_type* refs;
status_t result = NO_ERROR;
switch ((uint32_t)cmd) {
case BR_ERROR:
//从mIn中读取出错误码
result = mIn.readInt32();
break;
...
default:
result = UNKNOWN_ERROR;
break;
}
return result;
}
talkWithDriver过程便会跟Binder驱动交互
当服务端收到bind请求,则此时进入execTransact()过程。
Binder.execTransact
[Binder.java]
private boolean execTransact(int code, long dataObj, long replyObj, int flags) {
Parcel data = Parcel.obtain(dataObj);
Parcel reply = Parcel.obtain(replyObj);
boolean res;
try {
//执行onTransact方法[见小节3.1.1]
res = onTransact(code, data, reply, flags);
} catch (RemoteException e) {
if ((flags & FLAG_ONEWAY) != 0) {
Log.w(TAG, "Binder call failed.", e);
} else {
reply.setDataPosition(0);
reply.writeException(e); //[见后续]
}
res = true;
} catch (RuntimeException e) {
if ((flags & FLAG_ONEWAY) != 0) {
Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
} else {
reply.setDataPosition(0);
reply.writeException(e); //[见后续]
}
res = true;
} catch (OutOfMemoryError e) {
Log.e(TAG, "Caught an OutOfMemoryError from the binder stub implementation.", e);
RuntimeException re = new RuntimeException("Out of memory", e);
reply.setDataPosition(0);
reply.writeException(re); //[见后续]
res = true;
}
//[见小节3.3]
checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
reply.recycle();
data.recycle();
return res;
}
可以看到服务端发送异常有3大类:
还有一类见writeException
writeException
public final void writeException(Exception e) {
int code = 0;
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;
}
writeInt(code); //写入异常码
StrictMode.clearGatheredViolations();
if (code == 0) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new RuntimeException(e);
}
writeString(e.getMessage());
}
此处写入的异常类型:
checkParcel
static void checkParcel(IBinder obj, int code, Parcel parcel, String msg) {
// 检查parcel数据是否大于800KB
if (CHECK_PARCEL_SIZE && parcel.dataSize() >= 800*1024) {
StringBuilder sb = new StringBuilder();
sb.append(msg);
sb.append(": on ");
sb.append(obj);
sb.append(" calling ");
sb.append(code);
sb.append(" size ");
sb.append(parcel.dataSize());
sb.append(" (data: ");
parcel.setDataPosition(0);
sb.append(parcel.readInt());
sb.append(", ");
sb.append(parcel.readInt());
sb.append(", ");
sb.append(parcel.readInt());
sb.append(")");
Slog.wtfStack(TAG, sb.toString());
}
}
总结异常数据传输流程如下图:
结论:小米手机发出安全异常是binder服务端发出的,厂商设置了权限无法访问。