//携带参数-s时,才安装到SD卡
if(!
strcmp(argv[i],"-s")){
where=SD_DEST;
}
}
//解析参数,判断adb命令中是否携带了有效的apk文件名
...........
//取出apk名
std:
:
vectorapk_file={argv[last_apk]};
//构造apk目的地址
std:
:
stringapk_dest=android:
:
base:
:
StringPrintf(
where,adb_basename(argv[last_apk]).c_str());
//do_sync_push将此APK文件传输到手机的目标路径,失败的话将跳转到clenaup_apk
if(!
do_sync_push(apk_file,apk_dest.c_str()))gotocleanup_apk;
//执行pm_command
result=pm_command(transport,serial,argc,argv);
cleanup_apk:
//删除刚才传输的文件
//PKMS在安装过程中会将该APK复制一份到/data/app目录下,所有data/local/tmp目录下对应的文件可以删除
delete_file(transport,serial,apk_dest);
returnresult;
}
从代码来看,传统的安装方式就是将源机器中的APK文件拷贝到目的手机的tmp目录下,然后调用pm_command进行处理。
2、install_app
我们再看看支持FeatureCmd的机器,如何安装APK:
staticintinstall_app(TransportTypetransport,constchar*serial,intargc,constchar**argv){
//利用参数创建出本地文件的名称
constchar*file=argv[argc-1];
//解析参数,判断adb命令中是否携带了有效的apk文件名
.........
//adb_open中将创建出这个file对应的文件
intlocalFd=adb_open(file,O_RDONLY);
............
std:
:
stringcmd="exec:
cmdpackage";
//添加cmd参数
............
//连接源端,获取源APK文件的描述符
intremoteFd=adb_connect(cmd,&error);
............
//将remoteFd中的数据写入到localFd
copy_to_file(localFd,remoteFd);
//得到结果
read_status_line(remoteFd,buf,sizeof(buf));
adb_close(localFd);
adb_close(remoteFd);
..........
return0;
}
从代码来看install_app就是将源机器的文件复制到了目的机器中,并没有进行额外的操作。
猜想可能是支持特殊FeatureCmd的机器,PKMS能够监听到这个拷贝,然后触发后续的扫描工作。
这个过程没有研究过对应代码,暂时不做深入分析。
对于传统的安装方式,我们需要继续往下看看pm_command。
二、pm_command
我们先看看pm_command函数:
staticintpm_command(TransportTypetransport,constchar*serial,intargc,constchar**argv){
std:
:
stringcmd="pm";
//构造pmcmd
while(argc-->0){
cmd+=""+escape_arg(*argv++);
}
//发送shell命令给adbd
returnsend_shell_command(transport,serial,cmd,false);
}
我们跟进下send_shell_command:
//Connectstothedevice"shell"servicewith|command|andprintsthe
//resultingoutput.
staticintsend_shell_command(TransportTypetransport_type,constchar*serial,
conststd:
:
string&command,
booldisable_shell_protocol,
std:
:
string*output=nullptr,
std:
:
string*err=nullptr){
...........
while(true){
boolattempt_connection=true;
//Useshellprotocolifit'ssupportedandthecallerdoesn'texplicitlydisableit.
if(!
disable_shell_protocol){
.......
if(adb_get_feature_set(&features,&error)){
//如果定义了feature,则替换shellprotocol
use_shell_protocol=CanUseFeature(features,kFeatureShell2);
}else{
//Devicewasunreachable.
attempt_connection=false;
}
}
if(attempt_connection){
std:
:
stringerror;
//此时command中携带的就是以pm开头的命令
std:
:
stringservice_string=ShellServiceString(use_shell_protocol,"",command);
//向shell服务发送命令
fd=adb_connect(service_string,&error);
if(fd>=0){
break;
}
}
............
}
//读取返回结果
intexit_code=read_and_dump(fd,use_shell_protocol,output,err);
if(adb_close(fd)<0){
..........
}
returnintexit_code;
}
从上面的代码来看,pm_command就是向shell服务发送pm命令。
pm是一个可执行脚本,我们在终端上调用adbshell,然后执行pm,可以得到以下结果:
root:
/#pm
usage:
pmlistpackages[-f][-d][-e][-s][-3][-i][-u][--userUSER_ID][FILTER]
pmlistpermission-groups
pmlistpermissions[-g][-f][-d][-u][GROUP]
pmlistinstrumentation[-f][TARGET-PACKAGE]
..........
pm脚本定义在frameworks/base/cmds/pm中:
base=/system
exportCLASSPATH=$base/framework/pm.jar
execapp_process$base/binmands.pm.Pm"$@"
在编译system.img时,会根据Android.mk将该脚本复制到system/bin目录下。
从脚本的内容来看,当调用pm时,将向app_process目录的main函数传入Pm对应的参数:
我们看看对应的定义于app_main.cpp的main函数(前面的博客分析过,这个其实也是zygote启动的函数):
//app_process的main函数
intmain(intargc,char*constargv[]){
........
//解析参数
while(iconstchar*arg=argv[i++];
if(strcmp(arg,"--zygot