Modify the Power Menu in Android Jelly Bean

by djmcnz@xda-developers

Introduction

This mod alters the appearance and functionality of the standard Power menu in Android Jelly Bean. There is the opportunity to add a function for standard reboot, reboot to recovery and reboot to bootloader. In addition to this there are guidelines for adding an long-press option to any of these menu items to perform one of the other functions.

This guide is based on changes to xml, png and smali files, you will need tools such as apktool and baksmail and this guide does not cover their use. Certainly if you are starting out I can assure you that it will take far less time to learn a bit of Java than it will to understand smali and I firmly recommend you take that approach. By doing so you can review the Java in any number of repositories and better understand the logic.

Framework Changes

First you need to decode framework-res.apk with apktool because we need to add some strings (text) and resources (icons) to this file. They’ll actually sit there redundant until we make some other changes but it’s important we start here first. You need to complete these steps:

1. Decode then encode framework-res.apk without making any changes and without error to verify your environment before you begin.

2. Add icons for reboot, recovery etc in the Power menu to the appropriate "drawables" folder for your devices (e.g. XHDPU). Use the following naming convention as appropriate for your options (e.g.):

  • ic_lock_reboot.png
  • ic_lock_recovery.png

   

3. Add relevant strings to the end of /res/values/strings.xml (adapt to suit your language) to cover the option you want to display on the menu. For example:

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

        <string name="recovery">Recovery</string>

        <string name="bootloader">Bootloader</string>

    </resources>

   

4. Encode your new copy of framework-res.apk

5. Decode the resulting file.

6. Open the new /res/values/public.xml and record the values for the images you added in step 2 and the strings in step 3 as we'll need them later.

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

    <public type="string" name="recovery" id="0x01040505" />

    ...

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

    <public type="drawable" name="ic_lock_recovery" id="0x01080603" />

7. Take the resulting file from step 4 above, open it in 7-zip or similar and copy the entire /res folder and the resources.arsc file to the original framework-res.apk from your ROM (the one you used in step 1) overwriting the existing files.

8. Make a nandroid backup.

9. Push the file from step 7 to /system/framework/ replacing the existing one and reboot.

10. If all goes well you'll not see any changes but you know know that your mods haven't broken the framework. If you have problems, check your work from the beginning.

You have now finished your framework and created the necessary resources.

Functional Changes

This is where we set up the menu items to display and put some logic behind them to execute.

At first this guide works on standard press (touch) then it covers long-press additions.

1. Extract classes.dex from android.policy.jar and use baksmali to decompile it. If you have an odexed ROM you’ll need to work with the android.policy.odex file and read the baksmali wiki (Google it).

2. This is where we add the menu items. Open GlobalActions.smali and look for:

    iget-object v1, v0, Lcom/android/internal/policy/impl/GlobalActions;->mItems:Ljava/util/ArrayList;

You should see something similar to this below it:

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

   

This is where the array for the power menu is being created and we need to add two items to it, to do this we replicate the entire section (twice) but point our additions to a) the resources we added to the framework and b) directions to the commands we will add later.

Here's what the whole section will look like after you've added two items:

    .line 203

    move-object/from16 v0, p0

    iget-object v1, v0, Lcom/android/internal/policy/impl/GlobalActions;->mItems:Ljava/util/ArrayList;

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

    const v3, 0x1080030

    const v4, 0x1040137

    move-object/from16 v0, p0

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

    invoke-virtual {v1, v2}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z

    new-instance v2, Lcom/android/internal/policy/impl/GlobalActions$??;      <- NOTE THIS LINE

    const v3, 0x???????      <- NOTE THIS LINE (icon)

    const v4, 0x???????      <- NOTE THIS LINE (string)

    move-object/from16 v0, p0

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

    invoke-virtual {v1, v2}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z

    new-instance v2, Lcom/android/internal/policy/impl/GlobalActions$??;      <- NOTE THIS LINE

    const v3, 0x???????      <- NOTE THIS LINE (icon)

    const v4, 0x???????      <- NOTE THIS LINE (string)

    move-object/from16 v0, p0

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

    invoke-virtual {v1, v2}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z

    .line 228

