安卓6.0系统属性上层应用监听实现(属性监听)

原创: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有时间再添加讲。这个方法已经在我目前做的项目用上了的。
需要的人士可先下载来看即可。

下载链接:
https://github.com/bpbwan/syspropListener

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容