【JK团队|MIUI移植第五课】移植MIUI Framework

第五章移植MIUIFramework

1.为什么使用代码插桩
首先我们来回顾第一章中的Android软件架构图,
这个图中框架层的代码完全是由Java语言编写的,对于这两层的代码,在没有源代码的情况下我们可以采取代码插桩的方式来注入我们的代码。

但是对于下面几层的代码几乎都是以机器码的形式存在,机器码也是可以修改的,但是修改难度和修改smali代码的难度不可同日而语。

我们这个系列的文章不介绍如何修改这些机器码,大家有兴趣的可以参考网上的相关资料。MIUI是基于源码开发的,为了提升整个效率,我们会修改下面几层的代码,比如说我们修改了dalvik虚拟机,skia绘图库等。

幸好这些修改不多而且有些是为了提升性能的,不影响MIUI的整体功能。MIUI的绝大部分修改都是对框架层和核心应用层,这样保证了我们在原厂ROM的基础上修改这两个层的smali代码达到移植MIUI的目的。

大家看到这里可能有一个疑问,我们直接替换原厂ROM框架层和核心应用层这两层的代码不就得了。不行,因为各个层次之间是有关联的,框架层和下层代码的一些调用接口是各个厂家自己扩展的,简单的整个替换MIUI框架层和核心应用层的代码无法工作。

2.移植规范
本节我们通过详细的介绍android,miui和i9100这三个目录来探讨一下代码的组织规范。
2.1 android
android有两个子目录src和system,src目录是google发布的android2.3.7的部分源码。

system目录下放的是由源码编译而成的三个jar包。在移植的时候不要修改这个目录。
src/frameworks/base/services中的文件属于services.jar包,src/frameworks/base/policy中的文件属于android.policy.jar包,src/frameworks/base中的其它文件属于framework.jar包。
framework.jar是android运行的基础框架,比如每个APP需要用到的Actitity的实现等。
services.jar是android中的一些服务的实现,这些服务基本上都运行在system_server进程中。android.policy.jar是手机策略以及锁屏的实现。

2.2 miui
miui下也是有两个子目录src和system。其中src/frameworks/base子目录和android中的src/frameworks/base子目录是一对一的关系,放的是miui修改过的代码。src/frameworks/miui目录下放的是miui对原生资源的修改,有直接替换的图片,还有一些xml文件的改动。

system分为四个子目录,目录结构映射手机中的system目录,也就是说这些目录中的文件最终会放到手机相应的目录中:

system/framework:存放由miui源码编译而成的android.policy.jar,framework.jar,
services.jar以及miui专属资源包framework-miui-res.apk。
system/app:存放miui核心应用
system/media:一些锁屏,主题相关文件。
system/xbin:busybox程序和miui专属的invoke-as程序。
在移植的时候不要修改miui目录。

2.3 i9100
i9100是我们会移植三星i9100机型新建的一个目录,每个机型都需要新建一个目录。该目录的组织规范为:
放置一个原厂ROM刷机包,比如I9100ZCKJ1.zip。
基于原厂ROM中的文件反编译修改的,需要存放反编译后整个目录的内容。
比如
android.policy.jar.out,framework.jar.out,services.jar.out,framework-res,LogsProvider,MediaProvider,Phone,Settings。这些目录相应的是从刷机包中system/framework/android.policy.jar,system/framework/framework.jar,system/framework/services.jar,system/app/LogsProvider.apk,
这些目录相应的是从刷机包中system/framework/android.policy.jar,
system/framework/framework.jar,
system/framework/services.jar,
system/app/LogsProvider.apk,
system/app/MediaProvider.apk,
system/app/Phone.apk,
system/app/Settings.apk
这些文件反编译而来的。

基于MIUI的文件反编译修改的,只存放反编译后整个目录中修改的部分。

比如SystemUI目录修改了MIUISystemUI.apk反编译代码中的两个smali文件。

之所以这样组织是因为基于原厂ROM文件修改的,初始代码不再变动,只是后续的修改。

而基于MIUI文件修改的,MIUI自身的源代码不断变化,相应的整个反编译的代码也不断变化。因此只存放我们修改过的smali文件。

有一个makefile,i9100中的makefile给出了详细的注释,请参考该makfile写其它机型的makefile。有了makefile之后,很多工作就可以自动化了。具体的可以参考 build/porting.mk和build/util.mk中的注释。

misc目录:存放一些在打包过程中需要的文件,在makefile里面将该目录的文件放到刷机包中。

3.移植资源
移植资源就是修改framework-res.apk,先反编译原厂ROM中的framework-res.apk,然后根据miui/src/frameworks/miui目录下的文件,修改反编译framework-res目录中的相应文件。

4.修改smali
这一节我们以i9100为例,重点介绍如何修改原厂ROM的smali将MIUI的修改应用到上面去。我们不会将所有的修改都会在文中列出,挑选几个有代表性的讲解,剩下的大家可以自己去做。

