文章托管在gitee上 Android Notes , 同步csdn
本文基于Android12 分析

am命令

在shell通过命令am,可以输出该命令的帮助信息,常用的命令如下
几大组件相关的命令,通过名字大概知道用途

  • start-activity 启动activity
  • start-service 启动服务
  • start-foreground-service 启动前台服务
  • stop-service 停止相关服务
  • broadcast 发送意图广播

还有其他比较有用的命令:

  • instrument 启动Instrumentation
  • dumpheap dump某个进程heap
  • force-stop 完全停止指定包名的应用
  • kill 杀死后台某指定进程
  • hang [–allow-restart] 挂起系统进程,冻屏效果
  • restart 用户空间层面重启
  • stack、task 操作activity stacks、tasks

在命令行执行am命令

$  am
Activity manager (activity) commands:helpPrint this help text.start-activity [-D] [-N] [-W] [-P <FILE>] [--start-profiler <FILE>][--sampling INTERVAL] [--streaming] [-R COUNT] [-S][--track-allocation] [--user <USER_ID> | current] <INTENT>Start an Activity.  Options are:...start-service [--user <USER_ID> | current] <INTENT>Start a Service.  Options are:--user <USER_ID> | current: Specify which user to run as; if notspecified then run as the current user.start-foreground-service [--user <USER_ID> | current] <INTENT>Start a foreground Service.  Options are:--user <USER_ID> | current: Specify which user to run as; if notspecified then run as the current user.stop-service [--user <USER_ID> | current] <INTENT>Stop a Service.  Options are:--user <USER_ID> | current: Specify which user to run as; if notspecified then run as the current user.broadcast [--user <USER_ID> | all | current] <INTENT>Send a broadcast Intent.  Options are:--user <USER_ID> | all | current: Specify which user to send to; if notspecified then send to all users.--receiver-permission <PERMISSION>: Require receiver to hold permission.--allow-background-activity-starts: The receiver may start activitieseven if in the background.instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w][--user <USER_ID> | current][--no-hidden-api-checks [--no-test-api-access]][--no-isolated-storage][--no-window-animation] [--abi <ABI>] <COMPONENT>Start an Instrumentation.  Typically this target <COMPONENT> is in the
...

am使用示例

以dumpheap命令为例,其可以指定进程的名字或pid:

dumpheap [--user <USER_ID> current] [-n] [-g] <PROCESS> <FILE>Dump the heap of a process.  The given <PROCESS> argument maybe either a process name or pid.  Options are:-n: dump native heap instead of managed heap-g: force GC before dumping the heap--user <USER_ID> | current: When supplying a process name,specify user of process to dump; uses current user if not specified.

执行命令:

$ am dumpheap system_server
File: /data/local/tmp/heapdump-20221023-123406.profException occurred while executing 'dumpheap':
java.lang.IllegalArgumentException: Unknown process: system_serverat com.android.server.am.ActivityManagerService.dumpHeap(ActivityManagerService.java:18596)at com.android.server.am.ActivityManagerShellCommand.runDumpHeap(ActivityManagerShellCommand.java:964)at com.android.server.am.ActivityManagerShellCommand.onCommand(ActivityManagerShellCommand.java:208)at android.os.BasicShellCommandHandler.exec(BasicShellCommandHandler.java:98)at android.os.ShellCommand.exec(ShellCommand.java:44)at com.android.server.am.ActivityManagerService.onShellCommand(ActivityManagerService.java:10521)at android.os.Binder.shellCommand(Binder.java:929)at android.os.Binder.onTransact(Binder.java:813)at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:5027)at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2883)at android.os.Binder.execTransactInternal(Binder.java:1159)at android.os.Binder.execTransact(Binder.java:1123)

当adb shell 进入后直接执行 am dumpheap system_server, 会抛出上面一个异常,说找不到这个进程,不过换成 system 就可以。从错误的调用栈可以知道,这个命令是通过 Binder#shellCommand 来实现的,am具体的实现在ActivityManagerService端。

通常我们使用pid来dump某个进程的heap,先通过pidof拿到某个进程的pid,然后执行dump

$ pidof system_server
5113
$ am dumpheap 5113
File: /data/local/tmp/heapdump-20221023-124412.prof
Waiting for dump to finish..
$ adb pull /data/local/tmp/heapdump-20221023-124412.prof
$ ~/Android/Sdk-linux/platform-tools/hprof-conv heapdump-20221023-124412.prof heapdump-20221023-124412.hprof

在dump完成后,将其pull出来。此时需要再使用hprof-conv做一下转换,才能在MAT工具中打开。

am命令解析

查看am命令的位置

$ which am
/system/bin/am

将此命令pull出来内容如下,在frameworks路径下也可以找到am,可以发现有两个情况

  • 使用 cmd 命令,binder调用到AMS,通过Binder#shellCommand 实现
  • 执行 app_process 启动虚拟机执行 Am 类,这个类后续仍然会通过Binder调用到AMS
/// @frameworks/base/cmds/am/am
#!/system/bin/shif [ "$1" != "instrument" ] ; then  // 第一个参数非 instrumentcmd activity "$@"   // 通过 Binder#shellCommand 实现, $@ 表示后面的所有参数
else  // 执行 app_process 启动虚拟机执行 Am 类base=/systemexport CLASSPATH=$base/framework/am.jarexec app_process $base/bin com.android.commands.am.Am "$@"
fi

源码分析

首先分析第一种情况,cmd是一个命令,activity 是binder服务的名字,它对应的服务是ActivityManagerService

cmd activity "$@"   // 通过 Binder#shellCommand 实现, $@ 表示后面的所有参数

cmd activity …

cmd命令的源码在 frameworks/native/cmds/cmd,编译输出到/system/bin/cmd。从activity开始到结束的所有值将成为它的参数列表。首先看它的入口main方法:

main

/// @frameworks/native/cmds/cmd/main.cpp
int main(int argc, char* const argv[]) {signal(SIGPIPE, SIG_IGN); // 忽略pipe信号std::vector<std::string_view> arguments;arguments.reserve(argc - 1);// 0th argument is a program name, skipping.for (int i = 1; i < argc; ++i) {// 构造参数列表,0号参数是程序名,不加入arguments.emplace_back(argv[i]);}// 直接调用 cmdMainreturn cmdMain(arguments, android::aout, android::aerr, STDIN_FILENO, STDOUT_FILENO,STDERR_FILENO, RunMode::kStandalone);
}

cmdMain

