本文介绍了如何实现一个安卓native服务。
需求: 在native framework层实现一个服务接受云控配置,并执行云控命令。
已有条件:系统接受到云控配置会发送特定action的广播并将信息包含在intent中,我们只需要接受此广播并完成我们的逻辑即可。
一、预先分析
在开始写代码之前,做一些功能点大致的分析如下:
1:在java framework需要一个广播接收器,接受传来的广播,此广播接收器需要在开机时就注册。根据安卓开机流程,我选择在system_server启动AMS后,注册此Receiver。为了不卡住,此Receiver需要运行在自己的线程,要有自己的messageQueue。
接受到广播之后,通过binder调用到native服务执行native的逻辑。
2:native服务需要在开机时启动,根据安卓开机流程,我可能需要修改init.rc的开机脚本,并配置一些SELinux规则以成功在开机时将我的服务注册到servicemanager中。
二、Native服务代码
仿照audioflinger的做法,实现自己的native服务。
native服务的文件组织如下,具体路径为framework/base/dynamicopt/dynamicopt-service/:
其中,TestClient是在native测试native服务的客户端。.bp和.mk文件是用来编译代码的。
1:IDynaOption.h,这是一个接口
//
// Created by zxs on 21-6-10.
//
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/file.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <android/log.h>
#include <cutils/properties.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/IBinder.h>
#include <binder/Binder.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <android/log.h>
using namespace android;
#ifndef ANDROID_IDYNAOPT_H
#define ANDROID_IDYNAOPT_H
namespace android {
class IDynaOptService: public IInterface {
public:
DECLARE_META_INTERFACE(DynaOptService);
virtual void shellExecuteDirectly(String16 s) = 0;
};
enum{
SHELL_EXECUTE_DIRECTLY = IBinder::FIRST_CALL_TRANSACTION,
};
class BnDynaOptService: public BnInterface<IDynaOptService> {
public:
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,uint32_t flags = 0);
};
}// namespace android
#endif //ANDROID_IDYNAOPT_H
2:IDynaOption.cpp
//
// Created by zxs on 21-6-10.
//
#include "IDynaOption.h"
namespace android {
class BpDynaOptService: public BpInterface<IDynaOptService> {
public:
BpDynaOptService(const sp<IBinder>& impl)
:BpInterface<IDynaOptService>(impl) {
}
virtual void shellExecuteDirectly(String16 s) {
Parcel data, reply;
data.writeInterfaceToken(IDynaOptService::getInterfaceDescriptor());
data.writeString16(s);
remote()-> transact(SHELL_EXECUTE_DIRECTLY, data, &reply);
}
};
IMPLEMENT_META_INTERFACE(DynaOptService, "android.dynaOptServer.IDynaOptService");
status_t BnDynaOptService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
switch (code) {
case SHELL_EXECUTE_DIRECTLY: {
CHECK_INTERFACE(IDynaOption, data, reply);
shellExecuteDirectly(data.readString16());
reply->writeInt32(100);
if(flags) {
return 0;
}
return NO_ERROR;
break;
}
default:
break;
}
return NO_ERROR;
}
}// namespace android
3:DynaOptService.cpp
//
// Created by zxs on 21-6-8.
//
#include "IDynaOption.h"
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <cutils/log.h>
#include <binder/ProcessState.h>
#include <sys/stat.h>
#include <stdio.h>
namespace android {
class DynaOptService: public BnDynaOptService {
public:
DynaOptService();
virtual ~DynaOptService();
void shellExecuteDirectly(String16 s);
};
DynaOptService::DynaOptService() {
ALOGI("DynaOptService created.");
}
DynaOptService::~DynaOptService() {
ALOGI("DynaOptService destroyed.");
}
void DynaOptService::shellExecuteDirectly(String16 s) {
ALOGI("DynaOptService shellExecuteDirectly.");
std::string shell = String8(s).string();
if(shell.empty()) {
ALOGI("DynaOptService: shell is empty.");
return;
}
system(String8(s));
}
}// namespace android
int main() {
sp<ProcessState> proc(ProcessState::self());
defaultServiceManager()->addService(String16("service.dynaoptservice"), new DynaOptService());
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return 0;
}
4:Android.bp
cc_library_static {
name: "libzouserver",
cflags: [
"-Wno-unused-parameter",
],
srcs: [
"DynaOptService.cpp",
"IDynaOption.cpp",
],
shared_libs: [
"liblog",
"libcutils",
"libutils",
"libbinder",
],
export_include_dirs: ["."],
}
cc_binary {
name: "dynaopt",
srcs: [
"DynaOptService.cpp",
],
cflags: [
"-Wno-unused-parameter",
],
shared_libs: [
"libcutils",
"libutils",
"libbinder",
"liblog",
],
static_libs: ["libzouserver"],
init_rc: ["DynaOptService.rc"],
}
cc_binary {
name: "zouclient",
srcs: [
"TestClient.cpp",
"IDynaOption.cpp",
],
shared_libs: [
"libcutils",
"libutils",
"libbinder",
"liblog",
],
}
5:TestClient.cpp
//
// Created by zxs on 21-6-10.
//
#include "IDynaOption.h"
using namespace android;
int main()
{
sp <IBinder> binder = defaultServiceManager()->getService(String16("service.dynaoptservice"));
sp<IDynaOptService> cs = interface_cast <android::IDynaOptService> (binder);
ALOGI("DynaOptService :Hello Native service\n");
//std::vector<String16> vec = {"/system/bin/logcat -b all -d -f /data/syslog/test20210611.txt","/system/bin/logcat -b all -d -f /data/syslog/good20210611.txt"};
cs->shellExecuteDirectly(String16("/system/bin/logcat -b all -d -f /data/syslog/test20210611.txt"));
return 0;
}
6:Android.mk
LOCAL_PATH := $(call my-dir)
include $(call first-makefiles-under,$(LOCAL_PATH))
7:DynaOptService.rc
这个是rc脚本,安卓开机的时候会执行此命令运行服务的。
service dynaopt_server /system/bin/dynaopt
class core
8:common.mk
在device/xiaomi/sm"XXXX"-common/common.mk中增加以下,让自己的代码可以被编译,并在开机的时候可以启动自己的服务。
这里的“dynaopt”写自己在android.bp中编译出来的二进制文件的名字就行。我的话写的就是“dynaopt”。
# dynaoptservice
PRODUCT_PACKAGES += \
dynaopt
三、解决selinux问题
这个可以看我的上一篇文档自己解决一下。