Android系統註冊表(prop屬性)的研究與分析

在程序開發的時候,會使用很多的環境變量,有時候會遇到以下幾種情況:

1.該變量在多處需要使用,並且是跨進程或者跨線程的。

2.該環境變量一般只需讀取一次,不需要頻繁保存。

3.這個變量信息在關機重啓後任然可以保存。

對於這些需求,通常的做法是將這些信息保存到一個文件中,通過對該文件的讀寫來提取和保存信息,這些信息的數據量都比較小。這種方法是可以的,但是不是很系統完善,而且當需要讀取信息時都需要進行一次文件的io操作,這就很費時和浪費系統資源;還有一種情況,就是一個變量信息,開機啓動的時只需從flash中讀取一次,在系統運行時很少對它進行修改;如果保存到文件,每次讀取都要進行一次IO操作,如果保存不當很容易出錯,所以這種信息保存到內存更顯得合適。

Window中有註冊表這樣完善的模塊對少量配置信息進行存儲,使用起來安全、方便、快捷,android中是否也有類似的接口呢?答案是肯定的,那就是Prop模塊(目前還沒有更合適的名字)Prop模塊存儲着系統運行的很多配置信息,當程序運行時需要某種系統狀態時,會到該模塊中進行讀取和尋找。Prop模塊本質上來說,是系統運行時內存中保存的一塊數據區,讀寫數據都是對這一塊區域進行操作;好處是讀寫速度快,數據跨進程共享,缺點是突然斷電會丟失數據;當然Prop也能保存數據,這個在後面提到。

系統中的一些有用的配置信息:

本分基於android4.2源碼進行分析,Android的啓動後,在property_service.cproperty_init中完成prop的初始化。系統中存在着幾個文件,如build.propdefault.prop等,這些文件在系統構建時候生成的,裏面包含很多系統的配置。系統開機時回去加載這些文件中的信息並保存到prop模塊(內存)中去,以便其它程序進行讀取和使用。

例如在build.prop中有如下信息:

dalvik.vm.heapstartsize=5m

dalvik.vm.heapgrowthlimit=96m

dalvik.vm.heapsize=256m

dalvik.vm.heaptargetutilization=0.75

dalvik.vm.heapminfree=512k

dalvik.vm.heapmaxfree=2m

虛擬機的堆棧大小以及其它屬性。

persist.sys.timezone=Asia/Shanghai

時區信息。

ro.build.version.codename=REL

ro.build.version.release=4.2.2

ro.build.date=Fri Dec 26 15:56:10 UTC 2014

ro.build.date.utc=1419609370

軟件版本構建信息。

ro.product.cpu.abi=armeabi-v7a

ro.product.cpu.abi2=armeabi

ro.product.manufacturer=unknown

ro.product.locale.language=en

ro.product.locale.region=US

Cpu信息,默認語言設置。

dalvik.vm.stack-trace-file=/data/anr/traces.txt

虛擬機的調試信息保存。

 

以上動作都是在init.rc中完成的,該過程會調用property_service.c中的start_property_service函數,在該函數中完成以下文件的加載:

#define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"

#define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"

#define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"

#define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"

同時會調用load_persistent_properties函數,該函數會/data/property/中尋找用戶的保存設置。

Env環境變量中有一個ANDROID_PROPERTY_WORKSPACE變量,該變量中存儲着prop內存區域的大小,如果想知道詳細,可查閱init_property_area函數。

Prop模塊對應的Java接口:

Prop模塊是保存少量的全局共享信息,其保存的數據具有信息量少,跨進程共享數據等特性;每一條信息包含兩個屬性,鍵名和鍵名對應的鍵值,例如:

ro.product.locale.language=en

Ro.product.locale.language”表示本產品本地語言,表示該條信息的名字,“en”表示該條信息的取值爲英文,這樣任何一個應用程序就知道本機使用的語言情況。在接口設計時也需要有兩個參數,namevalue(鍵名和鍵值),方法有setget,例如:

Set(String name,String value); String Get(String name);

當然,無論上層怎麼設計,在C底層鍵名name和鍵值value都是以char數組進行保存的,因爲設計者並不知道傳入namevalue的數據大小。

android.os.SystemProperties類中對prop模塊進行了封裝,該類使用SetGet直接進行設置和獲取,當然這些java接口最終還是調用系統接口完成的。在jni層有一個property_service.c文件,文件中有對應的實際處理接口,這些接口即可以給java調用,也可以一些系統命令使用(例如setpropgetprop命令就是調用這些接口方法)SystemProperties類中的get方法沒有什麼限制,但是set方法就有權限的限制,應用程序是不能隨便使用set接口的。

SystemProperties類的訪問必須要有系統權限,並且應用的uid必須是系統id1000或者爲root:0。因爲setget操作不同,set時該操作建立了一個socket管道通過發cmd出去完成的,服務端接收cmd同時比較權限,關鍵代碼如下:

    if (uid == AID_SYSTEM || uid == AID_ROOT)

      return check_control_mac_perms(name, sctx);

只有權限是AID_SYSTEM(系統ID)AID_ROOT(root用戶ID)才能通過驗證;而get沒有權限檢查,不過試想也正常,如果誰都能進行修改,那這黑客也太好當了。當然個人覺得,對於set的設計使用權限驗證無可厚非,但對於查看系統屬性這樣的功能(get),應該還可以進行細分,比如有些屬性是不重要的,任何進程和用戶都可以讀取使用;當然有些敏感的數據在指定讀取權限時也可以進行指定(由於時間有限本人沒有更深入的細讀,也不知道android系統是否完成了這些功能)

設置鍵值名時需要注意的地方:

在進行設置時,包含兩個參數,變量名和變量值,形如:[[key]]: [[value]]。如果原來沒有對應的key值,那麼就會在該模塊中創建一個新的鍵值,否則覆蓋原有鍵值。對於鍵值名在設計時最好按規範書寫,比如“類名.模塊.用途”,這樣清晰可記而且不容易衝突。另外,如果屬性名稱以“ro.”開頭,那麼這個屬性被視爲只讀屬性。一旦設置,屬性值不能改變。這個判斷動作是在property_service.c中的property_set函數中完成的:

if(!strncmp(name, "ro.", 3)) return -1;

如果是以“persist.”開頭,當設置這個屬性時,其值也將寫入/data/property/目錄中,鍵值名就是該屬性名,下次開機重新加載和讀取該屬性;該文件中的load_persistent_properties函數就是用來完成該功能。特別的屬性名以“net.change”開頭那麼其值中必須以“net.”開頭,例如鍵值名爲[net.change]: 那麼鍵值爲[net.qtaguid_enabled],這個設置目前還沒想到有什麼作用。

Shell中對應的prop操作命令:

androidshell中也有對應的命令進行操作,有如下三個命令:

getprop [keyname]

Keyname爲需要獲取的鍵值名,如果沒有參數則打印全部的鍵值信息。

setprop [keyname] [value]

Keyname爲需要獲取的鍵值名,value爲設置的值,這個值爲字符串。

watchprops

監聽系統屬性的變化,如果期間系統的屬性發生變化則把變化的值顯示出來。

init.rc中也使用setprop來設置一些屬性狀態。


   [本文是基於android4.2源碼進行分析]

發佈了27 篇原創文章 · 獲贊 8 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章