Android6.0关机流程源码分析一

_前段时间盆友问我怎么实现关机功能,于是我就开始捣鼓,第一个想法就是发送关机广播,然而遇到了些问题,一是获取不到关机权限,二是Intent.ACTION_REQUEST_SHUT_DOWN和Intent.SHUTDOWN这两个属性不对上层开放。一条路行不通就换条走吧,既然没对上层提供shutdown()函数,那就用反射的方法来取,结果还是因为没有关机权限而无法调用shutdown()函数。无奈只好决定来看下源码,分析关机流程。这里主要是分析正常的关机流程。画了个简单的调用流程图。
Android6.0关机流程源码分析一_第1张图片

接下来一步步分析。我们正常关机一般就是长按关机键,然后系统弹出个关机对话框

Android6.0关机流程源码分析一_第2张图片

选择Power Off则开启关机。

长按关机键(KEYCODE_POWER),PhoneWindowManager.java拦截该事件,做出处理

 /** {@inheritDoc} */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    if (!mSystemBooted) {
        // If we have not yet booted, don't let key events do anything.
        return 0;
    }

    final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
    final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
    final boolean canceled = event.isCanceled();
    final int keyCode = event.getKeyCode();
    switch(keycode){
        case KeyEvent.KEYCODE_POWER: {
            result &= ~ACTION_PASS_TO_USER;
            isWakeKey = false; // wake-up will be handled separately
            if (down) {
                interceptPowerKeyDown(event, interactive);
            } else {
                interceptPowerKeyUp(event, interactive, canceled);
            }
            break;
        }

        case KeyEvent.KEYCODE_SLEEP: {
            result &= ~ACTION_PASS_TO_USER;
            isWakeKey = false;
            if (!mPowerManager.isInteractive()) {
                useHapticFeedback = false; // suppress feedback if already non-interactive
            }
            if (down) {
                sleepPress(event.getEventTime());
            } else {
                sleepRelease(event.getEventTime());
            }
            break;
        }

        case KeyEvent.KEYCODE_WAKEUP: {
            result &= ~ACTION_PASS_TO_USER;
            isWakeKey = true;
            break;
        }
    }
}

down事件:

private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
    //如果电源键尚未处理,则检测是短按,长按,或多按操作中的哪一种,再决定做下一步处理。
    mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
            || mScreenshotChordVolumeUpKeyTriggered;
    if (!mPowerKeyHandled) {
        //手机处于激活(亮屏)状态
        if (interactive) {
            // When interactive, we're already awake.
            // Wait for a long press or for the button to be released to decide what to do.
            //检查是否是长按操作
            //hasLongPressOnPowerBehavior=getResolvedLongPressOnPowerBehavior() != LONG_PRESS_POWER_NOTHING;
            if (hasLongPressOnPowerBehavior()) {//true
                Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
                msg.setAsynchronous(true);
                //发送消息通知主线程处理长按操作
                mHandler.sendMessageDelayed(msg,
                        ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
            }
        } 
        //手机待机状态
        else {
            //唤醒手机
            wakeUpFromPowerKey(event.getDownTime());

            if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
                Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
                msg.setAsynchronous(true);
                mHandler.sendMessageDelayed(msg,
                        ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
                mBeganFromNonInteractive = true;
            } else {
                final int maxCount = getMaxMultiPressPowerCount();

                if (maxCount <= 1) {
                    mPowerKeyHandled = true;
                } else {
                    mBeganFromNonInteractive = true;
                }
            }
        }
    }
}

主线程接收到长按的消息并做出下一步处理

private class PolicyHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_POWER_LONG_PRESS:
                //调用powerPress()函数
                powerLongPress();
            break;
        }
    }
}

那接着来看powerLongPress()函数是如何处理长按事件的

