Thursday, March 31, 2011

【翻译+实验】给电源键菜单里添加“重启”等选项

[Post only for the purpose of personal study]
转贴备忘,仅供个人学习参考

背景:其实八键开关神马的能轻易地给你添加一个重启按钮,怕麻烦的请不要再往下看了。即使不用八键开关,非要在电源菜单里加重启的话,XDA上也有直接可以刷进去就高枕无忧的包。我为什么贴了这个教程过来呢?因为我一直相信授之以鱼不如授之以渔这件事,做完这一系列看起来毫无意义的工作其实可以学到不少东西,比如我就学到了自定义xml元素。不只是重启菜单,用同样的原理以后还可以为手机定制不同的功能,让我想起了A2时代啊……好怀念。

准备工作:

  1. Java当然是必备品,记得配置好环境变量再来。验证配置是否正确的方法:在cmd中输入命令javac,显示所有命令帮助
  2. 确保xRecovery正常工作,出错了有后悔药可吃。或者你不介意用FlashTool或US、PCC什么的重刷。
  3. 准备好要修改的framework-res.apk和framework.jar,用RE或者adb从手机中提取;或者从固件中直接提取。
  4. 准备好你喜欢的反编译apk的工具。
  5. 懂得用两个非常简单的java命令反编译和编译jar包。

第一阶段:修改framework-res.apk

  1. 反编译framework-res.apk后,会得到framework-res包里的所有文件,其中的xml文件就可以用任意文本编辑器打开浏览,而不是乱码。
  2. 用记事本等单纯的文本编辑器打开“res/values/”路径下的“String.xml”文件。
  3. 给xml文件添加元素。添加电源菜单里要出现的字符串Reboot,如果需要的话还可以添加Recovery之类

      <string name="reboot_recovery">Recovery</string>
      <string name="reboot">Reboot</string>

      add string

      Click to view large pic

  4. 用记事本等单纯的文本编辑器打开“res/values/”路径下的“Public.xml”文件。
  5. 给刚刚定义的菜单项分配id.观察一下这个xml文件,会发现每一行都是这样的结构:

    eg:

    <public type="string[变量类型]" name="mochi_strings_playback_speed_mochi_lock_txt[变量名称]" id="0x0104042b[变量id]" />

    因为刚刚我们在string.xml里添加了元素,所以要在public.xml里也添加相应的定义。通过观察源文件的规律,发现id的分配是有顺序的十六进制数,为了避免添加重复的id,我们找到string类变量的最后一个,在这个教程里是mochi_strings_playback_speed_mochi_lock_txt

    有可能根据固件的不同而有所区别,请注意。

    string id 1

    Click to view large pic

  6. 观察一下最后一个string的id数值【eg: 0x0104042b】,同时对比一下紧挨着它的下一个元素的id【eg: 0x01050000】,最后的string的id的下一位没有被后面元素占用,那么我们就可以把最后string的id的下一位分配给新定义的菜单项Reboot了。
  7. [Tips]用Windows自带的计算器就可以方便地把十六进制数转换成我们熟悉的十进制数:

    hex2dec

    Click to view large pic

    在本教程中,换算得到结果

    最后一个String的id是:0x0104042b=17040427 它后面那个变量的id是:0x01050000=17104896

    可以看到17040427的下一位17040428=104042C(怎么换过来的再怎么换回去)没有被占用,所以我们可以把它分配给新定义的Reboot选项。在刚刚看到的最后一个String后面添加:

    <public type="string" name="reboot" id="0x0104042c" />

    add id

    Click to view large pic

    如果刚刚你还添加了别的选项,就按照同样的方法继续往下分配id.

  8. 为Reboot菜单项添加一个图标。很简单,找一个你喜欢的图标放到res\drawable-ldpi文件夹里。这个文件夹在不同的固件里可能会不一样,我看到原教程是drawable-hdpi文件夹(因为那是给三星Vibrant写的)。我们只要找到power off这个图标在哪儿就可以把我们新添加的图标放在它旁边了。而且还可以在power off图标的基础上修改一个Reboot的图标,既保险又风格统一。Click to view large picicon
  9. 假设你的新图标名称叫ic_lock_reboot.png(随便取吧,记住这个名字)我们还要在“res\values”里的public.xml给图标分配id,方法跟4、5步一样。这次变量类型就不是string而是图片drawable了,找到同类的最后一个,添加到它的下一位【这里我分配的是0x10800b1】。

    <public type="drawable" name="ic_lock_reboot" id="0x10800b1" />

    add icon id

    Click to view large pic

  10. 至此framework-res.apk已经修改完毕。用apk工具再给编译回去,我用的当然还是apktool。

