Android 关机流程分析-----(1)Framework层

最近遇到fastboot关机电流偏高的问题,虽然最后确认是硬件的问题,但还是顺便分析了一下android开关机的流程。总结一下,加深印象,也方便日后查阅。

Android智能手机和平板一般都有Power key,长按Power key弹出关机对话框,选择power off就会让系统关闭。关机动作从按键触发中断,linux kernel层给android framework层返回按键事件进入framework层,再从 framework层到kernel层执行关机任务。本文分析过程将分成两篇,(1)Framework层 (2)JNI和Kernel层,代码基于自己的android4.3源码。

一. 长按power key对应的handler代码:

frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java


    private final Runnable mPowerLongPress = new Runnable() {
        @Override
        public void run() {
            // The context isn't read
            if (mLongPressOnPowerBehavior < 0) {
                mLongPressOnPowerBehavior = mContext.getResources().getInteger(
                        com.android.internal.R.integer.config_longPressOnPowerBehavior);
            }
            int resolvedBehavior = mLongPressOnPowerBehavior;
            if (FactoryTest.isLongPressOnPowerOffEnabled()) {
                resolvedBehavior = LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
            }
 
            switch (resolvedBehavior) {
            case LONG_PRESS_POWER_NOTHING:
                break;
            case LONG_PRESS_POWER_GLOBAL_ACTIONS:
                mPowerKeyHandled = true;
                if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
                    performAuditoryFeedbackForAccessibilityIfNeed();
                }
                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
                showGlobalActionsDialog();
                break;
            case LONG_PRESS_POWER_SHUT_OFF:
            case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
                mPowerKeyHandled = true;
                performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
                mWindowManagerFuncs.shutdown(resolvedBehavior == LONG_PRESS_POWER_SHUT_OFF);
                break;
            }
        }
    };
run()函数中通过从mLongPressOnPowerBehavior向resolvedBehavior传入值,从而基于resolvedBehavior决定关机动作。下面是对代码的分析:

1. mLongPressOnPowerBehavior初始值为-1,所以会通过getInteger()从config_longPressOnPowerBehavior配置选项中得到mLongPressOnPowerBehavior。

    int mLongPressOnPowerBehavior = -1;
2. 检查是否是FactoryTest模式enable了长按powerkey进行关机的选项,是则LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM。

3. switch(resolvedBehavior)来决定关机动作:

LONG_PRESS_POWER_NOTHING = 0,Do nothing;
LONG_PRESS_POWER_GLOBAL_ACTIONS = 1,这是正常关机所走的流程。首先mPowerKeyHandled = ture,然后sendCloseSystemWindows()发送显示关闭系统的对话框的请求,reason是SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS,
    void sendCloseSystemWindows(String reason) {
        sendCloseSystemWindows(mContext, reason);
    }
 
    static void sendCloseSystemWindows(Context context, String reason) {
        if (ActivityManagerNative.isSystemReady()) {
            try {
                ActivityManagerNative.getDefault().closeSystemDialogs(reason);
            } catch (RemoteException e) {
            }
        }
    }
最后,showGlobalActionsDialog()会显示请求的对话框;
LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3,直接调用mWindowManagerFuncs.shutdown()进行关机,shutdown()原型:
        public void shutdown(boolean confirm);
二、显示power off对话框:showGlobalActionsDialog()

showGlobalActionsDialog()的定义:

    void showGlobalActionsDialog() {
        if (mGlobalActions == null) {
            mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
        }
        final boolean keyguardShowing = keyguardIsShowingTq();
        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
        if (keyguardShowing) {
            // since it took two seconds of long press to bring this up,
            // poke the wake lock so they have some time to see the dialog.
            mKeyguardMediator.userActivity();
        }
    }
下面是对代码的分析:

1. 这里首先new GlobalActions,注意一下GlobalActions()的后一个参数是mWindowManagerFuncs;

