文档章节

Android System Property

雨焰
 雨焰
发布于 2014/09/26 11:39
字数 2829
阅读 104
收藏 0
点赞 0
评论 0


一 System Property

  代码中大量存在:SystemProperties.set()/SystemProperties.get();通过这两个接口可以对系统的属性进行读取/设置,

顾名思义系统属性,肯定对整个系统全局共享。通常程序的执行以进程为单位各自相互独立,如何实现全局共享呢?

System Properties是怎么一回事,又是如何实现的呢?

       属性系统是android的一个重要特性。它作为一个服务运行,管理系统配置和状态。所有这些配置和状态都是属性。

每个属性是一个键值对(key/value pair),其类型都是字符串。

这些属性可能是有些资源的使用状态,进程的执行状态,系统的特有属性……

       可以通过命令adb shell :

    getprop查看手机上所有属性状态值。

       或者 getprop init.svc.bootanim制定查看某个属性状态

       使用setprop init.svc.bootanim start 设置某个属性的状态

特别属性 :

  如果属性名称以“ro.”开头,那么这个属性被视为只读属性。一旦设置,属性值不能改变。

  如果属性名称以“persist.”开头,当设置这个属性时,其值也将写入/data/property。

  如果属性名称以“net.”开头,当设置这个属性时,“net.change”属性将会自动设置,以加入到最后修改的属性名。

    (这是很巧妙的。 netresolve模块的使用这个属性来追踪在net.*属性上的任何变化。)

  属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务。每一项服务必须在/init.rc中定义.系统启动时,与init守护

    进程将解析init.rc和启动属性服务。一旦收到设置“ ctrl.start ”属性的请求,属性服务将使用该属性值作为服务

    名找到该服务,启动该服务。这项服务的启动结果将会放入“ init.svc.<服务名>“属性中。客户端应用程序可以轮询那个属性值,以确定结果。

二 framework访问系统服务流程

  framework通过SystemProperties接口操作系统属性,SystemProperties通过JNI调用访问系统属性。

  \frameworks\base\core\java\android\os\ SystemProperties.java:

复制代码

public class SystemProperties
{    //JNI
    private static native String native_get(String key, String def);    private static native void native_set(String key, String def);    public static String get(String key, String def) {        return native_get(key, def);
    }    public static void set(String key, String val) {
        native_set(key, val);
    }
}

复制代码

Jni代码位置:

\frameworks\base\core\jni\android_os_SystemProperties.cpp

获取系统属性 阻塞方式:

static jstring SystemProperties_getSS()
{
        len = property_get(key, buf, "");
}

操作在\bionic\libc\bionic\system_properties.c中:

int __system_property_get(const char *name, char *value)
{    //数据已经存储在内存中__system_property_area__ 等待读取完返回
    const prop_info *pi = __system_property_find(name);    return __system_property_read(pi, 0, value);
}

       进程启动后数据已经将系统属性数据读取到相应的共享内存中,保存在全局变量__system_property_area__;

进程之间都是独立的,系统属性数据是如何读取到当前进程空间中的呢?后续介绍。

设置属性异步socket通信:

复制代码

int __system_property_set(const char *key, const char *value)
{
      msg.cmd = PROP_MSG_SETPROP;
    strlcpy(msg.name, key, sizeof msg.name);
    strlcpy(msg.value, value, sizeof msg.value);
    err = send_prop_msg(&msg);
}static int send_prop_msg(prop_msg *msg)
{    //sokcet 通信 /dev/socket/property_service 
    s = socket(AF_LOCAL, SOCK_STREAM, 0);
    connect(s, (struct sockaddr *) &addr, alen)
    send(s, msg, sizeof(prop_msg), 0)
    close(s);
}

复制代码

  通过socket向property_service发送消息,property_service运行在哪里呢?

三 Property Service创建服务端socket

init进程启动监听过程中:\system\core\init\Init.c

复制代码

int main(int argc, char **argv)
{    
    //加入到action queue队列
    queue_builtin_action(property_service_init_action, "property_service_init");    for(;;)        //执行action queue队列        //接收通过socket向property service 发送的数据;
        nr = poll(ufds, fd_count, timeout);
        ……
        handle_property_set_fd();
}static int property_service_init_action(int nargs, char **args)
{
    start_property_service();

}

