Android——SystemProperties的應用

來源:互聯網
上載者:User

一.序

前文分析了build.prop這個系統屬性檔案的產生http://blog.csdn.net/jscese/article/details/18699155,每個屬性都有一個名稱和值,他們都是字串格式。屬性被大量使用在Android系統中,用來記錄系統設定或進程之間的資訊交換。屬性是在整個系統中全域可見的。每個進程可以get/set屬性,這裡主要記錄在java層或者c++層如果使用,以及整個system_property運作流程。


二.java層調用 源碼位於/frameworks/base/core/java/android/os/SystemProperties.java中:

get屬性:
  /**     * 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屬性:
    /**     * 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);    }

都會調用本地介面:
private static native String native_get(String key);private static native void native_set(String key, String def);


該介面類在初始化運行環境中註冊對應的cpp介面android_os_SystemProperties.cpp,實際操作通過JNI調用的是cpp檔案對應的介面:

/frameworks/base/core/jni/AndroidRuntime.cpp中:

extern int register_android_os_SystemProperties(JNIEnv *env);


/frameworks/base/core/jni/android_os_SystemProperties.cpp中JNI:

static 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 },};

以set屬性為例,調用:

static void SystemProperties_set(JNIEnv *env, jobject clazz,                                      jstring keyJ, jstring valJ){    int err;    const char* key;    const char* val;    if (keyJ == NULL) {        jniThrowNullPointerException(env, "key must not be null.");        return ;    }    key = env->GetStringUTFChars(keyJ, NULL);    if (valJ == NULL) {        val = "";       /* NULL pointer not allowed here */    } else {        val = env->GetStringUTFChars(valJ, NULL);    }    err = property_set(key, val);    env->ReleaseStringUTFChars(keyJ, key);    if (valJ != NULL) {        env->ReleaseStringUTFChars(valJ, val);    }    if (err < 0) {        jniThrowException(env, "java/lang/RuntimeException",                          "failed to set system property");    }}

調用到/system/core/libcutils/properties.c中的 property_set(key, val)。以java層的理解到這裡就OK。


三.c++層的調用/system/core/libcutils/properties.c中的:

int property_set(const char *key, const char *value){    return __system_property_set(key, value);}

到/bionic/libc/bionic/system_properties.c中:
int __system_property_set(const char *key, const char *value){....    memset(&msg, 0, sizeof msg);    msg.cmd = PROP_MSG_SETPROP;    strlcpy(msg.name, key, sizeof msg.name);    strlcpy(msg.value, value, sizeof msg.value);    err = send_prop_msg(&msg);    if(err < 0) {        return err;    }    return 0;}

通過一個普通的TCP(SOCK_STREAM)通訊端進行通訊。

static int send_prop_msg(prop_msg *msg){    struct pollfd pollfds[1];    struct sockaddr_un addr;    socklen_t alen;    size_t namelen;    int s;    int r;    int result = -1;    s = socket(AF_LOCAL, SOCK_STREAM, 0);    if(s < 0) {        return result;    }    memset(&addr, 0, sizeof(addr));    namelen = strlen(property_service_socket);...}

以上是作為client端,通過socket向service發送訊息。




四.property_service服務的啟動

property_service 服務的啟動是在android初始化的時候在/system/core/init/init.c時建立:


int main(int argc, char **argv){    int fd_count = 0;    struct pollfd ufds[4];...int property_set_fd_init = 0;... queue_builtin_action(property_service_init_action, "property_service_init");... if (!property_set_fd_init && get_property_set_fd() > 0) {            ufds[fd_count].fd = get_property_set_fd();            ufds[fd_count].events = POLLIN;            ufds[fd_count].revents = 0;            fd_count++;            property_set_fd_init = 1;        }...  if (ufds[i].revents == POLLIN) {                if (ufds[i].fd == get_property_set_fd())                    handle_property_set_fd();...}

由init守護進程分配一個共用記憶體區來儲存這些屬性。並且通過__libc_init(...)—— __libc_init_common(...)——__system_properties_init();

由/bionic/libc/bionic/system_properties.c中的__system_properties_init()來初始化屬性系統的共用記憶體。


在這裡啟動了property_service這個系統屬性服務,http://blog.csdn.net/jscese/article/details/17115395這裡有啟動相關細節。


從property_service_init_action調用到/system/core/init/property_service.c中的啟動函數:

void start_property_service(void){    int fd;    load_properties_from_file(PROP_PATH_SYSTEM_BUILD);    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);    load_override_properties();    /* Read persistent properties after all default values have been loaded. */    load_persistent_properties();    fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);    if(fd < 0) return;    fcntl(fd, F_SETFD, FD_CLOEXEC);    fcntl(fd, F_SETFL, O_NONBLOCK);    listen(fd, 8);    property_set_fd = fd;}

可以看到,在這裡載入了系統屬性檔案到共用記憶體,檔案定義在/bionic/libc/include/sys/_system_properties.h:

#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"

