Android 系統屬性-SystemProperties詳解***


  創建與修改android屬性用Systemproperties.set(name, value),獲取android屬性用Systemproperties.get(name),需要注意的是android屬性的名稱是有一定的格式要求的:

  如下:1.前綴必須用system\core\init\property_service.c中定義的前綴。 2. 進行系統屬性設置的程序也必須有system或root權限,

  如何將android程序的權限提升到system權限?方法是這樣的:

    1、在AndroidManifest.xml中,在manifest加入android:sharedUserId="android.uid.system"。

    2、在Android.mk中,將LOCAL_CERTIFICATE := XXX修改成LOCAL_CERTIFICATE :=platform

  經過以上兩步就可以把ap的權限提升到system權限了。

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.cpp

1     namespace android {
2     extern int register_android_os_SystemProperties(JNIEnv *env);
3     }

frameworks/base/core/jni/android_os_SystemProperties.cpp

 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屬性。

1、  native code

當編寫本地應用程序時,可以使用property_get和property_set 這兩個API來讀取/設置屬性。要使用它們,我們需要include cutils/properties.h,並鏈接libcutils庫。

2、  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屬性庫。

3、  shell腳本

Android提供getprop和setprop命令行工具來獲取和更新屬性。其依賴libcutils實現。 

         補充:通過查看property_service.c,我們可以明確以下事實:

1、  屬性名不是隨意取的。在property_perms數組中定義了當前系統上可用的所有屬性的前綴,以及相對應的存取權限UID。對屬性的設置要滿足權限要求,同時命名也要在這些定義的范圍內。

2、  PA_COUNT_MAX指定了系統(共享內存區域中)最多能存儲多少個屬性。

3、  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,具體使用如下:

1、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"


2、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實現的,有興趣的可以繼續去跟蹤

3、系統屬性怎么生成的
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 

4、系統屬性類別和加載優先級

屬性名稱以“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 
}

當同一屬性在多個文件中都有配置,先加載的會被后加載的覆蓋。

 
5、利用系統屬性動態設置程序中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文件


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM