Android中广播可以adb调试发送,am broadcast,本文主要分析这个广播源码。

用命令发送广播

这里不展开说明参数,具体可以参考这里解析。

1$ adb shell am broadcast -a android.intent.action.SENDLOVE -n com.example.broadcast/.MyTanabataReceiver –es “love” “爱你” –ei “days” 10000 –ez “reality” true
2$ adb shell cmd activity broadcast -a android.intent.action.SENDLOVE -n com.example.broadcast/.MyTanabataReceiver –es “love” “爱你” –ei “days” 10000 –ez “reality” true

例如-a指定action-d指定uri-p指定包名,-n指定组件名,--es指定{key,value}

--es 表示使用字符串类型,参数 --ei表示int类型,参数--ez表示boolean类型

1am

am命令是个shell脚本,非instrument时调用cmd命令。其中调用的jar路径是设备上的/system/framework/am.jar

 1#/frameworks/base/cmds/am/am
 2#!/system/bin/sh
 3
 4if [ "$1" != "instrument" ] ; then
 5    cmd activity "$@"
 6else
 7    base=/system
 8    export CLASSPATH=$base/framework/am.jar
 9    #这里的app_process实际上对应的就是zygote,fork and exec一个进程,执行/system/bin com.android.commands.am.Am "$@"
10    exec app_process $base/bin com.android.commands.am.Am "$@"
11fi

可以简单的看到,当am脚本后面的参数不为instrument时候,实际上执行的命令是cmd activity "$@",其中"$@"就是后面的参数集。

后续在bp文件中可以看到,/system/bin com.android.commands.am.Am实际是src/**/*.java文件中的调用

 1//frameworks/base/cmds/am/Android.bp
 2java_binary {
 3    name: "am",
 4    wrapper: "am",
 5    srcs: [
 6        "src/**/*.java",
 7        "proto/**/*.proto",
 8    ],
 9    proto: {
10        plugin: "javastream",
11    },
12    static_libs: ["libprotobuf-java-lite"],
13}
 1vince:/ $ cmd activity
 2Activity manager (activity) commands:
 3  help
 4      Print this help text.
 5  start-activity [-D] [-N] [-W] [-P <FILE>] [--start-profiler <FILE>]
 6          [--sampling INTERVAL] [--streaming] [-R COUNT] [-S]
 7          [--track-allocation] [--user <USER_ID> | current] <INTENT>
 8      Start an Activity.  Options are:
 9      -D: enable debugging
10      -N: enable native debugging
11      -W: wait for launch to complete
12---------------------------------------
13  broadcast [--user <USER_ID> | all | current] <INTENT>
14      Send a broadcast Intent.  Options are:
15      --user <USER_ID> | all | current: Specify which user to send to; if not
16          specified then send to all users.
17      --receiver-permission <PERMISSION>: Require receiver to hold permission.
18--------------------------------------- 
19    [-f <FLAG>]
20    [--grant-read-uri-permission] [--grant-write-uri-permission]
21    [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]
22    [--debug-log-resolution] [--exclude-stopped-packages]
23    [--include-stopped-packages]
24    [--activity-brought-to-front] [--activity-clear-top]
25    [--activity-clear-when-task-reset] [--activity-exclude-from-recents]
26    [--activity-launched-from-history] [--activity-multiple-task]
27    [--activity-no-animation] [--activity-no-history]
28    [--activity-no-user-action] [--activity-previous-is-top]
29    [--activity-reorder-to-front] [--activity-reset-task-if-needed]
30    [--activity-single-top] [--activity-clear-task]
31    [--activity-task-on-home]
32    [--receiver-registered-only] [--receiver-replace-pending]
33    [--receiver-foreground] [--receiver-no-abort]
34    [--receiver-include-background]
35    [--selector]
36    [<URI> | <PACKAGE> | <COMPONENT>]

因为这里实际上是广播,对其他的命令做了不必要的省略。这里的activity就是服务,可以理解成类似dumpsys出来的众多服务之一,可以通过cmd命令来调用。

2cmd