屬性資訊按照上面的順序被載入。後載入的屬性會覆蓋前面的屬性值(當屬性名稱相同的時候)。當上面載入完成後,最後載入的是駐留屬性,儲存在/data/property檔案中.

建立了一個SOCK_STREAM通訊端並進入監聽listen狀態!

到這裡property_service已經啟動完畢!



五.property_service服務訊息處理

在init守護進程中監聽到有屬性服務的事件時調用:

void handle_property_set_fd(){    prop_msg msg;... if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {        return;    }... r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0));... switch(msg.cmd) {    case PROP_MSG_SETPROP:...  if(memcmp(msg.name,"ctl.",4) == 0) {            // Keep the old close-socket-early behavior when handling            // ctl.* properties.            close(s);            if (check_control_perms(msg.value, cr.uid, cr.gid, source_ctx)) {                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_perms(msg.name, cr.uid, cr.gid, source_ctx)) {                property_set((char*) msg.name, (char*) msg.value);            } else {                ERROR("sys_prop: permission denied uid:%d  name:%s\n",                      cr.uid, msg.name);            }...}

接收socket請求串連,接收屬性請求數,處理資訊:

可以看到如果接收到的資訊是以“ctl”開頭,進行check_control_perms(msg.value, cr.uid, cr.gid, source_ctx)鑒權處理。

這裡的傳入訊息值,發送訊息進程的uid以及gid。

這裡是控制許可權數組:

/* * White list of UID that are allowed to start/stop services. * Currently there are no user apps that require. */struct {    const char *service;    unsigned int uid;    unsigned int gid;} control_perms[] = {    { "dumpstate",AID_SHELL, AID_LOG },    { "ril-daemon",AID_RADIO, AID_RADIO },     {NULL, 0, 0 }};

在/system/core/include/private/android_filesystem_config.h中有各種許可權的定義:

#define AID_ROOT             0  /* traditional unix root user */#define AID_SYSTEM        1000  /* system server */#define AID_RADIO         1001  /* telephony subsystem, RIL */#define AID_BLUETOOTH     1002  /* bluetooth subsystem */#define AID_GRAPHICS      1003  /* graphics devices */


有許可權 就執行/system/core/init/init.c中的:

void handle_control_message(const char *msg, const char *arg){    if (!strcmp(msg,"start")) {        msg_start(arg);    } else if (!strcmp(msg,"stop")) {        msg_stop(arg);    } else if (!strcmp(msg,"restart")) {        msg_stop(arg);        msg_start(arg);    } else {        ERROR("unknown control msg '%s'\n", msg);    }}

這個是用來 開啟和關閉或者重啟 服務!


一般的訊息值鑒權check_perms(msg.name, cr.uid, cr.gid, source_ctx)。

一般的控制許可權數組:

/* White list of permissions for setting property services. */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 },...};

有許可權則執行:

int property_set(const char *name, const char *value){... if(pi != 0) {        /* ro.* properties may NEVER be modified once set */        if(!strncmp(name, "ro.", 3))          return -1;... /* If name starts with "net." treat as a DNS property. */    if (strncmp("net.", name, strlen("net.")) == 0)  {        if (strcmp("net.change", name) == 0) {            return 0;        }       /*        * The 'net.change' property is a special property used track when any        * 'net.*' property name is updated. It is _ONLY_ updated here. Its value        * contains the last updated 'net.*' property.        */        property_set("net.change", name);    } else if (persistent_properties_loaded &&            strncmp("persist.", name, strlen("persist.")) == 0) {        /*         * Don't write properties to disk until after we have read all default properties         * to prevent them from being overwritten by default values.         */        write_persistent_property(name, value);#ifdef HAVE_SELINUX    } else if (strcmp("selinux.reload_policy", name) == 0 &&               strcmp("1", value) == 0) {        selinux_reload_policy();#endif    }    property_changed(name, value);    return 0;}

這個property_set就是set屬性真正的執行函數!


可以看到會判斷是否以“ro”字串開頭,如果接收到的訊息值,也就是要set的屬性值以這個作為開頭,就代表唯讀,不能被改變.


如果共用記憶體中有則update_prop_info(pi, value, valuelen);沒有就儲存到記憶體中。


如果屬性是有“net.”字串開頭,當設定這種屬性的時候,“net.change”這條屬性也會被自動化佈建,其內容設為最後更新過的屬性名稱,用來記錄net.*屬性上面的變化。


如果屬性是有“persist.”字串開頭,那麼就認為是駐留屬性,當修改的時候同時也會寫進/data/property檔案中。


最後調用property_changed(name, value),通知屬性已經改變,更新屬性,僅僅在運行時可用的屬性不需要調用這個方法,除非它們能被資料繫結。


到這裡property_service服務已經大體分析完!



六.adb shell 命令

adb shell getprop 列出系統所有屬性


adb shell getprop | grep lcd 列出包含lcd的屬性


adb shell setprop 修改指定的系統屬性




撰寫不易,轉載請註明出處http://blog.csdn.net/jscese/article/details/18700903。




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.