am broadcast发送广播源码分析
2300 Words|Read in about 11 Min|本文总阅读量次
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
命令。其中调用的ja
r路径是设备上的/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}
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
,你只能看到马路上的车和行人,但是你无法去修改他们,可以理解你对这个马路是只读的。关于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
方法,参与执行命令的cmd
为broadcast
,最终会调用到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}
clearCallingIdentity
和restoreCallingIdentity
,关于这两个作用具体可以点击这里
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}
- 线程A通过Binder远程调用线程B:则线程B的
IPCThreadState
中的mCallingUid
和mCallingPid
保存的就是线程A的UID
和PID
。这时在线程B中调用Binder.getCallingPid()
和Binder.getCallingUid()
方法便可获取线程A的UID
和PID
,然后利用UID
和PID
进行权限比对,判断线程A是否有权限调用线程B的某个方法。- 线程B通过
Binder
调用当前线程的某个组件:此时线程B是线程B某个组件的调用端,则mCallingUid
和mCallingPid
应该保存当前线程B的PID
和UID
,故需要调用clearCallingIdentity()
方法完成这个功能。当线程B调用完某个组件,由于线程B仍然处于线程A的被调用端,因此mCallingUid
和mCallingPid
需要恢复成线程A的UID
和PID
,这是调用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
命令为例
-
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
-
cmd
命令会建立Binder
通信,通过传入的参数为activity
,从ServiceManager
大管家中找到名叫"activity"
的服务,接着调用shellCommand
,通过IPC
中的command
序号SHELL_COMMAND_TRANSACTION
-
在
AMS
中找到Binder
通信的真实调用onShellCommand
,然后通过命令的形式在基类执行BasicShellCommandHandler
的exec
方法,最终会在AMS
中找到对应的实现方法onCommand
,这个就是处理参数broadcast
方式 -
在
AMS
开始处理broadcast
方式runSendBroadcast
。主要有创建广播的Intent
内容,并发送广播。发送广播完成之后,回调receiver
的performReceive
方法创建广播其实也是解析命令行的过程,这里解析之后,会被送到
AMS
中进行校验,包括selinux
权限、广播时机和广播发送的uid
等,最终通过标准流程broadcastIntentLocked
实现发送广播。