zoukankan      html  css  js  c++  java
  • Android 系统属性-SystemProperties详解***

      创建与修改android属性用Systemproperties.set(name, value),获取android属性用Systemproperties.get(name),需要注意的是android属性的名称是有一定的格式要求的:

      如下:1.前缀必须用systemcoreinitproperty_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] = '';
        }
        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
    ", 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
    ");
            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"
    ", 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
    ",
                            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
    ",
                          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文件

  • 相关阅读:
    NGUI Sprite 和 Label 改变Layer 或父物体后 未更新深度问题
    unity销毁层级物体及 NGUI 深度理解总结
    unity 2d 和 NGUI layer
    关于NGUI与原生2D混用相互遮盖的问题心得
    CentOS7为firewalld添加开放端口及相关操作
    Python 操作redis有序集合(sorted set)
    win10下安装redis 服务
    python2/3中 将base64数据写成图片,并将图片数据转为16进制数据的方法、bytes/string的区别
    解决最小化安装Centos7后无法上网的问题,以及安装成功后的基本配置
    在mysql中使用group by和order by取每个分组中日期最大一行数据,亲测有效
  • 原文地址:https://www.cnblogs.com/jiangzhaowei/p/11354451.html
Copyright © 2011-2022 走看看