/// @frameworks/native/cmds/cmd/cmd.cpp
int cmdMain(const std::vector<std::string_view>& argv, TextOutput& outputLog, TextOutput& errorLog,int in, int out, int err, RunMode runMode) {sp<ProcessState> proc = ProcessState::self(); // 初始化binder环境proc->startThreadPool();#if DEBUGALOGD("cmd: starting");
#endifsp<IServiceManager> sm = defaultServiceManager(); // 获取 ServiceManagerif (runMode == RunMode::kStandalone) {fflush(stdout);}if (sm == nullptr) {ALOGW("Unable to get default service manager!");errorLog << "cmd: Unable to get default service manager!" << endl;return 20;}int argc = argv.size();if (argc == 0) { // 没有参数会输出如下提示errorLog << "cmd: No service specified; use -l to list all running services. Use -w to start and wait for a service." << endl;return 20;}if ((argc == 1) && (argv[0] == "-l")) {// -l 参数列出所有在运行的服务名Vector<String16> services = sm->listServices();services.sort(sort_func);outputLog << "Currently running services:" << endl;for (size_t i=0; i<services.size(); i++) {sp<IBinder> service = sm->checkService(services[i]);if (service != nullptr) {outputLog << "  " << services[i] << endl;}}return 0;}bool waitForService = ((argc > 1) && (argv[0] == "-w"));// -w 需等待服务起来int serviceIdx = (waitForService) ? 1 : 0;const auto cmd = argv[serviceIdx];Vector<String16> args;String16 serviceName = String16(cmd.data(), cmd.size());for (int i = serviceIdx + 1; i < argc; i++) {args.add(String16(argv[i].data(), argv[i].size()));}sp<IBinder> service;if(waitForService) {service = sm->waitForService(serviceName);// 等待服务起来如果不存在} else {service = sm->checkService(serviceName); // 获取binder服务代理}if (service == nullptr) {// 没有找到服务if (runMode == RunMode::kStandalone) {ALOGW("Can't find service %.*s", static_cast<int>(cmd.size()), cmd.data());}errorLog << "cmd: Can't find service: " << cmd << endl;return 20;}sp<MyShellCallback> cb = new MyShellCallback(errorLog); // shell回调监听sp<MyResultReceiver> result = new MyResultReceiver(); // 获取结果receiver#if DEBUGALOGD("cmd: Invoking %.*s in=%d, out=%d, err=%d",static_cast<int>(cmd.size()), cmd.data(), in, out, err);
#endif// TODO: block until a result is returned to MyResultReceiver.// 调用 binder service 的 shellCommandstatus_t error = IBinder::shellCommand(service, in, out, err, args, cb, result);if (error < 0) { // 处理通信错误const char* errstr;switch (error) {case BAD_TYPE: errstr = "Bad type"; break;case FAILED_TRANSACTION: errstr = "Failed transaction"; break;case FDS_NOT_ALLOWED: errstr = "File descriptors not allowed"; break;case UNEXPECTED_NULL: errstr = "Unexpected null"; break;default: errstr = strerror(-error); break;}if (runMode == RunMode::kStandalone) {ALOGW("Failure calling service %.*s: %s (%d)", static_cast<int>(cmd.size()), cmd.data(),errstr, -error);}outputLog << "cmd: Failure calling service " << cmd << ": " << errstr << " (" << (-error)<< ")" << endl;return error;}cb->mActive = false;status_t res = result->waitForResult(); // 等待结果。
#if DEBUGALOGD("result=%d", (int)res);
#endifreturn res;
}

IBinder::shellCommand

重点看该方法,向服务端发起请求。

/// @frameworks/native/libs/binder/Binder.cpp
status_t IBinder::shellCommand(const sp<IBinder>& target, int in, int out, int err,Vector<String16>& args, const sp<IShellCallback>& callback,const sp<IResultReceiver>& resultReceiver)
{Parcel send;Parcel reply;// 写入 输入/输出/错误fdsend.writeFileDescriptor(in);send.writeFileDescriptor(out);send.writeFileDescriptor(err);// 写入参数列表const size_t numArgs = args.size();send.writeInt32(numArgs);for (size_t i = 0; i < numArgs; i++) {send.writeString16(args[i]);}// 写入 callback 和 resultReceiver binder 对象send.writeStrongBinder(callback != nullptr ? IInterface::asBinder(callback) : nullptr);send.writeStrongBinder(resultReceiver != nullptr ? IInterface::asBinder(resultReceiver) : nullptr);// 对target服务发起调用,调用码是 SHELL_COMMAND_TRANSACTION// 对于客户端而言,target的本质是一个BpBinder对象return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply);
}

BpBinder::transact

/// @frameworks/native/libs/binder/BpBinder.cpp
// NOLINTNEXTLINE(google-default-arguments)
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{// Once a binder has died, it will never come back to life.if (mAlive) { // 如果服务还存活bool privateVendor = flags & FLAG_PRIVATE_VENDOR;// don't send userspace flags to the kernelflags = flags & ~FLAG_PRIVATE_VENDOR;// user transactions require a given stability levelif (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) {using android::internal::Stability;auto category = Stability::getCategory(this);Stability::Level required = privateVendor ? Stability::VENDOR: Stability::getLocalLevel();if (CC_UNLIKELY(!Stability::check(category, required))) {ALOGE("Cannot do a user transaction on a %s binder (%s) in a %s context.",category.debugString().c_str(),String8(getInterfaceDescriptor()).c_str(),Stability::levelString(required).c_str());return BAD_TYPE;}}status_t status;if (CC_UNLIKELY(isRpcBinder())) {status = rpcSession()->transact(rpcAddress(), code, data, reply, flags);} else { // 此处,通过 IPCThreadState 发起 transactstatus = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);}// 通信返回状态码DEAD_OBJECT,说明服务已经挂了,将mAlive置为0if (status == DEAD_OBJECT) mAlive = 0;return status;}return DEAD_OBJECT;
}

IPCThreadState::transact

/// @frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle,uint32_t code, const Parcel& data,Parcel* reply, uint32_t flags)
{LOG_ALWAYS_FATAL_IF(data.isForRpc(), "Parcel constructed for RPC, but being used with binder.");status_t err;flags |= TF_ACCEPT_FDS;IF_LOG_TRANSACTIONS() {TextOutput::Bundle _b(alog);alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "<< handle << " / code " << TypeCode(code) << ": "<< indent << data << dedent << endl;}LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),(flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");// 写入通信参数,此处发起的通信命令是 BC_TRANSACTIONerr = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);if (err != NO_ERROR) {if (reply) reply->setError(err);return (mLastError = err);}if ((flags & TF_ONE_WAY) == 0) { /// 非oneway,需要等待对端响应if (UNLIKELY(mCallRestriction != ProcessState::CallRestriction::NONE)) {if (mCallRestriction == ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY) {ALOGE("Process making non-oneway call (code: %u) but is restricted.", code);CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(),ANDROID_LOG_ERROR);} else /* FATAL_IF_NOT_ONEWAY */ {LOG_ALWAYS_FATAL("Process may not make non-oneway calls (code: %u).", code);}}if (reply) { // 与binder驱动通信并获取回复err = waitForResponse(reply);} else {Parcel fakeReply;err = waitForResponse(&fakeReply);}IF_LOG_TRANSACTIONS() {TextOutput::Bundle _b(alog);alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "<< handle << ": ";if (reply) alog << indent << *reply << dedent << endl;else alog << "(none requested)" << endl;}} else {// oneway,无需等待,此处传递参数都是 nullptrerr = waitForResponse(nullptr, nullptr);}return err;
}

IPCThreadState::waitForResponse

这个方法的名字容易造成误解,它会先与binder驱动进行通信,然后根据返回的具体的cmd去执行相关逻辑。我们这里发起的是一个同步请求,请求码是BC_TRANSACTION,在最后会收到来自驱动的BR_REPLY,说明服务端已经响应并处理了请求,在这里拿到执行返回的结果。

