Android12 am命令的使用及实现流程分析

Posted pecuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android12 am命令的使用及实现流程分析相关的知识,希望对你有一定的参考价值。

文章托管在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:
  help
      Print 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 not
          specified 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 not
          specified 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 not
          specified 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 not
          specified then send to all users.
      --receiver-permission <PERMISSION>: Require receiver to hold permission.
      --allow-background-activity-starts: The receiver may start activities
          even 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 may
      be 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.prof

Exception occurred while executing 'dumpheap':
java.lang.IllegalArgumentException: Unknown process: system_server
        at 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/sh

if [ "$1" != "instrument" ] ; then  // 第一个参数非 instrument
    cmd activity "$@"   // 通过 Binder#shellCommand 实现, $@ 表示后面的所有参数
else  // 执行 app_process 启动虚拟机执行 Am 类
    base=/system
    export CLASSPATH=$base/framework/am.jar
    exec 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]);
    
    // 直接调用 cmdMain
    return 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 DEBUG
    ALOGD("cmd: starting");
#endif
    sp<IServiceManager> sm = defaultServiceManager(); // 获取 ServiceManager
    if (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 DEBUG
    ALOGD("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 的 shellCommand
    status_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 DEBUG
    ALOGD("result=%d", (int)res);
#endif
    return 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;
    // 写入 输入/输出/错误fd
    send.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 kernel
        flags = flags & ~FLAG_PRIVATE_VENDOR;

        // user transactions require a given stability level
        if (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 发起 transact
            status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
        
        // 通信返回状态码DEAD_OBJECT,说明服务已经挂了,将mAlive置为0
        if (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_TRANSACTION
    err = 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 以上是关于Android12 am命令的使用及实现流程分析的主要内容,如果未能解决你的问题,请参考以下文章

Android中AM、PM、dumpsys命令使用总结

Android 插件化Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )

android 11/12的 framework 框架systemserver源码中的AMS和WMS部分ProtoLog相关log的开放命令

android 11/12的 framework 框架systemserver源码中的AMS和WMS部分ProtoLog相关log的开放命令

Activity的启动流程分析

AMSActivity 启动流程详解