adb shell源码浅析

adb常用命令一文中有提到adb shell的指令在/system/bin路径下,现在本文以am指令为例,解析一条指令的执行流程

以 adb shell am broadcast -a zhihe.factorytest.action.PASS -f 0x1000000 为例

在android11中用adb发送广播不加后面的 -f 0x1000000,应用中接受不到广播,也顺便追溯一下 -f  0x1000000带表的什么意思

1.am的源码路径

 如图所示在frameworks/base/cmds/路径下看到了很多平时常用的命令程序am,pm等指令

2.源码流程

程序主入口为Am的main函数

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

public class Am extends BaseCommand {

    private IActivityManager mAm;
    private IPackageManager mPm;

    /**
     * Command-line entry point.
     *
     * @param args The command-line arguments
     */
    public static void main(String[] args) {
        (new Am()).run(args);
    }
    ...
}    

Am继承了BaseCommand,在Am中没有run这个方法,看看他的父类BaseCommand的run方法做了什么

./frameworks/base/core/java/com/android/internal/os/BaseCommand.java

public abstract class BaseCommand {

	...
    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();
        } 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);
        }
    }
    ...
}  

在BaseCommand的run方法中,判断了如果参数长度小于1,就会调onShowUsage方法(什么样的情况是参数小于1呢,例如adb shell am broadcast -a zhihe.factorytest.action.PASS -f 0x1000000中am是指令后面跟着的broadcast -a zhihe.factorytest.action.PASS -f 0x1000000才是参数,如果输入adb shell am就是参数长度小于1)。把参数存在mRawArgs中,和调onRun方法

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

public class Am extends BaseCommand {

	...
    @Override
    public void onShowUsage(PrintStream out) {
        try {
            runAmCmd(new String[] { "help" });//------>发送help指令
        } catch (AndroidException e) {
            e.printStackTrace(System.err);
        }
    }

    @Override
    public void onRun() throws Exception {

        mAm = ActivityManager.getService();//------>初始化mAm
        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();
        } else {
            runAmCmd(getRawArgs());//------>发送全参数指令
        }
    }
    ...
    
    void runAmCmd(String[] args) throws AndroidException {
        final MyShellCallback cb = new MyShellCallback();
        try {
            mAm.asBinder().shellCommand(FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
                    args, cb, new ResultReceiver(null) { });
        } catch (RemoteException e) {
            System.err.println(NO_SYSTEM_ERROR_CODE);
            throw new AndroidException("Can't call activity manager; is the system running?");
        } finally {
            cb.mActive = false;
        }
    }
    ...
}    

Am重载onShowUsage方法,就是发了一个help的指令,在重载onRun中发了一个全参数的指令,可以看到发送指令的是mAm.asBinder().shellCommand这样去发送的,其实这里mAm.asBinder()可以猜测到这个就是 ActivityManagerService,如何通过代码mAm.asBinder()就是ActivityManagerService呢?在onRun中mAm = ActivityManager.getService();

./frameworks/base/core/java/android/app/ActivityManager.java

public class ActivityManager {

	...
    /**
     * @hide
     */
    @UnsupportedAppUsage
    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static IActivityTaskManager getTaskService() {
        return ActivityTaskManager.getService();
    }

    @UnsupportedAppUsage
    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };
    ...
}  

am就是被保存到ServiceManager的名字为Context.ACTIVITY_SERVICE的一个服务,但是在ActivityManagerService中没有找到shellCommand这个方法,只能找它的父类Binder了

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

    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);
    }

调了onShellCommand,这个方法ActivityManagerService有重载

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

public class ActivityManagerService extends IActivityManager.Stub

	...
    public void setSystemProcess() {
        try {
            ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
            ...        
        }
        ...
    }
	...
    @Override
    public void onShellCommand(FileDescriptor in, FileDescriptor out,
            FileDescriptor err, String[] args, ShellCallback callback,
            ResultReceiver resultReceiver) {
        (new ActivityManagerShellCommand(this, false)).exec(
                this, in, out, err, args, callback, resultReceiver);
    }
    ...
}   