/// @frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{uint32_t cmd;int32_t err;while (1) {/// talkWithDriver通过ioctl命令与binder驱动通信并获取返回命令与数据if ((err=talkWithDriver()) < NO_ERROR) break;err = mIn.errorCheck();if (err < NO_ERROR) break;if (mIn.dataAvail() == 0) continue;cmd = (uint32_t)mIn.readInt32();switch (cmd) {case BR_ONEWAY_SPAM_SUSPECT:ALOGE("Process seems to be sending too many oneway calls.");CallStack::logStack("oneway spamming", CallStack::getCurrent().get(),ANDROID_LOG_ERROR);[[fallthrough]];case BR_TRANSACTION_COMPLETE:if (!reply && !acquireResult) goto finish;break;...case BR_REPLY: // 发起命令的最终,会收到一个回复{binder_transaction_data tr;err = mIn.read(&tr, sizeof(tr));ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");if (err != NO_ERROR) goto finish;if (reply) {if ((tr.flags & TF_STATUS_CODE) == 0) {reply->ipcSetDataReference(reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),tr.data_size,reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),tr.offsets_size/sizeof(binder_size_t),freeBuffer);} else {err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);freeBuffer(nullptr,reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),tr.data_size,reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),tr.offsets_size/sizeof(binder_size_t));}} else {freeBuffer(nullptr,reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),tr.data_size,reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),tr.offsets_size/sizeof(binder_size_t));continue;}}goto finish;default:err = executeCommand(cmd); // 执行返回的其他命令if (err != NO_ERROR) goto finish;break;}}finish:if (err != NO_ERROR) { // 处理错误。if (acquireResult) *acquireResult = err;if (reply) reply->setError(err);mLastError = err;}return err;
}

上面,使用cmd命令获取了 ActivityManagerService 的代理对象,向AMS发起binder调用。到此时通信请求已经通过binder驱动发送给了服务端,服务端的空闲binder线程会处理此请求。接下来看服务端的处理。

IPCThreadState::joinThreadPool

一个典型的binder线程会在启动后调用joinThreadPool来开始处理binder事务,关键函数是getAndExecuteCommand。服务端是在此处被binder驱动唤醒,来处理相关请求的。

void IPCThreadState::joinThreadPool(bool isMain)
{LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);mIsLooper = true;status_t result;do {processPendingDerefs();// now get the next command to be processed, waiting if necessaryresult = getAndExecuteCommand(); // 与binder驱动通信,获取命令来处理if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {LOG_ALWAYS_FATAL("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",mProcess->mDriverFD, result);}// Let this thread exit the thread pool if it is no longer// needed and it is not the main process thread.if(result == TIMED_OUT && !isMain) {break;}} while (result != -ECONNREFUSED && result != -EBADF);LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%d\n",(void*)pthread_self(), getpid(), result);mOut.writeInt32(BC_EXIT_LOOPER);mIsLooper = false;talkWithDriver(false);
}

IPCThreadState::getAndExecuteCommand

status_t IPCThreadState::getAndExecuteCommand()
{status_t result;int32_t cmd;result = talkWithDriver(); // 与binder驱动通信,获取命令来处理if (result >= NO_ERROR) {size_t IN = mIn.dataAvail();if (IN < sizeof(int32_t)) return result;cmd = mIn.readInt32();IF_LOG_COMMANDS() {alog << "Processing top-level Command: "<< getReturnString(cmd) << endl;}pthread_mutex_lock(&mProcess->mThreadCountLock);mProcess->mExecutingThreadsCount++; // 已使用binder线程计数增加if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &&mProcess->mStarvationStartTimeMs == 0) { // 达到上限,后续通信无线程可用,造成饥饿状态mProcess->mStarvationStartTimeMs = uptimeMillis();}pthread_mutex_unlock(&mProcess->mThreadCountLock);result = executeCommand(cmd); // 执行命令。pthread_mutex_lock(&mProcess->mThreadCountLock);mProcess->mExecutingThreadsCount--;// 已使用binder线程计数减少if (mProcess->mExecutingThreadsCount < mProcess->mMaxThreads &&mProcess->mStarvationStartTimeMs != 0) { // 打印饥饿持续时长int64_t starvationTimeMs = uptimeMillis() - mProcess->mStarvationStartTimeMs;if (starvationTimeMs > 100) {ALOGE("binder thread pool (%zu threads) starved for %" PRId64 " ms",mProcess->mMaxThreads, starvationTimeMs);}mProcess->mStarvationStartTimeMs = 0;}// Cond broadcast can be expensive, so don't send it every time a binder// call is processed. b/168806193if (mProcess->mWaitingForThreads > 0) { // 通知有线程可用pthread_cond_broadcast(&mProcess->mThreadCountDecrement);}pthread_mutex_unlock(&mProcess->mThreadCountLock);}return result;
}

IPCThreadState::executeCommand

