在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;