2. 然后会调用mGlobalActions.showDialog()来显示对话框;

3. 最后根据keyguardShowing的值,也就是是否在锁屏状态,决定是否需要userActivity。一般不会有这种情况。

显示对话框的具体调用是mGlobalActions.showDialog(),它在frameworks/base/policy/src/com/android/internal/policy/impl/GlobalActions.java中。

    /**
     * Show the global actions dialog (creating if necessary)
     * @param keyguardShowing True if keyguard is showing
     */
    public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
        mKeyguardShowing = keyguardShowing;
        mDeviceProvisioned = isDeviceProvisioned;
        if (mDialog != null) {
            mDialog.dismiss();
            mDialog = null;
            // Show delayed, so that the dismiss of the previous dialog completes
            mHandler.sendEmptyMessage(MESSAGE_SHOW);
        } else {
            handleShow();
        }
    }
再进入mGlobalActions.handleShow()中,handleShow()中调用了createDialog(),showDialog()、handleShow()和createDialog()都是GlobalActions类的私有函数。
    private void handleShow() {
        awakenIfNecessary();
        mDialog = createDialog();
        prepareDialog();
 
        WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
        attrs.setTitle("GlobalActions");
        mDialog.getWindow().setAttributes(attrs);
        mDialog.show();
        mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
    }
createDialog()是最后要显示的对话框的内容,这个函数虽然很长,但是耐心分析很快就能知道它都做了什么。
    /**
     * Create the global actions dialog.
     * @return A new dialog.
     */
    private GlobalActionsDialog createDialog() {
        // Simple toggle style if there's no vibrator, otherwise use a tri-state
        if (!mHasVibrator) {
            mSilentModeAction = new SilentModeToggleAction();
        } else {
            mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
        }
        mAirplaneModeOn = new ToggleAction(
                R.drawable.ic_lock_airplane_mode,
                R.drawable.ic_lock_airplane_mode_off,
                R.string.global_actions_toggle_airplane_mode,
                R.string.global_actions_airplane_mode_on_status,
                R.string.global_actions_airplane_mode_off_status) {
 
            void onToggle(boolean on) {
                if (mHasTelephony && Boolean.parseBoolean(
                        SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
                    mIsWaitingForEcmExit = true;
                    // Launch ECM exit dialog
                    Intent ecmDialogIntent =
                            new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
                    ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    mContext.startActivity(ecmDialogIntent);
                } else {
                    changeAirplaneModeSystemSetting(on);
                }
            }
 
            @Override
            protected void changeStateFromPress(boolean buttonOn) {
                if (!mHasTelephony) return;
 
                // In ECM mode airplane state cannot be changed
                if (!(Boolean.parseBoolean(
                        SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
                    mState = buttonOn ? State.TurningOn : State.TurningOff;
                    mAirplaneState = mState;
                }
            }
 
            public boolean showDuringKeyguard() {
                return true;
            }
 
            public boolean showBeforeProvisioning() {
                return false;
            }
        };
        onAirplaneModeChanged();
 
        mItems = new ArrayList<Action>();
 
        // first: power off
        mItems.add(
            new SinglePressAction(
                    com.android.internal.R.drawable.ic_lock_power_off,
                    R.string.global_action_power_off) {
 
                public void onPress() {
                    // shutdown by making sure radio and power are handled accordingly.
                    mWindowManagerFuncs.shutdown(true);
                }
 
                public boolean onLongPress() {
                    mWindowManagerFuncs.rebootSafeMode(true);
                    return true;
                }
 
                public boolean showDuringKeyguard() {
                    return true;
                }
 
                public boolean showBeforeProvisioning() {
                    return true;
                }
            });
 
        // next: airplane mode
        mItems.add(mAirplaneModeOn);
 
        // next: bug report, if enabled
        if (Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0) {
            mItems.add(
                new SinglePressAction(com.android.internal.R.drawable.stat_sys_adb,
                        R.string.global_action_bug_report) {
 
                    public void onPress() {
                        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
                        builder.setTitle(com.android.internal.R.string.bugreport_title);
                        builder.setMessage(com.android.internal.R.string.bugreport_message);
                        builder.setNegativeButton(com.android.internal.R.string.cancel, null);
                        builder.setPositiveButton(com.android.internal.R.string.report,
                                new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        // Add a little delay before executing, to give the
                                        // dialog a chance to go away before it takes a
                                        // screenshot.
                                        mHandler.postDelayed(new Runnable() {
                                            @Override public void run() {
                                                try {
                                                    ActivityManagerNative.getDefault()
                                                            .requestBugReport();
                                                } catch (RemoteException e) {
                                                }
                                            }
                                        }, 500);
                                    }
                                });
                        AlertDialog dialog = builder.create();
                        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
                        dialog.show();
                    }
 
                    public boolean onLongPress() {
                        return false;
                    }
 
                    public boolean showDuringKeyguard() {
                        return true;
                    }
 
                    public boolean showBeforeProvisioning() {
                        return false;
                    }
                });
        }
 
        // last: silent mode
        if (mShowSilentToggle) {
            mItems.add(mSilentModeAction);
        }
 
        // one more thing: optionally add a list of users to switch to
        if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
            addUsersToMenu(mItems);
        }
 
        mAdapter = new MyAdapter();
 
        AlertParams params = new AlertParams(mContext);
        params.mAdapter = mAdapter;
        params.mOnClickListener = this;
        params.mForceInverseBackground = true;
 
        GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params);
        dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
 
        dialog.getListView().setItemsCanFocus(true);
        dialog.getListView().setLongClickable(true);
        dialog.getListView().setOnItemLongClickListener(
                new AdapterView.OnItemLongClickListener() {
                    @Override
                    public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
                            long id) {
                        return mAdapter.getItem(position).onLongPress();
                    }
        });
        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
 
        dialog.setOnDismissListener(this);
 
        return dialog;
    }

