原創(chuàng):bpbwan
安卓系統(tǒng)中往往有一些狀態(tài)是靠系統(tǒng)屬性來保存的,linux層的嵌入式應(yīng)用鏈接底層模塊變更某些開關(guān)狀態(tài)時(shí)單靠localsocket傳輸更新給上層嫂拴,或多或少會(huì)比較有延遲,也特別多個(gè)底層應(yīng)用時(shí)贮喧,總不能每個(gè)要給上層通訊都開一個(gè)socket去弄吧筒狠。賴得弄那么煩的就直接靠個(gè)系統(tǒng)屬性來通知上層算了,這時(shí)候需要給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 就是我們等會(huì)實(shí)現(xiàn)的類。
這個(gè)方法的架構(gòu)是這樣
各個(gè)目錄改到的地方有
framework base
framework base下 os目錄都是新增文件谓形,SysPropertiesManager類重點(diǎn)是如下
public SysPropertiesManager() {
mSysPropertiesRegistrar = ISysPropertiesRegistrar.Stub.asInterface(
ServiceManager.getService("syspropserver"));
try {
if ( mSysPropertiesRegistrar != null )
mSysPropertiesRegistrar.asBinder().linkToDeath(mDeathRecipent, 0);
} catch (RemoteException e) {
}
}
通過getService 來獲取底層應(yīng)用的binder服務(wù)灶伊,做個(gè)托管,
BaseSysPropListener extends ISysPropertiesListener.Stub類就是需要繼承來往下注冊(cè)的入口
另外ISysPropertiesListener.aidl 和ISysPropertiesRegistrar.aidl一個(gè)是被注冊(cè)和一個(gè)保持注冊(cè)者的倆個(gè)文件套耕,就是個(gè)觀察者模型谁帕。倆個(gè)的主要實(shí)現(xiàn)binder接口內(nèi)容是在framewrok native里,等會(huì)介紹冯袍。ListeningProps.aidl 和ListeningProps.java 倆個(gè)文件的協(xié)助是為了和framework native對(duì)應(yīng)的ListeningProps結(jié)構(gòu)體實(shí)現(xiàn)序列化傳輸, ListeningProps.java是繼承Parcelable的,因?yàn)槲覀冃枰@個(gè)序列化來傳遞我們參數(shù)數(shù)據(jù)到native層康愤。同理SysProperty.aidl 和SysProperty.java則是單向由native層序列化傳輸?shù)絝ramework base(這里都是由binder來實(shí)現(xiàn)的傳輸)儡循。結(jié)果返回的就是被監(jiān)聽的屬性包含類,可以直接從獲得到的SysProperty實(shí)例里拿出當(dāng)前監(jiān)聽到的屬性名稱和屬性值還有自己原先定義的對(duì)應(yīng)ID征冷。
SystemServiceRegistry文件择膝,就單純的實(shí)例化一個(gè)SysPropertiesManager并托給SystemServer來保存使用。如下做即可
//add bpb 2017.7
registerService(Context.PROPERTY_SERVICE, SysPropertiesManager.class, new StaticServiceFetcher() {
@Override
public SysPropertiesManager createService() {
return new SysPropertiesManager();
}});
在SystemServiceRegistry.java的static初始化區(qū)添加這個(gè)就可以了检激。最后的Android.mk由于我們?cè)黾恿瞬簧賏idl文件肴捉,所以得修改編譯和導(dǎo)出到全局framework環(huán)境空間,修改如下:
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層級(jí),全是C++來實(shí)現(xiàn)ISysPropertiesListener.aidl 和ISysPropertiesRegistrar.aidl复濒,這里說說我對(duì)bnInterface 和bpInterface的理解脖卖,bnInterface繼承BBinder模板,主要是由remote方代碼塊所transact對(duì)應(yīng)的<int>code走的程序內(nèi)容巧颈, 而bpInterface則是本地想要條用remote方某接口前走的程序塊畦木,再在該代碼塊里面?zhèn)鬏攖ransaction內(nèi)容到remote方來判斷<int>code 來走onTransatc.理解流程下圖表示
bninterface和bpinterface理解
以上的ISysPropertiesListener,ISysPropertiesRegistrar都有實(shí)現(xiàn)client方的bpinterface砸泛,ISysPropertiesRegistrar還實(shí)現(xiàn)好bnInterface就形成了雙向binder綁定的局面馋劈。而真正在運(yùn)行的監(jiān)聽?wèi)?yīng)用時(shí)由一個(gè)init通過配置init.rc來啟動(dòng)的sysprobin來運(yùn)用這些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;
}
//定義屬性開關(guān) "sys.prop.listen" value 1 開妓雾, 0 關(guān)
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();
}
}
比較簡(jiǎn)單的單純往安卓data目錄下的myfifo寫入屬性數(shù)據(jù),然后同時(shí)也用系統(tǒng)屬性來控制這個(gè)開關(guān)垒迂。
最后是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);
.....................................
}
這個(gè)是epoll有event數(shù)據(jù)回調(diào)的函數(shù)地方。但在init進(jìn)程里寫myfifo管道需要se安全策略配置机断,在sepoliy目錄里的init.te 末尾加入這段防止執(zhí)行寫入失敗報(bào)錯(cuò)問題楷拳。必須的!
#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配置啟動(dòng)的syspropbin服務(wù)吏奸,配置如下
service syspropbin /sbin/syspropbin
class core
critical
seclabel u:r:syspropbin:s0
group root system
最后的重頭戲欢揖,就是我們的syspropbin服務(wù)的編寫,要實(shí)現(xiàn)不阻塞的監(jiān)聽管道和監(jiān)聽注冊(cè)應(yīng)用發(fā)來的ISysPropertiesListener奋蔚,主要利用好一個(gè)linux系統(tǒng)里的epoll機(jī)制就好了她混。
Android.mk文件直接mmm -B ./該目錄即可烈钞,這里面的過程仿照安卓原生的healthd電池充電服務(wù)來完成的。
重要的代碼段:
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有時(shí)間再添加講坤按。這個(gè)方法已經(jīng)在我目前做的項(xiàng)目用上了的毯欣。
需要的人士可先下載來看即可。