4.1 比较差异
这里的比较差异包含两个部分:比较miui和原生android的差异,比较i9100和原生android的差异。

以framework.jar为例,首先在android,miui和i9100这三个目录中,分别反编译其中的framework.jar文件,在这三个目录中会产生framework.jar.out目录。

为了方便比较差异,我们建议在android,miui,i9100中分别建一个目录noline,先将framework.jar.out目录复制到这个目录中,然后使用patchorom/tools中的脚本rmline.sh运行如下命令,假定在patchrom目录下:
$tools/rmline.shmiui/noline/framework.jar.out
rmline.sh是用以把smali所有以.line开头的行去掉,这样我们容易比较smali代码上的差别。但是对smali代码进行修改我们还是在没有去掉.line的版本上修改,.line对于我们调试代码很有帮助,在程序运行出错抛出异常的时候,adblogcat的异常错误信息会给出在出错代码的行号,这样方便我们定位错误。

接下来用大家说熟悉的文件比较工具来比较差异,Linux下推荐meld,Windows下推荐BeyondCompare。

在比较miui和android,i9100和android的区别时,我们不比较那些相同的和新加的文件,只比较修改过的文件。(注:当比较miui和android的smali代码时,你会发现我们并没有修改这些smali文件对应的java文件,不需要修改这些文件,我们只需要修改与我们修改的源文件对应的smali文件)。

下面我们就开始修改smali文件了,我将这些修改分成3种情况,选择有代表性的4个文件加以介绍,这4种情况难度依次增加。

4.2 直接替换
以ActivityThread.smali为例,比较发现miui改了其中一个方法getTopLevelResources,而i9100和原生android的实现完全一样,这种情形是最简单也是最happy的,我们改的地方要适配的机型原厂ROM完全没有修改,直接替换就可以了。

4.3 线性代码
还是以ActivityThread.smali为例,对于这个文件,miui一共改了两个方法,一个是上面介绍的,另一个是applyConfigurationToResourcesLocked。通过比较得知,miui修改了这个方法,i9100也修改了这个方法。怎么办呢,我们先分析一下miui修改的代码:.methodfinalapplyConfigurationToResourcesLocked(Landroid/content/res/Configuration;)Z。。。
invoke-virtual{v5,p1},
Landroid/content/res/Configuration;->updateFrom(Landroid/content/res/Configuration;)Imove-resultv0
.localv0,changes:I
invoke-static{v0},Landroid/app/MiuiThemeHelper;->handleExtraConfigurationChanges(I)Vinvoke-virtual{p0,v7},
Landroid/app/ActivityThread;->getDisplayMetricsLocked(Z)Landroid/util/DisplayMetrics;move-result-objectv1
.localv1,dm:Landroid/util/DisplayMetrics;
在上面将miui增加的代码用红色标出,在讲述之前,先解释一下smali代码的一些规律:所有的局部变量用v开头,方法的顶部.locals8表示这个方法使用8个局部变量。所有的参数用p开头,局部变量和参数都是从0开始编号。对于非静态方法来说,p0就是对象本身的引用,即this指针。

这里miui新增了一个静态方法调用,对于这种顺序执行的一段代码,我们称之为线性代码。这个例子比较简单,只新增了一个静态方法调用。线性代码的特点是只有一个入口和一个出口,在编译器的术语这叫做基本块。对于这种新增的代码,我们找出它的上下文,即修改的代码前后的操作。然后在i9100的该方法的smali代码中找到相应的位置,把这个修改应用到i9100中去。这种修改也相对简单,插入代码的相应位置比较好定位。

4.4 条件判断4.4
这种情况指的是miui插入的代码并不是一个线性代码,而是有条件判断的。我们以Resources.smali为例,miui修改了其中的loadDrawable方法,修改后的结果如下:
.methodloadDrawable(Landroid/util/TypedValue;I)Landroid/graphics/drawable/Drawable;.endlocalv8#e:Ljava/lang/Exception;
.endlocalv13#rnf:Landroid/content/res/Resources$NotFoundException;
:cond_6
invoke-virtual/range{p0..p2},
Landroid/content/res/Resources;->loadOverlayDrawable(Landroid/util/TypedValue;I)Landroid/graphics/drawable/Drawable;
move-result-objectv6
if-nezv6,:cond_1
:try_start_1
move-object/from16v0,p0
红色代码是miui插入的代码,我们再看一下i9100相对于原生android对这个方法的改动,发现改动非常大。这种情况怎么办呢,这种情况下的关键是找到所插入代码的入口点和出口点(即这段代码是从哪执行而来的,执行完毕后又往哪去开始执行代码)。

首先,我们发现插入代码的前面是一个标号:cond_6,这说明程序中应该有一个跳转语句跳转到这个标号:cond_6。而且这种程序应该也可以从:cond_6上面的语句顺序执行而来(即它可能有两个入口点),我们分别去找这两个入口点的代码。首先我们去找哪个语句使用了:cond_6,找到如下代码:
const-stringv15,”.xml”
invoke-virtual{v9,v15},Ljava/lang/String;->endsWith(Ljava/lang/String;)Z
move-resultv15
if-eqzv15,:cond_6
可以发现这段代码是在判断v9这个字符串是否以”.xml”结尾,如果不是的话,跳转到:cond_6。好,我们去i9100中找到对应的代码逻辑。