前面函数中为稍后的各类选项做了准备工作;
first: power off,这里主要定义了两种动作,短按(onPress)和长按(onLongPress),对应的两种结果就是shutdown()和rebootSafeMode();     

                                                                                                                                 
next: airplane mode
next: bug report, if enabled 
last: silent mode 
因为本文主要分析关机流程,所以对后面几个选项就不分析了。回到handleShow()中,然后调用mDialog类中的函数做显示窗口的动作。

三、执行ShutdownThread.shutdown(),启动关机线程执行关机动作。

前面在GlobalActions.createDialog()中,当进入power off,然后onPress()的时候,mWindowManagerFuncs.shutdown(ture)将被调用。

                public void onPress() {
                    // shutdown by making sure radio and power are handled accordingly.
                    mWindowManagerFuncs.shutdown(true);
                }
看一下mWindowManagerFuncs的定义,可以看到它是一个WindowManagerFuncs接口类。
    private final WindowManagerFuncs mWindowManagerFuncs;
WindowManagerFuncs类是在/frameworks/base/core/java/android/view/WindowManagerPolicy.java中定义,它被包含在WindowManagerPolicy接口类中。
在WindowManagerFuncs类中,shutdown(boolean confirm)只给出了原型,并没有具体实现。搜索一下很容易发现真正被调用执行的是在/frameworks/base/services/java/com/android/server/wm/WindowManagerService.java中定义的shutdown(),即WindowManagerService.shutdown()。

    // Called by window manager policy.  Not exposed externally.
    @Override
    public void shutdown(boolean confirm) {
        ShutdownThread.shutdown(mContext, confirm);
    }
 
    // Called by window manager policy.  Not exposed externally.
    @Override
    public void rebootSafeMode(boolean confirm) {
        ShutdownThread.rebootSafeMode(mContext, confirm);
    }