在setSystemProcess方法中可以看到在初始化的过程中确实是把ActivityManagerService以Context.ACTIVITY_SERVICE的名字保存到ServiceManager中去了,onShellCommand中又把具体的事情交给ActivityManagerShellCommand去做了,但是ActivityManagerShellCommand没有重载exec,他的父类ShellCommand的exec又调了父类BasicShellCommandHandler的exec,BasicShellCommandHandler的exec中调了onCommand,而ActivityManagerShellCommand重载了此方法

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

final class ActivityManagerShellCommand extends ShellCommand {

	...
	    @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 "stopservice":
                case "stop-service":
                    return runStopService(pw);
                case "broadcast"://---------------------->正式我们追踪的参数
                    return runSendBroadcast(pw);
                default:
                    return handleDefaultCommands(cmd);
            }
        } catch (RemoteException e) {
            pw.println("Remote exception: " + e);
        }
        return -1;
    }
    ...
    int runSendBroadcast(PrintWriter pw) throws RemoteException {
        Intent intent;
        try {
            intent = makeIntent(UserHandle.USER_CURRENT);//---------------------->看看intent包含了什么
        } catch (URISyntaxException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        intent.addFlags(Intent.FLAG_RECEIVER_FROM_SHELL);
        IntentReceiver receiver = new IntentReceiver(pw);
        String[] requiredPermissions = mReceiverPermission == null ? null
                : new String[] {mReceiverPermission};
        pw.println("Broadcasting: " + intent);
        pw.flush();
        Bundle bundle = mBroadcastOptions == null ? null : mBroadcastOptions.toBundle();
        mInterface.broadcastIntentWithFeature(null, null, intent, null, receiver, 0, null, null,
                requiredPermissions, android.app.AppOpsManager.OP_NONE, bundle, true, false,
                mUserId);//--------------->发送广播
        receiver.waitForFinish();
        return 0;
    }
    
    private Intent makeIntent(int defUser) throws URISyntaxException {
        mStartFlags = 0;
        mWaitOption = false;
        mStopOption = false;
        mRepeat = 0;
        mProfileFile = null;
        mSamplingInterval = 0;
        mAutoStop = false;
        mStreaming = false;
        mUserId = defUser;
        mDisplayId = INVALID_DISPLAY;
        mWindowingMode = WINDOWING_MODE_UNDEFINED;
        mActivityType = ACTIVITY_TYPE_UNDEFINED;
        mTaskId = INVALID_TASK_ID;
        mIsTaskOverlay = false;
        mIsLockTask = false;
        mBroadcastOptions = null;

        return Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() {
            @Override
            public boolean handleOption(String opt, ShellCommand cmd) {
				...
            }
        });
    }
}  

 在onCommand中可以看到我们的参数如果是broadcast,就会调runSendBroadcast,在runSendBroadcast中会把广播发送出去,这就完成了adb发送广播的流程,前面提到的-f是什么呢,在runSendBroadcast中有一个makeIntent,在makeIntent中又会进一步的解析参数,Intent.parseCommandArgs

./frameworks/base/core/java/android/content/Intent.java

public class Intent implements Parcelable, Cloneable {

