2013年1月3日木曜日

【Xperia AX SO-01E】戻るキー長押しでアプリキル(2)

戻るキー長押しによって、最前面で起動中のアプリを終了する機能を追加する記事、後編です。
今回は実際に機能する部分の説明になります。
※コードを一部見直しております。こちらの記事も参照してください。


前提は以下です。
・rootとってあること
・作業PCはadbコマンドが使える環境であること
・作業PCにjavaが入っていること
・端末のビルド番号は9.0.G.0.247
・CWMを導入してあること
・母艦はWindows 7
・トラブっても自力で復旧できること(つまり自己責任)

いじるファイルは、以下になります。
/system/framework/external-res.apk
/system/framework/android.policy.odex

■external-res.apk
アプリキルした時にメッセージ(トーストと言うらしいです)を表示させますが、これの文字列を外部リソースに追加しておきます。
external-res.apkの作業フォルダ内、「res」フォルダ以下のxmlにリソース追加します。
この記事でexternal-res.apkにすでにいろいろ追加している前提でやりますので、今回追加する文字列のIDは"0x7f030002"となります。この記事にも書いていますが、IDの振り方には若干縛りがあるので気を付けてください。のちにこのIDを使う場面が出てきますので、合わせるようにしてください。

まずres\values\public.xml。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <public type="drawable" name="ic_lock_reboot" id="0x7f020000" />
    <public type="string" name="global_action_reboot" id="0x7f030000" />
    <public type="string" name="global_action_cwmreboot" id="0x7f030001" />
    <public type="string" name="app_killed_message" id="0x7f030002" />
</resources>

次にres\values\strings.xml。英語表記です。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="global_action_reboot">Reboot</string>
    <string name="global_action_cwmreboot">CWM Reboot</string>
    <string name="app_killed_message">Application killed</string>
</resources>

続きましてres\values-ja\strings.xml。日本語表記です。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="global_action_reboot">再起動</string>
    <string name="global_action_cwmreboot">CWMで再起動</string>
    <string name="app_killed_message">アプリケーションを終了しました</string>
</resources>

新規でexternal-res.apkをこしらえる場合は、この記事のようにaaptで作成して署名とかしないといけませんが、すでに作っている場合はapktoolで展開、編集、再ビルドで署名のし直しは不要です。

■android.policy.odex
android.policy.odexをbaksmaliした展開先の中で、以下のフォルダを探して開きます。
com\android\internal\policy\impl

【1】PhoneWindowManager$KillConcept.smaliの作成
上記フォルダ内に、「PhoneWindowManager$KillConcept.smali」というファイルを新規作成します。
内容は以下になります。

.class Lcom/android/internal/policy/impl/PhoneWindowManager$KillConcept;
.super Ljava/lang/Object;
.source "PhoneWindowManager.java"

# interfaces
.implements Ljava/lang/Runnable;


# annotations
.annotation system Ldalvik/annotation/EnclosingClass;
    value = Lcom/android/internal/policy/impl/PhoneWindowManager;
.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/PhoneWindowManager;


# direct methods
.method constructor <init>(Lcom/android/internal/policy/impl/PhoneWindowManager;)V
    .registers 2
    .parameter

    .prologue
    .line 4204
    iput-object p1, p0, Lcom/android/internal/policy/impl/PhoneWindowManager$KillConcept;->this$0:Lcom/android/internal/policy/impl/PhoneWindowManager;

    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method