private void powerLongPress() {
    final int behavior = getResolvedLongPressOnPowerBehavior();
    switch (behavior) {
    case LONG_PRESS_POWER_NOTHING:
        break;
    case LONG_PRESS_POWER_GLOBAL_ACTIONS:
        mPowerKeyHandled = true;
        if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
            performAuditoryFeedbackForAccessibilityIfNeed();
        }
        showGlobalActionsInternal();
        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(behavior == LONG_PRESS_POWER_SHUT_OFF);
        break;
    }
}

这里根据长按的行为behavior来分情况处理的。

情况一:LONG_PRESS_POWER_NOTHING:不处理

情况二:LONG_PRESS_POWER_GLOBAL_ACTIONS:正常关机流程

void showGlobalActionsInternal() {
    //请求ActivityManagerNative关闭系统所有窗口
    sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
    if (mGlobalActions == null) {
        //初始化GlobalActions
        mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
    }
    final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
    //显示关机对话框
    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.
        mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
    }
}

(1)sendCloseSystemWindows();//发送请求关闭系统的对话框

void sendCloseSystemWindows(String reason) {
    PhoneWindow.sendCloseSystemWindows(mContext, reason);
}

调用的是PhoneWindow.java的方法,如下:

public static void sendCloseSystemWindows(Context context, String reason) {
    if (ActivityManagerNative.isSystemReady()) {
        try {
            ActivityManagerNative.getDefault().closeSystemDialogs(reason);
        } catch (RemoteException e) {
        }
    }
}

继续调用ActivityManagerNative.java的方法,如下:

public void closeSystemDialogs(String reason) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeString(reason);
    mRemote.transact(CLOSE_SYSTEM_DIALOGS_TRANSACTION, data, reply, 0);
    reply.readException();
    data.recycle();
    reply.recycle();
}

mRemote.transact(CLOSE_SYSTEM_DIALOGS_TRANSACTION, data, reply, 0)

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException {
    switch (code) {
        case CLOSE_SYSTEM_DIALOGS_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            String reason = data.readString();
            closeSystemDialogs(reason);
            reply.writeNoException();
            return true;
        }
    }
}

(2)mGlobalActions.showDialog();//显示关机对话框

/**
 * 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();
    }
}

调用HandleShow()方法创建GlobalActionsDialog对话框

private void handleShow() {
        awakenIfNecessary();
        //创建关机对话框
        mDialog = createDialog();
        prepareDialog();

        // 对话框视图只有一个item,当手单按时回调onPress()函数
        if (mAdapter.getCount() == 1
                && mAdapter.getItem(0) instanceof SinglePressAction
                && !(mAdapter.getItem(0) instanceof LongPressAction)) {
            ((SinglePressAction) mAdapter.getItem(0)).onPress();
        } 
        //否则显示关机对话框,设置对话框参数属性
        else {
            WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
            attrs.setTitle("GlobalActions");
            mDialog.getWindow().setAttributes(attrs);
            mDialog.show();
            mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
        }
    }

/**
 * 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();
    //framework/base/core/res/res/values/config.xml(Global Actions:power、bugreport、users、reboot、airplane)
    String[] defaultActions = mContext.getResources().getStringArray(
            com.android.internal.R.array.config_globalActionsList);

    ArraySet addedKeys = new ArraySet();
    //循环给对话框的的列表添加item
    for (int i = 0; i < defaultActions.length; i++) {
        String actionKey = defaultActions[i];
        if (addedKeys.contains(actionKey)) {
            // If we already have added this, don't add it again.
            continue;
        }
        if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
            //关机
            mItems.add(new PowerAction());
        } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
            //飞行模式
            mItems.add(mAirplaneModeOn);
        } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
            if (Settings.Global.getInt(mContext.getContentResolver(),
                    Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
                mItems.add(getBugReportAction());
            }
        } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
            //静音
            if (mShowSilentToggle) {
                mItems.add(mSilentModeAction);
            }
        } else {
            Log.e(TAG, "Invalid global action key " + actionKey);
        }
        // Add here so we don't add more than one.
        addedKeys.add(actionKey);
    }

    //创建适配器
    mAdapter = new MyAdapter();

    AlertParams params = new AlertParams(mContext);
    params.mAdapter = mAdapter;
    params.mOnClickListener = this;
    params.mForceInverseBackground = true;

    //创建GlobalActionsDialog对象
    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) {
                    final Action action = mAdapter.getItem(position);
                    if (action instanceof LongPressAction) {
                        return ((LongPressAction) action).onLongPress();
                    }
                    return false;
                }
    });
    dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);

    dialog.setOnDismissListener(this);

    return dialog;
}

关机Action:PowerAction

private final class PowerAction extends SinglePressAction implements LongPressAction {
    private PowerAction() {
        super(com.android.internal.R.drawable.ic_lock_power_off,
            R.string.global_action_power_off);
    }

    @Override
    public boolean onLongPress() {
        UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
        if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
            mWindowManagerFuncs.rebootSafeMode(true);
            return true;
        }
        return false;
    }

    @Override
    public boolean showDuringKeyguard() {
        return true;
    }

    @Override
    public boolean showBeforeProvisioning() {
        return true;
    }

    @Override
    public void onPress() {
        // shutdown by making sure radio and power are handled accordingly.
        mWindowManagerFuncs.shutdown(false /* confirm */);
    }
}

