編輯:關於Android編程
屬性在android中非常重要,我們基本的不多介紹了,主要說下其用法,原理等。
在java層主要通過SystemProperties這個類來訪問Android的系統屬性,通過一系列的native函數。
public class SystemProperties { ...... public static String get(String key) { if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } return native_get(key); } public static String get(String key, String def) { if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } return native_get(key, def); } public static int getInt(String key, int def) { if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } return native_get_int(key, def); } public static long getLong(String key, long def) { if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } return native_get_long(key, def); } public static boolean getBoolean(String key, boolean def) { if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } return native_get_boolean(key, def); } 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); }
我們再來看下android_os_SystemProperties.cpp中的這些native函數,注意都是靜態的,因為在java層也是靜態調用。
static jboolean SystemProperties_get_boolean(JNIEnv *env, jobject clazz, jstring keyJ, jboolean defJ) { int len; const char* key; char buf[PROPERTY_VALUE_MAX]; jboolean result = defJ; if (keyJ == NULL) { jniThrowNullPointerException(env, "key must not be null."); goto error; } key = env->GetStringUTFChars(keyJ, NULL); len = property_get(key, buf, ""); if (len == 1) { char ch = buf[0]; if (ch == '0' || ch == 'n') result = false; else if (ch == '1' || ch == 'y') result = true; } else if (len > 1) { if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) { result = false; } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) { result = true; } } env->ReleaseStringUTFChars(keyJ, key); error: return result; } 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文件中的下面函數
int property_set(const char *key, const char *value) { return __system_property_set(key, value); } 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; }最後在bionic/libc/bionic/system_properties.cpp中調用如下函數write都是通過socket來往init寫屬性的,然後在init中調用__system_property_update和__system_property_add來往共享內存中寫屬性,但是獲取屬性應該是通過共享內存讀取的。
int __system_property_get(const char *name, char *value) { const prop_info *pi = __system_property_find(name);//從共享內存上獲取相應的屬性內存 if (pi != 0) { return __system_property_read(pi, 0, value);//從屬性內存中讀取屬性內容 } else { value[0] = 0; return 0; } } 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; }
c層獲取屬性我們就是通過上面的property_set和property_get方法
int property_set(const char *key, const char *value) { return __system_property_set(key, value); } 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; }
系統中的每個進程都可以調用這些函數來讀取和修改屬性。讀取屬性值對任何進程都是沒有限制的,直接由本進程從共享區中讀取;但是修改屬性值則必須通過init進程完成,同時進程還需要檢查請求的進程是否有權限修改該屬性值。
屬性值成功修改後,init進程會檢查init.rc中是否定義了該屬性值的觸發器。如果有定義,就執行該觸發器下的命令。看下面:
on property:sys.lc.amtmode=0 class_start core class_start main class_start late_start start lc-oms-sa
我們看下屬性的一些分類:
1.ro前綴的,"ro."這樣的屬性是只讀屬性,一旦設置,屬性值不能再改變了。
2.persist前綴的,"persist."這樣的屬性改變會寫入目錄data/property下與屬性名相同的文件中。再次開機時這些值會被init進程讀取出來,因此關機再啟動也是生效的。
3.net前綴的,"net."這樣的屬性當它改變時,屬性"net.change"將會被自動設置為最後修改的屬性名
4.屬性"ctl.start" "ctl.stop"和 "ctl.restart"屬性控制類屬性,用於啟動和停止服務的。使用ctl.start啟動服務時,系統將會啟動結果放在名為"init.svc.服務名”屬性中。
property_init 主要是在__system_property_area_init函數中創建了共享內存
void property_init() { if (property_area_initialized) { return; } property_area_initialized = true; if (__system_property_area_init()) { return; } pa_workspace.size = 0; pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); if (pa_workspace.fd == -1) { ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno)); return; } }
我們來看__system_property_area_init函數,最後是在map_prop_area_rw函數中調用了mmap創建了共享內存
int __system_property_area_init() { return map_prop_area_rw(); }
static int map_prop_area_rw() { /* dev is a tmpfs that we can use to carve a shared workspace * out of, so let's do that... */ const int fd = open(property_filename,//文件/dev/__properties__,應該是匿名映射,沒有實際文件 O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444); if (fd < 0) { if (errno == EACCES) { /* for consistency with the case where the process has already * mapped the page in and segfaults when trying to write to it */ abort(); } return -1; } if (ftruncate(fd, PA_SIZE) < 0) { close(fd); return -1; } pa_size = PA_SIZE; pa_data_size = pa_size - sizeof(prop_area); compat_mode = false; void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//內存映射 if (memory_area == MAP_FAILED) { close(fd); return -1; } prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION); /* plug into the lib property services */ __system_property_area__ = pa; close(fd); return 0; }
共享內存使用名稱為如下的設備文件創建。
#define PROP_FILENAME "/dev/__properties__"
在init進程中的主函數中:在解析init.rc之前,先調用了start_property_service函數
property_load_amt_defaults(amt_mode); property_load_boot_defaults(); start_property_service(); init_parse_config_file("/init.rc");
start_property_service函數創建了socket,然後監聽,並且調用register_epoll_handler函數把socket的fd放入了epoll中
void start_property_service() { 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); } listen(property_set_fd, 8); register_epoll_handler(property_set_fd, handle_property_set_fd); }
register_epoll_handler函數就是把fd放入epoll中
void register_epoll_handler(int fd, void (*fn)()) { epoll_event ev; ev.events = EPOLLIN; ev.data.ptr = reinterpret_cast(fn); if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { ERROR("epoll_ctl failed: %s\n", strerror(errno)); } }
先我們就來解析下handle_property_set_fd函數
static void handle_property_set_fd() { prop_msg msg; int s; int r; struct ucred cr; struct sockaddr_un addr; socklen_t addr_size = sizeof(addr); socklen_t cr_size = sizeof(cr); char * source_ctx = NULL; struct pollfd ufds[1]; const int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */ int nr; if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {//獲取對端socket的fd return; } /* Check socket options here */ if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { close(s); ERROR("Unable to receive socket options\n"); return; } ufds[0].fd = s; ufds[0].events = POLLIN; ufds[0].revents = 0; nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms)); if (nr == 0) { ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid); close(s); return; } else if (nr < 0) { ERROR("sys_prop: error waiting for uid=%d to send property message: %s\n", cr.uid, strerror(errno)); close(s); return; } r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));//獲取socket數據 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; } 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); if(memcmp(msg.name,"ctl.",4) == 0) {//ctl類型 // Keep the old close-socket-early behavior when handling // ctl.* properties. close(s); if (check_control_mac_perms(msg.value, 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, 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); } // Note: bionic's property client code assumes that the // property server will not close the socket until *AFTER* // the property is written to memory. close(s); } freecon(source_ctx); break; default: close(s); break; } }
我們來看property_set函數調用了property_set_impl函數來設置屬性
int property_set(const char* name, const char* value) { int rc = property_set_impl(name, value); if (rc == -1) { ERROR("property_set(\"%s\", \"%s\") failed\n", name, value); } return rc; }property_set_impl函數主要講屬性值寫入,或者更新到共享內存中,然後當屬性是net類型的,把net類型的屬性名寫入net.change屬性,persist屬性寫入文件,最後調用property_changed函數來處理,屬性改變後的觸發器事件。
static int property_set_impl(const char* name, const char* value) { size_t namelen = strlen(name); size_t valuelen = strlen(value); if (!is_legal_property_name(name, namelen)) return -1; if (valuelen >= PROP_VALUE_MAX) return -1; if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) { if (selinux_reload_policy() != 0) { ERROR("Failed to reload policy\n"); } } else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) { if (restorecon_recursive(value) != 0) { ERROR("Failed to restorecon_recursive %s\n", value); } } prop_info* pi = (prop_info*) __system_property_find(name); if(pi != 0) { /* ro.* properties may NEVER be modified once set */ if(!strncmp(name, "ro.", 3)) return -1;//ro文件,直接退出 __system_property_update(pi, value, valuelen);//更新屬性數據到共享內存 } else { int rc = __system_property_add(name, namelen, value, valuelen);//增加屬性 if (rc < 0) { return rc; } } /* 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);//net類型的屬性,改變後需要寫屬性到net.change } 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);//persist類型的屬性寫入到data/property目錄下以屬性名命名的文件 } property_changed(name, value); return 0; }
我們先看下write_persistent_property函數,將屬性在data/property目錄下創建以屬性名命名的文件,然後寫入屬性值。寫入方式是先做了一個臨時文件,成功後改名。
static void write_persistent_property(const char *name, const char *value) { char tempPath[PATH_MAX]; char path[PATH_MAX]; int fd; snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR); fd = mkstemp(tempPath);//做臨時文件 if (fd < 0) { ERROR("Unable to write persistent property to temp file %s: %s\n", tempPath, strerror(errno)); return; } write(fd, value, strlen(value));//寫入數據 fsync(fd); close(fd); snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name); if (rename(tempPath, path)) {//改名 unlink(tempPath); ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path); } }
property_changed函數就是看有哪些滿足屬性的觸發器,然後放入執行隊列中。最後在init的循環中,執行觸發器相應的命令
void property_changed(const char *name, const char *value) { if (property_triggers_enabled) queue_property_triggers(name, value); }
void queue_property_triggers(const char *name, const char *value) { struct listnode *node, *node2; struct action *act; struct trigger *cur_trigger; bool match; int name_length; list_for_each(node, &action_list) { act = node_to_item(node, struct action, alist); match = !name; list_for_each(node2, &act->triggers) { cur_trigger = node_to_item(node2, struct trigger, nlist); if (!strncmp(cur_trigger->name, "property:", strlen("property:"))) { const char *test = cur_trigger->name + strlen("property:"); if (!match) { name_length = strlen(name); if (!strncmp(name, test, name_length) && test[name_length] == '=' && (!strcmp(test + name_length + 1, value) || !strcmp(test + name_length + 1, "*"))) { match = true; continue; } } const char* equals = strchr(test, '='); if (equals) { char prop_name[PROP_NAME_MAX + 1]; char value[PROP_VALUE_MAX]; int length = equals - test; if (length <= PROP_NAME_MAX) { int ret; memcpy(prop_name, test, length); prop_name[length] = 0; /* does the property exist, and match the trigger value? */ ret = property_get(prop_name, value); if (ret > 0 && (!strcmp(equals + 1, value) || !strcmp(equals + 1, "*"))) { continue; } } } } match = false; break; } if (match) { action_add_queue_tail(act);//最後將滿足的觸發器加入執行隊列中 } } }
我們先來看init.rc中的下面觸發器
on load_system_props_action load_system_props
而load_system_props_action是在late-init中觸發的
on late-init trigger early-fs trigger fs trigger post-fs # 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_system_props的處理,其中PROP_PATH_SYSTEM_BUILD就是/system/build.prop文件,load_properties_from_file函數最後會調用property_set函數設置屬性
void load_system_props() { load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL); load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL); load_properties_from_file(PROP_PATH_FACTORY, "ro.*"); load_recovery_id_prop(); }
同樣persist類型的屬性如下:
on load_persist_props_action load_persist_props start logd start logd-reinit
也是在late-init觸發,最後調用load_persist_props
on late-init trigger early-fs trigger fs trigger post-fs # 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 # Now we can mount /data. File encryption requires keymaster to decrypt # /data, which in turn can only be loaded when system properties are present trigger post-fs-data trigger load_persist_props_action
load_persist_props函數調用了load_override_properties load_persistent_properties來去讀屬性值
void load_persist_props(void) { load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties();//讀取data/property/下面persist類型的屬性 }load_override_properties函數,如果ro.debuggable為1.從文件/data/local.prop來讀取屬性。/data/local.prop文件時為了覆蓋系統缺省的屬性值。build.prop文件放在system目錄下,修改不是很方便,如果希望測試某個屬性,可以在/data/local.prop文件中修改,可以覆蓋build.prop中的定義。
static void load_override_properties() { if (ALLOW_LOCAL_PROP_OVERRIDE) { char debuggable[PROP_VALUE_MAX]; int ret = property_get("ro.debuggable", debuggable); if (ret && (strcmp(debuggable, "1") == 0)) { load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL); } } }
了解Android繪圖或者自定義View的同學,都知道Canvas類、Paint類等。今天就來看看Paint的有關描述。首先看看官網的定義:The Paint class
Android學習筆記二十七之ExpandableListView可折疊列表和StackView棧視圖ExpandableListView可折疊列表 這一節我們介紹第三
微信運動是微信開發的基於第三方個人運動數據實現微信好友運動數據記錄和PK的一項服務,微信運動通過讀取第三方運動數據實現與微信好友運動數據PK,目前微信運動支
正如我們知道的,android是不讓在子線程中更新ui的。在子線程中更新ui會直接拋出異常Only the original thread that created a