该文编写的目的一是作为个人的学习笔记,二是希望对各位技术同事有所启发,整个过程也可加深对部分系统流程的理解。以前个人笔记都是比较简单的,第一次写成这种可分享的形式,若有不合理之处敬请谅解。
这个过程中,会涉及到简单的反编译知识与需要一点点阅读代码的能力,刚开始接触可能会有些困难,但成功的喜悦会让你觉得是值得的。
反编译定制属于高风险的方法,并不一定适用于任何地方,若不清楚被修改目标的流程,则该过程有可能带来未知的风险!该方法不应该被当做优先使用的方法,若已有现成的系统属性请首先依照系统属性修改!!
下面会提到安卓开放源代码计划(ASOP, Android Open Source Project)
,因为反编译smali
代码较难读懂,但正因为安卓本身开源,所以可以查看开源项目的源码来辅助跟踪这一个过程(请千万不要将AOSP
的内容就认为与我们主板的内容完全一致,定制化程度是不一样的)
若你掌握科学上网的方法,AOSP
可以从 https://android.googlesource.com/ 处在线查看源码,但请注意查看git仓库时查看对应系统版本的TAG。
若没有科学上网那么你就需要安装一个ubuntu
系统来利用repo
在 https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/取得所有的代码,但这一过程有个好处,你可以编译自己的安卓系统!
可能会有点奇怪,为什么要关注状态栏显示,但之所以要知道状态栏是如何显示的,是因为我们需要知道在没有涉及设置的情况下,其默认值来源是什么,即一开机的状态,这也是修改的目的。
顶部状态栏是被SystemUI
所控制的;另外,24小时制是在系统设置Settings
中更改的;那么,先看一下保存流程,在这里,我们需要利用AOSP
源码来辅助跟踪这一流程。
在执行这一步之前,解包要修改的目标固件可以很简单的获得整个System分区,下面所有路径都是指对应的目录,不再赘述。
先于AOSP
查看这一流程
//首先找到Settings看一下正常情况下是如何保存修改24小时显示的,至于如何知道这个是设置时间相关的,简单粗暴的看名字猜测往往有效,实在不行还可以网上搜索一下
//Home/Garfield/Android/source/packages/apps/Settings/src/com/android/settings/DateTimeSettings.java
public boolean onPreferenceTreeClick(Preference preference) {
if (preference == mDatePref) {
//......
} else if (preference == mTimePref) {
//......
} else if (preference == mTime24Pref) {
//这里可以看出来,通过set24Hour方法来设置这一过程,继续跟踪
final boolean is24Hour = ((SwitchPreference)mTime24Pref).isChecked();
set24Hour(is24Hour);
//......
}
return super.onPreferenceTreeClick(preference);
}
//继续跟踪发现设置保存的真正方法
private void set24Hour(boolean is24Hour) {
//首先这一个java类里,能看到HOURS_24与HOURS_12是两个常量,分别是字符串"24"与"12"
//然后有一个Settings.System.putString,搜索便知道这是安卓用于写系统属性的方法。
Settings.System.putString(getContentResolver(),
Settings.System.TIME_12_24,
is24Hour? HOURS_24 : HOURS_12);
}
//然后在安卓开发者网站搜索Settings.System.TIME_12_24相关资料,就可以找到他的描述:https://developer.android.com/reference/android/provider/Settings.System#TIME_12_24
//该值本质上也是一个常量,是字符串"time_12_24",即写系统属性"time_12_24"的值为"12"或者"24"
经过这一流程,就可以发现,真正保存显示时间格式的是系统属性"time_12_24"
,那么只要我们找到办法让其默认值是"24"
理论上就可以实现了。
了解安卓是怎么初始化默认系统属性的
通过简单的搜索就知道,安卓系统的第一次开机各种设置的默认值基本上都来自frameworks/base/packages/SettingsProvider/res/values/defaults.xml
,即SettingsProvider
来初始化,那么下一步就是找到它是如何提供设置的默认值的。
根据搜索可知,该过程在DatabaseHelper.java
完成,那么直接看这部分的源码
//Settings/DatabaseHelper.java
private void loadSettings(SQLiteDatabase db) {
loadSystemSettings(db);//可以看到这个过程负责加载系统设置
loadSecureSettings(db);//从名字可以推测分别加载的是系统相关设置、安全相关设置
// The global table only exists for the 'owner/system' user
if (mUserHandle == UserHandle.USER_SYSTEM) {
loadGlobalSettings(db);
}
}
//往下跟踪loadSystemSettings()这个流程
private void loadSystemSettings(SQLiteDatabase db) {
SQLiteStatement stmt = null;
try {
//.......
//这里发现了Settings.System.SCREEN_BRIGHTNESS这样类似命名的参数,和之前发现的Settings.System.TIME_12_24十分类似,那么我们就应该找对了地方
//但是根据名字判断,这里加载的是整数、布尔值等,且资源文件应用的是R.bool.def_开头命名的内容
//那么def_开头就应该是对应属性的默认值
loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS,
R.integer.def_screen_brightness);
loadBooleanSetting(stmt, Settings.System.SCREEN_BRIGHTNESS_MODE,
R.bool.def_screen_brightness_automatic_mode);
loadDefaultAnimationSettings(stmt);
loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION,
R.bool.def_accelerometer_rotation);
loadDefaultHapticSettings(stmt);
loadBooleanSetting(stmt, Settings.System.NOTIFICATION_LIGHT_PULSE,
R.bool.def_notification_pulse);
loadUISoundEffectsSettings(stmt);
loadIntegerSetting(stmt, Settings.System.POINTER_SPEED,
R.integer.def_pointer_speed);
//........
} finally {
if (stmt != null) stmt.close();
}
}
上面提到def_
开头的参数有很多,而R.
开头的是应用的资源,安卓应用资源文件都单独都位于/res/
目录下,那么我们就可以进来看看
<resources>
<bool name="def_dim_screen">truebool>
<integer name="def_screen_off_timeout">60000integer>
<integer name="def_sleep_timeout">-1integer>
<bool name="def_airplane_mode_on">falsebool>
<bool name="def_theater_mode_on">falsebool>
<string name="def_airplane_mode_radios" translatable="false">cell,bluetooth,wifi,nfc,wimaxstring>
<string name="airplane_mode_toggleable_radios" translatable="false">bluetooth,wifi,nfcstring>
<string name="def_bluetooth_disabled_profiles" translatable="false">0string>
综上所述,我们需要让SettingProvider
的default.xml
加入
,并且在loadSettings
的某一部分加入loadStringSetting(stmt, Settings.System.TIME_12_24, R.string.def_time_12_24)
让其加载加入的属性。
后面完成后我也好奇在SettingProvider
没有写入初始参数时是由谁提供默认值的,后面发掘了一下可能是framework
层提供,即/system/framework.jar
文件,其具体流程待各位挖掘啦。
apktool if /System/framework/framework-res.apk
#第一步对于反编译任何与系统高度相关的apk至关重要,若不先将框架安装(if命令),则后续会无法回编译。另外,-p可以指定安装的路径,这样可以同时反编译多个不同的系统版本不印发冲突
apktool d /System/priv-app/Settings/Settings.apk
apktool d /System/priv-app/SettingProvider/SettingProvider.apk
#完成后反编译目标所在目录会多出一个同名文件夹(不包括后缀)
下面查看反编译出来的Settings
,查看其流程是否和此前提到的源码类似。
//System\priv-app\Settings\Settings\smali\com\android\settings
.method private set24Hour(Z)V //找到set24Hour这个方法
.locals 3
.param p1, "is24Hour" # Z
.prologue
.line 311
invoke-virtual {p0}, Lcom/android/settings/DateTimeSettings;->getContentResolver()Landroid/content/ContentResolver;
move-result-object v1
.line 312
const-string/jumbo v2, "time_12_24" //系统属性名,在源码里是常量,但背后本质上也是该字符串
.line 313
if-eqz p1, :cond_0
const-string/jumbo v0, "24"
.line 311
:goto_0
//关键的地方,可以看到与AOSP源码的流程是可以互相印证的,即我们板卡的settings也是写该参数
invoke-static {v1, v2, v0}, Landroid/provider/Settings$System;->putString(Landroid/content/ContentResolver;Ljava/lang/String;Ljava/lang/String;)Z
.line 310
return-void
.line 313
:cond_0
const-string/jumbo v0, "12"
goto :goto_0
.end method
那么我们到这里就验证了Settings
保存该属性的方法与AOSP
是一样的,那么理论上我们也可以通过修改SettingProvider
来实现初始化为24小时格式。
首先要在资源文件中添加这个属性,但这里有个地方要注意,资源文件在编译后不同属性的资源会分开存放,另外会有资源表,因此对应的两者都要添加:
<string name="def_time_12_24">24string>
<public type="string" name="def_time_12_24" id="0x7f060016" />
这样一来,就完成了约等于源码里添加到default.xml
这一步,接下来是修改DatabaseHelper.java
//smali/com/android/providers/seetings/DatabaseHelper.smali
//找到loadSystemSettings这一方法,并找到里面加载起来string类型的地方方便参照修改
.method private loadSystemSettings(Landroid/database/sqlite/SQLiteDatabase;)V
//....
.line 2407
const-string/jumbo v1, "screenshot_location"
const v2, 0x7f060014
invoke-direct {p0, v0, v1, v2}, Lcom/android/providers/settings/DatabaseHelper;->loadStringSetting(Landroid/database/sqlite/SQLiteStatement;Ljava/lang/String;I)V
//仿照上面的smali写一个类似的过程
const-string/jumbo v1, "time_12_24" //系统属性名"time_12_24"
const v2, 0x7f060016 //资源表中对应的id号
invoke-direct {p0, v0, v1, v2}, Lcom/android/providers/settings/DatabaseHelper;->loadStringSetting(Landroid/database/sqlite/SQLiteStatement;Ljava/lang/String;I)
//...
这样一来,理论上就应该完成了修改,最后就是回编译并测试了。
apktool b /System/priv-app/SettingProvider/SettingProvider.apk
#完成后回编译后,反编译所在目录会多出dist,里面存放回编译的apk,对其应用系统签名即可
将其替换同名文件后,恢复出厂设置即可观察到效果,或将其用固件修改工具做进固件中。
至此,这一流程就走完了,希望能给大家带来一点启发,谢谢阅读。
apktool b /System/priv-app/SettingProvider/SettingProvider.apk
#完成后回编译后,反编译所在目录会多出dist,里面存放回编译的apk,对其应用系统签名即可
将其替换同名文件后,恢复出厂设置即可观察到效果,或将其用固件修改工具做进固件中。
## 结语
至此,这一流程就走完了,希望能给大家带来一点启发,谢谢阅读。