Android rom開發:長按Power鍵關機/重啓優化,告別長時間等待

最近在項目上碰到了這樣的問題:在某個apk界面長按power鍵來選擇關機或者重啓,apk會出現重啓現象,並且更加尷尬的是,在另外的方案上面對比後發現沒有問題,明明白白地顯示這是系統的鍋。
這裏寫圖片描述
好吧,改!仔細研究關機/重啓的相關源碼後,修改了部分邏輯,問題解決。那就用一篇博客來記錄下踩過的這個坑吧。

本文切入點爲關機/重啓在framework層的邏輯。我們重點關注兩個類:PhoneWindowManager.java ShutdownThread.java

具體代碼調用這裏不作講解,有興趣的請自行查閱源碼,大體流程是:
接收到power長按事件—>powerLongPress()—>彈出對話框—>選擇關機/重啓選項—>執行關機/重啓

閱讀源碼後發現,關機過程的主要實現在ShutdownThread.java裏面,重點是以下幾個方法:

1.shutdown方法

    /**
     * Request a clean shutdown, waiting for subsystems to clean up their
     * state etc.  Must be called from a Looper thread in which its UI
     * is shown.
     *
     * @param context Context used to display the shutdown progress dialog.
     * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
     * @param confirm true if user confirmation is needed before shutting down.
     */
    public static void shutdown(final Context context, String reason, boolean confirm) {
        mReboot = false;
        mRebootSafeMode = false;
        mReason = reason;
        shutdownInner(context, confirm);
    }

2.reboot方法

    /**
     * Request a clean shutdown, waiting for subsystems to clean up their
     * state etc.  Must be called from a Looper thread in which its UI
     * is shown.
     *
     * @param context Context used to display the shutdown progress dialog.
     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
     * @param confirm true if user confirmation is needed before shutting down.
     */
    public static void reboot(final Context context, String reason, boolean confirm) {
        mReboot = true;
        mRebootSafeMode = false;
        mRebootHasProgressBar = false;
        mReason = reason;
        shutdownInner(context, confirm);
    }

3.rebootOrShutdown方法

    /**
     * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
     * or {@link #shutdown(Context, boolean)} instead.
     *
     * @param context Context used to vibrate or null without vibration
     * @param reboot true to reboot or false to shutdown
     * @param reason reason for reboot/shutdown
     */
    public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
        if (reboot) {
            Log.i(TAG, "Rebooting, reason: " + reason);
            PowerManagerService.lowLevelReboot(reason);
            Log.e(TAG, "Reboot failed, will attempt shutdown instead");
            reason = null;
        } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
            // vibrate before shutting down
            Vibrator vibrator = new SystemVibrator(context);
            try {
                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
            } catch (Exception e) {
                // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                Log.w(TAG, "Failed to vibrate during shutdown.", e);
            }

            // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
            try {
                Thread.sleep(SHUTDOWN_VIBRATE_MS);
            } catch (InterruptedException unused) {
            }
        }

        // Shutdown power
        Log.i(TAG, "Performing low-level shutdown...");
        PowerManagerService.lowLevelShutdown(reason);
    }

關機/重啓主要做的一些工作:
發送關機廣播
關閉AMS
關閉PMS
關閉MountService
PowerManagerService調用內核實現關機/重啓

敲黑板,重點來了!!!
這裏寫圖片描述
通過抓log發現,在關閉MountService的時候,進程殺死後重啓了,oh shit
這裏寫圖片描述
爲什麼會是在這個時候重啓?看起來好像跟SD卡有關聯。繼續百度,一下子百度不到就變換着方式百度。。。。。。
這裏寫圖片描述
終於功夫不負有心人,拜讀了大牛的文章後得到了解答,原來在關閉MountService的時候,如果有進程仍然在對SD卡進行操作(如:記錄日誌),佔據SD卡的進程通常比較頑固,因此問題來了,這個進程在殺死之後可能會立刻重啓,重啓的次數可能是1、2、3、4次,於是乎就有這個惡性循環:殺死-重啓-再殺死-再重啓。。。於是乎,你看到的現象就是一閃一閃
這裏寫圖片描述

