Android4.4-属性的使用总结

一、Android4.4属性系统系列文章

Android4.4属性系统-初始化
Android4.4属性系统-系统服务
Android4.4属性系统-内存空间共享
Android4.4属性系统-属性获取
Android4.4属性系统-属性设置
Android4.4-属性的使用总结

二、属性的使用总结

前面的几篇文章都是分析了属性系统的运作流程,但是日常开发中用到最多的还是对属性的获取和设置,以及不同类型的属性有什么特点,可读可写的权限问题,这篇文章就很接地气,总结了如何在安卓系统开发中获取、设置和添加属性。

2.1 属性的获取

属性的获取可以从java层接口和C接口进行分类,当然对于framework和APP开发者来说,用得最多的自然是java/JNI的接口,而对于JNI开发或者c库的开发来说,用的最多的应该是c接口。

2.1.1 java获取属性接口

获取属性并不需要额外的权限,对于是否是系统应用也没有要求。
使用示例

import android.os.SystemProperties;
//获取自定义的属性"persist.flyscale.wifi",默认值为-1
//获取int值
int wifiInt = SystemProperties.getInt("persist.flyscale.wifi.int", -1);
//获取long值
int wifiLong = SystemProperties.getLong("persist.flyscale.wifi.long", -1);
//获取bool值
boolean wifiBool = SystemProperties.getBoolean("persist.flyscale.wifi", false);
//获取string,如果没有则返回空字符串""
String wifiStr1 = SystemProperties.getInt("persist.flyscale.wifi");
//获取string,如果没有则返回指定默认值"def"
String wifiStr2 = SystemProperties.getInt("persist.flyscale.wifi", "def");

如果需要使用到c接口获取属性,请参考Android4.4属性系统-属性获取

2.2 安卓属性的设置

如果看过前面的Android4.4属性系统-属性设置,就容易得出结论:属性设置是有权限要求的,对于uid为root是绝对允许的,而对于其它用户则是禁止的,除非通过了白名单的权限检查

2.2.1 property权限检查

在说明如何进行设置之前,必须首先了解property的权限检查机制,否则只能是云里雾里,不知所云
看过Android4.4属性系统-属性设置的读者应该了解了,属性是有几种分类的:

2.2.1.1 控制类属性ctl.*

ctl.开头的,例如ctl.start=property_service表示启动名为"property_service"的服务,也即属性服务,这样就明白了,ctl.开头的属性表示控制服务的属性,需要AID_ROOT和AID_SYSTEM权限,除此之外在control_perms白名单中的也可以通过权限检查,如下:

