zoukankan      html  css  js  c++  java
  • debug:am、cmd命令源码分析

    debug:am、cmd命令源码分析

    am命令的实现

    手机里的am

    :/ # which am
    /system/bin/am
    :/ # file /system/bin/am
    /system/bin/am: /system/bin/sh script
    :/ # cat am 
    #!/system/bin/sh
    
    if [ "$1" != "instrument" ] ; then
        cmd activity "$@"
    else
        base=/system
        export CLASSPATH=$base/framework/am.jar
        exec app_process $base/bin com.android.commands.am.Am "$@"
    fi
    

    aosp里的am代码位置

    frameworks/base/cmds/am$

    am命令是个shell脚本,非instrument时调用cmd命令。am.jar也在手机里,/system/framework/am.jar

    am.jar

    只有俩文件

    frameworks/base/cmds/am/src/com/android/commands/am$ ls
    Am.java  Instrument.java
    

    frameworks/base/cmds/am/src/com/android/commands/am/Am.java

     39 public class Am extends BaseCommand {
     49     public static void main(String[] args) {
     50         (new Am()).run(args);
     51     }     
    ------------------------------------------------------------------
     62     @Override
     63     public void onRun() throws Exception {
     64 
     65         mAm = ActivityManager.getService();
     71         mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
     77         String op = nextArgRequired();
     78 
     79         if (op.equals("instrument")) {
     80             runInstrument();
     81         } else {
     82             runAmCmd(getRawArgs());
    ------------------------------------------------------------------
    138     void runAmCmd(String[] args) throws AndroidException {
    139         final MyShellCallback cb = new MyShellCallback();
    140         try {
    141             mAm.asBinder().shellCommand(FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,                                  
    142                     args, cb, new ResultReceiver(null) { });
    143         } catch (RemoteException e) {
    

    50行的run是在父类BaseCommand里,然后又分发到子类onRun方法

    如果是通过am命令来启动的话,这里只会走到80行,但是,am.jar也是可以完全支持cmd activity相同的效果。

    直接运行am.jar时就走到82行了,在141行binder沟通ams。

    cmd命令的实现

    手机里的cmd

    :/ # which cmd
    /system/bin/cmd
    :/ # file /system/bin/cmd
    /system/bin/cmd: ELF shared object, 64-bit LSB arm64, dynamic (/system/bin/linker64), for Android 30, BuildID=ab0acb6f2e4ab587eb5166cd5c29e254, stripped
    

    cmd是个二进制可执行程序

    aosp里的cmd代码位置

    frameworks/native/cmds/cmd

    cmd activity

    cmd activity发生了什么

    frameworks/native/cmds/cmd/main.cpp

    21 int main(int argc, char* const argv[]) {
     22     signal(SIGPIPE, SIG_IGN);
     23 
     24     std::vector<std::string_view> arguments;
     25     arguments.reserve(argc - 1);
     26     // 0th argument is a program name, skipping.
     27     for (int i = 1; i < argc; ++i) {
     28         arguments.emplace_back(argv[i]);
     29     }
     30 
     31     return cmdMain(arguments, android::aout, android::aerr, STDIN_FILENO, STDOUT_FILENO,
     32                    STDERR_FILENO, RunMode::kStandalone);
     33 }
    

    cmd.cpp#cmdMain

    啥都没干,跳到cmd.cpp#cmdMain

    frameworks/native/cmds/cmd/cmd.cpp

    167 int cmdMain(const std::vector<std::string_view>& argv, TextOutput& outputLog, TextOutput& errorLog,
    168             int in, int out, int err, RunMode runMode) {
    169     sp<ProcessState> proc = ProcessState::self();
    170     proc->startThreadPool();
    175     sp<IServiceManager> sm = defaultServiceManager();
    215     sp<IBinder> service;
    216     if(waitForService) {
    217         service = sm->waitForService(serviceName);
    218     } else {
    219         service = sm->checkService(serviceName);
    220     }
    239     status_t error = IBinder::shellCommand(service, in, out, err, args, cb, result);
    

    169、170、171行老三样,沟通binder驱动,拿到ServiceManager的代理

    217、219行拿到传入参数的binder代理。我们传的是activity,所以这就是ams的代理。

    239行,通过代理发起ipc调用。

    Binder.cpp#shellCommand

    frameworks/native/libs/binder/Binder.cpp

     66 status_t IBinder::shellCommand(const sp<IBinder>& target, int in, int out, int err,
     67     Vector<String16>& args, const sp<IShellCallback>& callback,
     68     const sp<IResultReceiver>& resultReceiver)
     69 {
     70     Parcel send;
     71     Parcel reply;
     72     send.writeFileDescriptor(in);
     73     send.writeFileDescriptor(out);
     74     send.writeFileDescriptor(err);
     75     const size_t numArgs = args.size();
     76     send.writeInt32(numArgs);
     77     for (size_t i = 0; i < numArgs; i++) {
     78         send.writeString16(args[i]);
     79     }
     80     send.writeStrongBinder(callback != nullptr ? IInterface::asBinder(callback) : nullptr);
     81     send.writeStrongBinder(resultReceiver != nullptr ? IInterface::asBinder(resultReceiver) : nullptr);
     82     return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply);                   
     83 }
    

    binder的流转细节就不说了,直接跳到服务端,看针对这个命令的处理SHELL_COMMAND_TRANSACTION。

    Binder.java#onTransact

    frameworks/base/core/java/android/os/Binder.java

     782     protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
     783             int flags) throws RemoteException {
     804         } else if (code == SHELL_COMMAND_TRANSACTION) {
     805             ParcelFileDescriptor in = data.readFileDescriptor();
     806             ParcelFileDescriptor out = data.readFileDescriptor();
     807             ParcelFileDescriptor err = data.readFileDescriptor();
     808             String[] args = data.readStringArray();
     812                 if (out != null) {
     813                     shellCommand(in != null ? in.getFileDescriptor() : null,
     814                             out.getFileDescriptor(),
     815                             err != null ? err.getFileDescriptor() : out.getFileDescriptor(),
     816                             args, shellCallback, resultReceiver);       
    ------------------------------------------------------------
     925     public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
     926             @Nullable FileDescriptor err,
     927             @NonNull String[] args, @Nullable ShellCallback callback,
     928             @NonNull ResultReceiver resultReceiver) throws RemoteException {
     929         onShellCommand(in, out, err, args, callback, resultReceiver);                   
    

    929行的实现在AMS里

    ActivityManagerService.java#onShellCommand

    frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

    10501     @Override
    10502     public void onShellCommand(FileDescriptor in, FileDescriptor out,                 
    10503             FileDescriptor err, String[] args, ShellCallback callback,
    10504             ResultReceiver resultReceiver) {
    10505         (new ActivityManagerShellCommand(this, false)).exec(
    10506                 this, in, out, err, args, callback, resultReceiver);
    10507     }
    

    继续跟10505

    public abstract class ShellCommand extends BasicShellCommandHandler {
    final class ActivityManagerShellCommand extends ShellCommand {  
    

    看一下继承关系,这里需要注意的是,10505的exec是调用到父类的父类,然后又返回返回到ActivityManagerShellCommand的onCommand

    ActivityManagerShellCommand#onCommand

    frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand

     176     @Override
     177     public int onCommand(String cmd) {      
     183             switch (cmd) {
     184                 case "start":
     185                 case "start-activity":
     186                     return runStartActivity(pw);
    ......
     203                 case "trace-ipc":
     204                     return runTraceIpc(pw);
    ......
    

    总结

    总体流程比较简单,需要关注的点有

    • am和cmd的binder客户端实现,也就是怎么写一个binder客户端(java、native)。

    • binder的流转,对端在哪

    • 最后是shellcommand的命令分发。

    以此做参考,可以添加一些自己的命令与实现,做一些调试小工具。

    作者:秋城 | 博客:https://www.cnblogs.com/houser0323 | 转载请注明作者出处
  • 相关阅读:
    C#单纯的字母数字ASCII码转换
    解析类型后加问号和双问号
    【转】composer autoload 自动加载性能优化指南
    【转】Laravel 三种中间件作用讲解
    【转】Laravel belongsTo 详解
    【转】docker-compose详解
    【转】laravel之Artisan命令操作Artisan Console
    【转】Shell中>/dev/null 2>&1 详解
    【转】docker-entrypoint.sh 文件的用处
    【转】解决Debian下sudo命令unable to initialize PAM问题
  • 原文地址:https://www.cnblogs.com/houser0323/p/15067133.html
Copyright © 2011-2022 走看看