不过这个cmd命令比较特殊,通常而言一般服务直接调用名字即可,不过这里需要通过cmd命令再来调用到服务。本质是前者直接调用是C/C++偏底层的服务,可以直接通过进程的方式直接使用,后者的话借助了Android的封装间接调用,这个属于Java偏上层的服务。

 1//frameworks/native/cmds/cmd/main.cpp
 2int main(int argc, char* const argv[]) {
 3    //忽略管道相关信号
 4    signal(SIGPIPE, SIG_IGN);
 5
 6    std::vector<std::string_view> arguments;
 7    //这里的reserve是预留大小,并没有分配大小
 8    arguments.reserve(argc - 1);
 9    //这里的arguments就是除了cmd之外所有的args
10    for (int i = 1; i < argc; ++i) {
11        arguments.emplace_back(argv[i]);
12    }
13	//最终调用到cmdMain
14    return cmdMain(arguments, android::aout, android::aerr, STDIN_FILENO, STDOUT_FILENO,
15                   STDERR_FILENO, RunMode::kStandalone);
16}   
  1. std::string_view属于C++ 17的特性

    std::string_view来获取一个字符串的视图,字符串视图并不真正的创建或者拷贝字符串,而只是拥有一个字符串的查看功能。std::string_view比std::string的性能要高很多,因为每个std::string都独自拥有一份字符串的拷贝,而std::string_view只是记录了自己对应的字符串的指针和偏移位置。当我们在只是查看字符串的函数中可以直接使用std::string_view来代替std::string。

    可以把原始的字符串当作一条马路,而我们是在马路边的一个房子里,我们只能通过房间的窗户来观察外面的马路。这个房子就是std::string_view,你只能看到马路上的车和行人,但是你无法去修改他们,可以理解你对这个马路是只读的。

  2. 关于resize()和reserve()区别

    打个比方:正在建造的一辆公交车,车里面可以设置40个座椅(reserve(40);),这是它的容量,但并不是说它里面就有了40个座椅,只能说明这部车内部空间大小可以放得下40张座椅而已。而车里面安装了40个座椅(resize(40);),这个时候车里面才真正有了40个座椅,这些座椅就可以使用了

 1//frameworks/native/cmds/cmd/cmd.cpp
 2//这里需要记住C++风格的特点,返回不为0,说明是有错误的,只有返回0才是正确的
 3int cmdMain(const std::vector<std::string_view>& argv, TextOutput& outputLog, TextOutput& errorLog,
 4            int in, int out, int err, RunMode runMode) {
 5    sp<ProcessState> proc = ProcessState::self();
 6    proc->startThreadPool();
 7	//获取默认的servicemanager大管家
 8    sp<IServiceManager> sm = defaultServiceManager();
 9    if (runMode == RunMode::kStandalone) {
10        fflush(stdout);
11    }
12    if (sm == nullptr) {
13        ALOGW("Unable to get default service manager!");
14        return 20;
15    }
16
17    int argc = argv.size();
18
19    if (argc == 0) {
20        return 20;
21    }
22	//罗列所有的服务,这里用到了checkService,这个非阻塞的服务查询
23    if ((argc == 1) && (argv[0] == "-l")) {
24        Vector<String16> services = sm->listServices();
25        services.sort(sort_func);
26        outputLog << "Currently running services:" << endl;
27
28        for (size_t i=0; i<services.size(); i++) {
29            sp<IBinder> service = sm->checkService(services[i]);
30            if (service != nullptr) {
31                outputLog << "  " << services[i] << endl;
32            }
33        }
34        return 0;
35    }
36
37    bool waitForService = ((argc > 1) && (argv[0] == "-w"));
38    int serviceIdx = (waitForService) ? 1 : 0;
39    //这个默认serviceIdx为0,那么就是 const auto cmd = argv[0];即为activity
40    const auto cmd = argv[serviceIdx];
41	
42    Vector<String16> args;
43    String16 serviceName = String16(cmd.data(), cmd.size());
44    //把参数添加到args中,args为broadcast -a android.intent.action.SENDLOVE -n com.example.broadcast/.MyTanabataReceiver –es “love” “爱你” –ei “days” 10000 –ez “reality” true
45    for (int i = serviceIdx + 1; i < argc; i++) {
46        args.add(String16(argv[i].data(), argv[i].size()));
47    }
48    sp<IBinder> service;
49    if(waitForService) {
50        service = sm->waitForService(serviceName);
51    } else {
52        //通过binder直接用sm大管家查询叫activity的服务
53        service = sm->checkService(serviceName);
54    }
55
56    if (service == nullptr) {
57        errorLog << "cmd: Can't find service: " << cmd << endl;
58        return 20;
59    }
60	//通常cb都是回调,这里设置错误信息的回调
61    sp<MyShellCallback> cb = new MyShellCallback(errorLog);
62    sp<MyResultReceiver> result = new MyResultReceiver();
63
64    // 这个同步命令,进行阻塞,直到有MyResultReceiver类的结果产生
65    //通过代理发起ipc调用
66    //其中service指的是上述BpBinder传递的activity的service,in是STDIN_FILENO,out是STDOUT_FILENO
67    //err是STDERR_FILENO,cb是上述的回调
68    status_t error = IBinder::shellCommand(service, in, out, err, args, cb, result);
69    if (error < 0) {
70        const char* errstr;
71        switch (error) {
72            case BAD_TYPE: errstr = "Bad type"; break;
73            case FAILED_TRANSACTION: errstr = "Failed transaction"; break;
74            case FDS_NOT_ALLOWED: errstr = "File descriptors not allowed"; break;
75            case UNEXPECTED_NULL: errstr = "Unexpected null"; break;
76            default: errstr = strerror(-error); break;
77        }
78        //如果shellCommand执行失败,会打印到这里
79        if (runMode == RunMode::kStandalone) {
80            ALOGW("Failure calling service %.*s: %s (%d)", static_cast<int>(cmd.size()), cmd.data(),
81                  errstr, -error);
82        }
83        outputLog << "cmd: Failure calling service " << cmd << ": " << errstr << " (" << (-error)
84                  << ")" << endl;
85        return error;
86    }
87
88    cb->mActive = false;
89    status_t res = result->waitForResult();
90    return res;
91}

