先来段英文的介绍吧:
Everyproperty has a name and value. Both name and value are text strings.Property is heavily used in Android to record system setting orexchange information between processes. The property is globallyvisible in the whole system. Every process can get/set a property.
Onsystem initialization, Android will allocates a block of shared memoryfor storing the properties. This is done in “init” daemon whose sourcecode is at: device/system/init. The “init” daemon will start a PropertyService. The Property Service is running in the process of “init”daemon. Every client that wants to SET property needs to connect to theProperty Service and send message to Property Service. Property Servicewill update/create the property in shared memory. Any client that wantsto GET property can read the property from the shared memory directly.This promotes the read performance.
Theclient application can invoke the API function exposed from libcutilsto GET/SET a property. The source code of libcutils locates at: device/libs/cutils.
The API function is:
int property_get(const char *key, char *value, const char *default_value);
int property_set(const char *key, const char *value);
Thelibcutils is in turn calling the __system_property_xxx function in libcto get a property from the shared memory. The source code of libc isat: device/system/bionic.
TheProperty Service is also in turn calling the __system_property_initfunction in libc to initiate the shared memory for properties. Whenstarting the Property Service will load the default properties frombelow files:
/default.prop
/system/build.prop
/system/default.prop
/data/local.prop
Theproperties are loaded in the above order. Later loaded properties willoverride the previous values. After those properties are loaded, thelast loaded is the persistent properties which is persisted in/data/property.
Special Properties
Ifa property’s name begins with “ro.”, then this property is treated as aread-only property. Once set, the value of the property can’t bechanged.
If a property’s name begins with “persist.”, then when setting this property, the value will be written to /data/property, too.
Ifa property’s name begins with “net.”, when when setting this property,the “net.change” property will be set automatically to contain the nameof the last updated property. (It’s tricky. The netresolve module usesthis property to track if there is any change on the net.* properties.)
Theproperty “ctrl.start” and “ctrl.stop” is used to start and stop aservice. Every service must be defined in /init.rc. On system startup,the init daemon will parse the init.rc and start the Property Service.Once received a request to set the property of “ctrl.start”, theProperty Service will use the property value as the service name tofind the service and then start the service. The service startingresult is then put to the property “init.svc.<service name>”. Theclient application can poll the value of that property to determine theresult.
Android toolbox
The Android toolbox provides two applets: setprop and getprop to get and set properties. The usage is:
getprop <property name>
setprop <property name> <property value>
Java
The java application can use the System.getProperty() and System.setProperty() function to Get and Set the property.
Action
Bydefault the set property will only cause "init" daemon to write toshared memory, it won't execute any script or binary. But you can addyour actions to correspond to property change in init.rc. For example,in the default init.rc, you can find.
# adbd on at boot in emulator
on property:ro.kernel.qemu=1
start adbd
on property:persist.service.adb.enable=1
start adbd
on property:persist.service.adb.enable=0
stop adbd
So if you set persist.service.adb.enable to 1, the "init" daemon knows it has actions to do, then it will start adbd service.
在使用Android的属性系统(property system)时遇到了一些问题,结合此次经历,对属性系统的使用做以简单介绍。
一、Property系统简介
属性系统是android的一个重要特性。它作为一个服务运行,管理系统配置和状态。所有这些配置和状态都是属性。每个属性是一个键值对(key/value pair),其类型都是字符串。
从功能上看,属性与windows系统的注册表非常相似。许多android应用程序和库直接或者间接地依赖此特性,以决定它们的运行时行为。例如,adbd进程查询属性服务已确认当前是否运行在模拟器环境中。另一个例子是java.io.File.pathSpearator,其返回存储于属性服务中的值。
二、Property系统使用
使用property系统,首先需要包含头文件<cutils/properties.h>,同时需要在Android.mk文件中加入库libcutils。
具体的使用实例如下:
property_set("hw.jpeg.path", "/data/test.jpg");
char propBuf[PROPERTY_VALUE_MAX];
property_get("hw.jpeg.path ", propBuf, "");
LOGI("property_get: %s.", propBuf);
Property系统的使用很简单,这里需要说明的,只有一个问题,那就是属性名称。
三、Property系统的属性名称
Property系统中使用的属性名称有一定的命名规则。
如果属性名称以“ro.”开头,那么这个属性被视为只读属性。一旦设置,属性值不能改变。
如果属性名称以“persist.”开头,当设置这个属性时,其值也将写入/data/property。
如果属性名称以“net.”开头,当设置这个属性时,“net.change”属性将会自动设置,以加入到最后修改的属性名。(这是很巧妙的。 netresolve模块的使用这个属性来追踪在net.*属性上的任何变化。)
属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务。每一项服务必须在/init.rc中定义.系统启动时,与init守护进程将解析init.rc和启动属性服务。一旦收到设置“ ctrl.start ”属性的请求,属性服务将使用该属性值作为服务名找到该服务,启动该服务。这项服务的启动结果将会放入“ init.svc.<服务名>“属性中 。客户端应用程序可以轮询那个属性值,以确定结果。
另外,还有其他的前缀;在介绍这些前缀之前,我们先来看点别的东西。
在文件system/core/init/property_service.h中,有如下两个函数定义:
extern void handle_property_set_fd(int fd);
extern int property_set(const char *name, const char *value);
它们的实现都位于system/core/init/property_service.c。
另外在system/core/init/init.c中,有如下code:
int main(int argc, char **argv)
{
……
for(;;) {
……
if (ufds[0].revents == POLLIN)
handle_device_fd(device_fd);
if (ufds[1].revents == POLLIN)
handle_property_set_fd(property_set_fd);
if (ufds[3].revents == POLLIN)
handle_keychord(keychord_fd);
}
……
}
在开机启动后的init操作中,会执行一个loop循环,当检测到有新的设置时,进入设置流程,鉴权失败会提示相关的异常,如:
sys_prop: permission denied uid:1013 name:hw.jpeg.path
这里得介绍一下函数handle_property_set_fd(),其实现为:
void handle_property_set_fd(int fd)
{
prop_msg msg;
……
switch(msg.cmd) {
case PROP_MSG_SETPROP:
msg.name[PROP_NAME_MAX-1] = 0;
msg.value[PROP_VALUE_MAX-1] = 0;
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 {
ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d/n",
msg.name + 4, msg.value, cr.uid, cr.pid);
}
} else {
if (check_perms(msg.name, cr.uid, cr.gid)) {
property_set((char*) msg.name, (char*) msg.value);
} else {
ERROR("sys_prop: permission denied uid:%d name:%s/n",
cr.uid, msg.name);
}
}
break;
default:
break;
}
}
其中check_perms()的实现如下:
static int check_perms(const char *name, unsigned int uid, int gid)
{
int i;
if (uid == 0)
return 1;
if(!strncmp(name, "ro.", 3))
name +=3;
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;
}
通过这两个函数,我们可以知道,我们所设置的属性名称hw.jpeg.path会与结构体property_perms中的前缀做比较,如果满足,则又会比较uid。
结构体property_perms的定义如下:
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 },
{ "ril.", AID_RADIO, 0 },
{ "gsm.", AID_RADIO, 0 },
{ "persist.radio", AID_RADIO, 0 },
{ "net.dns", AID_RADIO, 0 },
{ "net.", AID_SYSTEM, 0 },
{ "dev.", AID_SYSTEM, 0 },
{ "runtime.", AID_SYSTEM, 0 },
{ "hw.", AID_SYSTEM, 0 },
{ "sys.", AID_SYSTEM, 0 },
{ "service.", AID_SYSTEM, 0 },
{ "wlan.", AID_SYSTEM, 0 },
{ "dhcp.", AID_SYSTEM, 0 },
{ "dhcp.", AID_DHCP, 0 },
{ "vpn.", AID_SYSTEM, 0 },
{ "vpn.", AID_VPN, 0 },
{ "debug.", AID_SHELL, 0 },
{ "log.", AID_SHELL, 0 },
{ "service.adb.root", AID_SHELL, 0 },
{ "persist.sys.", AID_SYSTEM, 0 },
{ "persist.service.", AID_SYSTEM, 0 },
{ "persist.security.", AID_SYSTEM, 0 },
{ NULL, 0, 0 }
};
此时,我们就知道hw.在property_perms中是存在的,那么uid是否能匹配呢?我们看到hw.对应的uid为AID_SYSTEM。
AID_SYSTEM的定义位于:
system/core/include/private/android_filesystem_config.h
#define AID_SYSTEM 1000 /* system server */
#define AID_RADIO 1001 /* telephony subsystem, RIL */
#define AID_DHCP 1014 /* dhcp client */
#define AID_VPN 1016 /* vpn system */
#define AID_SHELL 2000 /* adb and debug shell user */
当我们使用属性名称为hw.jpeg.path时,那我们的应用程序的uid必须为1000,否则在函数handle_property_set_fd()中就会报出类似这样的错误:
sys_prop: permission denied uid:1013 name:hw.jpeg.path
四、使应用程序具有AID_SYSTEM的UID
那如何使应用程序的uid变为1000呢?
由于我可以在Android系统源码的环境下用make来编译,所以需要做如下几步:
l 在应用程序的AndroidManifest.xml文件中的manifest节点中加入属性:
android:sharedUserId="android.uid.system";
通过Shared User id,拥有同一个User id的多个APK可以配置成运行在同一个进程中。那么把程序的UID配成android.uid.system,也就是要让程序运行在系统进程中,这样就有权限来设置前缀为hw.的属性了。
l 修改Android.mk文件,加入LOCAL_CERTIFICATE := platform;
加入UID还不够,如果这时候安装APK的话发现无法安装,提示签名不符,原因是程序想要运行在系统进程中还要有目标系统的platform. key,就是platform.pk8和platform.x509.pem两个文件。通过这样的设置,系统才可以拿到platform.pk8和platform.x509.pem两个文件。使用这两个key签名后apk才真正可以放入系统进程中。
然后使用mm命令来编译,生成的apk的uid就会成为1000。在去调用property_set("hw.jpeg.path", "/data/test.jpg");就不会有问题了。
另外需要说明的是,property_get()函数的使用是没有这样的限制的。
如果需要深入地了解property系统,可以参考如下文章:
Android Property System | Android属性系统
http://blog.csdn.net/jackyu613/archive/2011/01/13/6136620.aspx
Android 的属性系统(翻译)
http://blog.csdn.net/tekkamanitachi/archive/2009/06/18/4280982.aspx
Android Property System | Android属性系统
http://www.bangchui.org/read.php?tid=13375
Android中如何修改系统时间(应用程序获得系统权限)
http://dev.10086.cn/blog/?uid-49302-action-viewspace-itemid-907
Android 的系统属性(SystemProperties)设置分析
http://blog.csdn.net/netpirate/archive/2009/11/11/4799272.aspx
作者QQ 240136495