服务端收到请求会获取一个BR_TRANSACTION的命令,这个命令会进一步调用服务的transact方法

  status_t IPCThreadState::executeCommand(int32_t cmd){BBinder* obj;RefBase::weakref_type* refs;status_t result = NO_ERROR;switch ((uint32_t)cmd) {//...case BR_TRANSACTION:{binder_transaction_data_secctx tr_secctx;binder_transaction_data& tr = tr_secctx.transaction_data;if (cmd == (int) BR_TRANSACTION_SEC_CTX) {result = mIn.read(&tr_secctx, sizeof(tr_secctx));} else {result = mIn.read(&tr, sizeof(tr));tr_secctx.secctx = 0;}ALOG_ASSERT(result == NO_ERROR,"Not enough command data for brTRANSACTION");if (result != NO_ERROR) break;Parcel buffer;buffer.ipcSetDataReference(reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),tr.data_size,reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),tr.offsets_size/sizeof(binder_size_t), freeBuffer);//...// ALOGI(">>>> TRANSACT from pid %d sid %s uid %d\n", mCallingPid,//    (mCallingSid ? mCallingSid : "<N/A>"), mCallingUid);Parcel reply;status_t error;if (tr.target.ptr) {// We only have a weak reference on the target object, so we must first try to// safely acquire a strong reference before doing anything else with it.if (reinterpret_cast<RefBase::weakref_type*>(tr.target.ptr)->attemptIncStrong(this)) {// 调用服务的transact函数。BBinder对象的实体是JavaBBinder,注册服务使用的是该对象指针error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,&reply, tr.flags);reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);} else {error = UNKNOWN_TRANSACTION;}} else {error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);}//...}break;//...  return result;}

BBinder::transact

// NOLINTNEXTLINE(google-default-arguments)
status_t BBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{data.setDataPosition(0);if (reply != nullptr && (flags & FLAG_CLEAR_BUF)) {reply->markSensitive();}status_t err = NO_ERROR;switch (code) {case PING_TRANSACTION:err = pingBinder();break;case EXTENSION_TRANSACTION:err = reply->writeStrongBinder(getExtension());break;case DEBUG_PID_TRANSACTION:err = reply->writeInt32(getDebugPid());break;default:err = onTransact(code, data, reply, flags); // 其他code调用onTransactbreak;}// In case this is being transacted on in the same process.if (reply != nullptr) {reply->setDataPosition(0);}return err;
}

对于Java层的binder服务而言,它有一个中间层的JavaBBinder对象,作为native的Java服务代表。而对于native层,服务通常是直接继承自BBinder。

JavaBBinder::onTransact

status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) override
{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");//回调java Binder 类的 execTransact 方法jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);if (env->ExceptionCheck()) { // 异常处理ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());binder_report_exception(env, excep.get(),"*** Uncaught remote exception!  ""(Exceptions are not yet supported across processes.)");res = JNI_FALSE;}// Check if the strict mode state changed while processing the// call.  The Binder state will be restored by the underlying// Binder system in IPCThreadState, however we need to take care// of the parallel Java state as well.if (thread_state->getStrictModePolicy() != strict_policy_before) {set_dalvik_blockguard_policy(env, strict_policy_before);}if (env->ExceptionCheck()) {ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());binder_report_exception(env, excep.get(),"*** Uncaught exception in onBinderStrictModePolicyChange");}// Need to always call through the native implementation of// SYSPROPS_TRANSACTION.if (code == SYSPROPS_TRANSACTION) {BBinder::onTransact(code, data, reply, flags);}//aout << "onTransact to Java code; result=" << res << endl//    << "Transact from " << this << " to Java code returning "//    << reply << ": " << *reply << endl;return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;
}

以上方法主要用来回调java层服务的execTransact方法,我们执行 cmd activity,对端服务是 ActivityManagerService,而binder服务一般都是继承Binder类,先看它的execTransact。

Binder#execTransact

// Entry point from android_util_Binder.cpp's onTransact
@UnsupportedAppUsage
private boolean execTransact(int code, long dataObj, long replyObj,int flags) {// At that point, the parcel request headers haven't been parsed so we do not know what// WorkSource the caller has set. Use calling uid as the default.final int callingUid = Binder.getCallingUid();final long origWorkSource = ThreadLocalWorkSource.setUid(callingUid);try { // 内部调用 execTransactInternalreturn execTransactInternal(code, dataObj, replyObj, flags, callingUid);} finally {ThreadLocalWorkSource.restore(origWorkSource);}
}// 真正执行的地方
private boolean execTransactInternal(int code, long dataObj, long replyObj, int flags,int callingUid) {// Make sure the observer won't change while processing a transaction.final BinderInternal.Observer observer = sObserver;final CallSession callSession =observer != null ? observer.callStarted(this, code, UNSET_WORKSOURCE) : null;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 {final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;if (heavyHitterWatcher != null) {// Notify the heavy hitter watcher, if it's enabledheavyHitterWatcher.onTransaction(callingUid, getClass(), code);}if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0) { // appops 相关AppOpsManager.startNotedAppOpsCollection(callingUid);try {res = onTransact(code, data, reply, flags);} finally {AppOpsManager.finishNotedAppOpsCollection();}} else {// 调用onTransact,相关服务一般会重写此对方法// 当然,现在一般都是aidl自动生成,服务端只需复写接口的相关方法即可res = onTransact(code, data, reply, flags);}} catch (RemoteException|RuntimeException e) {// 异常处理。。res = true;} finally {...}// 检查parcel 大小checkParcel(this, code, reply, "Unreasonably large binder reply buffer");reply.recycle();data.recycle();//...return res;
}

Binder#onTransact

执行transact的默认实现,处理一些特殊的code。
注:ActivityManagerService有重写onTransact方法,不过调用super来让父类处理,这些特殊的code最终还是在Binder类中处理。

protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,int flags) throws RemoteException {if (code == INTERFACE_TRANSACTION) { //获取接口描述符reply.writeString(getInterfaceDescriptor());return true;} else if (code == DUMP_TRANSACTION) { // 执行dump操作的。比如执行 dumpsys//...} else if (code == SHELL_COMMAND_TRANSACTION) { // 此处是我们关心的,执行 shell 命令ParcelFileDescriptor in = data.readFileDescriptor();ParcelFileDescriptor out = data.readFileDescriptor();ParcelFileDescriptor err = data.readFileDescriptor();String[] args = data.readStringArray();ShellCallback shellCallback = ShellCallback.CREATOR.createFromParcel(data);ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);try {if (out != null) { // 执行 shellCommandshellCommand(in != null ? in.getFileDescriptor() : null,out.getFileDescriptor(),err != null ? err.getFileDescriptor() : out.getFileDescriptor(),args, shellCallback, resultReceiver);}} finally {IoUtils.closeQuietly(in);IoUtils.closeQuietly(out);IoUtils.closeQuietly(err);// Write the StrictMode header.if (reply != null) {reply.writeNoException();} else {StrictMode.clearGatheredViolations();}}return true;}return false;
}// shellCommand 实现比较简单,直接调用onShellCommand, 而后者一般都会被相关服务复写,用来实现特定的功能
public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,@Nullable FileDescriptor err,@NonNull String[] args, @Nullable ShellCallback callback,@NonNull ResultReceiver resultReceiver) throws RemoteException {onShellCommand(in, out, err, args, callback, resultReceiver);
}

ActivityManagerService 复写了onShellCommand,接下来我们看它的实现

ActivityManagerService#onShellCommand

@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,FileDescriptor err, String[] args, ShellCallback callback,ResultReceiver resultReceiver) {// 通过具体的类来处理cmd,业务逻辑拆分(new ActivityManagerShellCommand(this, false)).exec(this, in, out, err, args, callback, resultReceiver);
}

ActivityManagerShellCommand继承自 ShellCommand,执行exec会回调它的onCommand方法,接下来看此方法的实现

ActivityManagerShellCommand#onCommand

该方法根据不同的命令,去执行不同的功能。

/// frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@Override
public int onCommand(String cmd) {if (cmd == null) {return handleDefaultCommands(cmd);}final PrintWriter pw = getOutPrintWriter();try {switch (cmd) {case "start":case "start-activity":return runStartActivity(pw);case "startservice":case "start-service":return runStartService(pw, false);case "startforegroundservice":case "startfgservice":case "start-foreground-service":case "start-fg-service":return runStartService(pw, true);...case "dumpheap":return runDumpHeap(pw);...
}

当执行完命令,怎么通知客户端?在ShellCommand的exec方法中

public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,String[] args, ShellCallback callback, ResultReceiver resultReceiver) {mShellCallback = callback;mResultReceiver = resultReceiver;final int result = super.exec(target, in, out, err, args);if (mResultReceiver != null) {mResultReceiver.send(result, null); // 发送结果到客户端}return result;
}

实验:查看cmd调用栈

做一个实验,使用 cmd 命令hang住系统,此时系统和cmd都处于阻塞状态。

# cmd activity hang
Hanging the system...

使用debuggerd -b命令输出cmd的调用栈:

$ adb shell ps -Af|grep cmd     # 查找cmd进程
root           6366   6293 0 14:21:18 pts/1 00:00:00 cmd activity hang
$ adb shell debuggerd -b 6366 > 6366.txt  # 输出 cmd 进程的 trace
$ adb shell pidof system_server
520
$ adb shell debuggerd -j 520 > 520.txt    # 输出系统进程的 trace

pull出binder调用的log

$ adb root
restarting adbd as root
$ adb pull /dev/binderfs/binder_logs/
/dev/binderfs/binder_logs/: 101 files pulled. 3.6 MB/s (858138 bytes in 0.229s)

查看transactions文件,找到6366的通信记录

binder transactions:
proc 6366
context binderthread 6366: l 10 need_return 0 tr 0// 此行指示通信两端的 进程id:线程idoutgoing transaction 109362: 0000000000000000 from 6366:6366 to 520:4420 code 5f434d44 flags 10 pri 0:120 r1node work 109363: u00000000f34c0690 c00000000f3500224node work 109365: u00000000f34c0530 c00000000f3400a94transaction complete

分别打开 6366.txt 和 520.txt ,查看cmd进程的6366线程和系统进程的4420线程

// cmd 进程执行binder调用,等待对端完成。
"cmd" sysTid=6366#00 pc 00000b97  [vdso] (__kernel_vsyscall+7)#01 pc 000cd46c  /apex/com.android.runtime/lib/bionic/libc.so (__ioctl+28) (BuildId: 6e3a0180fa6637b68c0d181c343e6806)#02 pc 00080e6a  /apex/com.android.runtime/lib/bionic/libc.so (ioctl+58) (BuildId: 6e3a0180fa6637b68c0d181c343e6806)#03 pc 0005113b  /system/lib/libbinder.so (android::IPCThreadState::talkWithDriver(bool)+331) (BuildId: 395e4893e4bc58851a34df93c2dd1b01)#04 pc 0005255c  /system/lib/libbinder.so (android::IPCThreadState::waitForResponse(android::Parcel*, int*)+124) (BuildId: 395e4893e4bc58851a34df93c2dd1b01)#05 pc 00052211  /system/lib/libbinder.so (android::IPCThreadState::transact(int, unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+177) (BuildId: 395e4893e4bc58851a34df93c2dd1b01)#06 pc 00049049  /system/lib/libbinder.so (android::BpBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+153) (BuildId: 395e4893e4bc58851a34df93c2dd1b01)#07 pc 00046e83  /system/lib/libbinder.so (android::IBinder::shellCommand(android::sp<android::IBinder> const&, int, int, int, android::Vector<android::String16>&, android::sp<android::IShellCallback> const&, android::sp<android::IResultReceiver> const&)+643) (BuildId: 395e4893e4bc58851a34df93c2dd1b01)#08 pc 00005eef  /system/bin/cmd (cmdMain(std::__1::vector<std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::allocator<std::__1::basic_string_view<char, std::__1::char_traits<char> > > > const&, android::TextOutput&, android::TextOutput&, int, int, int, RunMode)+1551) (BuildId: 532cc6903869a7093a7676d1a1f756e9)#09 pc 00005798  /system/bin/cmd (main+264) (BuildId: 532cc6903869a7093a7676d1a1f756e9)#10 pc 000522e3  /apex/com.android.runtime/lib/bionic/libc.so (__libc_init+115) (BuildId: 6e3a0180fa6637b68c0d181c343e6806)// 系统进程执行shell Command
"Binder:520_12" prio=5 tid=124 Blocked
| group="main" sCount=1 dsCount=0 flags=1 obj=0x12c40e08 self=0xb242a010
| sysTid=4420 nice=0 cgrp=foreground sched=0/0 handle=0xae7fc1e0
| state=S schedstat=( 258290515 8629757 1593 ) utm=17 stm=8 core=3 HZ=100
| stack=0xae701000-0xae703000 stackSize=1008KB
| held mutexes=
at com.android.server.am.ActivityManagerService.hang(ActivityManagerService.java:9341)
- waiting to lock <0x033c9ff3> (a com.android.server.am.ActivityManagerService) held by thread 118
at com.android.server.am.ActivityManagerShellCommand.runHang(ActivityManagerShellCommand.java:1693)
at com.android.server.am.ActivityManagerShellCommand.onCommand(ActivityManagerShellCommand.java:238)
at android.os.BasicShellCommandHandler.exec(BasicShellCommandHandler.java:98)
at android.os.ShellCommand.exec(ShellCommand.java:44)
at com.android.server.am.ActivityManagerService.onShellCommand(ActivityManagerService.java:10521)
at android.os.Binder.shellCommand(Binder.java:929)
at android.os.Binder.onTransact(Binder.java:813)
at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:5027)
at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2883)
at android.os.Binder.execTransactInternal(Binder.java:1159)
at android.os.Binder.execTransact(Binder.java:1123)

小结

大致总结一下上述流程

  • 执行cmd命令,查找对应服务,获取相关服务的代理BpBinder对象。cmd进程为客户端进程。
  • 通过该代理BpBinder对象向对应服务发起请求,指定通信吗为SHELL_COMMAND_TRANSACTION
  • binder驱动处理该事件,并把请求传递到服务端
  • 服务端收到请求,执行其onTransact方法处理请求
  • 在onTransact方法中,根据指定的code执行shellCommand方法,该方法会回调相关服务的onShellCommand方法实现
  • 在服务的onShellCommand方法中执行具体请求,并向发起端发送结果。
  • 客户端收到响应后执行相关逻辑退出,此次请求结束

exec app_process …

接下来看第二种情况,执行 app_process 启动虚拟机执行 Am 类,后面接的都会转换为参数列表

base=/system
export CLASSPATH=$base/framework/am.jar   // 指定classpath
exec app_process $base/bin com.android.commands.am.Am "$@"

执行 app_process ,父路径是 /system/bin , 指定的类是 com.android.commands.am.Am,它在am.jar 中。类后面的 $@ 是使用者传递的参数,会传递到 Am 类的main方法做参数。

app_main#main