shutdown()和rebootSafeMode()都在这里定义并会被window manager policy调用。这里已经指明真正执行关机线程的是ShutdownThread.shutdown(mContext, confirm)。
下面来看一下具体的关机流程:

ShutdownThread类在frameworks/base/services/java/com/android/server/power/ShutdownThread.java中定义,下面是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 confirm true if user confirmation is needed before shutting down.
     */
    public static void shutdown(final Context context, boolean confirm) {
        mReboot = false;
        mRebootSafeMode = false;
        shutdownInner(context, confirm);
    }
 
    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);
        }
    }
1. 注意两个参数:context 和 confirm,context用来用来显示关机进程的对话,confirm确保是true如果关机之前需要确认。这一点很容易理解。

2. 在shutdown()将会调用shutdownInner(),longPressBehavior和resourceId是用来记录一些标志。
一般情况下confirm为true,即表示确认shutdown,然后系统会显示确认shutdown的对话框,同时执行beginShutdownSequence(),启动shutdown线程。如果不需要confirm就可以shutdown,则直接执行beginShutdownSequence(),启动shutdown线程。

3. 下面看一下beginShutdownSequence()的具体流程。

    private static void beginShutdownSequence(Context context) {
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Shutdown sequence already running, returning.");
                return;
            }
            sIsStarted = true;
        }
 
        // throw up an indeterminate system dialog to indicate radio is
        // shutting down.
        ProgressDialog pd = new ProgressDialog(context);
        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.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();
    }
参数context非常重要,它继续用来显示关机进程的对话框;还要注意一点,sInstance是ShutdownThread静态类实例,也是this thread的实例,接下来还会用到很多它的函数。
    // static instance of this thread
    private static final ShutdownThread sInstance = new ShutdownThread();

ProgressDialog pd用来启动一个不明确的系统对话框来表明radio正在关闭,在Google原生系统中可以看到这样的对话框。pd的成员函数setTitle()、setMessage()、setIndeterminate()、setCancelable()会根据context内容对对话框属性进行设置,show()会显示对话框。
确保系统不会再进入睡眠。通过sInstance.mCpuWakeLock来获得wakelock,使系统不会睡眠。
保证screen不会马上黑下去,这样可以保证更好的用户体验。通过sInstance.mScreenWakeLock获得screen wakelock使屏幕长亮。
最后启动shutdown线程sInstance.start()。
四、framework层最后的一步:ShutdownThread.run()

