在线查看 Android 系统源码

想 hook 掉获取本机号码的方法,自己写的 demo 是成功的,但是设置里关于界面的本机号码还是没变,于是就想着去看看源码里设置里头的逻辑。在此之前,先说下事情起因

获取本机号码

正常来说,我们可以通过以下代码获取到本机号码

1
2
3
4
5
6
7
8
try {
val tm = this.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val tel = tm.line1Number

println("本机号码:$tel")
} catch (e: Exception) {
e.printStackTrace()
}

这里需要动态申请 android.permission.READ_PHONE_STATE 权限,不在本文的讨论的范围,代码就不搬了

用模拟器得到本机号码为

1
本机号码:15555218135

设置界面

没错就是这个方法,hook 掉

1
2
3
4
5
6
XposedBridge.hookAllMethods(TelephonyManager::class.java, "getLine1Number", object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam?) {
super.afterHookedMethod(param)
param?.result = "12345678900"
}
})

利用 Xposed 来玩黑科技也不在本文讨论范围,以后会单独来讲 Xposed。现在就只要明白我这里把 getLine1Number() 的返回值修改成了 12345678900 就行了。运行看下效果

自己 Demo 的 Log

1
本机号码:12345678900

嗯,成功了,再看设置界面,What!~ 还是一样的,因为设置界面里头啥也没变,就不贴图了,看上面的就行了。

这下郁闷了,明明我把 getLine1Number() 方法的返回值修改了,而且自己的 Demo 也是成功的,为啥系统的设置界面没有改变呢?为了弄清楚,我需要知道设置界面里头获取本机号码的逻辑。可能调用的不是 getLine1Number() 方法,去看下源码吧

开启源码之旅

想看完整的源码,可以去官网下载对应的版本的源码,然后再利用专门的工具查看。不过这种方式有点得不偿失了,毕竟源码那么大。于是就想到了在线看源码的网站,这里重点推荐

http://androidxref.com/

界面长这样

选择对应的版本,因为模拟器用的是 6.0.0 ,所以我这里选择 Marshmallow - 6.0.0_r5

会看到如下界面,右侧用于选择源码中对应的架构

  • Full Search: 进行全文搜索,会匹配所有的单词、字符串、标识符以及数字等,例如在 frameworks 下通过 Full Search 搜索”Settings“

  • Definition:搜索符号定义相关的代码,例如搜索 ondraw 函数的定义,显示结果如图

  • Symbol:搜索符号,例如可以搜索类中的成员变量等,下图显示了通过 Symbol 搜索FEATURE_NO_TITLE的结果

  • File Path:搜索源码文件名中包含给定字符串的文件,例如想要搜索文件名包含 mediacodec的源码文件,则可以在 File Path 中填入 mediacodec 进行搜索,结果如下所示

利用搜索确实可以快速定位到自己的需要的代码位置,但也可能不是想要的,所以别纠结为啥搜出来的不是自己想要的

首先我需要知道设置界面所属的 Activity,利用 adb 指令

1
adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p'

adb 指令查看当前与用户交互的 Activity 可以在以下文章查看

https://blog.csdn.net/hty1053240123/article/details/54312614

显示结果