上述的shellCommand执行时阻塞的,需要等待这个函数执行完成

 1//frameworks/native/libs/binder/Binder.cpp
 2status_t IBinder::shellCommand(const sp<IBinder>& target, int in, int out, int err,
 3    Vector<String16>& args, const sp<IShellCallback>& callback,
 4    const sp<IResultReceiver>& resultReceiver)
 5{
 6    Parcel send;
 7    Parcel reply;
 8    //in,out,error,分别为标准输入设备,标准输除设备,标准错误设备
 9    send.writeFileDescriptor(in);
10    send.writeFileDescriptor(out);
11    send.writeFileDescriptor(err);
12    //使用Parcel方式序列化参数broadcast -a android.intent.action.SENDLOVE -n com.example.broadcast/.MyTanabataReceiver –es “love” “爱你” –ei “days” 10000 –ez “reality” true
13    const size_t numArgs = args.size();
14    send.writeInt32(numArgs);
15    for (size_t i = 0; i < numArgs; i++) {
16        send.writeString16(args[i]);
17    }
18    send.writeStrongBinder(callback != nullptr ? IInterface::asBinder(callback) : nullptr);
19    send.writeStrongBinder(resultReceiver != nullptr ? IInterface::asBinder(resultReceiver) : nullptr);
20    //与服务端建立通信,IPC调用,通过command值为SHELL_COMMAND_TRANSACTION查找
21    return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply);
22}

onTransact

由于服务端ActivityManagerService没有SHELL_COMMAND_TRANSACTION命令,只能从继承的AIDL中的Binder.java去找

 1//frameworks/base/core/java/android/os/Binder.java
 2protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
 3                             int flags) throws RemoteException {
 4    ...
 5    else if (code == SHELL_COMMAND_TRANSACTION) {
 6        ParcelFileDescriptor in = data.readFileDescriptor();
 7        ParcelFileDescriptor out = data.readFileDescriptor();
 8        ParcelFileDescriptor err = data.readFileDescriptor();
 9        String[] args = data.readStringArray();
10        ShellCallback shellCallback = ShellCallback.CREATOR.createFromParcel(data);
11        ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);
12        try {
13            if (out != null) {
14                //正常执行回到这里,调用shellCommand
15                shellCommand(in != null ? in.getFileDescriptor() : null,
16                             out.getFileDescriptor(),
17                             err != null ? err.getFileDescriptor() : out.getFileDescriptor(),
18                             args, shellCallback, resultReceiver);
19            }
20        } finally {
21            ...
22        }
23        return true;
24    }
25    return false;
26}