\system\core\init\property_service.c:void start_property_service(void)
{    //加载属性配置文件    load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
    load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);
    load_persistent_properties();    
    //创建socket资源 并绑定
    fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);    //监听
    listen(fd, 8);
}

复制代码

  Property Service 是运行在init守护进程中。

  接收到消息之后干什么,还是要先弄清楚整个Property Service是如何实现的呢,后续介绍。

先看看Property Service接收到消息后的处理。

 

四 Property Service 监听socket处理

Property Service监听socket消息的处理过程:

复制代码

void handle_property_set_fd()
{    //等待建立通信
    s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)    //获取套接字相关信息 uid gid
    getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size);    //接收属性设置请求消息
    recv(s, &msg, sizeof(msg), 0);    //处理消息    
    switch(msg.cmd) {    case PROP_MSG_SETPROP:        //通过设置系统属性  处理ctl.开头消息
        if(memcmp(msg.name,"ctl.",4) == 0) 
        {            //权限检测
            if (check_control_perms(msg.value, cr.uid, cr.gid)) 
            {
                handle_control_message((char*) msg.name + 4, (char*) msg.value);
            }
        } else 
        {            //更改系统属性值
            if (check_perms(msg.name, cr.uid, cr.gid)) 
            {
                property_set((char*) msg.name, (char*) msg.value);
            }
        }    break;
    }
    close(s);
}

复制代码

通过设置系统属性启动/关闭Service

权限判断:

复制代码

static int check_control_perms(const char *name, unsigned int uid, unsigned int gid) 
{    // system /root用户直接有权限
    if (uid == AID_SYSTEM || uid == AID_ROOT)        return 1;    
    //查询用户名单,判断是否存在表中并具有对应权限
    for (i = 0; control_perms[i].service; i++) {        if (strcmp(control_perms[i].service, name) == 0) {            if ((uid && control_perms[i].uid == uid) ||
                (gid && control_perms[i].gid == gid)) {                return 1;
            }
        }
    }    return 0;
}

复制代码

 

 所以如果想要应用有权限启动/关闭某Native Service:

  需要具有system/root权限

  找到对应应用uid gid,将应用名称加入到control_perms列表中

 

处理消息 可以通过设置系统属性 改变服务的执行状态 start/stop:

复制代码

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);
    } 
}static void msg_start(const char *name)
{
    service_start(svc, args);
}void service_start(struct service *svc, const char *dynamic_args){    //创建进程启动服务
    pid = fork();
    execve(svc->args[0], (char**) svc->args, (char**) ENV);    
    //修改服务的系统属性 执行状态
    notify_service_state(svc->name, "running");
}

复制代码

连着前面就是ctr.start和ctr.stop系统属性:用来启动和停止服务的。

例如:

    // start boot animation

    property_set("ctl.start", "bootanim");

在init.rc中表明服务是否在开机时启动:

service adbd /sbin/adbd
    class core
    disabled //不自动启动

 

启动服务的时候会判断:

复制代码

static void service_start_if_not_disabled(struct service *svc)
{        //判断是否启动
        if (!(svc->flags & SVC_DISABLED)) {
            service_start(svc, NULL);
        }
}

复制代码

修改系统属性值:

复制代码

static int check_perms(const char *name, unsigned int uid, unsigned int gid)
{    //进行权限检测
    for (i = 0; property_perms[i].prefix; i++) {        int tmp;        if (strncmp(property_perms[i].prefix, name,
                    strlen(property_perms[i].prefix)) == 0) {            if ((uid && property_perms[i].uid == uid) ||
                (gid && property_perms[i].gid == gid)) {                return 1;
            }
        }
    }    return 0;
}

复制代码

 

看这个修改系统属性权限表:

复制代码

property_perms[] = {
    { "net.dns",          AID_RADIO,    0 },
    { "net.",             AID_SYSTEM,   0 },
    { "dev.",             AID_SYSTEM,   0 },
    { "runtime.",         AID_SYSTEM,   0 },
    { "sys.",             AID_SYSTEM,   0 },
    { "service.",         AID_SYSTEM,   0 },
    { "persist.sys.",     AID_SYSTEM,   0 },
    { "persist.service.", AID_SYSTEM,   0 },
    ……
     { NULL, 0, 0 }
};

