Android 关机

Android Framework层Power键关机流程(二,关机流程)

Android 关机(reboot)流程 -- "sys.powerctl"

一.framework

一.framework
1.1.按钮关机 mWindowManagerFuncs实现接口
frameworks\base\services\core\java\com\android\server\policy\GlobalActions.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    @Override
    public void onPress() {
       boolean isBox = "box".equals(SystemProperties.get("ro.target.product"));
       boolean notUseLegeacyWakeupRestartPlatform = "rk3399".equals(SystemProperties.get("ro.board.platform"));
       if(isBox && !notUseLegeacyWakeupRestartPlatform){
           wakeup_restart_set();
           Log.d(TAG,"don't shutdown here,only go to sleep for box!");
           mPowerManager.goToSleep(SystemClock.uptimeMillis());
       }else{
        // shutdown by making sure radio and power are handled accordingly.
        mWindowManagerFuncs.shutdown(false /* confirm */);
       }
    }
}

1.2.回调接口

frameworks\base\core\java\android\view\WindowManagerPolicy.java  

1
2
3
4
5
6
7
/**
 * Interface for calling back in to the window manager that is private
 * between it and the policy.
 */
public interface WindowManagerFuncs {
 
    public void shutdown(boolean confirm);

1.3.实现接口 shutdown方法
frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java

1
2
3
4
5
// Called by window manager policy.  Not exposed externally.
@Override
public void shutdown(boolean confirm) {
    ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
}

1.4.关机流程

frameworks\base\services\core\java\com\android\server\power\ShutdownThread.java  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
*请求清除关闭,等待子系统清除它们的
*状态等。必须从其UI中的环形线程中调用
*显示。
* @param context用于显示关机进度对话框的上下文。
* @param原因代码传递给android_reboot()(例如“userrequested”),或null。
* @param confirm true如果在关闭之前需要用户确认。
*/
public static void shutdown(final Context context, String reason, boolean confirm) {
    mReboot = false;
    mRebootSafeMode = false;
    mReason = reason;
    shutdownInner(context, confirm);
}

//shutdownInner 主要还是看 beginShutdownSequence这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
static void shutdownInner(final Context context, boolean confirm) {
    // ensure that only one thread is trying to power down.
    // any additional calls are just returned
    synchronized (sIsStartedGuard) {
        if (sIsStarted) {
            Log.d(TAG, "Request to shutdown already running, returning.");
            return;
        }
    }
 
    final int longPressBehavior = context.getResources().getInteger(
                    com.android.internal.R.integer.config_longPressOnPowerBehavior);
    final int resourceId = mRebootSafeMode
            ? com.android.internal.R.string.reboot_safemode_confirm
            : (longPressBehavior == 2
                    ? com.android.internal.R.string.shutdown_confirm_question
                    : com.android.internal.R.string.shutdown_confirm);
 
    Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
 
    if (confirm) {
        final CloseDialogReceiver closer = new CloseDialogReceiver(context);
        if (sConfirmDialog != null) {
            sConfirmDialog.dismiss();
        }
        sConfirmDialog = new AlertDialog.Builder(context)
                .setTitle(mRebootSafeMode
                        ? com.android.internal.R.string.reboot_safemode_title
                        : com.android.internal.R.string.power_off)
                .setMessage(resourceId)
                .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        beginShutdownSequence(context);
                    }
                })
                .setNegativeButton(com.android.internal.R.string.no, null)
                .create();
        closer.dialog = sConfirmDialog;
        sConfirmDialog.setOnDismissListener(closer);
        sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
        sConfirmDialog.show();
    } else {
        beginShutdownSequence(context);
    }
}

//beginShutdownSequence 对话框 关机动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
    // Used for shutdownanimation
private static final String SYSTEM_SHUTDOWNANIMATION_FILE = "/system/media/shutdownanimation.zip";
 
private static void beginShutdownSequence(Context context) {
    synchronized (sIsStartedGuard) {
        if (sIsStarted) {
            Log.d(TAG, "Shutdown sequence already running, returning.");
            return;
        }
        sIsStarted = true;
    }
 
    // Throw up a system dialog to indicate the device is rebooting / shutting down.
    ProgressDialog pd = new ProgressDialog(context);
 
    // Path 1: Reboot to recovery for update
    //   Condition: mReason == REBOOT_RECOVERY_UPDATE
    //
    //  Path 1a: uncrypt needed
    //   Condition: if /cache/recovery/uncrypt_file exists but
    //              /cache/recovery/block.map doesn't.
    //   UI: determinate progress bar (mRebootHasProgressBar == True)
    //
    // * Path 1a is expected to be removed once the GmsCore shipped on
    //   device always calls uncrypt prior to reboot.
    //
    //  Path 1b: uncrypt already done
    //   UI: spinning circle only (no progress bar)
    //
    // Path 2: Reboot to recovery for factory reset
    //   Condition: mReason == REBOOT_RECOVERY
    //   UI: spinning circle only (no progress bar)
    //
    // Path 3: Regular reboot / shutdown
    //   Condition: Otherwise
    //   UI: spinning circle only (no progress bar)
    if (PowerManager.REBOOT_RECOVERY_UPDATE.equals(mReason)) {
        // We need the progress bar if uncrypt will be invoked during the
        // reboot, which might be time-consuming.
        mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists()
                && !(RecoverySystem.BLOCK_MAP_FILE.exists());
        pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
        if (mRebootHasProgressBar) {
            pd.setMax(100);
            pd.setProgress(0);
            pd.setIndeterminate(false);
            pd.setProgressNumberFormat(null);
            pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            pd.setMessage(context.getText(
                        com.android.internal.R.string.reboot_to_update_prepare));
        } else {
            pd.setIndeterminate(true);
            pd.setMessage(context.getText(
                        com.android.internal.R.string.reboot_to_update_reboot));
        }
    } else if (PowerManager.REBOOT_RECOVERY.equals(mReason)) {
        // Factory reset path. Set the dialog message accordingly.
        pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
        pd.setMessage(context.getText(
                    com.android.internal.R.string.reboot_to_reset_message));
        pd.setIndeterminate(true);
    } else {
        pd.setTitle(context.getText(com.android.internal.R.string.power_off));
        pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
        pd.setIndeterminate(true);
    }
    pd.setCancelable(false);
    pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
 
    pd.show();
 
    sInstance.mProgressDialog = pd;
    sInstance.mContext = context;
    sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
 
    // make sure we never fall asleep again
    sInstance.mCpuWakeLock = null;
    try {
        sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
                PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
        sInstance.mCpuWakeLock.setReferenceCounted(false);
        sInstance.mCpuWakeLock.acquire();
    } catch (SecurityException e) {
        Log.w(TAG, "No permission to acquire wake lock", e);
        sInstance.mCpuWakeLock = null;
    }
 
    // also make sure the screen stays on for better user experience
    sInstance.mScreenWakeLock = null;
    if (sInstance.mPowerManager.isScreenOn()) {
        try {
            sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
                    PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
            sInstance.mScreenWakeLock.setReferenceCounted(false);
            sInstance.mScreenWakeLock.acquire();
        } catch (SecurityException e) {
            Log.w(TAG, "No permission to acquire wake lock", e);
            sInstance.mScreenWakeLock = null;
        }
    }
 
    // start the thread that initiates shutdown
    sInstance.mHandler = new Handler() {
    };
    sInstance.start();
}

run

1
2
3
4
5
6
7
8
public void run() {
    //一大堆代码
    if (checkAnimationFileExist()) {//跑开机动画
        start_shutdownanim();
        thaw_orien_shutdownanim();
    }      
    rebootOrShutdown(mContext, mReboot, mReason);
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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);
}