關個機居然也這麼糟心,浪費時間就算了,還一閃一閃,根本不能忍!

問題確認了,接下來就是how to fix。關機這裏,上層有這麼多操作了,遇到頑固進程不好使,咋整?簡單粗暴點,試試跳過某些操作,直接進rebootOrShutdown調用底層,上層殺不掉,底層直接秒殺?說幹就幹,打開ShutdownThread.java源碼,屏蔽掉什麼顯示彈框,發廣播,關閉這服務那服務的邏輯,編譯固件之後驗證。

結果:秒殺關機/重啓,時間還縮短了不少
這裏寫圖片描述

直接貼patch:

--- a/frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
@@ -251,7 +251,7 @@ public final class ShutdownThread extends Thread {
             }
             sIsStarted = true;
         }
-
+/*forlan modified to reboot and shutdown more quickly start
         // Throw up a system dialog to indicate the device is rebooting / shutting down.
         ProgressDialog pd = new ProgressDialog(context);

@@ -342,7 +342,7 @@ public final class ShutdownThread extends Thread {
                 sInstance.mScreenWakeLock = null;
             }
         }
-
+forlan modified to reboot and shutdown more quickly end*/
         // start the thread that initiates shutdown
         sInstance.mHandler = new Handler() {
         };
@@ -387,7 +387,7 @@ public final class ShutdownThread extends Thread {
         }

         Log.i(TAG, "Sending shutdown broadcast...");
-
+/*forlan modified to reboot and shutdown more quickly start
         // First send the high-level shut down broadcast.
         mActionDone = false;
         Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
@@ -499,7 +499,7 @@ public final class ShutdownThread extends Thread {
             // done yet, trigger it now.
             uncrypt();
         }
-
+forlan modified to reboot and shutdown more quickly end*/
         rebootOrShutdown(mContext, mReboot, mReason);
     }

PS:此方法有什麼不足之處或者有其他更好的方法,歡迎各位評論區賜教,不勝感謝

**********************華麗麗的分割線**********************

2018.8.25更新:

有坑!有坑!這裏寫圖片描述
採用上述改法之後,第三方採用以下方法進系統OTA升級會出錯:

public static void rebootInstallPackage(final Context context, final File packageFile) {
    Thread thr = new Thread("Reboot") {
        @Override
        public void run() {
            try {
                RecoverySystem.installPackage(context, packageFile);
            } catch (IOException e) {
                Logger.e("forlan debug IOException rebootInstallPackage " + e);
            }
        }
    };
    thr.start();
}

recovery裏面報錯,找不到/cache/recovery/block.map:

[    2.093052] Supported API: 3
[    2.104192] charge_status 1, charged 1, status -2, capacity -9223372036854775808
[    2.161059] Finding update package...
[    2.237453] I:Update location: @/cache/recovery/block.map
[    2.237512] Opening update package...
[    2.262859] sysutil: Unable to open '/cache/recovery/block.map': No such file or directory
[    2.262915] E:failed to map file
[    2.287740] W:failed to read uncrypt status: No such file or directory
[    2.287988] I:@/cache/recovery/block.map
[    2.288000] 0
[    2.288006] time_total: 0
[    2.288012] retry: 0
[    2.288017]
[    2.288043] Installation aborted.
[    2.387535] I:Saving locale "zh_CN"

分析:

應該是在關閉MountService的時候對cache有操作,由於屏蔽了這部分代碼,因此沒有操作,所以出錯。

最佳改法:

把MAX_SHUTDOWN_WAIT_TIME由20s改成10s

private static final int MAX_SHUTDOWN_WAIT_TIME = 10*1000;//forlan modified to 10s for reboot and shutdown more quickly

親測有效。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章