onPress()和onLongpress()是分别处理单击和长按的方法,那么WindownManagerService.java实现了WindowManagerPolicy.WindowManagerFuncs接口,所以实际上调用的是WindownManagerService.java类的shutdown()、rebootSafeMode()方法。

路径:framework/base/services/core/java/com/android/server/wm/WindowManagerService.java

最终都是调用ShutdownThread.java的方法。

该类路径:framework/base/services/core/java/com/android/server/power/ShutdownThread.java

/**
 * 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);
}
/**
 * Request a reboot into safe mode.  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 rebootSafeMode(final Context context, boolean confirm) {
    UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
    if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
        return;
    }

    mReboot = true;
    mRebootSafeMode = true;
    mRebootUpdate = false;
    mRebootReason = null;

    shutdownInner(context, confirm);
}

最后调用shutdownInner()

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);
    }
}

开始进入关机流程

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 = null;

    // Path 1: Reboot to recovery and install the update
    //   Condition: mRebootReason == REBOOT_RECOVERY and mRebootUpdate == True
    //   (mRebootUpdate is set by checking if /cache/recovery/uncrypt_file exists.)
    //   UI: progress bar
    //
    // Path 2: Reboot to recovery for factory reset
    //   Condition: mRebootReason == REBOOT_RECOVERY
    //   UI: spinning circle only (no progress bar)
    //
    // Path 3: Regular reboot / shutdown
    //   Condition: Otherwise
    //   UI: spinning circle only (no progress bar)
    //检查是否是定时关机
    if(!sIsPowerOffAlarm){
        //非定时关机
        pd = new ProgressDialog(context);
        if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
            mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists();
            if (mRebootUpdate) {
                //Reboot to Recovery Progress Dialog. This is shown before it reboots to recovery
                pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
                pd.setMessage(context.getText(
                        com.android.internal.R.string.reboot_to_update_prepare));
                pd.setMax(100);
                pd.setProgressNumberFormat(null);
                pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                pd.setProgress(0);
                pd.setIndeterminate(false);
            } else {
                // Factory reset path. Set the dialog message accordingly.
                //恢复出厂设置对话框属性设置Reboot to Recovery for factory reset
                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 {
        //关机Shutdown Progress Dialog. This is shown if the user chooses to power off the phone
        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);
    }
String[] bootcmd = {"bootanimation",
"shutdown"} ;
try {
     ////执行关机动画
     Log.i(TAG, "exec the bootanimation ");
            SystemProperties.set("service.bootanim.exit", "0");
            Runtime.getRuntime().exec(bootcmd);
        } catch (Exception e){
            Log.e(TAG,"bootanimation command exe err!");
        }
    }

    //初始化关机线程
    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
    //启动关机线程
    if(sIsPowerOffAlarm){
        Looper.prepare();
        sInstance.mHandler = new Handler();
        Log.d(TAG,">>> before start <<<");
        sInstance.start();
        Looper.loop();
    }else{
        sInstance.mHandler = new Handler() {
        };

        sInstance.start();
    }
}

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.
            //用于接收关机广播,bu,不允许appcancel this,忽略
            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;
    Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    mContext.sendOrderedBroadcastAsUser(intent,
            UserHandle.ALL, null, br, mHandler, 0, null, null);
    //等待10s,前面定义的广播接收器收到关机广播时mActionDone设置为true,同时取消等待
    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;
            } else if (mRebootUpdate) {
                int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
                        BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
                sInstance.setRebootProgress(status, null);
            }
            try {
                mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
            } catch (InterruptedException e) {
            }
        }
    }
    if (mRebootUpdate) {
        sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
    }

    Log.i(TAG, "Shutting down activity manager...");

    //10s内关闭ActivityManager服务
    final IActivityManager am =
        ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
    if (am != null) {
        try {
            am.shutdown(MAX_BROADCAST_TIME);
        } catch (RemoteException e) {
        }
    }
    if (mRebootUpdate) {
        sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
    }

    Log.i(TAG, "Shutting down package manager...");

    //关掉PackageManager服务
    final PackageManagerService pm = (PackageManagerService)
        ServiceManager.getService("package");
    if (pm != null) {
        pm.shutdown();
    }
    if (mRebootUpdate) {
        sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
    }

    // Shutdown radios.12秒内关闭收音机
    shutdownRadios(MAX_RADIO_WAIT_TIME);
    if (mRebootUpdate) {
        sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
    }

    // 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.
    //20s内关闭MountService服务
    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;
            } else if (mRebootUpdate) {
                int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 *
                        (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
                        MAX_SHUTDOWN_WAIT_TIME);
                status += RADIO_STOP_PERCENT;
                sInstance.setRebootProgress(status, null);
            }
            try {
                mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
            } catch (InterruptedException e) {
            }
        }
    }
    if (mRebootUpdate) {
        sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);

        // If it's to reboot to install update, invoke uncrypt via init service.
        uncrypt();
    }
    //重启或关机
    rebootOrShutdown(mContext, mReboot, mRebootReason);
}

重启或关机

/**
 * 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
 */
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");
    } 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的方法关闭电源。
    PowerManagerService.lowLevelShutdown();
}

run()方法主要完成以下一些工作:
(1)发送关机广播ACTION_SHUTDOWN;
(2)关闭服务;
(3)关闭Radio
(4)设置关机震动
(5)调用PowerManagerService的方法关闭电源。

路径:/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

/**
 * 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() {
    SystemProperties.set("sys.powerctl", "shutdown");
}

开始进入到jni层,暂时不深入分析。

情况三、四:LONG_PRESS_POWER_SHUT_OFF:

直接调用mWindowManagerFuncs.shutdown()进行关机。

把关机源码粗略的分析一遍,大概了解了关机的流程。对于篇头说的两个Intent的属性是定义在Intent.java

public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";

public static final String
ACTION_REQUEST_SHUTDOWN = "android.intent.action.ACTION_REQUEST_SHUTDOWN";

把自己写的apk放在源码里编译,调用关机的函数shutdown()就可以实现关机功能了,当然也可以参考源码中的定时关机功能通过启动ShutdownActivity关机。

Intent sdIntent = new Intent(android.intent.action.ACTION_REQUEST_SHUTDOWN);
/*是否显示关机确认对话框 */      sdIntent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
        sdIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
启动ShutdownActivity
this.startActivity(shutdown);

你可能感兴趣的:(关机源码分析)