// @frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{if (!LOG_NDEBUG) { // debug 打印出所有参数String8 argv_String;for (int i = 0; i < argc; ++i) {argv_String.append("\"");argv_String.append(argv[i]);argv_String.append("\" ");}ALOGV("app_process main with argv: %s", argv_String.string());}// 构建 runtimeAppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));// Process command line arguments// ignore argv[0] // 参数0通常是命令自身,此处应该是app_processargc--;argv++;// Everything up to '--' or first non '-' arg goes to the vm.//// The first argument after the VM args is the "parent dir", which// is currently unused.//// After the parent dir, we expect one or more the following internal// arguments ://// --zygote : Start in zygote mode// --start-system-server : Start the system server.// --application : Start in application (stand alone, non zygote) mode.// --nice-name : The nice name for this process.//// For non zygote starts, these arguments will be followed by// the main class name. All remaining arguments are passed to// the main method of this class.//// For zygote starts, all remaining arguments are passed to the zygote.// main function.//// Note that we must copy argument string values since we will rewrite the// entire argument block when we apply the nice name to argv0.//// As an exception to the above rule, anything in "spaced commands"// goes to the vm even though it has a space in it.const char* spaced_commands[] = { "-cp", "-classpath" };// Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).bool known_command = false;int i;for (i = 0; i < argc; i++) { // 解析虚拟机参数,包括匹配 spaced_commands 的参数if (known_command == true) {runtime.addOption(strdup(argv[i]));// The static analyzer gets upset that we don't ever free the above// string. Since the allocation is from main, leaking it doesn't seem// problematic. NOLINTNEXTLINEALOGV("app_process main add known option '%s'", argv[i]);known_command = false;continue;}for (int j = 0;j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));++j) {if (strcmp(argv[i], spaced_commands[j]) == 0) {// 匹配上则进行标记,会将其后面的参数加入虚拟机参数known_command = true;ALOGV("app_process main found known command '%s'", argv[i]);}}if (argv[i][0] != '-') { // 参数第一位需要是 -break;}if (argv[i][1] == '-' && argv[i][2] == 0) { // 参数是单独 -- ,则会被跳过++i; // Skip --.break;}runtime.addOption(strdup(argv[i]));// The static analyzer gets upset that we don't ever free the above// string. Since the allocation is from main, leaking it doesn't seem// problematic. NOLINTNEXTLINEALOGV("app_process main add option '%s'", argv[i]);}// Parse runtime arguments.  Stop at first unrecognized option.bool zygote = false;bool startSystemServer = false;bool application = false;String8 niceName;String8 className;++i;  // Skip unused "parent dir" argument. // 跳过parent dir,此处是$base/bin,即 /system/binwhile (i < argc) {const char* arg = argv[i++];if (strcmp(arg, "--zygote") == 0) { // 解析是否zygote模式zygote = true;niceName = ZYGOTE_NICE_NAME;} else if (strcmp(arg, "--start-system-server") == 0) { // 是否启动 system_serverstartSystemServer = true;} else if (strcmp(arg, "--application") == 0) {// 解析是否 application 模式application = true;} else if (strncmp(arg, "--nice-name=", 12) == 0) { // 是否指定名字niceName.setTo(arg + 12);} else if (strncmp(arg, "--", 2) != 0) { // 其他内容被设置成 className, 此处是com.android.commands.am.AmclassName.setTo(arg);break; // className 后面一般是参数,不需要解析,跳出循环。} else {--i;break;}}Vector<String8> args;if (!className.isEmpty()) { // className 不为空// We're not in zygote mode, the only argument we need to pass// to RuntimeInit is the application argument.//// The Remainder of args get passed to startup class main(). Make// copies of them before we overwrite them with the process name.args.add(application ? String8("application") : String8("tool")); // 非application模式,指定为toolruntime.setClassNameAndArgs(className, argc - i, argv + i); // 设置className和参数if (!LOG_NDEBUG) { // 调试打印。String8 restOfArgs;char* const* argv_new = argv + i;int argc_new = argc - i;for (int k = 0; k < argc_new; ++k) {restOfArgs.append("\"");restOfArgs.append(argv_new[k]);restOfArgs.append("\" ");}ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());}} else { // 下面是走zygote模式的// We're in zygote mode.maybeCreateDalvikCache();if (startSystemServer) {args.add(String8("start-system-server"));}char prop[PROP_VALUE_MAX];if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",ABI_LIST_PROPERTY);return 11;}String8 abiFlag("--abi-list=");abiFlag.append(prop);args.add(abiFlag);// In zygote mode, pass all remaining arguments to the zygote// main() method.for (; i < argc; ++i) {args.add(String8(argv[i]));}}if (!niceName.isEmpty()) { // 有指定名字此时设置runtime.setArgv0(niceName.string(), true /* setProcName */);}if (zygote) {runtime.start("com.android.internal.os.ZygoteInit", args, zygote);} else if (className) { // 指定了className走此处,runtime.start("com.android.internal.os.RuntimeInit", args, zygote);} else {fprintf(stderr, "Error: no class name or --zygote supplied.\n");app_usage();LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");}
}

AndroidRuntime::start

runtime启动虚拟机,并调用RuntimeInit类的main方法。此处className是com.android.internal.os.RuntimeInit

/// @frameworks/base/core/jni/AndroidRuntime.cpp
/** Start the Android runtime.  This involves starting the virtual machine* and calling the "static void main(String[] args)" method in the class* named by "className".** Passes the main function two arguments, the class name and the specified* options string.*/
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{ALOGD(">>>>>> START %s uid %d <<<<<<\n",className != NULL ? className : "(unknown)", getuid());static const String8 startSystemServer("start-system-server");// Whether this is the primary zygote, meaning the zygote which will fork system server.bool primary_zygote = false;/** 'startSystemServer == true' means runtime is obsolete and not run from* init.rc anymore, so we print out the boot start event here.*/for (size_t i = 0; i < options.size(); ++i) {if (options[i] == startSystemServer) { // 如果配置启动 system_serverprimary_zygote = true;/* track our progress through the boot sequence */const int LOG_BOOT_PROGRESS_START = 3000;// 输出event log : boot_progress_startLOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));}}const char* rootDir = getenv("ANDROID_ROOT");if (rootDir == NULL) {rootDir = "/system";if (!hasDir("/system")) {LOG_FATAL("No root directory specified, and /system does not exist.");return;}setenv("ANDROID_ROOT", rootDir, 1);}const char* artRootDir = getenv("ANDROID_ART_ROOT");if (artRootDir == NULL) {LOG_FATAL("No ART directory specified with ANDROID_ART_ROOT environment variable.");return;}const char* i18nRootDir = getenv("ANDROID_I18N_ROOT");if (i18nRootDir == NULL) {LOG_FATAL("No runtime directory specified with ANDROID_I18N_ROOT environment variable.");return;}const char* tzdataRootDir = getenv("ANDROID_TZDATA_ROOT");if (tzdataRootDir == NULL) {LOG_FATAL("No tz data directory specified with ANDROID_TZDATA_ROOT environment variable.");return;}//const char* kernelHack = getenv("LD_ASSUME_KERNEL");//ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);/* start the virtual machine */JniInvocation jni_invocation;jni_invocation.Init(NULL);JNIEnv* env;if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) { // 启动虚拟机return;}onVmCreated(env);  // 通知mv创建完成/** Register android functions.*/if (startReg(env) < 0) {  // 注册系统jni函数ALOGE("Unable to register all android natives\n");return;}// 接下来,创建参数数组,然后调用指定类的main方法,此处是RuntimeInit#main/** We want to call main() with a String array with arguments in it.* At present we have two arguments, the class name and an option string.* Create an array to hold them.*/jclass stringClass;jobjectArray strArray;jstring classNameStr;stringClass = env->FindClass("java/lang/String");assert(stringClass != NULL);strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL); // 创建String数组assert(strArray != NULL);classNameStr = env->NewStringUTF(className);assert(classNameStr != NULL);env->SetObjectArrayElement(strArray, 0, classNameStr);for (size_t i = 0; i < options.size(); ++i) { // 填充参数到新数组jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());assert(optionsStr != NULL);env->SetObjectArrayElement(strArray, i + 1, optionsStr);}/** Start VM.  This thread becomes the main thread of the VM, and will* not return until the VM exits.*/char* slashClassName = toSlashClassName(className != NULL ? className : "");jclass startClass = env->FindClass(slashClassName);if (startClass == NULL) {ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);/* keep going */} else {jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");if (startMeth == NULL) {ALOGE("JavaVM unable to find main() in '%s'\n", className);/* keep going */} else {  // 调用主类的main方法,此处是RuntimeInit#mainenv->CallStaticVoidMethod(startClass, startMeth, strArray);#if 0if (env->ExceptionCheck())threadExitUncaughtException(env);
#endif}}free(slashClassName);// main 方法退出后销毁虚拟机ALOGD("Shutting down VM\n");if (mJavaVM->DetachCurrentThread() != JNI_OK)ALOGW("Warning: unable to detach main thread\n");if (mJavaVM->DestroyJavaVM() != 0)ALOGW("Warning: VM did not shut down cleanly\n");
}

RuntimeInit#main

做一些初始化,之后调用指定类的main方法

/// @frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
@UnsupportedAppUsage
public static final void main(String[] argv) {preForkInit();if (argv.length == 2 && argv[1].equals("application")) {if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");redirectLogStreams();} else {if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");}commonInit(); // 做一些基础初始化,比如设置异常处理器/** Now that we're running in interpreted code, call back into native code* to run the system.*/nativeFinishInit(); // native 方法if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
}