/*
 * 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 },  //表示允许AID_SHELL用户控制 "dumpstate"服务
    { "ril-daemon",AID_RADIO, AID_RADIO },  //表示允许AID_RADIO用户控制 "ril-daemon"服务
     {NULL, 0, 0 }
};

除了白名单中的两种服务可以被ROOT和SYSTEM之外的AID_SHELL和AID_RADIO用户控制之外,其它的一概拒绝!!!

2.2.1.2 其它的属性

除了控制属性之外的属性也需要通过权限检查,一般来说ROOT用户是绝对是被允许的,但是系统中除了少数核心进程之外,大部分不是ROOT用户,它们是通过另外一个白名单property_perms来控制的,如下:

/* 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 },
    { "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 },
    { "persist.radio",    AID_SYSTEM,   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 },
    { "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.msms.",    AID_RADIO,   0 },//SPRD: add for dsds
    { "persist.msms.",    AID_SYSTEM,   0 },
    { "persist.service.", AID_SYSTEM,   0 },
    { "persist.security.", AID_SYSTEM,   0 },
    { "media.", AID_MEDIA,   0 },
    { "persist.service.bdroid.", AID_BLUETOOTH,   0 },
    { "selinux."         , AID_SYSTEM,   0 },
    { "persist.modem.",    AID_RADIO,    0 },//SPRD: add for lte
    { NULL, 0, 0 }
};

看着很多,其实很好理解 ,就拿 { "persist.sys.", AID_SYSTEM, 0 },来说吧,它表示以"persist.sys."开头的属性是允许AID_SYSTEM用户修改的,其它的类同。
还有很重要的一点,以ro.开头的表示只读属性,即使是AID_ROOT用户也是不允许修改的。
做一个小总结:如果需要修改/添加属性,那么修改属性的代码运行的进程必须要通过权限检查,否则会失败,权限检查的细节也已经分析过了,很简单吧,立即推==》一切难题都是纸老虎

2.2.2 java接口设置属性

java层的设置接口API只有一个SystemProperties.set(String key, String value),也就是说所有的属性value值要转为字符串被设置进去。

2.2.2.1uid(权限)配置为system

首先要想成功设置你的APP uid为system,就需要做一些配置了。

  1. 在AndroidManifest.xml中,以系统应用的Settings为例,配置android:sharedUserId属性
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.android.settings"
        coreApp="true"
        android:sharedUserId="android.uid.system">

  1. 另外在对应的Android.mk中使用系统平台签名:
    如果你有源码编译环境,对mk做如下配置
LOCAL_CERTIFICATE := platform

在模块目录下执行mm编译即可。
当然如果你没有源码环境,也是可以进行平台签名的,请参考的另一篇文章Android4.4-APP系统签名的实现方案,其最终目的是一样的

2.2.2.2 调用java接口

只是简单的接口调用,很简单了

import android.os.SystemProperties;

SystemProperties.set("persist.sys.volte.enable", "1");

2.2.3 案例分析

我预置的一个测试应用,测试代码如下,按上键获取"persist.sys.volte.enable",默认为false

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_DPAD_UP:
                Log.i("HomeActivity", "persist.sys.volte.enable=" + SystemProperties.getBoolean("persist.sys.volte.    enable", false));
                break;
            case KeyEvent.KEYCODE_DPAD_DOWN:
                SystemProperties.set("persist.sys.volte.enable", "false");
                Log.i("HomeActivity", "persist.sys.volte.enable=" + SystemProperties.getBoolean("persist.sys.volte.    enable", true));
                break;

此时用户uid为radio,因为AndroidManifest.xml中配置了android:sharedUserId="android.uid.phone":

USER     PID   PPID  VSIZE  RSS     WCHAN    PC          NAME
radio       845   145   222304 9032  ffffffff 00000000 S     com.flyscale.launcher

先获取一次,再设置为1,再获取,输出结果如下:

com.flyscale.launcher I/HomeActivity: persist.sys.volte.enable=true  //设置为false前
com.flyscale.launcher I/HomeActivity: persist.sys.volte.enable=true  //设置为false后仍然为true

在设置属性的时候报错如下:

init: sys_prop: permission denied uid:1001  name:persist.sys.volte.enable=true

显然权限未通过啊。在经过之前两步配置uid后:

system    864   142   222796 11388 ffffffff 00000000 S com.flyscale.launcher

再次运行结果

com.flyscale.launcher I/HomeActivity: persist.sys.volte.enable=true  //修改前
com.flyscale.launcher I/HomeActivity: persist.sys.volte.enable=false  //修改后

修改成功!

2.3属性的添加

在代码中添加属性与设置属性的接口是一样的,也需要注意权限检查。如果该属性存在就会更新,如果不存在就会创建,但是要注意,属性是不能被删除的。
另外我们经常在安卓源码中预置一些属性配置,那么都在哪些地方可以配置属性呢,要注意些什么呢?

  1. 注意权限检查
    例如我要添加一个属性persist.flyscale.wifi=0,那么它肯定是无法通过权限检查的,因为它不在白名单中,那么怎么办呢?可以改成persist.sys.flyscale.wifi=0,这样就可以了,因为白名单中有persist.sys.开头的属性,是可以被AID_SYSTEM修改的。

  2. 可以在源码中默认配置的文件
    首先工程目录下的system.prop是可以直接添加的,如下:
    device/***/scx35l/sp9820w_poc_780/system.prop

#add for test
persist.sys.flyscale.wifi=0

另外在工程编译的mk文件中也可以配置,但是要使用PRODUCT_PROPERTY_OVERRIDES语句

PRODUCT_PROPERTY_OVERRIDES += persist.sys.volte.enable=true

以上为本人使用中的总结,欢迎指正和补充,谢谢大家!