shellCommand会继续调用onShellCommand

1//frameworks/base/core/java/android/os/Binder.java
2public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
3                         @Nullable FileDescriptor err,
4                         @NonNull String[] args, @Nullable ShellCallback callback,
5                         @NonNull ResultReceiver resultReceiver) throws RemoteException {
6    onShellCommand(in, out, err, args, callback, resultReceiver);
7}

调用到服务端的ActivityManagerService中的onShellCommand

 1//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
 2@Override
 3public void onShellCommand(FileDescriptor in, FileDescriptor out,
 4                           FileDescriptor err, String[] args, ShellCallback callback,
 5                           ResultReceiver resultReceiver) {
 6    //这里的false指的是dumpsys中的函数,这里不dump关于AMS的信息
 7    //这里的new ActivityManagerShellCommand会初始化mInterface,mInterface就是this
 8    (new ActivityManagerShellCommand(this, false)).exec(
 9        this, in, out, err, args, callback, resultReceiver);
10}

由于类图的关系如上所示,所以调用exec实际上是抽象类BasicShellCommandHandler中的方法

 1//frameworks/base/core/java/android/os/BasicShellCommandHandler.java
 2public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
 3                String[] args) {
 4    String cmd;
 5    int start;
 6    if (args != null && args.length > 0) {
 7        //cmd实际上是broadcast
 8        cmd = args[0];
 9        start = 1;
10    } else {
11        cmd = null;
12        start = 0;
13    }
14    //这个init用于初始化一些command信息,target是用于之后执行错误之后的一些错误打印
15    init(target, in, out, err, args, start);
16    mCmd = cmd;
17
18    int res = -1;
19    try {
20        //这里传入的this是ActivityManagerService,所以onCommand是AMS中的
21        res = onCommand(mCmd);
22        if (DEBUG) Log.d(TAG, "Executed command " + mCmd + " on " + mTarget);
23    } catch (Throwable e) {
24        ...
25    } finally {
26        ...
27    }
28    return res;
29}

onCommand

 1//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
 2@Override
 3public int onCommand(String cmd) {
 4    if (cmd == null) {
 5        return handleDefaultCommands(cmd);
 6    }
 7    final PrintWriter pw = getOutPrintWriter();
 8    try {
 9        switch (cmd) {
10            case "start":
11            case "start-activity":
12                return runStartActivity(pw);
13			...
14            //显然,会调用到这里
15            case "broadcast":
16                return runSendBroadcast(pw);
17            ...
18            default:
19                return handleDefaultCommands(cmd);
20}
21    } catch (RemoteException e) {
22        pw.println("Remote exception: " + e);
23    }
24    return -1;
25}

3am broadcast

根据上面得知am broadcast通过cmd脚本,从ServiceManager大管家中找到名叫"activity"的服务,通过客服端IPC调用服务端AMS方法,参与执行命令的cmdbroadcast,最终会调用到runSendBroadcast

 1//frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
 2//传入的pw,其实就是JDK中的工具,向文本输出流打印对象的格式化表示形式
 3int runSendBroadcast(PrintWriter pw) throws RemoteException {
 4    Intent intent;
 5    try {
 6        //[3.1]这里的makeIntent开始真正解析传入的args
 7        //UserHandle.USER_CURRENT用来指示当前活动用户的用户id
 8        intent = makeIntent(UserHandle.USER_CURRENT);
 9    } catch (URISyntaxException e) {
10        throw new RuntimeException(e.getMessage(), e);
11    }
12    //Flags是intent的跳转类型,这里说明是通过shell来发送广播的
13    intent.addFlags(Intent.FLAG_RECEIVER_FROM_SHELL);
14    //这里的IntentReceiver是一个ActivityManagerShellCommand.java中的内部类
15    //主要用于设置回调完成广播会有打印
16    IntentReceiver receiver = new IntentReceiver(pw);
17    //这里的mReceiverPermission为null
18    String[] requiredPermissions = mReceiverPermission == null ? null
19        : new String[] {mReceiverPermission};
20    pw.println("Broadcasting: " + intent);
21    pw.flush();
22    //这里的Bundle依然是null
23    Bundle bundle = mBroadcastOptions == null ? null : mBroadcastOptions.toBundle();
24    //[3.2]mInterface就是初始化的ActivityManagerShellCommand注入的,为AMS
25    mInterface.broadcastIntentWithFeature(null, null, intent, null, receiver, 0, null, null,
26                                          requiredPermissions, android.app.AppOpsManager.OP_NONE, bundle, true, false, mUserId);
27    //这里让当前线程等待,直到上述的操作完成,直到receiver回调performReceive
28    receiver.waitForFinish();
29    return 0;
30}