# virtual methods
.method public run()V
    .registers 16

    .prologue
    :try_start_1
    iget-object v4, p0, Lcom/android/internal/policy/impl/PhoneWindowManager$KillConcept;->this$0:Lcom/android/internal/policy/impl/PhoneWindowManager;

    const/4 v5, 0x0

    const/4 v6, 0x0

    const/4 v7, 0x0

    invoke-virtual {v4, v5, v6, v7}, Lcom/android/internal/policy/impl/PhoneWindowManager;->performHapticFeedbackLw(Landroid/view/WindowManagerPolicy$WindowState;IZ)Z

    invoke-static {}, Landroid/app/ActivityManagerNative;->getDefault()Landroid/app/IActivityManager;

    move-result-object v3

    invoke-interface {v3}, Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List;

    move-result-object v1

    invoke-interface {v1}, Ljava/util/List;->iterator()Ljava/util/Iterator;

    move-result-object v2

    :cond_0
    invoke-interface {v2}, Ljava/util/Iterator;->hasNext()Z

    move-result v5

    if-eqz v5, :cond_1

    invoke-interface {v2}, Ljava/util/Iterator;->next()Ljava/lang/Object;

    move-result-object v0

    check-cast v0, Landroid/app/ActivityManager$RunningAppProcessInfo;

    iget v4, v0, Landroid/app/ActivityManager$RunningAppProcessInfo;->uid:I

    const/16 v5, 0x2710

    if-lt v4, v5, :cond_0

    const v5, 0x1869F

    if-gt v4, v5, :cond_0

    iget v4, v0, Landroid/app/ActivityManager$RunningAppProcessInfo;->importance:I

    const/16 v5, 0x64

    if-ne v4, v5, :cond_0

    new-instance v4, Landroid/content/Intent;

    const-string v5, "android.intent.action.MAIN"

    invoke-direct {v4, v5}, Landroid/content/Intent;-><init>(Ljava/lang/String;)V

    const-string v5, "android.intent.category.HOME"

    invoke-virtual {v4, v5}, Landroid/content/Intent;->addCategory(Ljava/lang/String;)Landroid/content/Intent;

    iget-object v6, p0, Lcom/android/internal/policy/impl/PhoneWindowManager$KillConcept;->this$0:Lcom/android/internal/policy/impl/PhoneWindowManager;

    iget-object v9, v6, Lcom/android/internal/policy/impl/PhoneWindowManager;->mContext:Landroid/content/Context;

    invoke-virtual {v9}, Landroid/content/Context;->getPackageManager()Landroid/content/pm/PackageManager;

    move-result-object v7

    const/4 v8, 0x0

    invoke-virtual {v7, v4, v8}, Landroid/content/pm/PackageManager;->resolveActivity(Landroid/content/Intent;I)Landroid/content/pm/ResolveInfo;

    move-result-object v5

    iget-object v6, v5, Landroid/content/pm/ResolveInfo;->activityInfo:Landroid/content/pm/ActivityInfo;

    if-eqz v6, :cond_03

    iget-object v7, v6, Landroid/content/pm/ActivityInfo;->packageName:Ljava/lang/String;

    const-string v8, "android"

    invoke-virtual {v7, v8}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

    move-result v7

    if-nez v7, :cond_03

    iget-object v8, v6, Landroid/content/pm/ActivityInfo;->packageName:Ljava/lang/String;

    goto :cond_04

    :cond_03
    const-string v8, "com.android.launcher"

    :cond_04

    iget-object v10, v0, Landroid/app/ActivityManager$RunningAppProcessInfo;->pkgList:[Ljava/lang/String;

    if-eqz v10, :cond_2

    array-length v11, v10

    if-lez v11, :cond_2

    const/4 v12, 0x0

    const/4 v14, 0x0

    :cond_05
    if-ge v12, v11, :cond_07

    aget-object v13, v10, v12

    const-string v4, "com.android.systemui"

    invoke-virtual {v13, v4}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

    move-result v4

    if-nez v4, :cond_06

    invoke-virtual {v13, v8}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

    move-result v4

    if-nez v4, :cond_06

    invoke-interface {v3, v13}, Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;)V

    const/4 v14, 0x1

    :cond_06
    add-int/lit8 v12, v12, 0x1

    goto :cond_05

    :cond_07
    if-eqz v14, :cond_1

    const-string v4, "com.android.externalres"

    const/4 v5, 0x4

    invoke-virtual {v9, v4, v5}, Landroid/content/Context;->createPackageContext(Ljava/lang/String;I)Landroid/content/Context;

    move-result-object v4

    invoke-virtual {v4}, Landroid/content/Context;->getResources()Landroid/content/res/Resources;

    move-result-object v4

    const v5, 0x7f030002

    invoke-virtual {v4, v5}, Landroid/content/res/Resources;->getText(I)Ljava/lang/CharSequence;

    move-result-object v4

    const/4 v5, 0x0

    invoke-static {v9, v4, v5}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v4

    invoke-virtual {v4}, Landroid/widget/Toast;->show()V

    :cond_1
    :goto_0
    return-void

    :cond_2
    iget v5, v0, Landroid/app/ActivityManager$RunningAppProcessInfo;->pid:I

    invoke-static {v5}, Landroid/os/Process;->killProcess(I)V
    :try_end_11
    .catch Landroid/os/RemoteException; {:try_start_1 .. :try_end_11} :catch_0

    const/4 v14, 0x1

    goto :cond_07

    :catch_0
    move-exception v5

    goto :goto_0
