欢迎参加我的知识星球Android体系开发攻略
欢迎重视微信大众号 无限无羡
欢迎重视知乎账号 无限无羡
native服务增加
native服务便是用c++写的体系服务,经过init进程发动,能够完成binder接口供client调用。
下面咱们以完成一个beanserver的后台服务为例:
- 首先需要写一个rc文件
//文件途径依据自己需求放置
//vendor/zzh/native-service/bean-server/beanserver.rc
servicebeanserver/system/bin/beanserver
classmain
- 写服务的main函数
//vendor/zzh/native-service/bean-server/main_beanserver.cpp
#defineLOG_TAG"beanserver"
//#defineLOG_NDEBUG0
#include<hidl/HidlTransportSupport.h>
usingnamespaceandroid;
intmain(intargc__unused,char**argv__unused)
{
ALOGD("beamserverstart......");
return0;
}
这个服务咱们发动后只是打印了一行日志就退出了,具体能够依据自己的需求参加自己开机处理的事务。
- 编写Android.bp
cc_binary{
//终究会生成到/system/bin/beanserver
name:"beanserver",
srcs:["main_beanserver.cpp"],
header_libs:[
],
shared_libs:[
"liblog",
"libutils",
"libui",
"libgui",
"libbinder",
"libhidlbase",
],
compile_multilib:"first",
cflags:[
"-Wall",
"-Wextra",
"-Werror",
"-Wno-unused-parameter",
],
//终究会生成到/system/etc/init/beanserver.rc
//init进程发动时会解析/system/etc/init/目录下的rc文件
init_rc:["beanserver.rc"],
vintf_fragments:[
],
}
beanserver.rc在经过编译后会生成到/system/etc/init/beanserver.rc,然后init进程发动的时分就会解析该文件发动进程。
init进程解析/system/etc/init/的代码
//system/core/init/init.cpp
//能够看到init会解析多个目录下的rc文件
staticvoidLoadBootScripts(ActionManager&action_manager,ServiceList&service_list){
Parserparser=CreateParser(action_manager,service_list);
std::stringbootscript=GetProperty("ro.boot.init_rc","");
if(bootscript.empty()){
parser.ParseConfig("/system/etc/init/hw/init.rc");
if(!parser.ParseConfig("/system/etc/init")){
late_import_paths.emplace_back("/system/etc/init");
}
//late_importisavailableonlyinQandearlierrelease.Aswedon't
//havesystem_extinthoseversions,skiplate_importforsystem_ext.
parser.ParseConfig("/system_ext/etc/init");
if(!parser.ParseConfig("/vendor/etc/init")){
late_import_paths.emplace_back("/vendor/etc/init");
}
if(!parser.ParseConfig("/odm/etc/init")){
late_import_paths.emplace_back("/odm/etc/init");
}
if(!parser.ParseConfig("/product/etc/init")){
late_import_paths.emplace_back("/product/etc/init");
}
}else{
parser.ParseConfig(bootscript);
}
}
- 加到编译镜像环境
//device/generic/car/emulator/aosp_car_emulator.mk
//找到自己项目的mk文件参加
PRODUCT_PACKAGES+=beanserver
这样才会将beanserver编译到镜像中。
- 编译镜像验证
编译完发动时看到有如下报错日志:
03-2907:13:38.36400Iinit:Parsingfile/system/etc/init/beanserver.rc...
03-2907:13:41.70600Einit:Couldnotstartservice'beanserver'aspartofclass'core':File/system/bin/beanserver(labeled"u:object_r:system_file:s0")hasincorrectlabelornodomaintransitionfromu:r:init:s0toanotherSELinuxdomaindefined.Haveyouconfiguredyourservicecorrectly?https://source.android.com/security/selinux/device-policy#label_new_services_and_address_denials.Note:thiserrorshowsupeveninpermissivemodeinordertomakeauditingdenialspossible.
提示缺少selinux权限装备,咱们能够在Google官网看到label_new_services_and_address_denials
下面咱们装备一下selinux权限
selinux权限装备
- 编写自界说服务的te文件
//我这儿将新加的selinux装备放在了自己创建目录中
//注意文件的首尾保存一行空行
//vendor/zzh/sepolicy/vendor/beanserver.te
#beanserver
typebeanserver,domain,coredomain;
typebeanserver_exec,exec_type,file_type,system_file_type;
init_daemon_domain(beanserver)
- 编写file_contexts文件
//vendor/zzh/sepolicy/vendor/file_contexts
//注意文件的首尾保存一行空行
/system/bin/beanserveru:object_r:beanserver_exec:s0
增加到sepolicy编译规矩
//device/generic/car/emulator/aosp_car_emulator.mk
BOARD_VENDOR_SEPOLICY_DIRS+=vendor/zzh/sepolicy/vendor
-
编译后发动模拟器
这个时分能看到服务发动了,可是发现beanserver一直在重启,日志如下:经过排查产生这个现象的原因有两个:
(1)咱们的进程发动后只是打印了一行日志就return 0退出了,经过检查init的代码发现,init进程会经过signal 9去kill掉退出的进程,这儿总感觉有点对立,或许时init忧虑进程自己退出的不完全,所以来个signal 9? init 里的代码如下:
//system/core/init/sigchld_handler.cpp
staticpid_tReapOneProcess(){
siginfo_tsiginfo={};
//Thisreturnsazombiepidorinformsusthattherearenozombieslefttobereaped.
//ItdoesNOTreapthepid;thatisdonebelow.
if(TEMP_FAILURE_RETRY(waitid(P_ALL,0,&siginfo,WEXITED|WNOHANG|WNOWAIT))!=0){
PLOG(ERROR)<<"waitidfailed";
return0;
}
autopid=siginfo.si_pid;
if(pid==0)return0;
//Atthispointweknowwehaveazombiepid,soweusethisscopeguardtoreapthepid
//wheneverthefunctionreturnsfromthispointforward.
//WedoNOTwanttoreapthezombieearlierasinService::Reap(),wekill(-pid,...)andwe
//wantthepidtoremainvalidthroughoutthat(andpotentiallyfuture)usages.
autoreaper=make_scope_guard([pid]{TEMP_FAILURE_RETRY(waitpid(pid,nullptr,WNOHANG));});
std::stringname;
std::stringwait_string;
Service*service=nullptr;
if(SubcontextChildReap(pid)){
name="Subcontext";
}else{
service=ServiceList::GetInstance().FindService(pid,&Service::pid);
if(service){
name=StringPrintf("Service'%s'(pid%d)",service->name().c_str(),pid);
if(service->flags()&SVC_EXEC){
autoexec_duration=boot_clock::now()-service->time_started();
autoexec_duration_ms=
std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
wait_string=StringPrintf("waitingtook%fseconds",exec_duration_ms/1000.0f);
}elseif(service->flags()&SVC_ONESHOT){
autoexec_duration=boot_clock::now()-service->time_started();
autoexec_duration_ms=
std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration)
.count();
wait_string=StringPrintf("oneshotservicetook%fsecondsinbackground",
exec_duration_ms/1000.0f);
}
}else{
name=StringPrintf("Untrackedpid%d",pid);
}
}
//beanserver退出后,si_code为CLD_EXITED
if(siginfo.si_code==CLD_EXITED){
LOG(INFO)<<name<<"exitedwithstatus"<<siginfo.si_status<<wait_string;
}else{
LOG(INFO)<<name<<"receivedsignal"<<siginfo.si_status<<wait_string;
}
if(!service){
LOG(INFO)<<name<<"didnothaveanassociatedserviceentryandwillnotbereaped";
returnpid;
}
//这儿将siginfo传入Reap函数
service->Reap(siginfo);
if(service->flags()&SVC_TEMPORARY){
ServiceList::GetInstance().RemoveService(*service);
}
returnpid;
}
voidService::Reap(constsiginfo_t&siginfo){
//假如没有oneshot特点或许设置了restart的特点,则会调用SIGKILL杀掉进程
//这儿的oneshot和restart指的是rc文件里边的装备,咱们的rc文件只声明晰classmain
//假如没有声明oneshot特点,则服务被杀后会从头发动,假如装备了oneshot则只会发动一次
if(!(flags_&SVC_ONESHOT)||(flags_&SVC_RESTART)){
KillProcessGroup(SIGKILL,false);
}else{
//Legacybehaviorfrom~2007untilAndroidR:thiselsebranchdidnotexistandwedidnot
//killtheprocessgroupinthiscase.
if(SelinuxGetVendorAndroidVersion()>=__ANDROID_API_R__){
//ThenewbehaviorinAndroidRistokilltheseprocessgroupsinallcases.The
//'true'parameterinstructionsKillProcessGroup()toreportawarningmessagewhereit
//detectsadifferenceinbehaviorhasoccurred.
KillProcessGroup(SIGKILL,true);
}
}
...
}
为了确保服务不退出,咱们先在main函数里加个死循环看下作用
intmain(intargc__unused,char**argv__unused)
{
ALOGD("beamserverstart......");
while(true){
sleep(3);
ALOGD("beanserver_thread...........");
}
return0;
}
编译后发动模拟器看下发动日志:
能够看到目前服务现已正常了,这个服务将一直在后台运行。
假如咱们只想开机后履行一些操作后退出,那也能够,咱们修正下看看作用。
修正beanserver.rc文件:
servicebeanserver/system/bin/beanserver
classmain
oneshot//只发动一次
修正main_beanserver.cpp文件:
#defineLOG_TAG"beanserver"
//#defineLOG_NDEBUG0
#include<hidl/HidlTransportSupport.h>
usingnamespaceandroid;
voidquickSort(intarr[],intleft,intright){
inti=left,j=right;
inttmp;
intpivot=arr[(left+right)/2];
/*partition*/
while(i<=j){
while(arr[i]<pivot)
i++;
while(arr[j]>pivot)
j--;
if(i<=j){
tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
i++;
j--;
}
};
/*recursion*/
if(left<j)
quickSort(arr,left,j);
if(i<right)
quickSort(arr,i,right);
}
intmain(intargc__unused,char**argv__unused)
{
ALOGD("beamserverstart...");
intdata[]={9,21,3,66,100,8,36};
quickSort(data,0,7);
for(inti=0;i<7;i++){
ALOGD("data[%d]=%d",i,data[i]);
}
return0;
}
将一组数据用快速排序进行排序后输出,然后服务退出,经过日志能够看到,服务退出后init发送了signal 9,到此服务进程退出。
经过binder拜访服务
假如想要被其他进程调用,就需要完成binder接口,这样才算是一个完整的体系服务。
- 界说aidl接口
//vendor/zzh/native-service/bean-server/libbeanservice/aidl/com/zzh/IBeanService.aidl
packagecom.zzh;
interfaceIBeanService{
voidsayHello();
}
- 参加Android.bp中进行编译
//vendor/zzh/native-service/bean-server/libbeanservice/Android.bp
cc_library_shared{
name:"libbeanservice_aidl",
aidl:{
export_aidl_headers:true,
local_include_dirs:["aidl"],
include_dirs:[
],
},
srcs:[
":beanservice_aidl",
],
shared_libs:[
"libbase",
"libcutils",
"libutils",
"liblog",
"libbinder",
"libgui",
],
cflags:[
"-Werror",
"-Wall",
"-Wextra",
],
}
filegroup{
name:"beanservice_aidl",
srcs:[
"aidl/com/zzh/IBeanService.aidl",
],
path:"aidl",
}
make libbeanservice_aidl 后能够看到out下生成的文件:
IBeanService.cpp
IBeanService.h
BpBeanService.h
BnBeanService.h
- 完成服务,承继BnBeanService
BeanService.h
//vendor/zzh/native-service/bean-server/libbeanservice/BeanService.h
#ifndefBEANSERVICE_H
#defineBEANSERVICE_H
#include<com/zzh/BnBeanService.h>
#include<binder/BinderService.h>
namespaceandroid{
classBeanService:
publicBinderService<BeanService>,
publicvirtual::com::zzh::BnBeanService,
publicvirtualIBinder::DeathRecipient
{
public:
//ImplementationofBinderService<T>
staticcharconst*getServiceName(){return"bean.like";}
//IBinder::DeathRecipientimplementation
virtualvoidbinderDied(constwp<IBinder>&who);
BeanService();
~BeanService();
virtualbinder::StatussayHello();
};
}
#endif
这儿BeanService承继了BinderService,必须重写getServiceName函数用来回来服务的称号,后续注册服务时会用到这个姓名。
BeanService.cpp
//vendor/zzh/native-service/bean-server/libbeanservice/BeanService.cpp
#defineLOG_TAG"BeanService"
//#defineLOG_NDEBUG0
#include"BeanService.h"
#include<iostream>
namespaceandroid{
usingbinder::Status;
BeanService::BeanService(){
}
BeanService::~BeanService(){
}
/*virtual*/voidBeanService::binderDied(constwp<IBinder>&who){
ALOGE("%s:Javaclient'sbinderdied,removingitfromthelistofactiveclients,who=%p",
__FUNCTION__,&who);
}
StatusBeanService::sayHello(){
ALOGD("BeanService::sayHello");
returnStatus::ok();
}
}
Android.bp
//vendor/zzh/native-service/bean-server/libbeanservice/Android.bp
cc_library_shared{
name:"libbeanservice",
srcs:[
"BeanService.cpp",
],
header_libs:[
],
shared_libs:[
"libbeanservice_aidl",
"libbase",
"libui",
"liblog",
"libutils",
"libbinder",
"libcutils",
],
static_libs:[
],
include_dirs:[
],
export_shared_lib_headers:[
"libbinder",
"libbeanservice_aidl",
],
export_include_dirs:["."],
cflags:[
"-Wall",
"-Wextra",
"-Werror",
"-Wno-ignored-qualifiers",
],
}
cc_library_shared{
name:"libbeanservice_aidl",
aidl:{
export_aidl_headers:true,
local_include_dirs:["aidl"],
include_dirs:[
],
},
srcs:[
":beanservice_aidl",
],
shared_libs:[
"libbase",
"libcutils",
"libutils",
"liblog",
"libbinder",
"libgui",
],
cflags:[
"-Werror",
"-Wall",
"-Wextra",
],
}
filegroup{
name:"beanservice_aidl",
srcs:[
"aidl/com/zzh/IBeanService.aidl",
],
path:"aidl",
}
- 注册服务
//vendor/zzh/native-service/bean-server/main_beanserver.cpp
#defineLOG_TAG"beanserver"
//#defineLOG_NDEBUG0
#include"BeanService.h"
#include<hidl/HidlTransportSupport.h>
usingnamespaceandroid;
intmain(intargc__unused,char**argv__unused)
{
ALOGD("beamserverstart...");
signal(SIGPIPE,SIG_IGN);
//Set5threadsforHIDLcalls.NowcameraserverwillserveHIDLcallsin
//additiontoconsumingthemfromtheCameraHALaswell.
hardware::configureRpcThreadpool(5,/*willjoin*/false);
sp<ProcessState>proc(ProcessState::self());
sp<IServiceManager>sm=defaultServiceManager();
ALOGI("ServiceManager:%p",sm.get());
BeanService::instantiate();
ALOGI("ServiceManager:%pdoneinstantiate",sm.get());
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
BeanService::instantiate()这个函数会调用父类BinderService的办法,终究会调用publish办法进行服务注册。
template<typenameSERVICE>
classBinderService
{
public:
//进行服务注册的办法,被instantiate()调用
staticstatus_tpublish(boolallowIsolated=false,
intdumpFlags=IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT){
sp<IServiceManager>sm(defaultServiceManager());
//这儿进行服务注册,SERVICE::getServiceName()这个便是BeanService重写的办法,回来"bean.like"
returnsm->addService(String16(SERVICE::getServiceName()),newSERVICE(),allowIsolated,
dumpFlags);
}
staticvoidpublishAndJoinThreadPool(
boolallowIsolated=false,
intdumpFlags=IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT){
publish(allowIsolated,dumpFlags);
joinThreadPool();
}
//调用publish()办法
staticvoidinstantiate(){publish();}
staticstatus_tshutdown(){returnNO_ERROR;}
...
}
- 编译后发动模拟器,看日志:
接下来依据提示装备selinux权限
adb pull /sys/fs/selinux/policy
adb logcat -b events -d | audit2allow -p policy
输出信息如下:
#=============beanserver==============
allowbeanserverservicemanager:bindercall;
#=============hal_audio_caremu==============
allowhal_audio_caremuaudioserver:fifo_filewrite;
allowhal_audio_caremudefault_prop:fileread;
#=============init==============
allowinitvendor_toolbox_exec:fileexecute_no_trans;
咱们将规矩参加beanserver.te即可:
selinux一般情况下并不能一次加完,每次都是增加了log中所报的权限后,再次运行时又会有新的权限错误,咱们依照上面的办法逐个增加即可,终究的te文件如下:
//vendor/zzh/sepolicy/vendor/beanserver.te
#beanserver
typebeanserver,domain,coredomain;
typebeanserver_exec,exec_type,file_type,system_file_type;
init_daemon_domain(beanserver)
allowbeanserverservicemanager:binder{calltransfer};
allowbeanserverbeanserver_service:service_manageradd;
除了上述修正外,还需要做如下修正:
//vendor/zzh/sepolicy/public/service.te
typebeanserver_service,service_manager_type;
//vendor/zzh/sepolicy/private/service_contexts
bean.likeu:object_r:beanserver_service:s0
//device/generic/car/emulator/aosp_car_emulator.mk
SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS+=vendor/zzh/sepolicy/public
SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS+=vendor/zzh/sepolicy/private
简单解释一下,咱们需要为界说的服务界说一个标签,ServiceManager履行addService操作时会进行检查,假如不界说标签的话会使用默认的default_android_service,但Selinux是不允许以这个标签add service的。
//system/sepolicy/public/domain.te
#Donotallowservice_manageraddfordefaultservicelabels.
#Insteaddomainsshoulduseamorespecifictypesuchas
#system_app_serviceratherthanthegenerictype.
#Newservice_typesaredefinedin{,hw,vnd}service.teandnewmappings
#fromservicenametoservice_typearedefinedin{,hw,vnd}service_contexts.
neverallow*default_android_service:service_manager*;
neverallow*default_android_vndservice:service_manager*;
neverallow*default_android_hwservice:hwservice_manager*;
再次编译发动模拟器:
到此,咱们的服务现已成功的增加到ServiceManager中。
- Client经过binder拜访服务
咱们写一个demo进行拜访服务
测试文件:
//vendor/zzh/native-service/bean-server/client/BeanServer_client_test.cpp
#defineLOG_TAG"beanclient"
//#defineLOG_NDEBUG0
#include<com/zzh/IBeanService.h>
#include<binder/IServiceManager.h>
usingnamespaceandroid;
usingcom::zzh::IBeanService;
intmain(intargc__unused,char**argv__unused){
ALOGD("beanclientstart...");
//先获取IServiceManager
sp<IServiceManager>sm=defaultServiceManager();
sp<IBinder>binder;
do{
//经过服务称号拿到代理对象
binder=sm->getService(String16("bean.like"));
if(binder!=0){
break;
}
usleep(500000);//0.5s
}while(true);
//转换成IBeanService
sp<IBeanService>beanService=interface_cast<IBeanService>(binder);
//经过代理调用服务端函数
beanService->sayHello();
return0;
}
Android.bp
//vendor/zzh/native-service/bean-server/client/Android.bp
cc_binary{
name:"beanserver_client",
srcs:["BeanServer_client_test.cpp"],
cflags:[
"-Wall",
"-Wextra",
"-Werror",
"-Wno-unused-parameter",
],
compile_multilib:"first",
shared_libs:[
"libbeanservice_aidl",
"libbase",
"libcutils",
"libutils",
"liblog",
"libbinder",
"libgui",
],
include_dirs:["."],
}
编译到体系目录
//device/generic/car/emulator/aosp_car_emulator.mk
PRODUCT_PACKAGES+=beanserver_client
也能够不加上面的编译选项,直接make beanserver_client,然后将生成的产品adb push到体系目录也能够。
发动模拟器,在终端履行beanserver_client,检查日志:
能够看出是不允许shell拜访beanserver服务,这儿在实践事务中要对client端装备selinux,因为咱们这儿是测试,就不赘述了。直接setenforce 0进行测试
su
setenforce0
然后再从头履行beanservere_client
调用成功!
为了方便我们明晰的理解代码结果,下面列出了本例中的代码目录:
zzh@ubuntu:~/work/android/aosp/android-13.0.0_r35/vendor/zzh$tree
.
├──native-service
│└──bean-server
│├──Android.bp
│├──beanserver.rc
│├──client
││├──Android.bp
││└──BeanServer_client_test.cpp
│├──libbeanservice
││├──aidl
│││└──com
│││└──zzh
│││└──IBeanService.aidl
││├──Android.bp
││├──BeanService.cpp
││└──BeanService.h
│└──main_beanserver.cpp
└──sepolicy
├──private
│└──service_contexts
├──public
│└──service.te
└──vendor
├──beanserver.te
└──file_contexts
11directories,13files
上述文件我现已整理好,我们想要的话能够重视我的微信大众号无限无羡,回复”nt” 即可获取,获取后只需修正部分称号,增加编译选项到自己项目的装备文件即可。