反编译定制安卓默认使用24H格式显示时间

反编译定制安卓默认使用24H格式显示时间

​ 该文编写的目的一是作为个人的学习笔记,二是希望对各位技术同事有所启发,整个过程也可加深对部分系统流程的理解。以前个人笔记都是比较简单的,第一次写成这种可分享的形式,若有不合理之处敬请谅解。

​ 这个过程中,会涉及到简单的反编译知识与需要一点点阅读代码的能力,刚开始接触可能会有些困难,但成功的喜悦会让你觉得是值得的。

警告

​ 反编译定制属于高风险的方法,并不一定适用于任何地方,若不清楚被修改目标的流程,则该过程有可能带来未知的风险!该方法不应该被当做优先使用的方法,若已有现成的系统属性请首先依照系统属性修改!!

AOSP

​ 下面会提到安卓开放源代码计划(ASOP, Android Open Source Project),因为反编译smali代码较难读懂,但正因为安卓本身开源,所以可以查看开源项目的源码来辅助跟踪这一个过程(请千万不要将AOSP的内容就认为与我们主板的内容完全一致,定制化程度是不一样的)

​ 若你掌握科学上网的方法,AOSP可以从 https://android.googlesource.com/ 处在线查看源码,但请注意查看git仓库时查看对应系统版本的TAG。

​ 若没有科学上网那么你就需要安装一个ubuntu系统来利用repohttps://mirrors.tuna.tsinghua.edu.cn/help/AOSP/取得所有的代码,但这一过程有个好处,你可以编译自己的安卓系统!

分析流程:

1. 首先,分析状态栏时间是如何显示的,格式是如何设置的,是由谁负责初始化的

​ 可能会有点奇怪,为什么要关注状态栏显示,但之所以要知道状态栏是如何显示的,是因为我们需要知道在没有涉及设置的情况下,其默认值来源是什么,即一开机的状态,这也是修改的目的。

​ 顶部状态栏是被SystemUI所控制的;另外,24小时制是在系统设置Settings中更改的;那么,先看一下保存流程,在这里,我们需要利用AOSP源码来辅助跟踪这一流程。

​ 在执行这一步之前,解包要修改的目标固件可以很简单的获得整个System分区,下面所有路径都是指对应的目录,不再赘述。

  1. 先于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"理论上就可以实现了。

  2. 了解安卓是怎么初始化默认系统属性的

    ​ 通过简单的搜索就知道,安卓系统的第一次开机各种设置的默认值基本上都来自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/目录下,那么我们就可以进来看看

    <!--查看/res/values/defaults.xml文件可以发现很多这种键值对,有bool,有interger,有string,这正对应上方不同的load方法-->
    <!--上面提到,12还是24小时制保存的是"time_12_24"这条属性,但是搜索没有发现,即默认是没有的-->
    <resources>
        <bool name="def_dim_screen">true</bool>
        <integer name="def_screen_off_timeout">60000</integer>
        <integer name="def_sleep_timeout">-1</integer>
        <bool name="def_airplane_mode_on">false</bool>
        <bool name="def_theater_mode_on">false</bool>
        <!-- Comma-separated list of bluetooth, wifi, and cell. -->
        <string name="def_airplane_mode_radios" translatable="false">cell,bluetooth,wifi,nfc,wimax</string>
        <string name="airplane_mode_toggleable_radios" translatable="false">bluetooth,wifi,nfc</string>
        <string name="def_bluetooth_disabled_profiles" translatable="false">0</string>
    

    ​ 综上所述,我们需要让SettingProviderdefault.xml加入<string name="def_time_12_24">24</string>,并且在loadSettings的某一部分加入loadStringSetting(stmt, Settings.System.TIME_12_24, R.string.def_time_12_24)让其加载加入的属性。

    ​ 后面完成后我也好奇在SettingProvider没有写入初始参数时是由谁提供默认值的,后面发掘了一下可能是framework层提供,即/system/framework.jar文件,其具体流程待各位挖掘啦。

2. 反编译Settings验证AOSP梳理出来的过程

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小时格式。

3. 反编译SettingProvider完成此前分析应该添加的修改

​ 首先要在资源文件中添加这个属性,但这里有个地方要注意,资源文件在编译后不同属性的资源会分开存放,另外会有资源表,因此对应的两者都要添加:

<!--SettingProvider/res/values/string.xml中加入下面行-->
<string name="def_time_12_24">24</string>

<!--SettingProvider/res/values/public.xml中string部分最后面加入下面行,注意id不可重复,且应按合理规则添加-->
<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)
    //...

​ 这样一来,理论上就应该完成了修改,最后就是回编译并测试了。

4. 回编译SettingProvider

   apktool b /System/priv-app/SettingProvider/SettingProvider.apk
   #完成后回编译后,反编译所在目录会多出dist,里面存放回编译的apk,对其应用系统签名即可

​ 将其替换同名文件后,恢复出厂设置即可观察到效果,或将其用固件修改工具做进固件中。

结语

​ 至此,这一流程就走完了,希望能给大家带来一点启发,谢谢阅读。

apktool b /System/priv-app/SettingProvider/SettingProvider.apk
#完成后回编译后,反编译所在目录会多出dist,里面存放回编译的apk,对其应用系统签名即可


​		将其替换同名文件后,恢复出厂设置即可观察到效果,或将其用固件修改工具做进固件中。

## 结语

​		至此,这一流程就走完了,希望能给大家带来一点启发,谢谢阅读。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值