android silent restart QUIESCENT REBOOT

background

Android’s native boot animation needs to be processed in the project, and under certain conditions, a silent restart is required (before the Android system starts to enter the desktop, the screen remains completely in a state of no brightness). Because the rom used in the project is supported by the MTK platform, I didn't know the QUIESCENT_REBOOT mode of Android at the beginning, so I found a way to achieve this function. For details, see the blog: Q-based Android boot animation . Because the subsequent Linux enabled SELinux to enhance the permission restrictions, the original solution required relatively large changes because the permissions restricted the execution and reading and writing of files. Later, I learned the QUIESCENT_REBOOT mode of Android and used it in the project to solve the problem. Here is the principle of QUIESCENT_REBOOT mode.

Principle and process of Android Quiescent

1. PowerManager.reboot(PowerManager.REBOOT_QUIESCENT) to restart silently (local test can trigger svc power reboot quiescent through the command line)

1、代码:frameworks/base/core/java/android/os/PowerManager.java

public void reboot(String reason) {
    
    
    try {
    
    
        // mService是PowerManagerService
        mService.reboot(false, reason, true);
    } catch (RemoteException e) {
    
    
        throw e.rethrowFromSystemServer();
    }
}

2、代码frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

// True if the lights should stay off until an explicit user action.
// 这个标记记录是否静默重启
private static boolean sQuiescent;

@Override // Binder call
public void reboot(boolean confirm, String reason, boolean wait) {
    
    
    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
    ......
    final long ident = Binder.clearCallingIdentity();
    try {
    
    
        shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
    } finally {
    
    
        Binder.restoreCallingIdentity(ident);
    }
}

private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
            final String reason, boolean wait) {
    
    
    // lowLevelReboot会去解析reason并存储到SystemProperties
    if (mHandler == null || !mSystemReady) {
    
    
        if (RescueParty.isAttemptingFactoryReset()) {
    
    
            // If we're stuck in a really low-level reboot loop, and a
            // rescue party is trying to prompt the user for a factory data
            // reset, we must GET TO DA CHOPPA!
            PowerManagerService.lowLevelReboot(reason);
        } else {
    
    
            throw new IllegalStateException("Too early to call shutdown() or reboot()");
        }
    }

    // 调用ShutdownThread重启
    Runnable runnable = new Runnable() {
    
    
        @Override
        public void run() {
    
    
            synchronized (this) {
    
    
                if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
    
    
                    ShutdownThread.rebootSafeMode(getUiContext(), confirm);
                } else if (haltMode == HALT_MODE_REBOOT) {
    
    
                    ShutdownThread.reboot(getUiContext(), reason, confirm);
                } else {
    
    
                    ShutdownThread.shutdown(getUiContext(), reason, confirm);
                }
            }
        }
    };
    ......
}

public static void lowLevelReboot(String reason) {
    
    
    if (reason == null) {
    
    
        reason = "";
    }
    
    // If the reason is "quiescent", it means that the boot process should proceed
    // without turning on the screen/lights.
    // The "quiescent" property is sticky, meaning that any number
    // of subsequent reboots should honor the property until it is reset.
    if (reason.equals(PowerManager.REBOOT_QUIESCENT)) {
    
    
        sQuiescent = true;
        reason = "";
    } else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) {
    
    
        sQuiescent = true;
        reason = reason.substring(0,
              reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
    }
    
    if (sQuiescent) {
    
    
        // Pass the optional "quiescent" argument to the bootloader to let it know
        // that it should not turn the screen/lights on.
        reason = reason + ",quiescent";
    }

    // 这里会存储到系统参数
    SystemProperties.set("sys.powerctl", "reboot," + reason);
    try {
    
    
        Thread.sleep(20 * 1000L);
    } catch (InterruptedException e) {
    
    
        Thread.currentThread().interrupt();
    }
    Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
}

sQuiescent records whether to restart silently, lowLevelReboot will parse the reason and set sQuiescent to true, at the same time store the reason in SystemProperties, and finally call ShutdownThread to pass the parameter with the reason.

3. Code frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java
This class is mainly processed separately according to some conditions, such as recovery, etc. may also display pop-up windows, here are Those who are interested can watch it by themselves according to the code path. It is not used in this class of silent restart.

4. I mentioned SystemProperties.set("sys.powerctl", "reboot," + reason), which is very important here. A silent restart will store this value in a kernel-specific startup parameter, which will be picked up by the system the next time it restarts. The principle and source code of SystemProperties are involved here. I don't specifically analyze it, I know what it is used for.

5. At the same time, the kernel will also parse the reason and store another SystemProperties; the approximate process is that if the Quiescent flag of rtc is read in lk, the boot logo is not displayed, and androidboot.quiescent=1 is added to cmdline; lk -> kernel -> init, init will parse the cmdline, and parse out the androidboot.quiescent=1, and set it to ro.boot.quiescent=1, so that all places in the subsequent Android will know that this is a quiescent boot.

6. In bootanimation (frameworks/base/cmds/bootanimation), if ro.boot.quiescent=1 is read, the boot animation
code is not displayed : frameworks/base/cmds/bootanimation/BootAnimationUtil.cpp

    bool bootAnimationDisabled() {
    
    
45    char value[PROPERTY_VALUE_MAX];
46    property_get("debug.sf.nobootanimation", value, "0");
47    if (atoi(value) > 0) {
    
    
48        return true;
49    }
50
51    property_get("ro.boot.quiescent", value, "0");
52    return atoi(value) > 0;
53}

Code: frameworks/base/cmds/bootanimation/bootanimation_main.cpp

36int main()
37{
    
    
38    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
39    
      // 如果noBootAnimation,不展示开机动画
40    bool noBootAnimation = bootAnimationDisabled();
41    ALOGI_IF(noBootAnimation,  "boot animation disabled");
42    if (!noBootAnimation) {
    
    
43
44        sp<ProcessState> proc(ProcessState::self());
45        ProcessState::self()->startThreadPool();
46
47        // create the boot animation object (may take up to 200ms for 2MB zip)
48        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
49
50        waitForSurfaceFlinger();
51
52        boot->run("BootAnimation", PRIORITY_DISPLAY);
53
54        ALOGV("Boot animation set up. Joining pool.");
55
56        IPCThreadState::self()->joinThreadPool();
57    }
58    return 0;
59}

7. Here also involves the hiding of the boot log. In the kernal layer, the logo or brightness is hidden according to the startup parameters stored in the kernel. This is probably a guess, if you are interested, you can study it yourself.

Solve problems in the project

In addition to hiding the boot animation in the project, you also need to adjust the brightness to the lowest.
1. It is necessary to shield the code for the default setting of the startup scheduling. Code
: frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java

    @VisibleForTesting
    DisplayManagerService(Context context, Injector injector) {
    
    
        super(context);
        ......
        PowerManager pm = mContext.getSystemService(PowerManager.class);

        // 注释掉的为源代码,这里会取一个默认160的亮度并设置
        //mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
           
        // 这里根据静默的参数,增加判断
        if (SystemProperties.getInt("ro.boot.quiescent", 0) == 0) {
    
    
            mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
        }
        ......
    }

2. Because in addition to this place, the default brightness will be set, and the brightness will be reset by the android layer after the system is up, and it will return to the normal brightness.

Guess you like

Origin blog.csdn.net/archie_7/article/details/109285017