当进程执行到sInstance.start()后,就进入了framework层最后的一步:ShutdownThread.run()。

    /**
     * Makes sure we handle the shutdown gracefully.
     * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
     */
    public void run() {
        BroadcastReceiver br = new BroadcastReceiver() {
            @Override public void onReceive(Context context, Intent intent) {
                // We don't allow apps to cancel this, so ignore the result.
                actionDone();
            }
        };
 
        /*
         * Write a system property in case the system_server reboots before we
         * get to the actual hardware restart. If that happens, we'll retry at
         * the beginning of the SystemServer startup.
         */
        {
            String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
            SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
        }
 
        /*
         * If we are rebooting into safe mode, write a system property
         * indicating so.
         */
        if (mRebootSafeMode) {
            SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
        }
 
        Log.i(TAG, "Sending shutdown broadcast...");
        
        // First send the high-level shut down broadcast.
        mActionDone = false;
        mContext.sendOrderedBroadcastAsUser(new Intent(Intent.ACTION_SHUTDOWN),
                UserHandle.ALL, null, br, mHandler, 0, null, null);
        
        final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
        synchronized (mActionDoneSync) {
            while (!mActionDone) {
                long delay = endTime - SystemClock.elapsedRealtime();
                if (delay <= 0) {
                    Log.w(TAG, "Shutdown broadcast timed out");
                    break;
                }
                try {
                    mActionDoneSync.wait(delay);
                } catch (InterruptedException e) {
                }
            }
        }
        
        Log.i(TAG, "Shutting down activity manager...");
        
        final IActivityManager am =
            ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
        if (am != null) {
            try {
                am.shutdown(MAX_BROADCAST_TIME);
            } catch (RemoteException e) {
            }
        }
 
        // Shutdown radios.
        shutdownRadios(MAX_RADIO_WAIT_TIME);
 
        // Shutdown MountService to ensure media is in a safe state
        IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
            public void onShutDownComplete(int statusCode) throws RemoteException {
                Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
                actionDone();
            }
        };
 
        Log.i(TAG, "Shutting down MountService");
 
        // Set initial variables and time out time.
        mActionDone = false;
        final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
        synchronized (mActionDoneSync) {
            try {
                final IMountService mount = IMountService.Stub.asInterface(
                        ServiceManager.checkService("mount"));
                if (mount != null) {
                    mount.shutdown(observer);
                } else {
                    Log.w(TAG, "MountService unavailable for shutdown");
                }
            } catch (Exception e) {
                Log.e(TAG, "Exception during MountService shutdown", e);
            }
            while (!mActionDone) {
                long delay = endShutTime - SystemClock.elapsedRealtime();
                if (delay <= 0) {
                    Log.w(TAG, "Shutdown wait timed out");
                    break;
                }
                try {
                    mActionDoneSync.wait(delay);
                } catch (InterruptedException e) {
                }
            }
        }
 
        rebootOrShutdown(mReboot, mRebootReason);
    }
run()函数虽然长,但是还是比较好理解的。
1. 如果分配的时间已经过去,不管radio和bluetooth状态如何都关闭。

2. 如果是rebootSafeMode,会设置一些系统属性。

3. 接下来是shutdown的流程,首先向APP层发送shutdown的广播。注意synchronized。

4. shutdown radios,调用shutdownRadios(),它也是ShutdownThread的成员函数。

5. shutdown mountservice,这样可以保证media的安全。

6. 设置初始化参数和时间。

7. rebootOrShutdown(),最后执行的shutdown流程的函数。

    /**
     * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
     * or {@link #shutdown(Context, boolean)} instead.
     *
     * @param reboot true to reboot or false to shutdown
     * @param reason reason for reboot
     */
    public static void rebootOrShutdown(boolean reboot, String reason) {
        if (reboot) {
            Log.i(TAG, "Rebooting, reason: " + reason);
            try {
                PowerManagerService.lowLevelReboot(reason);
            } catch (Exception e) {
                Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
            }
        } else if (SHUTDOWN_VIBRATE_MS > 0) {
            // vibrate before shutting down
            Vibrator vibrator = new SystemVibrator();
            try {
                vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
            } 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();
    }
}
1. 注释表明:这是由shutdown(final Context context, boolean confirm)调用的。参数reboot如果是true,则reboot,false则shutdown;参数reason就是reboot reason。
2. 如果reboot是true,则会执行PowerManagerService.lowLevelReboot(reason)的过程;否则继续shutdown的过程。文章中分析shutdown的流程,所以reboot暂时不看。

3. 接下来系统会确保shutdown之前的机器震动,从注释很容易看明白。

4. Shutdown power开始进入底层的shutdown,即进入JNI层和kernel层。

PowerManagerService.lowLevelShutdown()是在frameworks/base/services/java/com/android/server/power/PowerManagerService.java中定义的。它是一个通往下层JNI的函数。

    /**
     * Low-level function turn the device off immediately, without trying
     * to be clean.  Most people should use {@link ShutdownThread} for a clean shutdown.
     */
    public static void lowLevelShutdown() {
        nativeShutdown();
    }
nativeShutdown()就是通往下层的接口了。

上层framework层的分析就到此为止了,后续的文章将继续分析JNI层以及kernel的关机流程。
 

猜你喜欢

转载自blog.csdn.net/sdkdlwk/article/details/89392593