1 Introduce
在android 系统中,为统一管理系统的属性,设计了一个统一的属性系统。每个属性都有一个名称和值,他们都是字符串格式。属性被大量使用在Android系统中,用来记录系统设置或进程之间的信息交换。属性是在整个系统中全局可见的。每个进程可以get/set属性。在编译的过程中会将各种系统参数汇总到build.proc 以及default.proc 这两个文件中,主要属性集中在build.proc中。系统在开机后将读取配置信息并构建共享缓冲区,加快查询速度。另外一个方面,SettingsProvider会在系统第一次初始化时(刷机第一次启动)后,将从Defaults.xml中读取数据然后写入数据库Settings.db 目录。并构建一个缓冲系统供其他应用查询。下面将详细讲述。
2 Properties Type
系统属性根据不同的应用类型,分为不可变型,持久型,网络型,启动和停止服务等。
特别属性:
属性名称以“ro.”开头,那么这个属性被视为只读属性。一旦设置,属性值不能改变。
属性名称以“persist.”开头,当设置这个属性时,其值也将写入/data/property。
属性名称以“net.”开头,当设置这个属性时,“net.change”属性将会自动设置,以加入到最后修改的属性名。(这是很巧妙的。 netresolve模块的使用这个属性来追踪在net.*属性上的任何变化。)
属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务。每一项服务必须在/init.rc中定义.系统启动时,与init守护进程将解析init.rc和启动属性服务。一旦收到设置“ ctrl.start ”属性的请求,属性服务将使用该属性值作为服务名找到该服务,启动该服务。这项服务的启动结果将会放入“ init.svc.<服务名>“属性中 。客户端应用程序可以轮询那个属性值,以确定结果。
3 Android toolbox
Android toolbox程序提供了两个工具: setprop和getprop获取和设置属性。其使用方法:
getprop <属性名>
setprop <属性名> <属性值>
Java
在Java应用程序可以使用System.getProperty()和System.setProperty()函数获取和设置属性。
Action
默认情况下,设置属性只会使"init"守护程序写入共享内存,它不会执行任何脚本或二进制程序。但是,您可以将您的想要的实现的操作与init.rc中某个属性的变化相关联.例如,在默认的init.rc中有:
# 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
5 Properties Source
原则上,属性的设置可以出现在make android的任何环节。目前Properties 的设置以oppo版本为例:
alps\build\target\board\generic_arm64\ system.prop
alps\build\target\product\core.mk
alps\build\tools\buildinfo.sh
编译好后,被设置的系统属性主要存放在:
这样,如果你设置persist.service.adb.enable为1 ,"init"守护程序就知道需要采取行动:开启adbd服务。
\ default.prop 手机厂商自己定制使用
\system\build.prop 系统属性主要存放处
\system\default.prop default properties, 有存放与security 相关的属性
\data\local.prop 目前还没有看到有内置的情况
\data\property下有4个prop文 件:persist.sys.timezone, persist.sys.language, persist.sys.country, persist.sys.localevar, 里面保存着属性名称以“persist.”开头的属性值。用户的persist 开头的属性都会保存副本在这个目录下
5 Properties Run
5.1 Properties init.
在linux kernel 启动时,Android将分配一个共享内存区来存储的属性。这些是由“init”守护进程完成的,其源代码位于:device/system /init。“init”守护进程将启动一个属性服务。属性服务在“init”守护进程中运行。每一个客户端想要设置属性时,必须连接属性服务,再向其发送信息。属性服务将会在共享内存区中修改和创建属性。客户端想获得属性信息,可以从共享内存直接读取。这提高了读取性能。
客户端应用程序可以调用libcutils中的API函数以GET/SET属性信息。libcutils的源代码位于:device/libs/cutils。获取和设置属性的代码在properties.c里面,读取属性通过读共享内存得到,设置属性通过发送请求到property_service进行设置。API函数是:
int property_get(const char *key, char *value, const char *default_value);
int property_set(const char *key, const char *value);
int property_list(void(*propfn)(const char * key, const char * value, void * cookie), void * cookie);
system / init/ init. c= > main( ) 进程将调用
= > property_init
= > init_property_area
void property_init( void ) {
//ashmem_area - android shared memory area是android共享内容存的一种方式
//打开ashmem设备,申请一段size大小的kernel空间内存,不去释放,以便供所有用户空间进程共享.
//内核驱动位于linux/mm/ashmem.c文件[luther.gliethttp].
init_property_area( ) ;
//#define PROP_PATH_RAMDISK_DEFAULT "/default.prop"
//从ramdisk中读取default.prop文件,将文件中的所有java环境中使用到的propt释放到
//这个共享内存中.
load_properties_from_file( PROP_PATH_RAMDISK_DEFAULT) ;
}
后面将调用properties_service.c, 启动最原始的properties service.
然后通过libc_init_common. c, 的__system_properties_init函数完成内核的初始化工作。
5.2 属性的访问
如果在C/C++ 层次,则可以使用libcutils 的
int property_get(const char *key, char *value, const char *default_value);
int property_set(const char *key, const char *value);
int property_list(void(*propfn)(const char * key, const char * value, void * cookie), void * cookie);
来访问。
如果在Java 层次,则可以使用System.getProperties/setProperties. 该方法(set/get)实际将调用SystemProperties.java 进行访问,而SystemProperties.java 通过jni 调用libcutils进行访问。
5.3 Enlarge System Property
Android Default System Property 默认是可以存储247笔 properties. 但因为我司有大量的数据被写入到这个system property 中,导致容易出现system property mmap 到kernel ashmem中的缓存ashmem 溢出。一方面需要确认数据是否确实有必要写入System property, 另外一方面,可以扩大缓存的区间。
缓存区间可以分成两大部分(byte), Info Area , Array Area. Info Area 又分成Header 和 Name Mapping Area.
8 Header *4 |
Every Property Name Mapping * 4 |
Every Value row and the max length is 127 |
因为,给出固定的最大笔数N. 缓存定义上要求
#define PA_COUNT_MAX N
#define PA_INFO_START ((8+PA_COUNT_MAX) * 4) 最好保证这个值为32的整数倍
#define PA_SZE (PA_INFO_START + (128 * PA_COUNT_MAX))
5.4 控制属性
在system properties 中提供两个特殊的key, ctl.start 和 ctl.stop 来启动和关闭服务。
其API 描述是:
SystemProperties.set(“ctl.start”,serviceName);
SystemProperties.set(“ctl.stop”,serviceName);
注意的是,这个serviceName 可在init.rc 中查询
5.5 Init 属性
Init 属性是System properties 中的一种特殊的属性,由init.c 定义,init 会监控定义在init.rc 中的服务,并定义init.svc.xxxx 的System properties.
具体实现,可参考init.c 中的notify_service_state 函数。
我们除了直接ps 来查看这些进程信息外,还可以直接通过查看system properties 来确认这些服务的状态,如
[init.svc.bootlogoupdater]: [stopped]
[init.svc.pvrsrvinit]: [stopped]
[init.svc.servicemanager]: [running]
[init.svc.vold]: [running]
[init.svc.netd]: [running]
[init.svc.netdiag]: [running]
[init.svc.hald]: [running]
[init.svc.debuggerd]: [running]
[init.svc.zygote]: [running]
[init.svc.drmserver]: [running]
[init.svc.media]: [running]
[init.svc.dbus]: [running]
[init.svc.installd]: [running]
[init.svc.keystore]: [running]
[init.svc.console]: [running]
[init.svc.adbd]: [running]
[init.svc.ril-daemon]: [running]
5.6 属性安全性
作为一个共享的缓存系统,并非任何AP 都可以随意去修改其中的属性,针对这些属性,如果进行更改时,会有UID上的约束。
具体的UID 映射为:
即如persist.sys 开头的属性,只能有system user(包括root) 进行修改,其他用户无法进行修改。
这个user id 表定义在 \system\core\include\private\android_filesystem_config.h 文件中
6 Setting Provider
在android framework 中还定义了Setting Provider 来对一些比较通用的数据进行初始化,并将数据写入Settings.db. 其中直接在Setting Provider 中被初始化的属性写在defaults.xml中:
/system/media/audio/ui/LowBattery.ogg
/system/media/audio/ui/Dock.ogg
/system/media/audio/ui/Undock.ogg
/system/media/audio/ui/Dock.ogg
/system/media/audio/ui/Undock.ogg
/system/media/audio/ui/Lock.ogg
/system/media/audio/ui/Unlock.ogg
SettingsProvider 将通过DatabaseHelper 将这些数据读入Settings.db, 同时SettingsProvider作为控制Settings.db的Provider ,所有对该数据库的操作都要通过它来进行。
其他具体的属性的描述都在Settings.java 这个类中描述。
Settings 为提高访问的效率,建立了cache, 只有当cache 中找不到时,才会调用SettingsProvider去查询Settings.db 数据库。
具体的Setiings.db 中包括的数据库表有:
关键的system表中的数据有:
一般通过修改defaults.xml 和 make 中的配置文件即可。
7 About Phone Properties.
About Phone中的一些关键属性通过buildinfo.sh 来焊接(make - build), 经过测试,可修改alps\build\tools\buildinfo.sh 来修改显示的情况,整理一下如下:
修改echo "ro.build.display.id=$BUILD_DISPLAY_ID", 把 $BUILD_DISPLAY_ID 修改成其他的名称可改变 Build Nubmer. 注意此时要去除$.
修改echo "ro.product.model=$PRODUCT_MODEL", 把 $PRODUCT_MODEL 修改成其他的名称可改变 Model Nubmer. 注意此时要去除$
修改echo "ro.build.version.release=2.1" , 中的2.1 可改变显示的Firmware version, 这个不建议修改。
Baseband Version 直接写在 modem.img 中,开机后modem 自动推送到Android端,需要专门的tool 才能修改。
Kernel Version 为linux 编译过程中产生, 按照标准的linux格式生成(compile.h), 最后版本信息写在文件/proc/version 下,所有的版本信息即在该文件的第一行,然后使用了一个正则表达式过滤了版本信息中一些字符。
8 About USER Build and Eng Build
在User <-> Eng 版本中自由切换。
郑重声明: 在出厂正式版本的时候,请务必关闭该feature, 不然将导致机器异常容易被root.
1. 首先说明一下User Eng 版本之间的差异
eng This is the default flavor. A plain make is the same as make eng.
• Installs modules tagged with: eng, debug, user, and/or development.
• Installs non-APK modules that have no tags specified.
• Installs APKs according to the product definition files, in addition to tagged APKs.
• ro.secure=0
• ro.debuggable=1
• ro.kernel.android.checkjni=1
• adb is enabled by default.
• Setupwizard is optional
user make user
This is the flavor intended to be the final release bits.
• Installs modules tagged with user.
• Installs non-APK modules that have no tags specified.
• Installs APKs according to the product definition files; tags are ignored for APK modules.
• ro.secure=1
• ro.debuggable=0
• adb is disabled by default.
• Enable dex pre-optimization for all TARGET projects in default to speed up device first boot-up
userdebug make userdebug
The same as user, except:
• Also installs modules tagged with debug.
• ro.debuggable=1
• adb is enabled by default.
2. 从安全角度来将,其差别主要是四个system properties 的设置,在正常情况下,如果不更新boot image, 那么
ro.secure
ro.allow.mock.location
ro.debuggable
是无法被修改的,即在编译的时候就已经决定了
3. 要使得这几个值能够被修改,那么必须修改system properties 的实现,system properties 的实现上,driver 是ashmem, 上次实现是property_service.c, 需要修改的地方有两处。
3.1 check_perms 函数中,增加下面一段,放在这个函数的最前面
//add for user -> root
if(!strcmp(name,"ro.secure") || !strcmp(name,"ro.allow.mock.location") || !strcmp(name,"ro.debuggable") || !strcmp(name,"persist.service.adb.enable")){
return 1;
}
3.2 property_set 函数中屏蔽
/* ro.* properties may NEVER be modified once set */
//for user -> root
// if(!strncmp(name, "ro.", 3)) return -1;
至此,我们已经完全开放了property 中这四个property 的控制权, 当然如果您还想稍微加以控制,您可以增加pid, uid 等的控制,减小这个权限范围。
4. 编写一个Application, 来实现自由调控,您可以创建一个简单的Application, 里面包括这个Activity 即可。
package com.example.user2root;
import android.app.Activity;
import android.os.Bundle;
import android.os.SystemProperties;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
/**
*
* This is a demo for User To Root.
* If you use this app, you MUST open system properties write security.
*
*/
public class User2rootActivity extends Activity {
/** Called when the activity is first created. */
private Button mRootButton;
private Button mUserButton;
private static final String RO_SECURE = "ro.secure";
private static final String RO_ALLOW_MOCK_LOCATION="ro.allow.mock.location";
private static final String RO_DEBUG = "ro.debuggable";
private static final String ADB_ENABLE = "persist.service.adb.enable";
private OnClickListener mRootListener = new Button.OnClickListener(){
@Override
public void onClick(View v) {
SystemProperties.set(ADB_ENABLE, "1");
SystemProperties.set(RO_SECURE, "0");
SystemProperties.set(RO_ALLOW_MOCK_LOCATION,"1" );
SystemProperties.set(RO_DEBUG, "1");
Toast.makeText(User2rootActivity.this, "Update to Root Success", Toast.LENGTH_LONG).show();
}
};
private OnClickListener mUserListener = new Button.OnClickListener(){
@Override
public void onClick(View v) {
SystemProperties.set(ADB_ENABLE, "0");
SystemProperties.set(RO_SECURE, "1");
SystemProperties.set(RO_ALLOW_MOCK_LOCATION,"0" );
SystemProperties.set(RO_DEBUG, "0");
Toast.makeText(User2rootActivity.this, "Update to User Success", Toast.LENGTH_LONG).show();
}
};
protected void findViews(){
this.mRootButton = (Button) this.findViewById(R.id.root);
this.mUserButton = (Button) this.findViewById(R.id.user);
}
protected void setActionListener() {
this.mRootButton.setOnClickListener(this.mRootListener);
this.mUserButton.setOnClickListener(this.mUserListener);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.findViews();
this.setActionListener();
}
}
至此已经完成了全部的工作,把这个apk 编译进去后,您将发现您可以自由的切换user eng 了。