3.1makeIntent

 1//frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
 2private Intent makeIntent(int defUser) throws URISyntaxException {
 3
 4    mUserId = defUser;
 5	...
 6	//这里开始解析args,这里的this指的是ActivityManagerShellCommand
 7    //对于发送广播而言,后者的匿名内部类new Intent.CommandOptionHandler()的回调不会发生
 8    return Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() {
 9        @Override
10        public boolean handleOption(String opt, ShellCommand cmd) {
11            if (opt.equals("-D")) {
12                mStartFlags |= ActivityManager.START_FLAG_DEBUG;
13            ...
14            } else {
15                return false;
16            }
17            return true;
18        }
19    });
20}

parseCommandArgs

  1//frameworks/base/core/java/android/content/Intent.java
  2@UnsupportedAppUsage
  3public static Intent parseCommandArgs(ShellCommand cmd, CommandOptionHandler optionHandler)
  4    throws URISyntaxException {
  5    Intent intent = new Intent();
  6    Intent baseIntent = intent;
  7    boolean hasIntentInfo = false;
  8
  9    Uri data = null;
 10    String type = null;
 11
 12    String opt;
 13    //对cmd进行遍历,然后对每一个符号进行解析
 14    //getNextArgRequired就是参数后面紧跟的值
 15    //最终不管是es,ei,ez其实都是存放到Bundle中的k-v键值对
 16    while ((opt=cmd.getNextOption()) != null) {
 17        switch (opt) {
 18            case "-a":
 19                //intent.setAction(android.intent.action.SENDLOVE);
 20                intent.setAction(cmd.getNextArgRequired());
 21                if (intent == baseIntent) {
 22                    hasIntentInfo = true;
 23                }
 24                break;
 25            case "-e":
 26            case "--es": {
 27                //key="love",value="爱你"
 28                String key = cmd.getNextArgRequired();
 29                String value = cmd.getNextArgRequired();
 30                intent.putExtra(key, value);
 31            }
 32                break;
 33            case "--ei": {
 34                //key="days",value="10000"
 35                String key = cmd.getNextArgRequired();
 36                String value = cmd.getNextArgRequired();
 37                intent.putExtra(key, Integer.decode(value));
 38            }
 39                break;
 40            case "--ez": {
 41                //key="reality",value="true"
 42                String key = cmd.getNextArgRequired();
 43                String value = cmd.getNextArgRequired().toLowerCase();
 44                boolean arg;
 45                if ("true".equals(value) || "t".equals(value)) {
 46                    arg = true;
 47                } else if ("false".equals(value) || "f".equals(value)) {
 48                    arg = false;
 49                } else {
 50                    try {
 51                        arg = Integer.decode(value) != 0;
 52                    } catch (NumberFormatException ex) {
 53                        throw new IllegalArgumentException("Invalid boolean value: " + value);
 54                    }
 55                }
 56
 57                intent.putExtra(key, arg);
 58            }
 59                break;
 60            case "-n": {
 61                //str=com.example.broadcast/.MyTanabataReceiver
 62                String str = cmd.getNextArgRequired();
 63                ComponentName cn = ComponentName.unflattenFromString(str);
 64                if (cn == null)
 65                    throw new IllegalArgumentException("Bad component name: " + str);
 66                intent.setComponent(cn);
 67                //这里的intent和baseIntent是一致的
 68                if (intent == baseIntent) {
 69                    hasIntentInfo = true;
 70                }
 71            }
 72                break;
 73            ...
 74            default:
 75                if (optionHandler != null && optionHandler.handleOption(opt, cmd)) {
 76                    // Okay, caller handled this option.
 77                } else {
 78                    throw new IllegalArgumentException("Unknown option: " + opt);
 79                }
 80                break;
 81        }
 82    }
 83    //这里的data和type依然是null
 84    intent.setDataAndType(data, type);
 85	//这里有两个操作intent.replaceExtras((Bundle)null);和intent.replaceExtras(extras);
 86    //把它置空,又把它放进去,这里的目的跟baseIntent相关,用于Uri和extras的交换,这里没有uri所以流程比较简单
 87    if (baseIntent != null) {
 88        Bundle extras = intent.getExtras();
 89        intent.replaceExtras((Bundle)null);
 90        Bundle uriExtras = baseIntent.getExtras();
 91        baseIntent.replaceExtras((Bundle)null);
 92        if (intent.getAction() != null && baseIntent.getCategories() != null) {
 93            HashSet<String> cats = new HashSet<String>(baseIntent.getCategories());
 94            for (String c : cats) {
 95                baseIntent.removeCategory(c);
 96            }
 97        }
 98        intent.fillIn(baseIntent, Intent.FILL_IN_COMPONENT | Intent.FILL_IN_SELECTOR);
 99        if (extras == null) {
100            extras = uriExtras;
101        } else if (uriExtras != null) {
102            uriExtras.putAll(extras);
103            extras = uriExtras;
104        }
105        intent.replaceExtras(extras);
106        hasIntentInfo = true;
107    }
108
109    if (!hasIntentInfo) throw new IllegalArgumentException("No intent supplied");
110    return intent;
111}

