Android-SystemPropertiesの応用
12705 ワード
一.序
前述のbuild.propというシステム属性ファイルの生成Android-build.prop解析を分析しました.各属性には名前と値があり、文字列フォーマットです.属性は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です.
3.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);
...
}
以上はクライアント側としてsocketを介してサービスにメッセージを送信する.
四.property_サービスの開始
property_サービスサービスの開始は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_init()によって初期化されます.
ここでproperty_serviceというシステム属性サービスが起動しました.Android--起動プロセスの詳細はここで起動に関する詳細があります.
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ソケットが作成され、リスニングステータスに入ります!
ここまで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
",
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
",
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'
", 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属性の本当の実行関数です!
「ロ」文字列で始まるか否かを判断する、受信メッセージ値、すなわち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.