1
2
3
4
5
6
7
8
9
10
11
12
$ adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p'
Running activities (most recent first):
TaskRecord{94f2ae6 #121 A=com.android.settings U=0 sz=4}
Run #3: ActivityRecord{3e6c25c u0 com.android.settings/.deviceinfo.SimStatus t121}
Run #2: ActivityRecord{7f87582 u0 com.android.settings/.deviceinfo.Status t121}
Run #1: ActivityRecord{1a41e5d u0 com.android.settings/.SubSettings t121}
Run #0: ActivityRecord{befd34b u0 com.android.settings/.Settings t121}
Running activities (most recent first):
TaskRecord{508fb48 #123 A=com.android.systemui U=0 sz=1}
Run #1: ActivityRecord{ce003fb u0 com.android.systemui/.recents.RecentsActivity t123}
TaskRecord{9ab8827 #120 I=com.android.launcher3/.Launcher U=0 sz=1}
Run #0: ActivityRecord{7c1da7f u0 com.android.launcher3/.Launcher t120}

如果不需要看到所有栈里的 Activity,只想知道当前 Activity 的界面,可以用如下指令

1
adb shell dumpsys activity | grep "mFocusedActivity"

结果

1
2
$ adb shell dumpsys activity | grep "mFocusedActivity"
mFocusedActivity: ActivityRecord{3e6c25c u0 com.android.settings/.deviceinfo.SimStatus t121}

这样我们就知道了,显示本机号码的这个界面的 Activity 叫 SimStatus ,其中com.android.settings 是包名,设置 在手机里也是一个应用,包名就是com.android.settings

知道了要查看的类,现在我们去搜索下

其实如果我们对 Android 系统有些了解的话,就应该知道所有系统里面的应用,都在 packages 工程下面,所以在选择工程的时候,选择对应的工程,会给我们过滤掉很多无关的结果,如

这个不是很明显,只少了3个结果,不过我们已经找到想找的类了,点击 SimStatus 进去,简单查看之后,找到了关键代码

这就是 6.0.0 系统里头,关于本机号码的代码,一看就明白为啥我 hook 掉 getLine1Number() ,但是设置界面里头却没有改变,本来想直接getLine1NumberForSubscriber() 方法 hook 掉就可以了,但自己在 Android Studio 去查看 TelephonyManager 源码的时候,发现没有 getLine1NumberForSubscriber() 这个方法,因为我用的 SDK 源码是 android-27,也就是 8.1 的系统。既然不同系统里头 TelephonyManager 的源码不同,那设置界面的源码可能也会不同。为了验证下,打开一个 8.0 系统的模拟器(因为有一个现成的)

利用 adb 指令查看到当前界面的 Activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p'
Running activities (most recent first):
TaskRecord{c2ff9d #679 A=com.android.settings U=0 StackId=1 sz=5}
Run #4: ActivityRecord{57ebfcb u0 com.android.settings/.Settings$SimStatusActivity t679}
Run #3: ActivityRecord{e36cc30 u0 com.android.settings/.SubSettings t679}
Run #2: ActivityRecord{84dfdcd u0 com.android.settings/.SubSettings t679}
Run #1: ActivityRecord{999a704 u0 com.android.settings/.Settings$SystemDashboardActivity t679}
Run #0: ActivityRecord{6e438c5 u0 com.android.settings/.Settings t679}
Running activities (most recent first):
TaskRecord{d56dc12 #676 I=com.google.android.apps.nexuslauncher/.NexusLauncherActivity U=0 StackId=0 sz=1}
Run #0: ActivityRecord{5180a24 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t676}
Running activities (most recent first):
TaskRecord{cf55fe3 #678 A=com.android.systemui U=0 StackId=5 sz=1}
Run #0: ActivityRecord{b454879 u0 com.android.systemui/.recents.RecentsActivity t678}

这里又发现一个问题,那就是

1
adb shell dumpsys activity | grep "mFocusedActivity"

这个指令得不到想要的结果,没办法用查看到所有的了

通过上面的结果,我们知道我们想要的界面的 Activity 是 SimStatusActivity,那去搜索一下试试

静态内部类,实现里头还是空的,这里不去分析怎么找到具体代码了,我们返回一步,来到 AndroidManifest.xml,找到 SimStatusActivity

点击里头的 com.android.settings.deviceinfo.SimStatus

然后再来到 DeviceInfoUtils

发现这里又是调用的 getLine1Number() 方法了,果然不同系统不同。不同也发现一个相同的地方,那就是他们最后都会调用

1
PhoneNumberUtils.formatNumber(rawNumber)

更改策略,把 formatNumber() 方法 hook 掉

1
2
3
4
5
6
XposedBridge.hookAllMethods(PhoneNumberUtils::class.java, "formatNumber", object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam?) {
super.afterHookedMethod(param)
param?.result = "+8612345678900"
}
})

现在再来看设置界面的本机号码

OK,成功了!

因为想玩点黑科技,又去拾起了系统源码,虽然很浅显,但也明白源码并不惧怕,以后如果需要找点什么,也不会一头雾水了,关于文中提到的 Xposed ,将会在另一篇文章中简单介绍。

仅以此记录。