Note the lines that end in 'Lcom/android/internal/policy/impl/GlobalActions$??;' as you'll need to replace those question marks with numbers. Have a look for the GlobalActions$x.smali files and pick the first free number (e.g. if you have up to GlobalActions$9.smali then use GlobalActions$10.smali and GlobalActions$11.smali) and add those new numbers to the code above. One will be for reboot, the other for recovery.

3. Referring back to these lines from the last step:

    iget-object v1, v0, Lcom/android/internal/policy/impl/GlobalActions;->mItems:Ljava/util/ArrayList;

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

   

Go find the file that this refers to (in this example GlobalActions$2.smali) and make two copies of it, rename them to the numbers you used above (e.g. GlobalActions$10.smali and GlobalActions$11.smali)

4. Open each of these files and replace $2 with $10 or $11 (as appropriate for your numbers) throughout the file.

5. Now, below are two example files (e.g. $10 and $11) the first does reboot and the second does recovery, I discuss the bootloader later. These files have been added complete because I can’t prescribe the changes for your ROM, many will be different. Therefore use the following as a guide only.

Reboot (Key changes highlighted)

.class Lcom/android/internal/policy/impl/GlobalActions$10;

.super Lcom/android/internal/policy/impl/GlobalActions$SinglePressAction;

.source "GlobalActions.java"

# annotations

.annotation system Ldalvik/annotation/EnclosingMethod;

    value = Lcom/android/internal/policy/impl/GlobalActions;->createDialog()Landroid/app/AlertDialog;

.end annotation

.annotation system Ldalvik/annotation/InnerClass;

    accessFlags = 0x0

    name = null

.end annotation

# instance fields.field final synthetic this$0:Lcom/android/internal/policy/impl/GlobalActions;

# direct methods

.method constructor <init>(Lcom/android/internal/policy/impl/GlobalActions;II)V

    .registers 4

    .parameter

    .parameter "x0"

    .parameter "x1"

    .prologue

    .line 198

    iput-object p1, p0, Lcom/android/internal/policy/impl/GlobalActions$10;->this$0:Lcom/android/internal/policy/impl/GlobalActions;

    invoke-direct {p0, p2, p3}, Lcom/android/internal/policy/impl/GlobalActions$SinglePressAction;-><init>(II)V

    return-void

.end method

# virtual methods

.method public onPress()V

    .registers 3

    .prologue

    .line 202

    iget-object v0, p0, Lcom/android/internal/policy/impl/GlobalActions$10;->this$0:Lcom/android/internal/policy/impl/GlobalActions;

    #getter for: Lcom/android/internal/policy/impl/GlobalActions;->mContext:Landroid/content/Context;

    invoke-static {v0}, Lcom/android/internal/policy/impl/GlobalActions;->access$200(Lcom/android/internal/policy/impl/GlobalActions;)Landroid/content/Context;

    move-result-object v0

    const/4 v1, 0x0

    const-string p0, "now"

    invoke-static {v0, p0, v1}, Lcom/android/server/pm/ShutdownThread;->reboot(Landroid/content/Context;Ljava/lang/String;Z)V

    .line 204

    return-void

.end method

.method public showBeforeProvisioning()Z

    .registers 2

    .prologue

    .line 210

    const/4 v0, 0x1

    return v0

.end method

.method public showDuringKeyguard()Z

    .registers 2

    .prologue

    .line 220

    const/4 v0, 0x1

    return v0

.end method

Recovery

.class Lcom/android/internal/policy/impl/GlobalActions$11;

.super Lcom/android/internal/policy/impl/GlobalActions$SinglePressAction;

.source "GlobalActions.java"

# annotations

.annotation system Ldalvik/annotation/EnclosingMethod;

    value = Lcom/android/internal/policy/impl/GlobalActions;->createDialog()Landroid/app/AlertDialog;

.end annotation

