編輯:關於Android編程
前文分析了build.prop這個系統屬性文件的生成http://blog.csdn.net/jscese/article/details/18699155,每個屬性都有一個名稱和值,他們都是字符串格式。屬性被大量使用在Android系統中,用來記錄系統設置或進程之間的信息交換。屬性是在整個系統中全局可見的。每個進程可以get/set屬性,這裡主要記錄在java層或者c++層如果使用,以及整個system_property運作流程。
/** * 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); }
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);
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"); } }
int property_set(const char *key, const char *value) { return __system_property_set(key, value); }
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; }
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); ... }
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_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文件中.
到這裡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); } ... }
可以看到如果接收到的信息是以“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; }
可以看到會判斷是否以“ro”字符串開頭,如果接收到的消息值,也就是要set的屬性值以這個作為開頭,就代表只讀,不能被改變.
如果共享內存中有則update_prop_info(pi, value, valuelen);沒有就保存到內存中。
如果屬性是有“net.”字符串開頭,當設置這種屬性的時候,“net.change”這條屬性也會被自動設置,其內容設為最後更新過的屬性名,用來記錄net.*屬性上面的變化。
如果屬性是有“persist.”字符串開頭,那麼就認為是駐留屬性,當修改的時候同時也會寫進/data/property文件中。
最後調用property_changed(name, value),通知屬性已經改變,更新屬性,僅僅在運行時可用的屬性不需要調用這個方法,除非它們能被數據綁定。
到這裡property_service服務已經大體分析完!
adb shell getprop 列出系統所有屬性
adb shell getprop | grep lcd 列出包含lcd的屬性
adb shell setprop
撰寫不易,轉載請注明出處http://blog.csdn.net/jscese/article/details/18700903。
仿微信通訊錄右側快速定位字母表控件先看效果圖: 界面比較單調,湊合看,主要看功能。這種控件在很多應用的通訊錄的界面,MIUI裡面的通訊錄都有這個功能,其實這是一
AIDL:Android Interface Definition Language,它是一種android內部進程通信接口的描述語言,通過它我們可以定義進程間的通信接口
在web頁面中,有a標簽的超鏈接實現跳轉,同樣在Android當中,用TextView控件來顯示文字,實現它的事件來跳轉。用過微博Android手機端的朋友的都知道微博正
前言 高效的設計稿標注及測量工具Markman介紹。最近有個煩惱是UI設計師可能太忙了,經常給出的UI設計稿中有很多地方都沒有標注,比如長度和顏色值等。這個時候每次都要通