复制代码

 

  指定了特定的用户有用修改 带有某些前缀的系统属性值。

  到这里基本就是Property对外的基本工作流程,Property Service内部具体如何实现,操作运行,

  跨进程空想内存等问题仍未清除是如何处理的。

五 属性系统设计

         属性系统的上层架构如下图所示:

     

  Property Service运行在init进程中,开机从属性文件中加载到共享内存中;设置系统属性通过socket与Property Service通信。

  Property Consumer进程将存储系统属性值的共享内存,加载到当前进程虚拟空间中,实现对系统属性值的读取。

  Property Setter进程修改系统属性,通过socket向Property Service发送消息,更改系统属性值。

六 属性系统实现

       属性系统设计的关键就是:跨进程共享内存的实现。

下面将看看属性系统实现具体过程:

Init进程执行:

复制代码

int main(int argc, char **argv){    //将属性系统初始化函数加入action queue  queue_builtin_action(property_init_action, "property_init");
  for(;;)
}static int property_init_action(int nargs, char **args)
{
    property_init(load_defaults);
}

复制代码

初始化Property Service

       \system\core\init\property_service.c

复制代码

void property_init(bool load_defaults)
{    //初始化共享内存空间    init_property_area();    //加载属性文件  load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
}

复制代码

初始化共享内存空间:

复制代码

static int init_property_area(void)
{    
    //创建匿名内存空间PA_SIZE = 32768
    init_workspace(&pa_workspace, PA_SIZE)    //将内存区域分成两部分:属性系统基本信息和属性键值对
    pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);    
    //初始化属性系统信息
    pa = pa_workspace.data;
    memset(pa, 0, PA_SIZE);
    pa->magic = PROP_AREA_MAGIC;
    pa->version = PROP_AREA_VERSION;    /* plug into the lib property services */
    __system_property_area__ = pa;
}

复制代码

__system_property_area__

  每个进程都会使用此变量,指向系统属性共享内存区域,访问系统属性,很重要。

位于:\bionic\libc\bionic\system_properties.c中,属于bionic库。后面将介绍各进程如何加载共享内存。

将文件作为共享内存映射到进程空间内存使用:

复制代码

static int init_workspace(workspace *w, size_t size)
{    //dev is a tmpfs是一种虚拟内存文件系统
    int fd = open("/dev/__properties__", O_RDWR | O_CREAT, 0600);    //将文件映射为共享进程空间内存 使其可以与操作内存方式一致
    void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, 
      MAP_SHARED, fd, 0);
    close(fd);    
    //删除文件
    fd = open("/dev/__properties__", O_RDONLY);
    unlink("/dev/__properties__");    //保存fd size 将作为环境变量传递给每个进程
     w->data = data; w->size = size; w->fd = fd;
}

复制代码

加载系统属性默认数据文件:

复制代码

#define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"static void load_properties_from_file(const char *fn)
{    //读取系统属性键值对数据写入到共享内存中
    data = read_file(fn, &sz);
    load_properties(data);
}

复制代码

  加上上面所述:Property Service Socket资源的创建,来监听socket通信连接设置系统属性,

  在Init进程中Property Service完成了初始化。

将得到该内存区域数据结构:

     

七 进程共享系统属性内存空间实现

  Property Service运行于init进程中,将文件映射为创建一块共享内存空间,但在整个系统中,

其他进程也能够读取这块内存映射到当前进程空间中,是如何实现的呢?

Service进程启动:将共享内存空间fd size作为环境变量传递给新创建进程

复制代码

void service_start(struct service *svc, const char *dynamic_args)
{    //创建进程
    pid = fork();    if (pid == 0) {        if (properties_inited()) {            //获取系统属性空间文件描述
            get_property_workspace(&fd, &sz);            //dup最小的可用文件描述符
            sprintf(tmp, "%d,%d", dup(fd), sz);            //加入ANDROID_PROPERTY_WORKSPACE环境变量到ENV 
        //包含共享内存fd
            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        }        //执行程序 传递环境变量ENV
        execve(svc->args[0], (char**) svc->args, (char**) ENV)        //设置Service系统属性
        notify_service_state(svc->name, "running");
    }
}void get_property_workspace(int *fd, int *sz)
{   *fd = pa_workspace.fd;   *sz = pa_workspace.size;
}

复制代码

共享内存空间fd size作为环境变量传递给新创建进程后,将在何处使用呢?