最终makeIntent的结果

3.2broadcastIntentWithFeature

 1//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
 2public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
 3                                            Intent intent, String resolvedType, IIntentReceiver resultTo,
 4                                            int resultCode, String resultData, Bundle resultExtras,
 5                                            String[] requiredPermissions, int appOp, Bundle bOptions,
 6                                            boolean serialized, boolean sticky, int userId) {
 7    //强制非隔离调用者,判断调用方是否有selinux权限
 8    enforceNotIsolatedCaller("broadcastIntent");
 9    synchronized(this) {
10        //校验intent的正确性
11        intent = verifyBroadcastLocked(intent);
12		//获取client端caller进程record,如果对端进程已经被kill则直接抛出异常。这里传入的是null不处理
13        final ProcessRecord callerApp = getRecordForAppLocked(caller);
14        final int callingPid = Binder.getCallingPid();
15        final int callingUid = Binder.getCallingUid();
16        //作用是清空远程调用端的uid和pid,用当前本地进程的uid和pid替代
17        final long origId = Binder.clearCallingIdentity();
18        try {
19            //这里就是真正发送广播,由于这里是标准流程,这里不展开
20            return broadcastIntentLocked(callerApp,
21                                         callerApp != null ? callerApp.info.packageName : null, 
22                                         callingFeatureId, intent, resolvedType, resultTo, resultCode, 
23                                         resultData, resultExtras, requiredPermissions, appOp, bOptions, 
24                                         serialized, sticky, callingPid, callingUid, callingUid, callingPid, 
25                                         userId);
26        } finally {
27            //作用是恢复远程调用端的uid和pid信息,正好是`clearCallingIdentity`的反过程
28            Binder.restoreCallingIdentity(origId);
29        }
30    }
31}