1.5.PowerManagerService 设置关机属性
frameworks\base\services\core\java\com\android\server\power\PowerManagerService.java

1
2
3
4
5
6
public static void lowLevelShutdown(String reason) {
    if (reason == null) {
        reason = "";
    }
    SystemProperties.set("sys.powerctl", "shutdown," + reason);
}

 二.kernel

2.1.system/core/rootdir/init.rc

1
2
on property:sys.powerctl=*
   powerctl ${sys.powerctl}  

2.2.system\core\init\builtins.cpp 解析属性 showdown ANDROID_RB_POWEROFF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
static int do_powerctl(const std::vector<std::string>& args) {
    const char* command = args[1].c_str();
    int len = 0;
    unsigned int cmd = 0;
    const char *reboot_target = "";
    void (*callback_on_ro_remount)(const struct mntent*) = NULL;
 
    if (strncmp(command, "shutdown", 8) == 0) {
        cmd = ANDROID_RB_POWEROFF;
        len = 8;
    } else if (strncmp(command, "reboot", 6) == 0) {
        cmd = ANDROID_RB_RESTART2;
        len = 6;
    } else {
        ERROR("powerctl: unrecognized command '%s'\n", command);
        return -EINVAL;
    }
 
    if (command[len] == ',') {
        if (cmd == ANDROID_RB_POWEROFF &&
            !strcmp(&command[len + 1], "userrequested")) {
            // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
            // Run fsck once the file system is remounted in read-only mode.
            callback_on_ro_remount = unmount_and_fsck;
        } else if (cmd == ANDROID_RB_RESTART2) {
            reboot_target = &command[len + 1];
        }
    } else if (command[len] != '\0') {
        ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
        return -EINVAL;
    }
 
    std::string timeout = property_get("ro.build.shutdown_timeout");
    unsigned int delay = 0;
 
    if (android::base::ParseUint(timeout.c_str(), &delay) && delay > 0) {
        Timer t;
        // Ask all services to terminate.
        ServiceManager::GetInstance().ForEachService(
            [] (Service* s) { s->Terminate(); });
 
        while (t.duration() < delay) {
            ServiceManager::GetInstance().ReapAnyOutstandingChildren();
 
            int service_count = 0;
            ServiceManager::GetInstance().ForEachService(
                [&service_count] (Service* s) {
                    // Count the number of services running.
                    // Exclude the console as it will ignore the SIGTERM signal
                    // and not exit.
                    // Note: SVC_CONSOLE actually means "requires console" but
                    // it is only used by the shell.
                    if (s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
                        service_count++;
                    }
                });
 
            if (service_count == 0) {
                // All terminable services terminated. We can exit early.
                break;
            }
 
            // Wait a bit before recounting the number or running services.
            usleep(kTerminateServiceDelayMicroSeconds);
        }
        NOTICE("Terminating running services took %.02f seconds", t.duration());
    }
 
    return android_reboot_with_callback(cmd, 0, reboot_target,
                                        callback_on_ro_remount);
}

2.3.看android_reboot_with_callback 这个回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int android_reboot_with_callback(
    int cmd, int flags __unused, const char *arg,
    void (*cb_on_remount)(const struct mntent*))
{
    int ret;
    remount_ro(cb_on_remount);
    switch (cmd) {
        case ANDROID_RB_RESTART:
            ret = reboot(RB_AUTOBOOT);
            break;
 
        case ANDROID_RB_POWEROFF:
            ret = reboot(RB_POWER_OFF);
            break;
 
        case ANDROID_RB_RESTART2:
            ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                           LINUX_REBOOT_CMD_RESTART2, arg);
            break;
 
        default:
            ret = -1;
    }
 
    return ret;
}
 
int android_reboot(int cmd, int flags, const char *arg)
{
    return android_reboot_with_callback(cmd, flags, arg, NULL);
}

  

 

 

posted @   CrushGirl  阅读(939)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示