将系统属性内存空间映射到当前进程虚拟空间:

进程在启动时,会加载动态库bionic libc库:

  \bionic\libc\bionic\libc_init_dynamic.c中:

  void __attribute__((constructor)) __libc_preinit(void);          

根据GCCconstructor/destructor属性:

    给一个函数赋予constructor或destructor,其中constructor在main开始运行之前被调用,

  destructor在main函数结束后被调用。如果有多个constructor或destructor,可以给每个constructor

  或destructor赋予优先级,对于constructor,优先级数值越小,运行越早。destructor则相反。

多个constructor需要加优先级:

复制代码

__attribute__((constructor(1))) void func1()  
{  
    printf("in constructor of foo\n");  
}  
__attribute__((constructor(2))) void func2()  
{  
    printf("in constructor of foo1\n");  
} 
__attribute__((destructor)) void bar()  
{  
    printf("in constructor of bar\n");  
}

复制代码

 

__libc_preinitbionic libc库加载的时候会被调用:

复制代码

void __libc_preinit(void)
{   
    __libc_init_common(elfdata);
}void __libc_init_common(uintptr_t *elfdata)
{
    __system_properties_init();
}int __system_properties_init(void)
{
    prop_area *pa;    int s, fd;        unsigned sz;        char *env;    //获取环境变量ANDROID_PROPERTY_WORKSPACE 
  //与上面init进程中设置对应
    env = getenv("ANDROID_PROPERTY_WORKSPACE");    //共享内存文件描述符 内存大小
    fd = atoi(env);
    sz = atoi(env + 1);    //将文件描述符映射到当前进程虚拟空间内存,实现共享内存
    pa = mmap(0, sz, PROT_READ, MAP_SHARED, fd, 0);    //全局变量指向共享系统属性内存首地址
    __system_property_area__ = pa;
}

复制代码

  这就是整个System Property的访问交互和实现过程,具体请参考源码。

参考文档:

  http://blog.chinaunix.net/uid-20459533-id-3168973.html

  http://www.cnblogs.com/simonshi/archive/2010/04/08/1707516.html

  http://blog.csdn.net/zhangchiytu/article/details/7539101


本文转载自:http://www.cnblogs.com/bastard/archive/2012/10/11/2720314.html

共有 人打赏支持
雨焰
粉丝 59
博文 219
码字总数 42769
作品 0
新乡
程序员
Android SystemProperties设置/取得系统属性的用法总结

1.Android SystemProperties简介 介绍了设置属性需要的权限,已经设置权限的方法。 Systemproperties类在android.os下,但这个类是隐藏的,上层程序开发无法直接使用。其实用java的反射机制是...

chengche
2014/02/10
0
0
Android 的cpu 硬盘 内存 网络设置 系统信息 硬件信息

1.手机信息查看助手可行性分析 开始进入编写程序前,需要对需求的功能做一些可行性分析,以做到有的放矢,如果有些无法实现的功能,可以尽快调整。 这里分析一下项目需要的功能,主要是信息查...

冯京宝
2012/08/29
0
1
SystemProperties源码分析 获取 Android WIFI接口

SystemProperties.java这个类是@hide的,所以不对外公开,一般开发者是访问不到的,但是我们可以通过反射机制来使用。通过反射取得wifi的接口名为例! 下面通过Android源码讲解SystemPropert...

wangxigui
2013/07/12
0
0
Android 的系统属性(SystemProperties)设置分析

Android 的系统属性(SystemProperties)设置分析作者:徐建祥(netpirate@gmail.com) 日期:2009/11/11 网址:http://www.anymobile.org Android 的系统属性包括两部分:文件保存的持久属性和每...

billy_lin
2012/09/21
0
0
有关android的property机制

其实property就是name&value对,与常见系统中的配置项没有差异。在android系统中,大量使用了propertyset和propertyget,用来设置和获取某一property。下面来分析一下。 propertyset/proper...

wangjian19
2014/03/05
0
0
如何使Android应用程序获得root权限

分类: Android开发2011-04-11 16:28 4279人阅读 评论(5) 收藏 举报 写这篇文章前,首先要感谢 Simonfu ,他的两篇关于 root 权限的文章对于我的工作起到了非常大的帮助,这篇文章可以说是对...

brucema
2012/05/02
0
0
Android Studio, gradle and NDK integration