verifyBroadcastLocked

 1//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
 2final Intent verifyBroadcastLocked(Intent intent) {
 3    // Refuse possible leaked file descriptors
 4    if (intent != null && intent.hasFileDescriptors() == true) {
 5        throw new IllegalArgumentException("File descriptors passed in Intent");
 6    }
 7
 8    int flags = intent.getFlags();
 9	//这里面指的是system系统还未完整引导完成,引导完成mProcessesReady为true
10    if (!mProcessesReady) {
11        if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {
12            // This will be turned into a FLAG_RECEIVER_REGISTERED_ONLY later on if needed.
13        } else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
14            Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent
15                   + " before boot completion");
16            throw new IllegalStateException("Cannot broadcast before boot completed");
17        }
18    }
19	//不能直接发送广播升级
20    if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
21        throw new IllegalArgumentException(
22            "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
23    }
24	//我们的flag是这个
25    if ((flags & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) {
26        //显而易见我们的广播发送必须是root权限或者是shell权限,不然没有权限发送
27        switch (Binder.getCallingUid()) {
28            case ROOT_UID:
29            case SHELL_UID:
30                break;
31            default:
32                Slog.w(TAG, "Removing FLAG_RECEIVER_FROM_SHELL because caller is UID "
33                       + Binder.getCallingUid());
34                intent.removeFlags(Intent.FLAG_RECEIVER_FROM_SHELL);
35                break;
36        }
37    }
38
39    return intent;
40}

clearCallingIdentityrestoreCallingIdentity,关于这两个作用具体可以点击这里

1//frameworks/base/core/java/android/os/Binder.java
2@CriticalNative
3public static final native long clearCallingIdentity();
4
5/* @see #clearCallingIdentity
6*/
7public static final native void restoreCallingIdentity(long token);

调用到native方法里面

 1//frameworks/native/libs/binder/IPCThreadState.cpp
 2int64_t IPCThreadState::clearCallingIdentity()
 3{
 4    // ignore mCallingSid for legacy reasons
 5    int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
 6    clearCaller();
 7    return token;
 8}
 9
10void IPCThreadState::clearCaller()
11{
12    mCallingPid = getpid();
13    mCallingSid = nullptr;  // expensive to lookup
14    mCallingUid = getuid();
15}
16
17void IPCThreadState::restoreCallingIdentity(int64_t token)
18{
19    mCallingUid = (int)(token>>32);
20    mCallingSid = nullptr;  // not enough data to restore
21    mCallingPid = (int)token;
22}
  1. 线程A通过Binder远程调用线程B:则线程B的IPCThreadState中的mCallingUidmCallingPid保存的就是线程A的UIDPID。这时在线程B中调用Binder.getCallingPid()Binder.getCallingUid()方法便可获取线程A的UIDPID,然后利用UIDPID进行权限比对,判断线程A是否有权限调用线程B的某个方法。
  2. 线程B通过Binder调用当前线程的某个组件:此时线程B是线程B某个组件的调用端,则mCallingUidmCallingPid应该保存当前线程B的PIDUID,故需要调用clearCallingIdentity()方法完成这个功能。当线程B调用完某个组件,由于线程B仍然处于线程A的被调用端,因此mCallingUidmCallingPid需要恢复成线程A的UIDPID,这是调用restoreCallingIdentity()即可完成。

4总结

总的来说,am broadcast还是比较常用的shell命令,通过一系列的操作,最终可以达到Context.sendBroadcast同等效果的广播。

1$ adb shell am broadcast -a android.intent.action.SENDLOVE -n com.example.broadcast/.MyTanabataReceiver –es “love” “爱你” –ei “days” 10000 –ez “reality” true

以上面的shell命令为例

  1. am命令会调用到/frameworks/base/cmds/am目录中的am脚本,并且在脚本里会调用cmd命令

    1$ adb shell cmd activity broadcast -a android.intent.action.SENDLOVE -n com.example.broadcast/.MyTanabataReceiver –es “love” “爱你” –ei “days” 10000 –ez “reality” true
    
  2. cmd命令会建立Binder通信,通过传入的参数为activity,从ServiceManager大管家中找到名叫"activity"的服务,接着调用shellCommand,通过IPC中的command序号SHELL_COMMAND_TRANSACTION

  3. AMS中找到Binder通信的真实调用onShellCommand,然后通过命令的形式在基类执行BasicShellCommandHandlerexec方法,最终会在AMS中找到对应的实现方法onCommand,这个就是处理参数broadcast方式

  4. AMS开始处理broadcast方式runSendBroadcast。主要有创建广播的Intent内容,并发送广播。发送广播完成之后,回调receiverperformReceive方法

    创建广播其实也是解析命令行的过程,这里解析之后,会被送到AMS中进行校验,包括selinux权限、广播时机和广播发送的uid等,最终通过标准流程broadcastIntentLocked实现发送广播。

源码下载

am broadcast源码