.end method

長いですね…。とにかくこれがアプリ終了を実行する部分になります。
途中、「const v5, 0x7f030002」という部分が出てきますが、これはもちろん、先ほどexternal-res.apkに追加した文字列のIDに合わせてください。

【2】PhoneWindowManager.smaliの編集
PhoneWindowManager.smaliを編集します。

「.field final mAllowSystemUiDelay:Ljava/lang/Runnable;」を検索(211行目くらい)。
その下に1行追加します。

.field final mAllowSystemUiDelay:Ljava/lang/Runnable;

.field mBackLongPress:Ljava/lang/Runnable;  #追加

.field mBootMsgDialog:Landroid/app/ProgressDialog;

「.method public constructor <init>()V」を検索(628行目くらい)。
このメソッドが終わる部分(「.end method」、893行目あたり)の上、「return-void」の直前に、数行追加します。

    iput-object v0, p0, Lcom/android/internal/policy/impl/PhoneWindowManager;->mScreenLockTimeout:Ljava/lang/Runnable;

    new-instance v0, Lcom/android/internal/policy/impl/PhoneWindowManager$KillConcept;  #この行から追加

    invoke-direct {v0, p0}, Lcom/android/internal/policy/impl/PhoneWindowManager$KillConcept;-><init>(Lcom/android/internal/policy/impl/PhoneWindowManager;)V

    iput-object v0, p0, Lcom/android/internal/policy/impl/PhoneWindowManager;->mBackLongPress:Ljava/lang/Runnable;  #この行まで追加

    return-void
.end method

.method static synthetic access$000(Lcom/android/internal/policy/impl/PhoneWindowManager;)V

「.method public interceptKeyBeforeDispatching」を検索(7708行目くらい)。
その156行くらい下に、「const/16 v26, 0x3」という部分があります(7864行目くらい)。その前に処理を追加します。

    .line 1635
    :cond_64
    const-wide/16 v26, -0x1

    goto :goto_49

    .line 1643
    :cond_67
    const/16 v26, 0x4  #この行から追加

    move/from16 v0, v26

    if-ne v13, v0, :cond_68

    if-nez v6, :cond_68

    move-object/from16 v0, p0

    iget-object v0, v0, Lcom/android/internal/policy/impl/PhoneWindowManager;->mHandler:Landroid/os/Handler;

    move-object/from16 v26, v0

    move-object/from16 v0, p0

    iget-object v0, v0, Lcom/android/internal/policy/impl/PhoneWindowManager;->mBackLongPress:Ljava/lang/Runnable;

    move-object/from16 v27, v0

    invoke-virtual/range {v26 .. v27}, Landroid/os/Handler;->removeCallbacks(Ljava/lang/Runnable;)V

    :cond_68  #この行まで追加
    const/16 v26, 0x3

    move/from16 v0, v26