With the recent changes (release 0.7.3 around Dec 27), the new Android Build System starts to be really interesting also if you are using the NDK! Now this is really easy to int......

Jerikc
2015/05/28
0
0
深入理解SELinux/SEAndroid(最后部分)

接第二部分的内容(http://blog.csdn.net/innost/article/details/19641487) SEAndroid最后一部分 全文PDF下载地址为:http://vdisk.weibo.com/s/z68f8l0xZUS9w 深入理解SELinux/SEAndroid......

邓凡平
2014/03/02
0
0
Android深入浅出之Zygote

一 目的 zygote,是Android搞出来的一个东西。网上讲这个的也非常多。第一次看到这个名字我就挺郁闷,想干嘛这是?Linux下名字都取得挺通俗易懂,深得人心。zygote?不就想模仿Linux下的for...

sflfqx
2013/03/13
0
0
A20关机长按关机按钮提示进入安全模式

长按电源键弹出关机按钮,长按关机按钮会弹出进入安全模式的设置,进入安全模式后将停止所有的第三方APP的加载,由于我们的系统只有自己定制的lunch,所以已经去掉系统的加载项LUNCHER2,进入...

wangjian19
2014/04/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

sklearn学习笔记之简单线性回归

简单线性回归 线性回归是数据挖掘中的基础算法之一,从某种意义上来说,在学习函数的时候已经开始接触线性回归了,只不过那时候并没有涉及到误差项。线性回归的思想其实就是解一组方程,得到...

wangxuwei
4分钟前
0
0
feign之动态interceptor(二)

背景 上文提到了按照不同的feignClient可以根据多个不同的key来进行多个不同的bean的配置 那么我们如何完成多个interceptor的配置呢? 分析 我们刚提到多个配置的玄机就在FeignClientProper...

Mr_Qi
6分钟前
1
0
Linux Kernel 4.16 系列停止维护,用户应升级至 4.17

知名 Linux 内核维护人员兼开发人员 Greg Kroah-Hartman 近日在发布 4.16.18 版本的同时,宣布这是 4.16 系列的最后一个维护版本,强烈建议用户立即升级至 4.17 系列。 Linux 4.16 于 2018 年...

问题终结者
31分钟前
0
0
Apache配置时.htaccess失效不起作用的原因分析

.htaccess 失效的原因 1. 重写规则有问题,检查自己的重写规则 2.Apache配置问题,配置中没有配置启用 rewrite a2enmod rewrite 3.网站配置文件没有启用配置需要配置 000-default.conf <Dire...

TU-DESGIN
51分钟前
1
0
两个求最大公约数C/C++算法实现

#include<stdio.h> #include<time.h> #include <iostream>using namespace std;//求最大公约数 LCD(Largest Common Division)//短除法 //m=8251, n=6105; int LCD_ShortDiv(int m, ......

失落的艺术
57分钟前
1
0
QueryPerformanceCounter

windows的Sleep函数,睡眠线程指定毫秒数,可以用来做毫秒延时。 对于微秒延时,没有一个现成的函数,但是可以通过 QueryPerformanceFrequency QueryPerformanceCounter 来间接实现。原理就是...

开飞色
今天
1
0
log4j2使用AsyncRoot不显示行号问题处理

<AsyncRoot level="info" includeLocation="true"> <AppenderRef ref="File"/></AsyncRoot><!--1.异步logger,还需要在pom.xml中添加disruptor的依赖。2.includeLocation结合异......

小翔
今天
3
0
安卓手机上 K 歌,声音延迟怎么解决?

这篇文章可以为你提供一个解决录音和播放同步问题的思路,而且解决了声音从手机传输到耳机上有延时的问题。 初识音频 在开始之前,我先简单介绍一下音频相关的基础知识,方便下文理解。 我们...

编辑部的故事
今天
2
0
使用token实现在有效期内APP自动登录功能

使用token实现在有效期内APP自动登录功能 http://sevennight.cc/2016/07/19/auto_login_impl.html

风云海滩
今天
2
0
Spring Boot集成RabbitMQ发送接收JSON

默认情况下RabbitMQ发送的消息是转换为字节码,这里介绍一下如何发送JSON数据。 ObjectMapper 最简单发送JSON数据的方式是把对象使用ObjectMapper等JSON工具类把对象转换为JSON格式,然后发送...

小致dad
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部