nativeFinishInit方法的实现在哪呢?可以看其对应jni函数注册函数,在AndroidRuntime.cpp中

/// @frameworks/base/core/jni/AndroidRuntime.cpp
int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{const JNINativeMethod methods[] = {{"nativeFinishInit", "()V",(void*)com_android_internal_os_RuntimeInit_nativeFinishInit},{"nativeSetExitWithoutCleanup", "(Z)V",(void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},};return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",methods, NELEM(methods));
}

com_android_internal_os_RuntimeInit_nativeFinishInit

/// @frameworks/base/core/jni/AndroidRuntime.cpp
/** Code written in the Java Programming Language calls here from main().*/
static void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz)
{gCurRuntime->onStarted(); // 回调AndroidRuntime的onStarted
}

AppRuntime#onStarted

AndroidRuntime的onStarted函数是纯虚函数,因此具体实现在其子类AppRuntime。
在onVmCreated加载指定的类,在onStarted中调用指定类的main方法

class AppRuntime : public AndroidRuntime
{public:...// 创建虚拟机后回调,如指定了主类,则会在此处去加载virtual void onVmCreated(JNIEnv* env){if (mClassName.isEmpty()) {return; // Zygote. Nothing to do here.}/** This is a little awkward because the JNI FindClass call uses the* class loader associated with the native method we're executing in.* If called in onStarted (from RuntimeInit.finishInit because we're* launching "am", for example), FindClass would see that we're calling* from a boot class' native method, and so wouldn't look for the class* we're trying to look up in CLASSPATH. Unfortunately it needs to,* because the "am" classes are not boot classes.** The easiest fix is to call FindClass here, early on before we start* executing boot class Java code and thereby deny ourselves access to* non-boot classes.*/char* slashClassName = toSlashClassName(mClassName.string());mClass = env->FindClass(slashClassName);if (mClass == NULL) {ALOGE("ERROR: could not find class '%s'\n", mClassName.string());}free(slashClassName);mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));}// 接上,gCurRuntime->onStarted 实际调用到此处。virtual void onStarted(){   // 初始化binder环境,开启binder线程池sp<ProcessState> proc = ProcessState::self();ALOGV("App process: starting thread pool.\n");proc->startThreadPool();AndroidRuntime* ar = AndroidRuntime::getRuntime();ar->callMain(mClassName, mClass, mArgs); // 调用主类(即com.android.commands.am.Am)的main方法,// 执行到此处,说明主main方法已退出,此时需要结束进程,进行一些清理。IPCThreadState::self()->stopProcess();hardware::IPCThreadState::self()->stopProcess();}...

AndroidRuntime::callMain

此处的className是com.android.commands.am.Am

frameworks/base/core/jni/AndroidRuntime.cpp
status_t AndroidRuntime::callMain(const String8& className, jclass clazz,const Vector<String8>& args)
{JNIEnv* env;jmethodID methodId;ALOGD("Calling main entry %s", className.string());env = getJNIEnv();if (clazz == NULL || env == NULL) {return UNKNOWN_ERROR;}// 获取主类的main方法idmethodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");if (methodId == NULL) {ALOGE("ERROR: could not find method %s.main(String[])\n", className.string());return UNKNOWN_ERROR;}/** We want to call main() with a String array with our arguments in it.* Create an array and populate it.*/jclass stringClass;jobjectArray strArray;// 构造参数列表const size_t numArgs = args.size();stringClass = env->FindClass("java/lang/String");strArray = env->NewObjectArray(numArgs, stringClass, NULL);for (size_t i = 0; i < numArgs; i++) {jstring argStr = env->NewStringUTF(args[i].string());env->SetObjectArrayElement(strArray, i, argStr);}// 调用主类com.android.commands.am.Am的main方法env->CallStaticVoidMethod(clazz, methodId, strArray);return NO_ERROR;
}

Am#main

/// @frameworks/base/cmds/am/src/com/android/commands/am/Am.java
/*** Command-line entry point.** @param args The command-line arguments*/
public static void main(String[] args) {(new Am()).run(args);
}

Am 继承 BaseCommand,会调用其run方法

/// @frameworks/base/core/java/com/android/internal/os/BaseCommand.java
/*** Call to run the command.*/
public void run(String[] args) {if (args.length < 1) {onShowUsage(System.out); // 没有指定参数,显示帮助return;}mRawArgs = args;mArgs.init(null, null, null, null, args, 0); // 初始化参数try {onRun(); /// 回调 Am#onRun} catch (IllegalArgumentException e) {onShowUsage(System.err);System.err.println();System.err.println("Error: " + e.getMessage());} catch (Exception e) {e.printStackTrace(System.err);System.exit(1);}
}

Am#onRun

@Override
public void onRun() throws Exception {mAm = ActivityManager.getService();if (mAm == null) {System.err.println(NO_SYSTEM_ERROR_CODE);throw new AndroidException("Can't connect to activity manager; is the system running?");}mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));if (mPm == null) {System.err.println(NO_SYSTEM_ERROR_CODE);throw new AndroidException("Can't connect to package manager; is the system running?");}String op = nextArgRequired();if (op.equals("instrument ")) {runInstrument();  // 指定 instrument 走此逻辑} else {runAmCmd(getRawArgs()); // 否则还是直接cmd,使用shellCommand}
}

Am#runInstrument

public void runInstrument() throws Exception {Instrument instrument = new Instrument(mAm, mPm);String opt;while ((opt=nextOption()) != null) {  // 参数解析if (opt.equals("-p")) {instrument.profileFile = nextArgRequired();} else if (opt.equals("-w")) {instrument.wait = true;} else if (opt.equals("-r")) {instrument.rawMode = true;} else if (opt.equals("-m")) {instrument.protoStd = true;} else if (opt.equals("-f")) {instrument.protoFile = true;if (peekNextArg() != null && !peekNextArg().startsWith("-"))instrument.logPath = nextArg();} else if (opt.equals("-e")) {final String argKey = nextArgRequired();final String argValue = nextArgRequired();instrument.args.putString(argKey, argValue);} else if (opt.equals("--no_window_animation")|| opt.equals("--no-window-animation")) {instrument.noWindowAnimation = true;} else if (opt.equals("--no-hidden-api-checks")) {instrument.disableHiddenApiChecks = true;} else if (opt.equals("--no-test-api-access")) {instrument.disableTestApiChecks = false;} else if (opt.equals("--no-isolated-storage")) {instrument.disableIsolatedStorage = true;} else if (opt.equals("--user")) {instrument.userId = parseUserArg(nextArgRequired());} else if (opt.equals("--abi")) {instrument.abi = nextArgRequired();} else if (opt.equals("--no-restart")) {instrument.noRestart = true;} else {System.err.println("Error: Unknown option: " + opt);return;}}if (instrument.userId == UserHandle.USER_ALL) {System.err.println("Error: Can't start instrumentation with user 'all'");return;}instrument.componentNameArg = nextArgRequired();instrument.run(); // 启动 instrument
}

Instrument#run

/// @frameworks/base/cmds/am/src/com/android/commands/am/Instrument.java
/*** Run the instrumentation.*/
public void run() throws Exception {StatusReporter reporter = null;float[] oldAnims = null;try {// Choose which output we will do.if (protoFile || protoStd) {reporter = new ProtoStatusReporter();} else if (wait) {reporter = new TextStatusReporter(rawMode);}// Choose whether we have to wait for the results.InstrumentationWatcher watcher = null;UiAutomationConnection connection = null;if (reporter != null) {watcher = new InstrumentationWatcher(reporter);connection = new UiAutomationConnection();}// Set the window animation if necessaryif (noWindowAnimation) {oldAnims = mWm.getAnimationScales();mWm.setAnimationScale(0, 0.0f);mWm.setAnimationScale(1, 0.0f);mWm.setAnimationScale(2, 0.0f);}// Figure out which component we are trying to do.final ComponentName cn = parseComponentName(componentNameArg);// Choose an ABI if necessaryif (abi != null) {final String[] supportedAbis = Build.SUPPORTED_ABIS;boolean matched = false;for (String supportedAbi : supportedAbis) {if (supportedAbi.equals(abi)) {matched = true;break;}}if (!matched) {throw new AndroidException("INSTRUMENTATION_FAILED: Unsupported instruction set " + abi);}}// Start the instrumentationint flags = 0;if (disableHiddenApiChecks) {flags |= INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;}if (disableTestApiChecks) {flags |= INSTR_FLAG_DISABLE_TEST_API_CHECKS;}if (disableIsolatedStorage) {flags |= INSTR_FLAG_DISABLE_ISOLATED_STORAGE;}if (noRestart) {flags |= INSTR_FLAG_NO_RESTART;}/// 关键点,通过startInstrumentation binder调用到AMS,之后AMS开始对某应用执行instrumentif (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,abi)) {throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());}// If we have been requested to wait, do so until the instrumentation is finished.if (watcher != null) {if (!watcher.waitForFinish()) {reporter.onError("INSTRUMENTATION_ABORTED: System has crashed.", false);return;}}} catch (Exception ex) {// Report failuresif (reporter != null) {reporter.onError(ex.getMessage(), true);}// And re-throw the exceptionthrow ex;} finally {// Clean upif (oldAnims != null) {mWm.setAnimationScales(oldAnims);}}
}

小结

大致总结一下上述流程:

  • exec app_process 启动虚拟机,执行com.android.internal.os.RuntimeInit的main方法。
  • RuntimeInit的main方法做一些初始化,并调用nativeFinishInit去执行Am的main方法
  • Am的main方法执行IActivityManager#startInstrumentation向AMS发起请求
  • AMS处理instrument请求。
  • Am的main方法执行结束后,停止虚拟机,app_process进程结束

Android12 am命令的使用及实现流程分析相关推荐

  1. Android12 应用启动流程分析

    最近因为一些业务上的需求,需要梳理 Android 应用的启动链路,从中寻找一些稳定的锚点来实现一些特殊的功能.本文即为对应用端启动全过程的一次代码分析记录. 注: 本文所分析的代码基于 AOSP a ...

  2. Alian解读SpringBoot 2.6.0 源码(三):启动流程分析之命令行参数解析

    目录 一.背景 1.1.run方法整体流程 1.2.本文解读范围 二.默认应用参数解析 2.1.接口ApplicationArguments 2.2.实现类DefaultApplicationArgu ...

  3. VLC架构及流程分析

    0x00 前置信息 VLC是一个非常庞大的工程,我从它的架构及流程入手进行分析,涉及到一些很细的概念先搁置一边,日后详细分析. 0x01 源码结构(Android Java相关的暂未分析) # bui ...

  4. DbgPrint 函数流程分析

    DbgPrint 函数流程分析 前言 Windows 下编写内核驱动时经常用到 DbgPrint 函数输出一些调试信息,用来辅助调试.当正在用 WinDbg 内核调试时,调试信息会输出到 WinDbg ...

  5. 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | dvmDexFileOpenPartial | dexFileParse | 脱壳点 | 获取 dex 文件在内存中的首地址 )

    文章目录 前言 一.DexPrepare.cpp 中 rewriteDex() 方法分析 二.DvmDex.cpp 中 dvmDexFileOpenPartial() 方法分析 ( 脱壳点 ) 三.D ...

  6. 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )

    文章目录 前言 一.DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 二./bin/dexopt 源码分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ...

  7. 内核启动流程分析(一)编译体验

    目录 2.打补丁 3.配置 总结 配置方式 配置体验 4.编译 链接 链接脚本 5.烧写内核 1.总体概述 2.打补丁 解压 tar xjf linux-2.6.22.6.tar.bz2 打补丁,ca ...

  8. 【网络安全】Metasploit生成的Shellcode的导入函数解析以及执行流程分析(2)

    密码破解的利器--彩虹表(rainbow table) 确定 shellcode 依赖于哪些导入将使研究人员进一步了解其其余逻辑.不用动态分析shellcode,并且考虑到研究人员已经弄清楚了上面的哈 ...

  9. python开源聊天机器人ChatterBot——聊天机器人搭建、流程分析、源码分析

    开源聊天机器人ChatterBot 3.1  ChatterBot简介 ChatterBot是一个Python库,可以轻松生成对用户输入的自动响应.ChatterBot使用一系列机器学习算法来产生不同 ...

最新文章

  1. 双系统如何删除Linux
  2. 阿里巴巴60万年薪抢毕业生 必须是公认技术牛人
  3. mysql的单行注释_MySQL基础--会这些就够了
  4. 项目管理理论与实践(1)——企业项目管理介绍
  5. Android NDK的生命周期JNI_OnLoad与JNI_OnUnload(转)
  6. java 抽象类 final_Java8 final关键字与抽象类
  7. VTK:可视化之HideActor
  8. 【Linux】一步一步学Linux——nslookup命令(161)
  9. 程序员面试金典 - 面试题 10.11. 峰与谷(排序/不排序)
  10. Vue、element-ui的resetFields()方法重置表单无效问题及解决办法
  11. android os5.0 优点,Funtouch OS升级5.0 性能大幅提升
  12. java计算图形面积实验_java源码——计算立体图形的表面积和体积
  13. 抖音被“逼”出个“视频朋友圈”
  14. Win10 永久关闭自动更新,禁止windows10自动更新
  15. 汽车年检,备忘一下,估计2年以后才有用了
  16. matlab相关值计算公式,相关系数计算公式(Correlation coefficient calculation formula).doc...
  17. python中lis的意思_Python语言入门(二)
  18. 在uni-app中如何使用一键登录,如何使用手机号一键登录
  19. javaScript-模块化开发
  20. python实现监听键盘

热门文章

  1. Python批量转存百度网盘资源
  2. 在王者荣耀角度下分析面向对象程序设计B中23种设计模式之访问者模式
  3. 双11蓝牙耳机哪个牌子好?双十一热销蓝牙耳机推荐
  4. 巴比特 | 元宇宙每日必读:奋起直追,谷歌内部发布“红色指令”,要求几个月内将生成式人工智能技术集成到所有主要产品中...
  5. 富华力鼎:短视频拍摄技巧有哪些
  6. 计算机科学系教师队伍建设,计算机专业“卓越计划”师资队伍建设
  7. 计算机电脑硬盘怎么删除内存,电脑虚拟内存怎么删除
  8. 苹果手机怎么打开.java文件,好文推荐
  9. 使用stc15F101w单片机,无法下载程序 ,提示程序被截断
  10. 你真的知道 ARGB_8888 格式图片的 A、R、G、B 每个通道的排列顺序吗?