さらにその下に、「const/16 v27, -0x1」という部分があります(8319行目くらい)。その数行上にある「:cond_うんたら」の後に結構長い処理を追加します。

    invoke-virtual {v0, v1}, Lcom/android/internal/policy/impl/PhoneWindowManager;->showOrHideRecentAppsDialog(I)V

    .line 1746
    :cond_19e
    const-wide/16 v26, -0x1

    goto/16 :goto_49

    .line 1754
    :cond_1a2
    const/16 v26, 0x4  #この行から追加

    move/from16 v0, v26

    if-ne v13, v0, :cond_1a3

    move-object/from16 v0, p0

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

    move-object/from16 v26, v0

    invoke-virtual/range {v26 .. v26}, Landroid/content/Context;->getContentResolver()Landroid/content/ContentResolver;

    move-result-object v26

    const-string v27, "kill_app_longpress_back"

    const/16 v28, 0x0

    invoke-static/range {v26 .. v28}, Landroid/provider/Settings$Secure;->getInt(Landroid/content/ContentResolver;Ljava/lang/String;I)I

    move-result v26

    const/16 v27, 0x1

    move/from16 v0, v26

    move/from16 v1, v27

    if-ne v0, v1, :cond_1a3

    if-eqz v6, :cond_1a3

    if-nez v18, :cond_1a3

    move-object/from16 v0, p0

    iget-object v0, v0, Lcom/android/internal/policy/impl/PhoneWindowManager;->mHandler:Landroid/os/Handler;

    move-object/from16 v26, v0

    move-object/from16 v0, p0

    iget-object v0, v0, Lcom/android/internal/policy/impl/PhoneWindowManager;->mBackLongPress:Ljava/lang/Runnable;

    move-object/from16 v27, v0

    invoke-static {}, Landroid/view/ViewConfiguration;->getGlobalActionKeyTimeout()J

    move-result-wide v28

    invoke-virtual/range {v26 .. v29}, Landroid/os/Handler;->postDelayed(Ljava/lang/Runnable;J)Z

    :cond_1a3  #この行まで追加
    move-object/from16 v0, p0

    iget v0, v0, Lcom/android/internal/policy/impl/PhoneWindowManager;->mShortcutKeyPressed:I

    move/from16 v26, v0

    const/16 v27, -0x1

    move/from16 v0, v26

    move/from16 v1, v27

    if-eq v0, v1, :cond_25f

ということでソースの編集は以上になります。

■仕上げ
上記変更後、deodex_android.policy.jarを生成し、external-res.apkも含めCWMモードで端末に送信、dexopt-wrapper等の作業を行いつつ、/system/frameworkのexternal-res.apkとandroid.policy.odexを置き換えてください。
reboot後、設定→開発者向けオプションの最下部「長押しでアプリを終了」にチェックを入れ、何かのアプリを実行中に戻るキーを長押ししてみて、アプリが終了することを確認してください。

6 件のコメント:

  1. こんばんは。参考にさせてもらってます。某掲示板でMOD頂きました。掲示板で投稿できないのでこちらで御礼させて貰いました。ありがとうございます。

    返信削除
    返信
    1. 御礼なんてとんでもないです。
      もし不具合等ありましたら、お知らせいただけると幸いです。
      今後もよろしくお付き合いくださいませ。

      削除
  2. こんばんは、こちらの記事を参考にXperia TXに、再起動メニュ追加~アプリキルまで導入完成しました。
    当然ながら若干AXとは違いがありましたが、試行錯誤で今は問題なく動作しております。
    多分GXやVでもこれでいけるんじゃないかなと思います。あとは再起動時のダイアログが「電源を切ります。」になっているので、「再起動します。」に変更できれば最高です。
    ありがとうございました。

    返信削除
    返信
    1. 他機種での検証報告、恐れ入ります。
      多分イケるとは思っておりましたが、当方AXでしか検証できないので、このようなコメントはありがたいです。

      再起動時のダイアログ変更は、さらに別の場所を結構いじらないといけないと思いますが、余裕があればまた検証しておきます。

      削除
  3. こんばんは。こちらの記事を参考にXperia NX(S ROM stock)にアプリキルを導入させて頂きました。
    とても便利ですね。ありがとうございます!

    返信削除
    返信
    1. おお。
      他機種で上手くいったというお知らせがどんどんくると嬉しいですね。
      今後ともよろしくお願いいたします。

      削除