	...
    /** @hide */
    @UnsupportedAppUsage
    public static Intent parseCommandArgs(ShellCommand cmd, CommandOptionHandler optionHandler)
            throws URISyntaxException {
        Intent intent = new Intent();
        Intent baseIntent = intent;
        boolean hasIntentInfo = false;

        Uri data = null;
        String type = null;

        String opt;
        while ((opt=cmd.getNextOption()) != null) {
            switch (opt) {
				...
                case "-f":
                    String str = cmd.getNextArgRequired();
                    intent.setFlags(Integer.decode(str).intValue());
                    break;
                ...    
    }
}    

 在parseCommandArgs中可以看到-f代表的的是flags,在Intent中0x1000000的定义为如下

    /**
     * If set, the broadcast will always go to manifest receivers in background (cached
     * or not running) apps, regardless of whether that would be done by default.  By
     * default they will only receive broadcasts if the broadcast has specified an
     * explicit component or package name.
     *
     * NOTE: dumpstate uses this flag numerically, so when its value is changed
     * the broadcast code there must also be changed to match.
     *
     * @hide
     */
    public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;

<think>好的,用户想了解ADB Shell源码。首先,我需要确认ADB是什么,以及ADB Shell在其中的角色。ADBAndroid Debug Bridge,属于Android SDK的一部分,主要用于调试和与设备通信。ADB Shell则是通过命令行与设备交互的工具。 接下来,用户需要源码介绍,可能需要结构、关键模块或如何参与开发。我应该先概述ADB的整体结构,然后深入Shell部分。源码可能在AOSP的哪个目录?记得ADB代码在platform/system/core/adb下。 ADB分为客户端、服务端和守护进程adbd。Shell的实现涉及客户端发送命令,adbd处理并返回结果。需要解释这些组件如何协作,比如adb client解析命令,通过USB或网络与adbd通信,然后执行shell命令。 关键函数和类可能包括处理命令的shell_service,或者像start_shell函数。还要提到不同Android版本的差异,比如旧版用fork+exec,新版可能用shell协议。 用户可能的背景是开发者或学生,想要学习或贡献代码。可能需要指导如何下载源码,建议查看官方文档,或者调试方法。也可能需要注意许可证问题,Apache 2.0。 是否需要举例子,比如执行adb shell ls的流程?或者指出代码中的关键文件,比如commandline.cpp、adb_client.cpp,以及adbd中的shell_service.cpp? 还要考虑用户可能的深层需求,比如定制ADB功能,或者理解其工作原理以解决问题。确保回答既全面又不至于太技术化,用中文解释清楚。</think>ADBAndroid Debug Bridge)是 Android 开发工具链中的核心组件,`adb shell` 是其子命令,用于在连接的 Android 设备上执行 Shell 命令。以下是 **ADB Shell 源码的简要解析**: --- ### 源码位置 ADB 的源代码属于 **AOSP(Android Open Source Project)** 的一部分,主要位于: ``` platform/system/core/adb/ ``` - **关键文件**: - `adb_client.cpp`: 客户端命令处理(如 `adb shell` 的触发) - `adb.cpp`: ADB 主逻辑 - `services.cpp`: ADB 服务管理(包括 Shell 服务) - `commandline.cpp`: 命令行参数解析 - `shell_protocol.h`: Android 9+ 引入的 Shell 协议定义(优化交互) --- ### 核心流程解析 当执行 `adb shell [command]` 时,源码逻辑如下: 1. **客户端发起请求** - `adb_client.cpp` 解析命令,调用 `adb_shell()` 函数。 - 通过 `_adb_connect()` 建立与设备端 `adbd` 守护进程的连接。 2. **服务端处理(adbd)** - `adbd`(位于设备端)监听客户端请求,匹配服务名为 `shell` 的请求。 - 在 `services.cpp` 中注册 Shell 服务处理函数 `shell_service()`。 3. **创建 Shell 会话** - 若 Android 版本 ≥ 9,使用 `shell_protocol.h` 定义的协议进行交互(结构化数据传输)。 - 低版本通过 `fork()` + `exec()` 启动 `/system/bin/sh` 进程,通过管道(Pipe)或伪终端(PTY)与客户端通信。 4. **数据传输** - 客户端通过 Socket 发送输入数据(如命令),并接收设备返回的输出流。 - 使用 `select()` 或 `poll()` 多路复用机制管理 I/O。 --- ### 关键代码片段 ```cpp // services.cpp 中的 Shell 服务注册 void shell_service(int fd, const char* args) { // 解析参数(如是否使用交互式终端) bool use_pty = (strcmp(args, "-t") == 0); // 启动 Shell 进程 launch_shell(fd, use_pty); } ``` --- ### 扩展知识 1. **adbd 的权限控制** - 普通设备需解锁 `ro.debuggable=1` 才能运行完整 adbd。 - 通过 SELinux 策略限制 Shell 用户权限。 2. **Shell 协议优化** - Android 9 引入二进制协议替代纯文本,支持更高效的数据传输(如文件传输、退出码返回)。 3. **调试技巧** - 编译自定义 adb:通过 AOSP 修改源码后,执行 `make adb`。 - 使用 `adb shell --trace` 可打印协议级日志。 --- ### 学习建议 1. **阅读源码**:从 `adb shell` 入口逐步跟踪代码流。 2. **调试实践**:修改 adb 源码并刷入设备,观察行为变化。 3. **参考文档**:查阅 [Google ADB 实现文档](https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/main/docs/) 和内核的 PTY 机制。 如需深入某个模块(如 Shell 协议加密或权限控制),可进一步探讨!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值