【Android源码 栏目提醒】:网学会员为需要Android源码 的朋友们搜集整理了Android修改汇编代码支持原生高清来电大头贴 - 电子设计相关资料,希望对各位网友有所帮助!
Android修改汇编代码支持原生高清来电大头贴 1、
Android手机中的来电大头贴 来电大头贴其实包括来电去电两种状态只是通常都称作“来电大头贴”。
这个功能有三种实现方法包括 - A使用第三方的软件在来去电时显示出大头贴遮盖在原生的拔号面板上面 - B少数开发商提供的ROM中集成了带大头贴功能的拔号面板软件 - C在现有的ROM的拔号面板中原生支持大头贴功能。
首先谈谈为什么大头贴支持起来这么难。
这是因为在
Android原生系统中联系人头像大小的标准是96x96强制是正方形的。
这个标准的一部分原因是
Android的手机联系人与Google联系人支持同步而Google的在线联系人就是这个标准。
一些情况下可以在手机的联系人中存储高画质的联系的但是一旦该帐户与Google帐户同步则联系人的头像品质立即就下降了。
这一问题是Google整体的机制导致的我们这里不谈。
所以也必须先强调本文中讨论的“高画质来电大头贴”可能会受到
Android手机上的Google帐户同步的影响建议关掉同步功能。
因为这一标准的缘故所以
Android手机中的“联系人”功能在设置头像时打开的剪切框都是正方形的而且保存到联系人数据库中时图片也会自动降低品质。
显然缘于这些因素要实现“全屏来电大头贴”就必须绕开原生的联系人和拨号面板工具。
这里补充一点在
Android中“联系人”是指Contacts.apk而“拨号面板”是指的Phone.apk。
两者一般是相互独立的可以各自更换。
但由于它们事实上都使用较特殊的签名并且直接依赖ROM中的framework所以不同ROM之间很难做到通用。
2、不同实现方案的思路 上面提到过三种方案这里再进一步地详细解释一下。
首先A方案是最常见的。
在谷歌市场中就能搜索出一大堆。
例如 - 来电全屏大头贴:Full Screen Caller ID - HD Caller ID Pro高清来电 - BIG caller ID来电大头贴 - 视频来电大头贴:Video Caller Id - Ultimate_Call_Screen_HD 等等。
如前所述的这类软件自带一个联系人管理的功能用于设定“哪些人需要显示大头贴”。
一般来说考虑到一致性的体验对于“未设置大头贴”人则显示默认的大头贴。
由于A方案本质上是通过“遮盖在原生的拔号面板”来实现的所以它实际上天生存在两个问题。
其一它总是比原生的拔号面板弹出来得要晚一点操作过程相当不流畅其二由于它遮盖了原来的拨号面板所以它必须自己再实现一个“接听”界面。
这后者其实又催生了另外一种市场各种大头贴软件都提供自己的接听界面Theme电子市场也因此一度火爆。
最后一般来说大头贴软件的通话功能并没有原生的拔号面板完整所以也就通常会提供一个“一键切换到原生面板”的功能。
总而言之A方案基本上就是在原来的拨号面板上做一层皮揭开了或是没遮好的时候都会不那么“好看”。
然而B方案呢却实在不常见因为只有少数ROM生产商有能力提供在Phone.apk上面的开发。
我目前注意到的有 - Lewa OS采用了将联系人、短信和通话管理功能集成在一个PIM.apk中的方案并基于此而实现了大头贴功能 - Miui手机在联系人管理中集成了大头贴管理并在拔号面板上实现了大头贴显示。
总的来说要在拔号面板上动手脚风险还是挺大的并且如果要动这个手脚还得提供一整套的通话功能。
综合上面观察B方案通常都被实现为一个折衷方案 - B1在Contacts.apk中集成一个联系人功能方便用户为联系人设定或不设定大头贴 - B2当Phone.apk检测到呼入时如果在B1功能中设定了大头贴则在通话界面背景中显示大头贴。
显然这涉及到
Android手机功能的两个重要功能的改动这也是“只有少数ROM生产商”在做的原因。
那么B方案有什么问题呢也有两点。
其一通常会在系统的联系人之外独立实现大头贴的管理功能亦即是联系人头像是一个放在
Android系统的内部数据库中而大头贴又是另外一个放在单独的图片文件夹中其二这些ROM中的大头贴功能无法通用依赖相应ROM中大量改动过的framework。
第三种方案基于原生拔号面板以及联系人功能来实现“全屏、高画质大头贴”是我们接下来要讨论的。
它主要的目的就是通过极少的代价使得ROM发烧友可以快速地集成一些“有大头贴功能的”拨号面板到自己喜欢的ROM中去。
3、基础 首先我们要知道“系统内置的联系人数据库”其实是可以存放高画质的头像的只是当联系人与Google联系人同步时才会导致这些头像被自动降低品质。
此其一。
其二内置数据的存取接口并没有要求联系人头像必须是正方形而只是联系人软件Contacts.apk实现的联系人编辑功能在指定头像时强调地要求选框为正方形而已 好的有了这两个前提我们就来找找能“给联系人添加高清头像”功能的软件。
电子市场中其实确实有两款这样的好货而且名字还很相近 - HD Contacts - HD Contact Photos或Contact_Photos_Updater 后者有两个名字Contact_Photos_Updater这个名字是在xda论坛中使用的作者也主要是在xda中发布和更新这个软件。
这两个软件使用起来还是有一些问题的。
但就目前我们要做的事情来说后者HD Contact Photos基本能满足要求。
它提供一个独立的头像管理功能尽管这些头像也被保存在磁盘目录中但只要你“同步”一下那么这些头像都到系统库中去了而原始目录中的删除掉也没关系。
——当然建议你还是留下来做个备份也不错。
有了HD Contact Photos这个软件“向系统库中写大头贴”就算暂时地解决了我们后面主要讨论“如何在通话面板中显示大头贴”的问题。
而在此之前我先对这两款“写大头贴”的软件作些备注 - 两款软件功能和界面都比较接近但后者HD Contact Photos完整得多。
- 前者HD Contacts不能用于ICS并且它有个BUG联系人必须先设置一个头像才能“修改成”大头像。
- 两个软件都只能将头像设置成正方形前者HD Contacts要修正起来很麻烦后者比较容易。
- 后者HD Contact Photos其实有限制图片大小但修正起来比较容易。
所以事实上本文中也会提供一个后者HD Contact Photos的修改版本否则仍然达不到我们的目的。
附件先贴两个修改效果 【点心vv-ice定制版本】 【Lezo定制版本】 4、修改前的准备工作 这里介绍一些
Android上的逆向工程的基础。
首先我们要操作Phone.apk它其实也就是一个.zip文件其中包括四个主要信息 - 资源文件res.和resources.arsc - 代码文件classes.dex - 应用描述AndroidManifest.xml - 签名信息META-INF. apktool这个工具可以处理前三种数据而签名信息则必须使用一个signapk.jar有些工具包称为AutoSign。
1解包缺省至Phone目录 apktool d -f Phone.apk 注意我们接下来的修改都不会动到资源所以事实上也可以不解开其中的资源文件。
可以这样使用命令行 apktool d -f -r Phone.apk 这样在编译回去的时候会快一点而且也可以避免一些错误。
——但很多时候的修改需要对照着资源文件来看所以你也可以解一份有资源文件的版本放在旁边作参照。
2 编译回.apk指定从Phone目录 apktool b -f Phone Phone2.apk 3 对Phone2.apk加签名 java -jar signapk.jar platform.x509.pem platform.pk8 Phone2.apk Phone2_signed.apk 注意这时使用的签名文件为platform.x509.pem和platform.pk8而不是我们平常用的testkey.。
这是很关键的一处Phone.apk必须使用platform.来签名。
5、修改初步 我们将Phone.apk解到Phone目录之后。
可以找到如下子目录 Phonesmalicomandroidphone 我们接下来主要修改两个文件 CallCard.smali InCallScreen.smali 注意这里的.smali是另一种格式的源代码它反编译自Dalvik虚拟机中执行码opcode。
基本上你可以认为这.smali就是汇编代码基于寄存器的虚拟机引擎。
好吧但我们既然要“原生的”那么就只好来改改这些汇编代码了。
: 1对InCallScreen.smali只需要做一处修改 --------- 找到 plain view plaincopy 1. .field private mMainFrame:Landroid/view/ViewGroup 改成 plain view plaincopy 1. .field public mMainFrame:Landroid/view/ViewGroup 我们需要在CallCard.smali中访问这个成员所以它必须是公开的public。
2 修改CallCard.smali针对updateDisplayForPerson方法 --------- 找到 plain view plaincopy 1. .method private updateDisplayForPersonLcom/
android/internal/telephony/CallerInfo ... 在该方法中找到唯一一处showCachedImage调用 plain view plaincopy 1. invoke-static v0 v1 Lcom/
android/phone/CallCard-gtshowCachedImageLandroid/widget/ImageViewLcom/
android/internal/telephony/CallerInfoZ 2. move-result v4 3. if-nez v4 :cond_2 注意两点一是我们要修改这个 plain view plaincopy 1. if-nez v4 :cond_2 所以要先记下这个cond_2。
第二点上面的v0 v1 v4可能在具体的代码中有所不同这也要留意查找时不能依据这些寄存器。
而修改时也要注意寄存器的冲突和使用有修改经验的就不多言了没有经验的则要仔细回顾一下汇编语言的知识。
接下来我们改动上述一行使之变成为 plain view plaincopy 1. 》》》 2. if-eqz v4 :cond_20 3. move-object/from16 v0 p0 4. move-object/from16 v5 p4 5. invoke-virtual v0 v1 v5 Lcom/
android/phone/CallCard-gtshowCachedBackgroundLcom/
android/internal/telephony/CallerInfoLcom/
android/internal/telephony/CallZ 6. move-result v4 7. goto :cond_2 8. :cond_20 9. end fix. 这里if-nez变成了if-eqz而:cond_20这一标签用于插入一段代码当这些代码执行完成时仍然会到 plain view plaincopy 1. goto :cond_2 这就是上面要记住cond_2的原因。
至于:cond_20是可以随意取的编译程序是按16进制自动升序来编号这些标签而0x20号标签一般已经比较大了不会与现有的标签冲突。
当然写成cond_30或cond_50也行的。
此外这里还必须要注意v0 v5 v1和v4这四个寄存器的使用必须参考这里的代码上下文为决定使用哪些个空闲的寄存器。
其中v1是继承使用了前面的寄存器值如果此前v1中不是放着CallerInfo的话则要根据上下文再调整。
反正如果寄存器用错了的话……哈哈……Crash 【注有一个简单的法子可以避免用错寄存器的问题就是在将方法开始处的.locals nnn这里的nnn值改大几个需要用几个寄存器就加上几个。
然后在我们插入的代码中只使用最后的这几个寄存器号也就不会冲突了。
例如原来是.locals 4改成.locals 8则v4v5v6v7这新添加的4个寄存器号就总是安全的。
】 3 修改CallCard.smali针对updatePhotoForCallState方法 --------- 这里的修改与上一例是相似的只不过是针对updatePhotoForCallState方法而已。
找到 plain view plaincopy 1. invoke-static v9 v3 Lcom/
android/phone/CallCard-gtshowCachedImageLandroid/widget/ImageViewLcom/
android/internal/telephony/CallerInfoZ 2. move-result v9 3. if-nez v9 :cond_2 《《《《修改此处 改动上面最后一行。
变成 plain view plaincopy 1. 》》》 2. if-eqz v9 :cond_20 3. move-object/from16 v5 p1 4. invoke-virtual p0 v3 v5 Lcom/
android/phone/CallCard-gtshowCachedBackgroundLcom/
android/internal/telephony/CallerInfoLcom/
android/internal/telephony/CallZ 5. move-result v9 6. goto :cond_2 7. :cond_20 8. end fix. 4 添加新上面代码所需要的方法showCachedBackground --------- 上面两段函数都调用了一个方法这个showCachedBackground是我们这里“来电全屏大头贴”的主要功能实现代码。
其它的修改其实只是插个桩而已。
代码如下把它直接插到CallCard.smali这个文件的某个方法前/后面就可以了我一般将它放在showCachedImage函数声明的后面 plain view plaincopy 1. 2. 【主函数更新全屏大头贴】 3. 4. .method public showCachedBackgroundLcom/
android/internal/telephony/CallerInfoLcom/
android/internal/telephony/CallZ 5. .locals 6 6. .parameter quotciquot 7. .parameter quotcallquot 8. 9. 10. .prologue 11. invoke-virtual p2 Lcom/
android/internal/telephony/Call-gtgetStateLcom/
android/internal/telephony/CallState 12. move-result-object v0 13. invoke-virtual v0 Lcom/
android/internal/telephony/CallState-gtisAliveZ 14. move-result v0 15. if-nez v0 :cond_0 16. 17. 18. :goto_0 19. return v0 20. 21. 22. :cond_0 23. if-nez p1 :cond_1 24. 25. 26. :goto_1 27. const/4 v0 0x0 28. goto :goto_0 29. 30. 31. :cond_1 32. iget-boolean v2 p1 Lcom/
android/internal/telephony/CallerInfo-gtisCachedPhotoCurrent:Z 33. if-eqz v2 :goto_1 34. 35. 36. iget-object v2 p1 Lcom/
android/internal/telephony/CallerInfo-gtcachedPhoto:Landroid/graphics/drawable/Drawable 37. if-eqz v2 :goto_1 38. 39. 40. iget-object v3 p0 Lcom/
android/phone/CallCard-gtmInCallScreen:Lcom/
android/phone/InCallScreen 41. const/16 v4 0xF0 42. invoke-virtual v2 Landroid/graphics/drawable/Drawable-gtgetIntrinsicWidthI 43. move-result v5 44. if-lt v5 v4 :goto_1 45. 46. 47. const/16 v4 0xF0 48. invoke-virtual v2 Landroid/graphics/drawable/Drawable-gtgetIntrinsicHeightI 49. move-result v5 50. if-lt v5 v4 :goto_1 51. 52. 53. iget-object v3 v3 Lcom/
android/phone/InCallScreen-gtmMainFrame:Landroid/view/ViewGroup 54. if-eqz v3 :goto_1 55. 56. 57. invoke-virtual v3 v2 Landroid/view/ViewGroup-gtsetBackgroundDrawableLandroid/graphics/drawable/DrawableV 58. 59. 60. const/16 v2 0x8 61. iget-object v3 p0 Lcom/
android/phone/CallCard-gtmPhoto:Landroid/widget/ImageView 62. invoke-virtual v3 v2 Landroid/widget/ImageView-gtsetVisibilityIV 63. 64. 65. const/16 v2 0x0 66. invoke-virtual p0 v2 Lcom/
android/phone/CallCard-gtsetPersonInfoStyleZV 67. 68. 69. goto :goto_0 70. .end method 6、对初步修改的说明 除了2、3步中的插桩代码之外整个功能其实就依赖一个完全手写汇编的showCachedBackground。
它需要操作到当前类CallCard的 plain view plaincopy 1. CallCard.InCallScreen.mMainFrame 成员。
而该成员在InCallScreen类中被声明为private所以需要在第1步中把InCallScreen中的该声明改成public。
showCachedBackground的思路很简单。
由于CallCard.smali总是要从“联系人”中装载头像而此前我们已经用“HD Contact Photos”把这个头像存成了“高清、全屏大头贴图片”那么就只要将这个取出来的头像贴在背景上就可以了。
高清全屏来电大头贴不就是把大头贴在背景上嘛。
. 所以在分析整个Phone.apk时我发现它原本取到一个mPhoto之后为了便于显示就将它存在CallerInfo.cachedPhoto里了。
既如此那么在原有流程的updatePhotoForCallState与updateDisplayForPerson方法中当它调用showCachedImage来显示了图片之后我们也就只需要把这个图片在背景上“贴”一下就可以了。
而这个背景就是CallCard.InCallScreen.mMainFrame 很简单嘛。
上面的showCachedBackground汇编代码翻译成java代码就是 java view plaincopy 1. public boolean showCachedBackgroundCallerInfo paramCallerInfo Call paramCall 2. 3. // 电话是在用状态来电或呼出或接通 4. boolean bool paramCall.getState.isAlive 5. 6. 7. // paramCallerInfo.isCachedPhotoCurrent有效 8. bool bool ampamp paramCallerInfo null ampamp paramCallerInfo.isCachedPhotoCurrent 9. 10. 11. if bool 12. Drawable localDrawable paramCallerInfo.cachedPhoto 13. 14. 15. if localDrawable.getIntrinsicWidth lt 240 localDrawable.getIntrinsicHeight lt 240 16. bool false 17. 18. else 19. // 置mMainFrame的背景 20. this.mInCallScreen.mMainFrame.setBackgroundDrawablelocalDrawable 21. 22. // 使mPhoto不显示原来的头像就不必显示了嘛 23. this.mPhoto.setVisibility8 24. 25. 26. // 修改作个人信息的显示风格备用后文解释 27. // setPersonInfoStylefalse 28. 29. 30. return bool 31. 7、一些遗留问题的修改 上面是最简版本只是为了突出核心功能的实现但实际留下的问题还是不少的。
下面一一道来。
1 呀。
是成功了呢不过一会儿就被改回来了。
--------- 修改 2012.09.04。
方法名写错的应该是updateInCallBackground而不是updateScreen 这并不是普遍性的问题有些拔号面板是有背景的有些则是背景透明而直接显示桌面的。
对于有背景的拔号面板Phone.apk通常会在InCallScreen.smali中有一个updateInCallBackground方法找到它的几处调用注释掉即可。
或者干脆把updateInCallBackground改成空函数就好了。
如果找不到updateInCallBackground函数则尝试找一个setBackgroundResource这个方法看哪里重绘了mMainFrame的背景即可。
2 下一次电话呼入的时候会残留上一个电话使用的大头贴 --------- 是的。
这应该在此次电话结束时清理掉。
这很简单修改InCallScreen.smali找到 plain view plaincopy 1. .method private delayedCleanupAfterDisconnectV 这个方法。
然后找到其中return-void这行代码往上数几行找个地方插入如下代码即可 plain view plaincopy 1. ... 2. 3. 4. fixed by aimingoo 5. 重置背景 6. const/4 v0 0x0 7. iget-object v1 p0 Lcom/
android/phone/InCallScreen-gtmMainFrame:Landroid/view/ViewGroup 8. invoke-virtual v1 v0 Landroid/view/ViewGroup-gtsetBackgroundResourceIV 9. 10. 11. 这里参见本遗留问题第5项有关setPersonInfoStyle的说明 12. const/4 v0 0x1 13. iget-object v1 p0 Lcom/
android/phone/InCallScreen-gtmCallCard:Lcom/
android/phone/CallCard 14. invoke-virtual v1 v0 Lcom/
android/phone/CallCard-gtsetPersonInfoStyleZV 15. end fix. 16. :cond_3 17. :goto_0 18. return-void ltlt- 注意从这行代码往上找 19. 20. 21. ... 3 好象原来的头像还是会闪一下 --------- 原来的头像是这样的一个获得过程 - 首先开始接听或拔打电话 - 拔号程序显示面板面板中头像位置显示为“无头像”的icon - 异步发起调用从联系人数据库中读取头像 - 当上述异步调用返回时更新显示上面的“无头像”icon为真正的头像图片 所以事实上原生的应用在“显示头像”时都会是两步这是为了更快地绘制出拨号面板以便用户操作。
而们的代码事实上也是依赖这个原理在上述的过程异步得到“高清大头贴”的数据之后显示在背景上的。
那么.