.annotation system Ldalvik/annotation/InnerClass;

    accessFlags = 0x0

    name = null

.end annotation

# instance fields

.field final synthetic this$0:Lcom/android/internal/policy/impl/GlobalActions;

# direct methods

.method constructor <init>(Lcom/android/internal/policy/impl/GlobalActions;II)V

    .registers 4

    .parameter

    .parameter "x0"

    .parameter "x1"

    .prologue

    .line 198

    iput-object p1, p0, Lcom/android/internal/policy/impl/GlobalActions$11;->this$0:Lcom/android/internal/policy/impl/GlobalActions;

    invoke-direct {p0, p2, p3}, Lcom/android/internal/policy/impl/GlobalActions$SinglePressAction;-><init>(II)V

    return-void

.end method

# virtual methods

.method public onPress()V

    .registers 3

    .prologue

    .line 202

    iget-object v0, p0, Lcom/android/internal/policy/impl/GlobalActions$11;->this$0:Lcom/android/internal/policy/impl/GlobalActions;

    #getter for: Lcom/android/internal/policy/impl/GlobalActions;->mContext:Landroid/content/Context;

    invoke-static {v0}, Lcom/android/internal/policy/impl/GlobalActions;->access$200(Lcom/android/internal/policy/impl/GlobalActions;)Landroid/content/Context;

    move-result-object v0

    const/4 v1, 0x0

    const-string p0, "recovery"

    invoke-static {v0, p0, v1}, Lcom/android/server/pm/ShutdownThread;->reboot(Landroid/content/Context;Ljava/lang/String;Z)V

    .line 204

    return-void

.end method

.method public showBeforeProvisioning()Z

    .registers 2

    .prologue

    .line 210

    const/4 v0, 0x1

    return v0

.end method

.method public showDuringKeyguard()Z

    .registers 2

    .prologue

    .line 220

    const/4 v0, 0x1

    return v0

.end method

Booterloader

This is as simple as replacing one of the red highlighted words above with “bootloader” to reboot to the bootloader.

BUT THERE’S A TRICK!

There’s a reference in the smali above:

Lcom/android/internal/policy/impl/GlobalActions;->access$200(Lcom/android/internal/policy/impl/GlobalActions;)Landroid/content/Context;

That you must make sure is accurate. So, find this entry in GlobalActions.smali:

    iget-object v0, p0, Lcom/android/internal/policy/impl/GlobalActions;->mContext:Landroid/content/Context;

And make sure the “access” number 5 or 6 lines above it (e.g. access$200) is the one you use in your new smali files.

Adding Long Press Options

These are relatively straight forward all you need to do is add this section to your new smali files and change the highlighted instruction as appropriate (“now”, “recovery”, “bootloader”). I’ve kept the surrounding lines so you can see where it goes in the file and highlighted the things you may need to change.

.end method

# virtual methods

.method public onLongPress()Z

    .registers 3

    .prologue

    .line 214

    iget-object v0, p0, Lcom/android/internal/policy/impl/GlobalActions$10;->this$0:Lcom/android/internal/policy/impl/GlobalActions;

    #getter for: Lcom/android/internal/policy/impl/GlobalActions;->mContext:Landroid/content/Context;

    invoke-static {v0}, Lcom/android/internal/policy/impl/GlobalActions;->access$200(Lcom/android/internal/policy/impl/GlobalActions;)Landroid/content/Context;

    move-result-object v0

    const/4 v1, 0x0

    const-string p0, "recovery"

    invoke-static {v0, p0, v1}, Lcom/android/server/pm/ShutdownThread;->reboot(Landroid/content/Context;Ljava/lang/String;Z)V

    .line 215

    const/4 v0, 0x1

    return v0

.end method

# virtual methods

.method public onPress()V

6. Now use smali.jar to create a classes.dex file from your edits, add that to android.policy.jar, discard the android.policy.odex file and put the new .jar file in /system/framework/. Delete the android.policy.odex from this location also.

7. Reboot and enjoy!

djmcnz @ xda