对于这个例子,我们完全可以以”.xml”作为一个关键字去i9100的loadDrawable方法中搜索一下,定位到如下代码:
const-stringv17,”.xml”
move-objectv0,v10
move-object/from16v1,v17
invoke-virtual{v0,v1},Ljava/lang/String;->endsWith(Ljava/lang/String;)Z
move-resultv17
if-eqzv17,:cond_b
这段代码的逻辑和我们在miui中找到的代码一样,看来,我们应该把miui插入的代码插入到:cond_b之后。找到i9100代码中的:cond_b之后,我们看看这条代码后面的代码,发现和我们插入的代码后面的代码基本类似,这下可以确定miui新插入的代码应该放在:cond_b之后了。

再来看看出口点,miui插入的代码有两个出口点(是一个条件判断),
if-nezv6,:cond_1
如果v6为空往下执行,如果不为空,则跳转到:cond_1,好,我们来看看:cond_1的代码是 :cond_1的代码如下:
:cond_1
:goto_1
if-eqzv6,:cond_2
move-object/from16v0,p1
igetv0,v0,Landroid/util/TypedValue;->changingConfigurations:I
我们在i9100中发现了一段类似的代码:
:cond_1
:goto_1
if-eqzv7,:cond_2
move-object/from16v0,p1
igetv0,v0,Landroid/util/TypedValue;->changingConfigurations:I
只不过是v6变成了v7,说明这段代码检测v7的值,因此我们需要将我们插入的代码改为:invoke-virtual/range{p0..p2},
Landroid/content/res/Resources;->loadOverlayDrawable(Landroid/util/TypedValue;I)Landroid/graphics/drawable/Drawable;
move-result-objectv7
if-nezv7,:cond_1

5 逻辑推理4.4.5
这种情况一般和内部类相关,你会发现在源码中的改动很小,但是在反编译后的smali代码改动确很大。

对于Java文件中的每一个内部类,都会产生一个单独的smali文件,比如ActivityThread$1.smali,这些文件的命名规范是如果是匿名类,外部类+$+数字。否则的话是外部类+$+内部类的名字。

当在内部类中调用外部类的私有方法时,编译器会自动合成一个静态函数。比如下面这个类:
publicclassHello{
publicclassA{
voidfunc(){
setup();
}
}
privatevoidsetup(){
}
}
我们在内部类A的func方法中调用了外部类的setup方法,最终编译的smali代码为:Hello$A.smali文件代码片段:
#virtualmethods
.methodfunc()V
.locals1
.prologue
.line5
iget-objectv0,p0,LHello$A;->this$0:LHello;
#calls:LHello;->setup()V
invoke-static{v0},LHello;->access$000(LHello;)V
.line6
return-void
.endmethod
Hello.smali代码片段:
.methodstaticsyntheticaccess$000(LHello;)V
.locals0
.parameter
.prologue
.line1
invoke-direct{p0},LHello;->setup()V
return-void
.endmethod
可以看到,编译器自动合成了一个access$000方法,假如当我们在一个较复杂的内部类中加入了一个对外部类私有方法的调用,虽然只是导致新合成了一个方法,但是这些合成的方法名可能都会有变化,这样的结果就是smali文件差异较大,
比如说对于KeyguardViewMediator.java文件,我们在其中的mBroadcastReceiver的定义最后加了一行代码adjustStatusBarLocked(),但是最后生成的smali文件却差别比较大。

这个正是由于编译器为这个私有方法合成了一个方法导致所有合成的方法命名被打乱,这种情况下我们修改代码无需作这么大的改动,自己为这个私有方法合成一个和其它合成方法不冲突的名字,具体的做法可以参照i9100中android.policy.jar.out中对这个文件smali代码的修改。

5.建议
最后想对修改smali代码给出一些建议:
(1)细心,仔细的定位插入代码在相应机型代码中的插入位置。
(2)要注意局部变量序号的改变。
(3)不要一次修改完所有的文件再用apktool重新编译,如果插入代码有错误,会无法编译。但是apktool的编译出错信息是天书,你无从知道是哪个文件改错了。
(4)出现错误不要紧,检查adblogcat的错误信息,找出错误发生的原因。修改smali代码没那么难,多实践一定会掌握相应的技巧。

移植教程

可能对你有帮助的内容:极客币获取 | 话费充值 | 下载帮助 | 刷机必看 | 阿里云代金券

文章名称:【JK团队|MIUI移植第五课】移植MIUI Framework

文章链接:https://www.xtdiguo.com/1501.html

免责声明:根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。

(0)
打赏
番茄小子的头像番茄小子盟主
上一篇 2019年 10月 24日
下一篇 2019年 10月 31日

相关推荐

发表回复

登录后才能评论