原创:bpbwan
安卓系统中往往有一些状态是靠系统属性来保存的,linux层的嵌入式应用链接底层模块变更某些开关状态时单靠localsocket传输更新给上层,或多或少会比较有延迟,也特别多个底层应用时,总不能每个要给上层通讯都开一个socket去弄吧。赖得弄那么烦的就直接靠个系统属性来通知上层算了,这时候需要给framework改的比较多,以下先给出上层使用方法。
public class MainActivity extends Activity {
SysPropertiesManager m;
static final int PROPID_1 = 100;
static final int PROPID_2 = 101;
static final int PROPID_3 = 102;
Button button1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
m = (SysPropertiesManager)getSystemService(Context.PROPERTY_SERVICE);
if (m == null) {
Log.d("bpb", "can not find SysPropertiesManager!");
} else {
m.startPropListener();
try {
m.registeredPropListener(mMyPropListener);
mMyPropListener.addProperty("sss.sss.sss1", PROPID_1);
mMyPropListener.addProperty("sss.sss.sss2", PROPID_2);
mMyPropListener.addProperty("sss.sss.sss3", PROPID_3);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
button1 = (Button)findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
Log.d("bpb", "onClick ");
SystemProperties.set("xxx.xxx.xxx1", "xxx1");
SystemProperties.set("sss.sss.sss2", "sss2");
SystemProperties.set("xxx.xxx.xxx1", "xxx1");
SystemProperties.set("xxx.xxx.xxx1", "xxx1");
SystemProperties.set("sss.sss.sss1", "sss1");
}
});
}
MyPropListener mMyPropListener = new MyPropListener();
class MyPropListener extends BaseSysPropListener {
@Override
public void onPropertiesChanged(SysProperty props) {
super.onPropertiesChanged(props);
Log.d("bpb", "MainActivity onPropertiesChanged name: "+props.name+" value: "+props.value+" "+props.mPropId);
switch(props.mPropId) {
case PROPID_1:
break;
case PROPID_2:
break;
case PROPID_3:
break;
}
}
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
try {
m.unRegisteredPropListener(mMyPropListener);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
以上的SysPropertiesManager 和BaseSysPropListener 就是我们等会实现的类。
这个方法的架构是这样
各个目录改到的地方有
framework base
framework base下 os目录都是新增文件,SysPropertiesManager类重点是如下
public SysPropertiesManager() {
mSysPropertiesRegistrar = ISysPropertiesRegistrar.Stub.asInterface(
ServiceManager.getService("syspropserver"));
try {
if ( mSysPropertiesRegistrar != null )
mSysPropertiesRegistrar.asBinder().linkToDeath(mDeathRecipent, 0);
} catch (RemoteException e) {
}
}
通过getService 来获取底层应用的binder服务,做个托管,
BaseSysPropListener extends ISysPropertiesListener.Stub类就是需要继承来往下注册的入口
另外ISysPropertiesListener.aidl 和ISysPropertiesRegistrar.aidl一个是被注册和一个保持注册者的俩个文件,就是个观察者模型。俩个的主要实现binder接口内容是在framewrok native里,等会介绍。ListeningProps.aidl 和ListeningProps.java 俩个文件的协助是为了和framework native对应的ListeningProps结构体实现序列化传输, ListeningProps.java是继承Parcelable的,因为我们需要这个序列化来传递我们参数数据到native层。同理SysProperty.aidl 和SysProperty.java则是单向由native层序列化传输到framework base(这里都是由binder来实现的传输)。结果返回的就是被监听的属性包含类,可以直接从获得到的SysProperty实例里拿出当前监听到的属性名称和属性值还有自己原先定义的对应ID。
SystemServiceRegistry文件,就单纯的实例化一个SysPropertiesManager并托给SystemServer来保存使用。如下做即可
//add bpb 2017.7
registerService(Context.PROPERTY_SERVICE, SysPropertiesManager.class, new StaticServiceFetcher() {
@Override
public SysPropertiesManager createService() {
return new SysPropertiesManager();
}});
在SystemServiceRegistry.java的static初始化区添加这个就可以了。最后的Android.mk由于我们增加了不少aidl文件,所以得修改编译和导出到全局framework环境空间,修改如下:
LOCAL_SRC_FILES += \...
...........................................
core/java/android/os/ISysPropertiesListener.aidl \
core/java/android/os/ISysPropertiesRegistrar.aidl \
添加到末尾也可。还有
aidl_files := \...
............................................
frameworks/base/core/java/android/os/SysProperty.aidl \
frameworks/base/core/java/android/os/ListeningProps.aidl \
这里ok,需要编译framework base之前,别忘了,make updateapi 先。
framework native
framework native层级,全是C++来实现ISysPropertiesListener.aidl 和ISysPropertiesRegistrar.aidl,这里说说我对bnInterface 和bpInterface的理解,bnInterface继承BBinder模板,主要是由remote方代码块所transact对应的<int>code走的程序内容, 而bpInterface则是本地想要条用remote方某接口前走的程序块,再在该代码块里面传输transaction内容到remote方来判断<int>code 来走onTransatc.理解流程下图表示
bninterface和bpinterface理解
以上的ISysPropertiesListener,ISysPropertiesRegistrar都有实现client方的bpinterface,ISysPropertiesRegistrar还实现好bnInterface就形成了双向binder绑定的局面。而真正在运行的监听应用时由一个init通过配置init.rc来启动的sysprobin来运用这些native syspropservice库。
system core和external修改地方
init目录需要修改的地方不多,就property_service.cpp如下代码段
...........................................
void initPropFiFo() {
if (gFifoFd < 0) {
gFifoFd = open("/data/myfifo", O_RDWR|O_NONBLOCK);
ERROR("open myfifo %d\n", gFifoFd);
if (gFifoFd < 0) {
mkfifo("/data/myfifo",0777);
gFifoFd = open("/data/myfifo", O_RDWR|O_NONBLOCK);
ERROR("open myfifo %d\n", gFifoFd);
}
}
}
//==============bpbp
void start_property_change_service() {
propFiFo_ready = true;
//initPropFiFo();
}
void stop_property_change_service() {
propFiFo_ready = false;
close(gFifoFd);
gFifoFd = -1;
}
//定义属性开关 "sys.prop.listen" value 1 开, 0 关
void property_change_to_fifo(const char *name, const char *value)
{
//ERROR("property_change_ctl.to_fifo fd:%d name:[%s] [%s]\n", gFifoFd, name, value);
if( memcmp(name,"sys.prop.listen", 15) == 0 ) {
if (memcmp(value,"1", 1) == 0 ) {
start_property_change_service();
} else {
stop_property_change_service();
}
}
if(gFifoFd > 0) {
char buf[100];
memset(buf, 0, 100);
int allLen = strlen(name)+strlen(value);
sprintf(buf, "%d%d%s:%s", allLen/10, allLen%10, name,value);
write(gFifoFd, buf, strlen(buf));
} else if (propFiFo_ready){
initPropFiFo();
}
}
比较简单的单纯往安卓data目录下的myfifo写入属性数据,然后同时也用系统属性来控制这个开关。
最后是property_change_to_fifo接口放置的地方,
static void handle_property_set_fd(){
..............................................
if(r != sizeof(prop_msg)) {
ERROR("sys_prop: mis-match msg size received: %d expected: %zu: %s\n",
r, sizeof(prop_msg), strerror(errno));
close(s);
return;
}
property_change_to_fifo(msg.name, (char*) msg.value);
.....................................
}
这个是epoll有event数据回调的函数地方。但在init进程里写myfifo管道需要se安全策略配置,在sepoliy目录里的init.te 末尾加入这段防止执行写入失败报错问题。必须的!
#add bpb create /data/myfifo
#allow init proc_security:fifo_file rw_file_perms;
allow init system_data_file:fifo_file rw_file_perms;
allow init system_file:file execute;
在rootdir目录里面的init.rc配置启动的syspropbin服务,配置如下
service syspropbin /sbin/syspropbin
class core
critical
seclabel u:r:syspropbin:s0
group root system
最后的重头戏,就是我们的syspropbin服务的编写,要实现不阻塞的监听管道和监听注册应用发来的ISysPropertiesListener,主要利用好一个linux系统里的epoll机制就好了。
Android.mk文件直接mmm -B ./该目录即可,这里面的过程仿照安卓原生的healthd电池充电服务来完成的。
重要的代码段:
static void healthd_mainloop(void) {
while (1) {
struct epoll_event events[eventct];
int nevents;
int timeout = awake_poll_interval;
int mode_timeout;
healthd_mode_ops->preparetowait();
nevents = epoll_wait(epollfd, events, eventct, -1);
if (nevents == -1) {
if (errno == EINTR)
continue;
KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
break;
}
for (int n = 0; n < nevents; ++n) {
if (events[n].data.ptr)
(*(void (*)(int))events[n].data.ptr)(events[n].events);
}
}
return;
}
syspropbin有时间再添加讲。这个方法已经在我目前做的项目用上了的。
需要的人士可先下载来看即可。