*如果实在学不会或者懒得改framework,直接下载我改好的吧……当然仅限U20i的2.1.0.A.06固件,我还改过状态栏图标。其他机型、固件的,请去XDA找。

第二阶段:修改framework.jar

  1. 从手机中提取出framework.jar

  2. 在cmd中反编译之。反编译得到的文件夹里,找到“com\android\internal\app”路径下的ShutdownThread.smali文件。

  3. 用记事本打开,在# instance fields段的末尾,也就是.field的最后一行,添加以下语句:

    .field public static mReboot:I

    add integer

    Click to view large pic

  4. 在# direct methods段中找查找invoke-static {}, Landroid/os/Power;->shutdown()V这一行,在其之前添加以下语句:
  5. sget v1, Lcom/android/internal/app/ShutdownThread;->mReboot:I

    const/4 v2, 0x1

    if-eq v1, v2, :reboot

    add methodClick to view large pic

  6. 然后紧接着在下面的

    .line 303
        return-void

    之后添加以下语句:

    :reboot

    const-string v4, "now"

    invoke-static {v4}, Landroid/os/Power;->reboot(Ljava/lang/String;)V

    return-void

    add method 2Click to view large pic

  7. 至此framework.jar已经修改完毕,编译回去:

*反编译和编译jar包的方法请参考使用smali的教程。简单来说就三步:

反编译:java –jar baksmali.jar 源文件.jar –o 输出文件夹

编译: java –jar smali.jar 待编译的文件夹 –o classes.dex

然后把得到的classes.dex拖拽到原来没解压的jar包里覆盖旧的classes.dex。

第三阶段:修改android.policy.jar

  1. 从手机中提取android.policy.jar
  2. 在cmd中反编译之,得到的文件夹里一层一层点开,直到最后一层“\com\android\internal\policy\impl”,找到GlobalActions.smali,用记事本打开。
  3. 找到createDialog方法(查找const/4 v0, 0x3),在431行处把原来长度为3的数组改成4,因为我们给原来只有三项的菜单添加了一项。如果你除了Reboot还添加了其他的选项,那这个数组的长度要对应着增加。
  4. 把    const/4 v0, 0x3    改为

        const/4 v0, 0x4

    array


     


  5. 查找aput-object v2, v0, v1,定位到第457行。在这句后面添加以下语句:



    1. const/4 v1, 0x3


          new-instance v2, Lcom/android/internal/policy/impl/GlobalActions$7;


          const v3, 0x10800b1 # reboot icon resource id


          const v4, 0x0104042c # reboot string resource id


          invoke-direct {v2, p0, v3, v4}, Lcom/android/internal/policy/impl/GlobalActions$7;-><init>(Lcom/android/internal/policy/impl/GlobalActions;II)V


          aput-object v2, v0, v1



      红色部分的id就是刚才改framework-res.apk的时候我们分配的id,第一行icon的id对应drawable那个,第二行string的id就对应string那个,请参照第一阶段的步骤改。


      add item



    *如果除了Reboot,还添加了Recovery等项,也需要添加相应的代码,方法一样,只需要改一下id的值、变量名称以及数组的索引号。


  6. 添加按了新选项之后执行的动作。复制一个GlobalActions$3.smali动作文件,更名为GlobalActions$7.smali,这个是给Reboot选项用的。

  7. 用记事本打开刚刚造出来的GlobalActions$7.smali文件,查找替换所有的GlobalActions$3变成GlobalActions$7.(一共好像就3处)

  8. 在52行处,“invoke-static {v0, v1}, Lcom/android/internal/app/ShutdownThread;->shutdown(Landroid/content/Context;Z)V”这一句之前添加以下语句:

  9. const/4 v2, 0x1


    sput v2, Lcom/android/internal/app/ShutdownThread;->mReboot:I


    action


    *如果还添加了其他选项,就依照6、7步,为其他选项也建立动作,例如为Recovery选项复制修改一个GlobalActions$8,然后文件中所有的GlobalActions$3也都替换成8,添加语句的时候把对应的数组索引号改成0x2。


  10. 至此android.policy.jar修改完毕,编译回jar。

 


————————————————————————————


framework-res.apk,framework.jar和android.policy.jar三个文件修改完成之后,修改权限为644然后刷入手机中,重启即可看到效果。


*644就是第一行前两个勾,第二行第一个勾,第三行第一个勾。



     


 




     


 



    Original Post:
untermensch@XDA How to add Reboot to power menu

No comments:

Post a Comment