建立與修改android屬性用Systemproperties.set(name, value),獲取android屬性用Systemproperties.get(name),須要注意的是android屬性的名稱是有必定的格式要求的:java
以下:1.前綴必須用system\core\init\property_service.c中定義的前綴。 2. 進行系統屬性設置的程序也必須有system或root權限,linux
如何將android程序的權限提高到system權限?方法是這樣的:android
一、在AndroidManifest.xml中,在manifest加入android:sharedUserId="android.uid.system"。shell
二、在Android.mk中,將LOCAL_CERTIFICATE := XXX修改爲LOCAL_CERTIFICATE :=platform。vim
通過以上兩步就能夠把ap的權限提高到system權限了。windows
Android 的系統屬性包括兩部分:文件保存的持久屬性和每次開機導入的cache屬性。前者主要保存在下面幾個文件中:
bionic/libc/include/sys/_system_properties.h數組
1 #define PROP_SERVICE_NAME "property_service" 2 #define PROP_PATH_RAMDISK_DEFAULT "/default.prop" 3 #define PROP_PATH_SYSTEM_BUILD "/system/build.prop" 4 #define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop" 5 #define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop"
後者則經過frameworks/base/core/java/android/os/SystemProperties.java的接口定義,架構
1 private static native String native_get(String key); 2 private static native String native_get(String key, String def); 3 private static native void native_set(String key, String def); 4 public static void set(String key, String val) { 5 if (key.length() > PROP_NAME_MAX) { 6 throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); 7 } 8 if (val != null && val.length() > PROP_VALUE_MAX) { 9 throw new IllegalArgumentException("val.length > " + 10 PROP_VALUE_MAX); 11 } 12 native_set(key, val); 13 }
該接口類在初始化運行環境中註冊對應的cpp接口android_os_SystemProperties.cpp,實際操做經過JNI調用的是cpp文件對應的接口:
frameworks/base/core/jni/AndroidRuntime.cppapp
1 namespace android { 2 extern int register_android_os_SystemProperties(JNIEnv *env); 3 }
frameworks/base/core/jni/android_os_SystemProperties.cppdom
1 static void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ, jstring valJ) 2 { 3 int err; 4 const char* key; 5 const char* val; 6 key = env->GetStringUTFChars(keyJ, NULL); 7 if (valJ == NULL) { 8 val = ""; /* NULL pointer not allowed here */ 9 } else { 10 val = env->GetStringUTFChars(valJ, NULL); 11 } 12 err = property_set(key, val); 13 env->ReleaseStringUTFChars(keyJ, key); 14 if (valJ != NULL) { 15 env->ReleaseStringUTFChars(valJ, val); 16 } 17 }
設置key的value時,須要做鑑權,根據設置程序所在進程的fd獲知uid值,好比system server進程能夠設置net打頭的key,不能夠設置gsm打頭的key,相關的定義以下:
system/core/include/private/android_filesystem_config.h
1 #define AID_ROOT 0 /* traditional unix root user */ 2 #define AID_SYSTEM 1000 /* system server */ 3 #define AID_RADIO 1001 /* telephony subsystem, RIL */ 4 #define AID_DHCP 1014 /* dhcp client */ 5 #define AID_SHELL 2000 /* adb and debug shell user */ 6 #define AID_CACHE 2001 /* cache access */ 7 #define AID_APP 10000 /* first app user */
vim ./system/core/init/property_service.c
struct { const char *prefix; unsigned int uid; unsigned int gid; } property_perms[] = { { "net.rmnet0.", AID_RADIO, 0 }, { "net.gprs.", AID_RADIO, 0 }, { "net.ppp", AID_RADIO, 0 }, { "net.qmi", AID_RADIO, 0 }, { "net.lte", AID_RADIO, 0 }, { "net.cdma", AID_RADIO, 0 }, { "ril.", AID_RADIO, 0 }, { "gsm.", AID_RADIO, 0 }, { "persist.radio", AID_RADIO, 0 }, { "net.dns", AID_RADIO, 0 }, { "sys.usb.config", AID_RADIO, 0 }, { "net.", AID_SYSTEM, 0 }, { "dev.", AID_SYSTEM, 0 }, { "runtime.", AID_SYSTEM, 0 }, { "hw.", AID_SYSTEM, 0 }, { "sys.", AID_SYSTEM, 0 }, { "sys.powerctl", AID_SHELL, 0 }, { "service.", AID_SYSTEM, 0 }, { "wlan.", AID_SYSTEM, 0 }, { "bluetooth.", AID_BLUETOOTH, 0 }, { "dhcp.", AID_SYSTEM, 0 }, { "dhcp.", AID_DHCP, 0 }, //MStar Android Patch Begin { "dhcp.offer", AID_NOBODY, 0 }, //MStar Android Patch End { "debug.", AID_SYSTEM, 0 }, { "debug.", AID_SHELL, 0 }, { "log.", AID_SHELL, 0 }, { "service.adb.root", AID_SHELL, 0 }, { "service.adb.tcp.port", AID_SHELL, 0 }, { "persist.sys.", AID_SYSTEM, 0 }, { "persist.service.", AID_SYSTEM, 0 }, { "persist.security.", AID_SYSTEM, 0 }, { "persist.service.bdroid.", AID_BLUETOOTH, 0 }, { "selinux." , AID_SYSTEM, 0 }, // MStar Android Patch Begin { "mstar.", AID_SYSTEM, 0 }, { "mstar.", AID_MEDIA, 0 }, { "http.", AID_SYSTEM, 0 }, { "mstar.media.", AID_MEDIA, 0 }, { "mstar.dvfs.", AID_MEDIA, 0 }, { "persist.bt.", AID_BLUETOOTH, 0 }, { "mstar.", AID_GRAPHICS, 0 }, // MStar Android Patch End { NULL, 0, 0 } };
在開機啓動後的init操做中,會執行一個loop循環,當檢測到有新的設置時,進入設置流程,鑑權失敗會提示相關的異常,如sys_prop: permission denied uid:1000 name:gsm.phone.id
system/core/init/init.c
1 void property_changed(const char *name, const char *value) 2 { 3 if (property_triggers_enabled) { 4 queue_property_triggers(name, value); 5 drain_action_queue(); 6 } 7 } 8 int main(int argc, char **argv) 9 { 10 parse_config_file("/init.rc"); 11 qemu_init(); 12 device_fd = device_init(); 13 property_init(); 14 fd = open(console_name, O_RDWR); 15 property_set_fd = start_property_service(); 16 ufds[0].fd = device_fd; 17 ufds[0].events = POLLIN; 18 ufds[1].fd = property_set_fd; 19 ufds[1].events = POLLIN; 20 ufds[2].fd = signal_recv_fd; 21 ufds[2].events = POLLIN; 22 fd_count = 3; 23 for(;;) { 24 if (ufds[0].revents == POLLIN) 25 handle_device_fd(device_fd); 26 27 if (ufds[1].revents == POLLIN) 28 handle_property_set_fd(property_set_fd); 29 if (ufds[3].revents == POLLIN) 30 handle_keychord(keychord_fd); 31 } 32 return 0; 33 }
屬性系統是android的一個重要特性。它做爲一個服務運行,管理系統配置和狀態。全部這些配置和狀態都是屬性。每一個屬性是一個鍵值對(key/value pair),其類型都是字符串。
從功能上看,屬性與windows系統的註冊表很是類似。許多android應用程序和庫直接或者間接地依賴此特性,以決定它們的運行時行爲。例如,adbd進程查詢屬性服務已確認當前是否運行在模擬器環境中。另外一個例子是java.io.File.pathSpearator,其返回存儲於屬性服務中的值。
屬性系統是如何工做的
屬性系統的上層架構以下圖所示。
圖中有3個進程、一組永久屬性文件和一塊共享內存區域。共享內存區域是全部屬性記錄的存儲所在。只有屬性服務進程才能夠寫入共享內存區域,它負責從永久文件中加載屬性記錄並將它們保存在共享內存中。
consumer進程將共享內存加載到其自身的虛擬地址空間並直接訪問這些屬性。setter進程一樣將共享內存加載到其自身的虛擬地址空間,但其不能直接寫該內存。當setter試圖增長或者更新一個屬性時,它將該屬性經過unix domain socket發送至屬性服務。屬性服務表明setter進程將該屬性寫入共享內存和永久文件中。
屬性服務運行於init進程中。init進程首先建立一個共享內存區域,並保存一個指向該區域的描述符fd。init進程將該區域經過使用了MAP_SHARED標誌的mmap映射至它自身的虛擬地址空間,這樣,任何對於該區域的更新對於全部進程都是可見的。fd和區域大小被存儲在一個名爲ANDROID_PROPERTY_WORKSPACE的變量中。任何其餘進程,好比consumer和setter將使用這個變量來得到fd和尺寸,這樣它們就能mmap這個區域到它們自身的虛擬地址空間中。該共享內存區域以下圖所示。
在這以後,init進程將從下列文件加載屬性:
/default.prop /system/build.prop /system/default.prop /data/local.prop
下一步是啓動屬性服務。在這一步中,一個unix domain socket服務被建立。此socket的路徑是/dev/socket/property_service,該路徑對於其餘客戶端進程是熟知的。最後,init進程調用poll來等待該socket上的鏈接事件。
在consumer一邊,當它初始化libc(bionic/libc/bionic/libc_common.c __libc_init_common 函數),它將從環境變量中返回fd和尺寸,並映射共享內存到其自身的地址空間(bionic/libc/bionic/system_properties.c __system_properties_init 函數)。在這以後,libcutils能夠想讀取普通內存那樣爲consumer讀取屬性。
目前,屬性是不可以被刪除的。也就是說,一旦添加了一個屬性,它將不可以被刪除,其鍵也不可以被改變。
如何讀取/設置屬性
Android上有三種主要途徑來get/set屬性。
一、 native code
當編寫本地應用程序時,可使用property_get和property_set 這兩個API來讀取/設置屬性。要使用它們,咱們須要include cutils/properties.h,並連接libcutils庫。
二、 java code
在Java包(java.lang.System)中提供有System.getProperty和System.setProperty方法。但值得注意的是,儘管這兩個API在語義上等同native函數,但其將數據存儲於徹底不一樣的位置。實際上,dalvik VM使用一個哈希表來存儲這些屬性。因此,用這兩個API存儲的屬性是獨立的,不能存取native屬性,反之亦然。
然而Android有一個內部隱藏類(@hide,對SDK不可見)android.os.SystemProperties來操縱native屬性。其經過jni來存取native屬性庫。
三、 shell腳本
Android提供getprop和setprop命令行工具來獲取和更新屬性。其依賴libcutils實現。
補充:經過查看property_service.c,咱們能夠明確如下事實:
一、 屬性名不是隨意取的。在property_perms數組中定義了當前系統上可用的全部屬性的前綴,以及相對應的存取權限UID。對屬性的設置要知足權限要求,同時命名也要在這些定義的範圍內。
二、 PA_COUNT_MAX指定了系統(共享內存區域中)最多能存儲多少個屬性。
三、 PROP_NAME_MAX指定了一個屬性的key最大容許長度;PROP_VALUE_MAX則指定了value的最大容許長度。
此外,http://blog.csdn.net/tekkamanitachi/archive/2009/06/18/4280982.aspx 這篇文章翻譯了Android的官方文檔,從另外一個角度敘述了屬性系統,須要者請參看。
Android系統源碼中,存在大量的SystemProperties.get或SystemProperties.set,具體使用以下:
一、SystemProperties的使用
SystemProperties的使用很簡單,在SystemProperties.java中因此方法都是static,直接經過SystemProperties.get(String key)或SystemProperties.set(String key, String val)就能夠了,系統屬性都是以鍵值對的形式存在即name和value
須要注意的是對name和value的length是有限制的,name的最大長度是31,value最大長度是91,具體定義以下:
//frameworks/base/core/java/android/os/SystemProperties.java public class SystemProperties { public static final int PROP_NAME_MAX = 31; public static final int PROP_VALUE_MAX = 91; ... private static native String native_get(String key); private static native void native_set(String key, String def); /** * Get the value for the given key. * @return an empty string if the key isn't found * @throws IllegalArgumentException if the key exceeds 32 characters */ public static String get(String key) { if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } return native_get(key); } ... /** * Set the value for the given key. * @throws IllegalArgumentException if the key exceeds 32 characters * @throws IllegalArgumentException if the value exceeds 92 characters */ public static void set(String key, String val) { if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } if (val != null && val.length() > PROP_VALUE_MAX) { throw new IllegalArgumentException("val.length > " + PROP_VALUE_MAX); } native_set(key, val); } ... }
在調用get或set時,都是經過調用native方法去操做的,開始原本想一筆帶過的,仍是看看native方法中的具體流程吧,若是不感興趣能夠直接看第三條
ps:在調用SystemProperties.set時所在的apk uid必須在system group,不然設置屬性會報錯,在manifest配置下行:
android:sharedUserId="android.uid.system"
二、SystemProperties中方法具體實現
SystemProperties.java中全部native方法都是在android_os_SystemProperties.cpp裏實現,先來看看java中個方法是怎麼和cpp中方法對應起來的
//frameworks/base/core/jni/android_os_SystemProperties.cpp static const JNINativeMethod method_table[] = { { "native_get", "(Ljava/lang/String;)Ljava/lang/String;", (void*) SystemProperties_getS }, { "native_get", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (void*) SystemProperties_getSS }, { "native_get_int", "(Ljava/lang/String;I)I", (void*) SystemProperties_get_int }, { "native_get_long", "(Ljava/lang/String;J)J", (void*) SystemProperties_get_long }, { "native_get_boolean", "(Ljava/lang/String;Z)Z", (void*) SystemProperties_get_boolean }, { "native_set", "(Ljava/lang/String;Ljava/lang/String;)V", (void*) SystemProperties_set }, { "native_add_change_callback", "()V", (void*) SystemProperties_add_change_callback }, }; int register_android_os_SystemProperties(JNIEnv *env) { return RegisterMethodsOrDie(env, "android/os/SystemProperties", method_table, NELEM(method_table)); }
register_android_os_SystemProperties方法是在AndroidRuntime.cpp中調用的,RegisterMethodsOrDie能夠簡單的理解爲把method_table數組裏的方法一一對應起來。在android_os_SystemProperties.cpp中能夠發現最終實現是不區分SystemProperties_get_int或 SystemProperties_getS,基本全部的方法都是調用property_get和property_set方法來實現的
property_get和property_set是在properties.c裏實現的,以下
//system/core/libcutils/properties.c int property_get(const char *key, char *value, const char *default_value) { int len; len = __system_property_get(key, value); if(len > 0) { return len; } if(default_value) { len = strlen(default_value); if (len >= PROPERTY_VALUE_MAX) { len = PROPERTY_VALUE_MAX - 1; } memcpy(value, default_value, len); value[len] = '\0'; } return len; } int property_set(const char *key, const char *value) { return __system_property_set(key, value); }
進程啓動後數據已經將系統屬性數據讀取到相應的共享內存中,保存在全局變量__system_property_area__,具體操做在system_properties.cpp中
//bionic/libc/bionic/system_properties.cpp int __system_property_get(const char *name, char *value) { const prop_info *pi = __system_property_find(name); if (pi != 0) { //數據已經存儲在內存中__system_property_area__ 等待讀取完返回 return __system_property_read(pi, 0, value); } else { value[0] = 0; return 0; } }
設置屬性經過異步socket通訊,向property_service發送消息
int __system_property_set(const char *key, const char *value) { if (key == 0) return -1; if (value == 0) value = ""; if (strlen(key) >= PROP_NAME_MAX) return -1; if (strlen(value) >= PROP_VALUE_MAX) return -1; prop_msg msg; memset(&msg, 0, sizeof msg); msg.cmd = PROP_MSG_SETPROP; strlcpy(msg.name, key, sizeof msg.name); strlcpy(msg.value, value, sizeof msg.value); const int err = send_prop_msg(&msg); if (err < 0) { return err; } return 0; } static int send_prop_msg(const prop_msg *msg) { //sokcet 通訊 /dev/socket/property_service const int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd == -1) { return -1; } //static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME; const size_t namelen = strlen(property_service_socket); sockaddr_un addr; memset(&addr, 0, sizeof(addr)); strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path)); addr.sun_family = AF_LOCAL; socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1; if (TEMP_FAILURE_RETRY(connect(fd, reinterpret_cast<sockaddr*>(&addr), alen)) < 0) { close(fd); return -1; } const int num_bytes = TEMP_FAILURE_RETRY(send(fd, msg, sizeof(prop_msg), 0)); ... close(fd); return result; }
property_service是在init進程調用start_property_service啓動的,在property_service.cpp中
//system/core/init/property_service.cpp void start_property_service() { //建立socket property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0666, 0, 0, NULL); if (property_set_fd == -1) { ERROR("start_property_service socket creation failed: %s\n", strerror(errno)); exit(1); } //監聽socket listen(property_set_fd, 8); register_epoll_handler(property_set_fd, handle_property_set_fd); }
仍是在property_service.cpp的handle_property_set_fd()處理對應屬性
static void handle_property_set_fd() { //等待創建通訊 if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { return; } /獲取套接字相關信息 uid gid if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { close(s); ERROR("Unable to receive socket options\n"); return; } ... //接收屬性設置請求消息 r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT)); //處理消息 switch(msg.cmd) { case PROP_MSG_SETPROP: msg.name[PROP_NAME_MAX-1] = 0; msg.value[PROP_VALUE_MAX-1] = 0; //檢查屬性名是否合法 if (!is_legal_property_name(msg.name, strlen(msg.name))) { ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name); close(s); return; } getpeercon(s, &source_ctx); //處理ctl.開頭消息 if(memcmp(msg.name,"ctl.",4) == 0) { // Keep the old close-socket-early behavior when handling // ctl.* properties. close(s); //檢查權限,處理以ctl開頭的屬性 if (check_control_mac_perms(msg.value, source_ctx, &cr)) { handle_control_message((char*) msg.name + 4, (char*) msg.value); } else { ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n", msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid); } } else { //檢查權限,設置對應的屬性 if (check_mac_perms(msg.name, source_ctx, &cr)) { property_set((char*) msg.name, (char*) msg.value); } else { ERROR("sys_prop: permission denied uid:%d name:%s\n", cr.uid, msg.name); } close(s); } ... } }
不繼續看了,後面的坑還有不少,考慮到後面還有幾個方面沒講,簡單說下property_set最後是調用了property_set_impl實現的,有興趣的能夠繼續去跟蹤
三、系統屬性怎麼生成的
Android的build.prop文件是在Android編譯時刻收集的各類property(LCD density/語言/編譯時間, etc.),編譯完成以後,文件生成在out/target/product/<board>/system/目錄下,build.prop的生成是由make系統解析build/core/Makefile完成。
3.1 Makefile中直接把$(TARGET_DEVICE_DIR)/system.prop的內容追加到build.prop中
3.2 收集ADDITIONAL_BUILD_PROPERTIES中的屬性,追加到build.prop中
3.3 ADDITIONAL_BUILD_PROPERTIES又會收集PRODUCT_PROPERTY_OVERRIDES中定義的屬性
在配置系統屬性時,若是是在*.prop文件中配置直接是在文件添加一行「persist.timed.enable=true」就行,但在*.mk配置時就須要加上PRODUCT_PROPERTY_OVERRIDES屬性,須要注意最後一個沒有「\」,下面提供了一個實例
PRODUCT_PROPERTY_OVERRIDES += \ persist.timed.enable=true \ persist.timed.enable=true \ ... \ key=value
四、系統屬性類別和加載優先級
屬性名稱以「ro.」開頭,被視爲只讀屬性。一旦設置,屬性值不能改變。
屬性名稱以「persist.」開頭,當設置這個屬性時,其值也將寫入/data/property。
屬性名稱以「net.」開頭,當設置這個屬性時,「net.change」屬性將會自動設置,以加入到最後修改的屬性名。
(這是很巧妙的。 netresolve模塊的使用這個屬性來追蹤在net.*屬性上的任何變化。)
屬性「 ctrl.start 」和「 ctrl.stop 」是用來啓動和中止服務。每一項服務必須在init.rc中定義。系統啓動時,與init守護進程將解析init.rc和啓動屬性服務(此處在7.0上有改動,作了相關優化,後面會說到)。一旦收到設置「 ctrl.start 」屬性的請求,屬性服務將使用該屬性值做爲服務名找到該服務,啓動該服務。這項服務的啓動結果將會放入「 init.svc.<服務名>「屬性中。客戶端應用程序能夠輪詢那個屬性值,以肯定結果。
須要注意的一點是在Android7.0之後在mk文件中提供了一個編譯宏LOCAL_INIT_RC用於將服務相關的RC文件編譯到相應位置。這能確保服務定義和服務的可執行文件同時存在,避免了以前出現的服務對應的可執行程序不存在的問題。
單一的init*.rc,被拆分,服務根據其二進制文件的位置(/system,/vendor,/odm)定義到對應分區的etc/init目錄中,每一個服務一個rc文件。與該服務相關的觸發器、操做等也定義在同一rc文件中。
在init執行mount_all指令掛載分區時,會加載這些目錄中的rc文件,並在適當的時機運行這些服務和操做,具體能夠參考system/core/init/readme.txt文件裏面有詳細的介紹。
系統屬性是在init.rc中加載的,具體啓動方式以下:
//system/core/rootdir/init.rc on property:sys.boot_from_charger_mode=1 trigger late-init # Load properties from /system/ + /factory after fs mount. on load_system_props_action load_system_props #定義在property_service.cpp on load_persist_props_action load_persist_props #定義在property_service.cpp start logd start logd-reinit # Mount filesystems and start core system services. on late-init # Load properties from /system/ + /factory after fs mount. Place # this in another action so that the load will be scheduled after the prior # issued fs triggers have completed. trigger load_system_props_action # Load persist properties and override properties (if enabled) from /data. trigger load_persist_props_action
當屬性值sys.boot_from_charger_mode爲1時,會觸發late-init,在late-init又會觸發load_system_props_action和load_persist_props_action,具體看看property_service.cpp這兩個方法對應幹啥了
//system/core/init/property_service.cpp void load_system_props() { load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);//加載system/build.prop load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);//加載vendor/build.prop load_properties_from_file(PROP_PATH_FACTORY, "ro.*");//加載factory/factory.prop load_recovery_id_prop();//加載recovery相關prop } void load_persist_props(void) { load_override_properties();//若是"ro.debuggable"爲1加載data/local.prop裏的屬性 /* Read persistent properties after all default values have been loaded. */ load_persistent_properties();//加載/data/property裏的persistent properties }
當同一屬性在多個文件中都有配置,先加載的會被後加載的覆蓋。
五、利用系統屬性動態設置程序中Log的開關android 動態控制logcat日誌開關,經過Log.isLoggable(TAG,level)方法動態控制,以FlashlightController類爲例 private static final String TAG = "FlashlightController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);利用adb命令設置屬性值來控制該日誌開關adb shell setprop log.tag.FlashlightController DEBUG 設置該TAG的輸出級別爲DEBUG,則 level爲DEBUG以上的都返回true須要注意的是經過adb shell setprop設置的屬性值每次重啓後都會恢復以前的值,log的級別以下:where <tag> is a log component tag (or * for all) and priority is: V Verbose (default for <tag>) D Debug (default for '*') I Info W Warn E Error F Fatal S Silent (suppress all output)也能夠將該屬性添加在data/local.prop屬性文件中,不一樣的是隻要存在local.prop,該手機重啓與否屬性值都存在,另外須要注意的是user版ro.debuggable=0,系統啓動時不會讀取/data/local.prop文件