Android 电源键事件流程分析

这篇具有很好参考价值的文章主要介绍了Android 电源键事件流程分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Android 电源键事件流程分析

电源按键流程处理逻辑在 PhoneWindowManager.java类中的 dispatchUnhandledKey 方法中

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

从dispatchUnhandledKey方法开始分析

    @Override
    public KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags) {
        // Note: This method is only called if the initial down was unhandled.
        if (DEBUG_INPUT) {
            final KeyInterceptionInfo info =
                    mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
            final String title = info == null ? "<unknown>" : info.windowTitle;
            Slog.d(TAG, "Unhandled key: inputToken=" + focusedToken
                    + ", title=" + title
                    + ", action=" + event.getAction()
                    + ", flags=" + event.getFlags()
                    + ", keyCode=" + event.getKeyCode()
                    + ", scanCode=" + event.getScanCode()
                    + ", metaState=" + event.getMetaState()
                    + ", repeatCount=" + event.getRepeatCount()
                    + ", policyFlags=" + policyFlags);
        }

        KeyEvent fallbackEvent = null;
        if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
            final KeyCharacterMap kcm = event.getKeyCharacterMap();
            final int keyCode = event.getKeyCode();
            final int metaState = event.getMetaState();
            final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
                    && event.getRepeatCount() == 0;

            // Check for fallback actions specified by the key character map.
            final FallbackAction fallbackAction;
            if (initialDown) {
                fallbackAction = kcm.getFallbackAction(keyCode, metaState);
            } else {
                fallbackAction = mFallbackActions.get(keyCode);
            }

            if (fallbackAction != null) {
                if (DEBUG_INPUT) {
                    Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode
                            + " metaState=" + Integer.toHexString(fallbackAction.metaState));
                }

                final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
                fallbackEvent = KeyEvent.obtain(
                        event.getDownTime(), event.getEventTime(),
                        event.getAction(), fallbackAction.keyCode,
                        event.getRepeatCount(), fallbackAction.metaState,
                        event.getDeviceId(), event.getScanCode(),
                        flags, event.getSource(), event.getDisplayId(), null);

                //核心代码
                if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) {
                    fallbackEvent.recycle();
                    fallbackEvent = null;
                }

                if (initialDown) {
                    mFallbackActions.put(keyCode, fallbackAction);
                } else if (event.getAction() == KeyEvent.ACTION_UP) {
                    mFallbackActions.remove(keyCode);
                    fallbackAction.recycle();
                }
            }
        }

        if (DEBUG_INPUT) {
            if (fallbackEvent == null) {
                Slog.d(TAG, "No fallback.");
            } else {
                Slog.d(TAG, "Performing fallback: " + fallbackEvent);
            }
        }
        return fallbackEvent;
    }

关于电源按键的核心逻辑在interceptFallback方法里的interceptKeyBeforeQueueing方法里

   @Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        ...

        if (!mSystemBooted) {
            // If we have not yet booted, don't let key events do anything.
            // Exception: Wake and power key events are forwarded to PowerManager to allow it to
            // wake from quiescent mode during boot.
            if (down && (keyCode == KeyEvent.KEYCODE_POWER
                    || keyCode == KeyEvent.KEYCODE_TV_POWER)) {
                wakeUpFromPowerKey(event.getDownTime());
            } else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)
                    && isWakeKeyWhenScreenOff(keyCode)) {
                wakeUpFromWakeKey(event);
            }
            return 0;
        }

        ....

        // Handle special keys.
        switch (keyCode) {
            ...

            case KeyEvent.KEYCODE_POWER: {
                EventLogTags.writeInterceptPower(
                        KeyEvent.actionToString(event.getAction()),
                        mPowerKeyHandled ? 1 : 0,
                        mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
                // Any activity on the power button stops the accessibility shortcut
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
                    interceptPowerKeyDown(event, interactiveAndOn);
                } else {
                    interceptPowerKeyUp(event, canceled);
                }
                break;
            }
        	}
    	}
        return result;
    }

当按下的时候,执行interceptPowerKeyDown方法

private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
        // Hold a wake lock until the power key is released.
        if (!mPowerKeyWakeLock.isHeld()) {
            mPowerKeyWakeLock.acquire();
        }

        mWindowManagerFuncs.onPowerKeyDown(interactive);

        // Stop ringing or end call if configured to do so when power is pressed.
        TelecomManager telecomManager = getTelecommService();
        boolean hungUp = false;
        if (telecomManager != null) {
            if (telecomManager.isRinging()) {
                // Pressing Power while there's a ringing incoming
                // call should silence the ringer.
                telecomManager.silenceRinger();
            } else if ((mIncallPowerBehavior
                    & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
                    && telecomManager.isInCall() && interactive) {
                // Otherwise, if "Power button ends call" is enabled,
                // the Power button will hang up any current active call.
                hungUp = telecomManager.endCall();
            }
        }

        final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);

        // Inform the StatusBar; but do not allow it to consume the event.
        sendSystemKeyToStatusBarAsync(event.getKeyCode());

        // If the power key has still not yet been handled, then detect short
        // press, long press, or multi press and decide what to do.
        mPowerKeyHandled = mPowerKeyHandled || hungUp
                || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
        if (!mPowerKeyHandled) {
            if (!interactive) {
                wakeUpFromPowerKey(event.getDownTime());
            }
        } else {
            // handled by another power key policy.
            if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {
                mSingleKeyGestureDetector.reset();
            }
        }
    }

1、Android 按电源键亮屏/息屏流程

1、亮屏

1.1、PhoneWindowManager.java 中的 interceptKeyBeforeQueueing 方法
    @Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        // Handle special keys.
        switch (keyCode) {
            case KeyEvent.KEYCODE_POWER: {
                EventLogTags.writeInterceptPower(
                        KeyEvent.actionToString(event.getAction()),
                        mPowerKeyHandled ? 1 : 0,
                        mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
                // Any activity on the power button stops the accessibility shortcut
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
                    // 按下power键调用
                    interceptPowerKeyDown(event, interactiveAndOn);
                } else {
                    interceptPowerKeyUp(event, canceled);
                }
                break;
            }
        }
    }
1.2、息屏时按下power键调用 interceptPowerKeyDown() 方法
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
        // Hold a wake lock until the power key is released.
        if (!mPowerKeyWakeLock.isHeld()) {
            mPowerKeyWakeLock.acquire();
        }

        mWindowManagerFuncs.onPowerKeyDown(interactive);

        // Stop ringing or end call if configured to do so when power is pressed.
        TelecomManager telecomManager = getTelecommService();
        boolean hungUp = false;
        if (telecomManager != null) {
            if (telecomManager.isRinging()) {
                // Pressing Power while there's a ringing incoming
                // call should silence the ringer.
                telecomManager.silenceRinger();
            } else if ((mIncallPowerBehavior
                    & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
                    && telecomManager.isInCall() && interactive) {
                // Otherwise, if "Power button ends call" is enabled,
                // the Power button will hang up any current active call.
                hungUp = telecomManager.endCall();
            }
        }

        final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);

        // Inform the StatusBar; but do not allow it to consume the event.
        sendSystemKeyToStatusBarAsync(event.getKeyCode());

        // If the power key has still not yet been handled, then detect short
        // press, long press, or multi press and decide what to do.
        mPowerKeyHandled = mPowerKeyHandled || hungUp
                || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
        if (!mPowerKeyHandled) {
            if (!interactive) {
                // 表示当前未交互或者息屏状态下
                wakeUpFromPowerKey(event.getDownTime());
            }
        } else {
            // handled by another power key policy.
            if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {
                mSingleKeyGestureDetector.reset();
            }
        }
    }

1.3、wakeUpFromPowerKey()

可以看到,在按下电源键的时候,如果判断,当前是息屏状态,则执行wakeUpFromPowerKey()方法,

private void wakeUpFromPowerKey(long eventTime) {
    if (wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey,
            PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER")) {
        // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
        if (shouldWakeUpWithHomeIntent()) {
            startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ false, /*wakenFromDreams*/ true,
                    PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_POWER_BUTTON));
        }
    }
}

调用 wakeUp() 方法

private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason,
        String details) {
    final boolean theaterModeEnabled = isTheaterModeEnabled();
    if (!wakeInTheaterMode && theaterModeEnabled) {
        return false;
    }

    if (theaterModeEnabled) {
        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.THEATER_MODE_ON, 0);
    }
	
    //核心代码
    mPowerManager.wakeUp(wakeTime, reason, details);
    return true;
}
1.4、PowerManager 中的 WakeUp() 方法
// android.os.PowerManager
public void wakeUp(long time, @WakeReason int reason, String details) {
    try {
        mService.wakeUp(time, reason, details, mContext.getOpPackageName());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}
1.5、PowerManagerServicewakeUp() 方法

通过跨进程通信,我们知道真正的操作是在PowerManagerService类里的wakeUp()方法

@Override // Binder call
public void wakeUp(long eventTime, @WakeReason int reason, String details,
        String opPackageName) {
    if (eventTime > mClock.uptimeMillis()) {
        throw new IllegalArgumentException("event time must not be in the future");
    }

    mContext.enforceCallingOrSelfPermission(
            android.Manifest.permission.DEVICE_POWER, null);

    final int uid = Binder.getCallingUid();
    final long ident = Binder.clearCallingIdentity();
    try {
        wakeDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, details, uid,
                opPackageName, uid);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

调用 wakeDisplayGroup() 方法

private void wakeDisplayGroup(int groupId, long eventTime, @WakeReason int reason,
        String details, int uid, String opPackageName, int opUid) {
    synchronized (mLock) {
        if (wakeDisplayGroupNoUpdateLocked(groupId, eventTime, reason, details, uid,
                opPackageName, opUid)) {
            updatePowerStateLocked();
        }
    }
}
1.6、先分析 wakeDisplayGroupNoUpdateLocked() 方法,如果它返回true,则执行updatePowerStateLocked方法
private boolean wakeDisplayGroupNoUpdateLocked(int groupId, long eventTime,
        @WakeReason int reason, String details, int uid, String opPackageName, int opUid) {
    if (DEBUG_SPEW) {
        Slog.d(TAG, "wakeDisplayGroupNoUpdateLocked: eventTime=" + eventTime
                + ", groupId=" + groupId + ", uid=" + uid);
    }

    if (eventTime < mLastSleepTime || mForceSuspendActive || !mSystemReady) {
        return false;
    }

    final int currentState = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
    if (currentState == WAKEFULNESS_AWAKE) {
        if (!mBootCompleted && sQuiescent) {
            mDirty |= DIRTY_QUIESCENT;
            return true;
        }
        return false;
    }

    Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOnDisplay");
    try {
        Slog.i(TAG, "Powering on display group from"
                + PowerManagerInternal.wakefulnessToString(currentState)
                + " (groupId=" + groupId
                + ", uid=" + uid
                + ", reason=" + PowerManager.wakeReasonToString(reason)
                + ", details=" + details
                + ")...");
        Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId);

        //更新屏幕亮屏超时时间
        setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid,
                opPackageName, details);
        mDisplayGroupPowerStateMapper.setLastPowerOnTimeLocked(groupId, eventTime);
        mDisplayGroupPowerStateMapper.setPoweringOnLocked(groupId, true);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_POWER);
    }

    return true;
}

先来看看setWakefulnessLocked方法

@VisibleForTesting
void setWakefulnessLocked(int groupId, int wakefulness, long eventTime, int uid, int reason,
        int opUid, String opPackageName, String details) {
    if (mDisplayGroupPowerStateMapper.setWakefulnessLocked(groupId, wakefulness)) {
        mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS;
        setGlobalWakefulnessLocked(mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(),
                eventTime, reason, uid, opUid, opPackageName, details);
        if (wakefulness == WAKEFULNESS_AWAKE) {
            // Kick user activity to prevent newly awake group from timing out instantly.
            userActivityNoUpdateLocked(
                    groupId, eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid);
        }
    }
}

setGlobalWakefulnessLocked 方法

private void setGlobalWakefulnessLocked(int wakefulness, long eventTime, int reason, int uid,
        int opUid, String opPackageName, String details) {
    if (getWakefulnessLocked() == wakefulness) {
        return;
    }

    // Phase 1: Handle pre-wakefulness change bookkeeping.
    final String traceMethodName;
    switch (wakefulness) {
        case WAKEFULNESS_ASLEEP:
            traceMethodName = "reallyGoToSleep";
            Slog.i(TAG, "Sleeping (uid " + uid + ")...");
            break;

        case WAKEFULNESS_AWAKE:
            traceMethodName = "wakeUp";
            Slog.i(TAG, "Waking up from "
                    + PowerManagerInternal.wakefulnessToString(getWakefulnessLocked())
                    + " (uid=" + uid
                    + ", reason=" + PowerManager.wakeReasonToString(reason)
                    + ", details=" + details
                    + ")...");
            mLastWakeTime = eventTime;
            mLastWakeReason = reason;
            break;

        case WAKEFULNESS_DREAMING:
            traceMethodName = "nap";
            Slog.i(TAG, "Nap time (uid " + uid + ")...");
            break;

        case WAKEFULNESS_DOZING:
            traceMethodName = "goToSleep";
            Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason)
                    + " (uid " + uid + ")...");

            mLastSleepTime = eventTime;
            mLastSleepReason = reason;
            mDozeStartInProgress = true;
            break;

        default:
            throw new IllegalArgumentException("Unexpected wakefulness: " + wakefulness);
    }

    Trace.traceBegin(Trace.TRACE_TAG_POWER, traceMethodName);
    try {
        // Phase 2: Handle wakefulness change and bookkeeping.
        // Under lock, invalidate before set ensures caches won't return stale values.
        mInjector.invalidateIsInteractiveCaches();
        mWakefulnessRaw = wakefulness;
        mWakefulnessChanging = true;
        mDirty |= DIRTY_WAKEFULNESS;

        // This is only valid while we are in wakefulness dozing. Set to false otherwise.
        mDozeStartInProgress &= (getWakefulnessLocked() == WAKEFULNESS_DOZING);

        if (mNotifier != null) {
            mNotifier.onWakefulnessChangeStarted(wakefulness, reason, eventTime);
        }
        mAttentionDetector.onWakefulnessChangeStarted(wakefulness);

        // Phase 3: Handle post-wakefulness change bookkeeping.
        switch (wakefulness) {
            case WAKEFULNESS_AWAKE:
                mNotifier.onWakeUp(reason, details, uid, opPackageName, opUid);
                if (sQuiescent) {
                    mDirty |= DIRTY_QUIESCENT;
                }
                break;

            case WAKEFULNESS_DOZING:
                // Report the number of wake locks that will be cleared by going to sleep.
                int numWakeLocksCleared = 0;
                final int numWakeLocks = mWakeLocks.size();
                for (int i = 0; i < numWakeLocks; i++) {
                    final WakeLock wakeLock = mWakeLocks.get(i);
                    switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
                        case PowerManager.FULL_WAKE_LOCK:
                        case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
                        case PowerManager.SCREEN_DIM_WAKE_LOCK:
                            numWakeLocksCleared += 1;
                            break;
                    }
                }
                EventLogTags.writePowerSleepRequested(numWakeLocksCleared);
                break;
        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_POWER);
    }
}

继续往下追,查看Notifier类里的onWakefulnessChangeStarted方法

//com.android.server.power.Notifier
/**
 * Notifies that the device is changing wakefulness.
 * This function may be called even if the previous change hasn't finished in
 * which case it will assume that the state did not fully converge before the
 * next transition began and will recover accordingly.
 */
public void onWakefulnessChangeStarted(final int wakefulness, int reason, long eventTime) {
    final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
    if (DEBUG) {
        Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness
                + ", reason=" + reason + ", interactive=" + interactive);
    }

    // Tell the activity manager about changes in wakefulness, not just interactivity.
    // It needs more granularity than other components.
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            mActivityManagerInternal.onWakefulnessChanged(wakefulness);
        }
    });

    // Handle any early interactive state changes.
    // Finish pending incomplete ones from a previous cycle.
    if (mInteractive != interactive) {
        // Finish up late behaviors if needed.
        if (mInteractiveChanging) {
            handleLateInteractiveChange();
        }

        // Start input as soon as we start waking up or going to sleep.
        mInputManagerInternal.setInteractive(interactive);
        mInputMethodManagerInternal.setInteractive(interactive);

        // Notify battery stats.
        try {
            mBatteryStats.noteInteractive(interactive);
        } catch (RemoteException ex) { }
        FrameworkStatsLog.write(FrameworkStatsLog.INTERACTIVE_STATE_CHANGED,
                interactive ? FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON :
                        FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__OFF);

        // Handle early behaviors.
        mInteractive = interactive;
        mInteractiveChangeReason = reason;
        mInteractiveChangeStartTime = eventTime;
        mInteractiveChanging = true;
        handleEarlyInteractiveChange();
    }
}

继续看 handleEarlyInteractiveChange方法

/**
 * Handle early interactive state changes such as getting applications or the lock
 * screen running and ready for the user to see (such as when turning on the screen).
 */
private void handleEarlyInteractiveChange() {
    synchronized (mLock) {
        if (mInteractive) {
            // Waking up...
            mHandler.post(() -> mPolicy.startedWakingUp(mInteractiveChangeReason));

            // Send interactive broadcast.
            mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
            mPendingWakeUpBroadcast = true;
            updatePendingBroadcastLocked();
        } else {
            // Going to sleep...
            // Tell the policy that we started going to sleep.
            mHandler.post(() -> mPolicy.startedGoingToSleep(mInteractiveChangeReason));
        }
    }
}

继续看 updatePendingBroadcastLocked 方法

private void updatePendingBroadcastLocked() {
    if (!mBroadcastInProgress
            && mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN
            && (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
                    || mPendingInteractiveState != mBroadcastedInteractiveState)) {
        mBroadcastInProgress = true;
        mSuspendBlocker.acquire();
        //发送广播
        Message msg = mHandler.obtainMessage(MSG_BROADCAST);
        msg.setAsynchronous(true);
        mHandler.sendMessage(msg);
    }
}

继续看 Handler 调用的方法

private final class NotifierHandler extends Handler {

    public NotifierHandler(Looper looper) {
        super(looper, null, true /*async*/);
    }
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            ...
            case MSG_BROADCAST:
                sendNextBroadcast();
                break;
            ....
        }
    }
}

在 sendNextBroadcast() 方法中 调用了 sendWakeUpBroadcast() 方法

private void sendWakeUpBroadcast() {
    if (DEBUG) {
        Slog.d(TAG, "Sending wake up broadcast.");
    }

    if (mActivityManagerInternal.isSystemReady()) {
        mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,
                mWakeUpBroadcastDone, mHandler, 0, null, null);
    } else {
        EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
        sendNextBroadcast();
    }
}
1.7、在 分析 updatePowerStateLocked 方法,此方法目的是更新PackageManagerService全局状态
/**
 * Updates the global power state based on dirty bits recorded in mDirty.
 *
 * This is the main function that performs power state transitions.
 * We centralize them here so that we can recompute the power state completely
 * each time something important changes, and ensure that we do it the same
 * way each time.  The point is to gather all of the transition logic here.
 */
private void updatePowerStateLocked() {
    if (!mSystemReady || mDirty == 0) {
        return;
    }
    if (!Thread.holdsLock(mLock)) {
        Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked");
    }

    Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState");
    try {
        // Phase 0: Basic state updates.
        updateIsPoweredLocked(mDirty);
        updateStayOnLocked(mDirty);
        updateScreenBrightnessBoostLocked(mDirty);

        // Phase 1: Update wakefulness.
        // Loop because the wake lock and user activity computations are influenced
        // by changes in wakefulness.
        final long now = mClock.uptimeMillis();
        int dirtyPhase2 = 0;
        for (;;) {
            int dirtyPhase1 = mDirty;
            dirtyPhase2 |= dirtyPhase1;
            mDirty = 0;

            updateWakeLockSummaryLocked(dirtyPhase1);
            updateUserActivitySummaryLocked(now, dirtyPhase1);
            updateAttentiveStateLocked(now, dirtyPhase1);
            if (!updateWakefulnessLocked(dirtyPhase1)) {
                break;
            }
        }

        // Phase 2: Lock profiles that became inactive/not kept awake.
        updateProfilesLocked(now);

        // Phase 3: Update display power state.
        /**
         * 异步更新显示器电源状态。更新完成后,mDisplayReady 将设置为 true。显示控制器会发布一条消息,告诉我们实际显示电源状态何时		 * 更新,因此我们回到这里仔细检查并完成。此功能每次重新计算显示器电源状态。返回:如果显示已准备好,则为true。
         * 经过这一步后,会将系统的亮度、显示状态等全部设置完毕,此时屏幕已经亮了
         */
        final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);

        // Phase 4: Update dream state (depends on display ready signal).
        updateDreamLocked(dirtyPhase2, displayBecameReady);

        // Phase 5: Send notifications, if needed.
        finishWakefulnessChangeIfNeededLocked();

        // Phase 6: Update suspend blocker.
        // Because we might release the last suspend blocker here, we need to make sure
        // we finished everything else first!
        updateSuspendBlockerLocked();
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_POWER);
    }
}

再分析finishWakefulnessChangeIfNeededLocked() 方法,看看亮屏收尾工作都做了那些

private void finishWakefulnessChangeIfNeededLocked() {
    if (mWakefulnessChanging && mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
        if (getWakefulnessLocked() == WAKEFULNESS_DOZING
                && (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {
            return; // wait until dream has enabled dozing
        } else {
            // Doze wakelock acquired (doze started) or device is no longer dozing.
            mDozeStartInProgress = false;
        }
        if (getWakefulnessLocked() == WAKEFULNESS_DOZING
                || getWakefulnessLocked() == WAKEFULNESS_ASLEEP) {
            logSleepTimeoutRecapturedLocked();
        }
        mWakefulnessChanging = false;
        mNotifier.onWakefulnessChangeFinished();
    }
}

继续看 Notifier类里的onWakefulnessChangeFinished() 方法

//com.android.server.power.Notifier
/**
 * Notifies that the device has finished changing wakefulness.
 */
public void onWakefulnessChangeFinished() {
    if (DEBUG) {
        Slog.d(TAG, "onWakefulnessChangeFinished");
    }

    if (mInteractiveChanging) {
        mInteractiveChanging = false;
        handleLateInteractiveChange();
    }
}

看调用的handleLateInteractiveChange() 方法

/**
 * Handle late interactive state changes once they are finished so that the system can
 * finish pending transitions (such as turning the screen off) before causing
 * applications to change state visibly.
 */
private void handleLateInteractiveChange() {
    synchronized (mLock) {
        final int interactiveChangeLatency =
                (int) (SystemClock.uptimeMillis() - mInteractiveChangeStartTime);
        if (mInteractive) {
            // Finished waking up...
            mHandler.post(() -> {
                LogMaker log = new LogMaker(MetricsEvent.SCREEN);
                log.setType(MetricsEvent.TYPE_OPEN);
                log.setSubtype(WindowManagerPolicyConstants.translateWakeReasonToOnReason(
                        mInteractiveChangeReason));
                log.setLatency(interactiveChangeLatency);
                log.addTaggedData(
                        MetricsEvent.FIELD_SCREEN_WAKE_REASON, mInteractiveChangeReason);
                MetricsLogger.action(log);
                EventLogTags.writePowerScreenState(1, 0, 0, 0, interactiveChangeLatency);
                //通知PhoneWindowManager唤醒操作结束
                mPolicy.finishedWakingUp(mInteractiveChangeReason);
            });
        } else {
            .....
        }
    }
}

2、息屏

同唤醒操作,首先执行PhoneWindowManager.java中的 interceptKeyBeforeQueueing() 方法中 KEYCODE_POWER

@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    ....

    // Handle special keys.
    switch (keyCode) {
        ....

        case KeyEvent.KEYCODE_POWER: {
            EventLogTags.writeInterceptPower(
                    KeyEvent.actionToString(event.getAction()),
                    mPowerKeyHandled ? 1 : 0,
                    mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
            // Any activity on the power button stops the accessibility shortcut
            result &= ~ACTION_PASS_TO_USER;
            isWakeKey = false; // wake-up will be handled separately
            if (down) {
                interceptPowerKeyDown(event, interactiveAndOn);
            } else {
                //手指抬起
                interceptPowerKeyUp(event, canceled);
            }
            break;
        }
    }

    ....

    return result;
}

private void interceptPowerKeyUp(KeyEvent event, boolean canceled) {
    final boolean handled = canceled || mPowerKeyHandled;

    if (!handled) {
        if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) == 0) {
            // Abort possibly stuck animations only when power key up without long press case.
            mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
        }
    } else {
        // handled by single key or another power key policy.
        if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {
            mSingleKeyGestureDetector.reset();
        }
    }

    finishPowerKeyPress();
}

再 PhoneWindowManager 类中 init() 方法中调用了 initSingleKeyGestureRules() -> PowerKeyRule() -> powerPress()

private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) {
    if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
        Slog.i(TAG, "Suppressed redundant power key press while "
                + "already in the process of turning the screen on.");
        return;
    }

    final boolean interactive = Display.isOnState(mDefaultDisplay.getState());

    Slog.d(TAG, "powerPress: eventTime=" + eventTime + " interactive=" + interactive
            + " count=" + count + " beganFromNonInteractive=" + beganFromNonInteractive
            + " mShortPressOnPowerBehavior=" + mShortPressOnPowerBehavior);

    if (count == 2) {
        powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
    } else if (count == 3) {
        powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
    } else if (interactive && !beganFromNonInteractive) {
        if (mSideFpsEventHandler.onSinglePressDetected(eventTime)) {
            Slog.i(TAG, "Suppressing power key because the user is interacting with the "
                    + "fingerprint sensor");
            return;
        }
        switch (mShortPressOnPowerBehavior) {
            case SHORT_PRESS_POWER_NOTHING:
                break;
            case SHORT_PRESS_POWER_GO_TO_SLEEP:
                sleepDefaultDisplayFromPowerButton(eventTime, 0);
                break;
            case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
                sleepDefaultDisplayFromPowerButton(eventTime,
                        PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                break;
            
            .....
        }
    }
}

查看 mShortPressOnPowerBehavior 赋值

mShortPressOnPowerBehavior = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_shortPressOnPowerBehavior);

//查看配置文件
<!-- Control the behavior when the user short presses the power button.
        0 - Nothing
        1 - Go to sleep (doze)
        2 - Really go to sleep (don't doze)
        3 - Really go to sleep and go home (don't doze)
        4 - Go to home
        5 - Dismiss IME if shown. Otherwise go to home
-->
<integer name="config_shortPressOnPowerBehavior">1</integer>

执行 case SHORT_PRESS_POWER_GO_TO_SLEEP 方法,即 sleepDefaultDisplayFromPowerButton()

/**
 * Sends the default display to sleep as a result of a power button press.
 *
 * @return {@code true} if the device was sent to sleep, {@code false} if the device did not
 * sleep.
 */
//由于按下电源按钮,设备进入睡眠状态。返回:如果设备被发送到睡眠状态,则返回 true,如果睡眠被抑制,则返回 false
private boolean sleepDefaultDisplayFromPowerButton(long eventTime, int flags) {
    // Before we actually go to sleep, we check the last wakeup reason.
    // If the device very recently woke up from a gesture (like user lifting their device)
    // then ignore the sleep instruction. This is because users have developed
    // a tendency to hit the power button immediately when they pick up their device, and we
    // don't want to put the device back to sleep in those cases.
    final PowerManager.WakeData lastWakeUp = mPowerManagerInternal.getLastWakeup();
    if (lastWakeUp != null && lastWakeUp.wakeReason == PowerManager.WAKE_REASON_GESTURE) {
        final int gestureDelayMillis = Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
                POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS);
        final long now = SystemClock.uptimeMillis();
        if (mPowerButtonSuppressionDelayMillis > 0
                && (now < lastWakeUp.wakeTime + mPowerButtonSuppressionDelayMillis)) {
            Slog.i(TAG, "Sleep from power button suppressed. Time since gesture: "
                    + (now - lastWakeUp.wakeTime) + "ms");
            return false;
        }
    }

    sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
    return true;
}
private void sleepDefaultDisplay(long eventTime, int reason, int flags) {
    mRequestedOrSleepingDefaultDisplay = true;
    mPowerManager.goToSleep(eventTime, reason, flags);
}

继续往下追PowerManagerService 的goToSleep()

@Override // Binder call
public void goToSleep(long eventTime, int reason, int flags) {
    if (eventTime > mClock.uptimeMillis()) {
        throw new IllegalArgumentException("event time must not be in the future");
    }

    mContext.enforceCallingOrSelfPermission(
            android.Manifest.permission.DEVICE_POWER, null);

    final int uid = Binder.getCallingUid();
    final long ident = Binder.clearCallingIdentity();
    try {
        sleepDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, flags, uid);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

private void sleepDisplayGroup(int groupId, long eventTime, int reason, int flags,
        int uid) {
    synchronized (mLock) {
        if (sleepDisplayGroupNoUpdateLocked(groupId, eventTime, reason, flags, uid)) {
            updatePowerStateLocked();	
        }
    }
}

先分析 sleepDisplayGroupNoUpdateLocked()

private boolean sleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int reason,
        int flags, int uid) {
    if (DEBUG_SPEW) {
        Slog.d(TAG, "sleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime
                + ", groupId=" + groupId + ", reason=" + reason + ", flags=" + flags
                + ", uid=" + uid);
    }

    if (eventTime < mLastWakeTime
            || !PowerManagerInternal.isInteractive(getWakefulnessLocked())
            || !mSystemReady
            || !mBootCompleted) {
        return false;
    }

    final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId);
    if (!PowerManagerInternal.isInteractive(wakefulness)) {
        return false;
    }

    Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOffDisplay");
    try {
        reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX,
                Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN));
        Slog.i(TAG, "Powering off display group due to "
                + PowerManager.sleepReasonToString(reason) + " (groupId= " + groupId
                + ", uid= " + uid + ")...");

        mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true);
        //核心代码
        setWakefulnessLocked(groupId, WAKEFULNESS_DOZING, eventTime, uid, reason,
                /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null);
        if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
            reallySleepDisplayGroupNoUpdateLocked(groupId, eventTime, uid);
        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_POWER);
    }
    return true;
}

由setWakefulnessLocked() -> setGlobalWakefulnessLocked() -> mNotifier.onWakefulnessChangeStarted()

同wakeup一样,会走到Notifer类的onWakefulnessChangeStarted()方法

/**
 * Notifies that the device is changing wakefulness.
 * This function may be called even if the previous change hasn't finished in
 * which case it will assume that the state did not fully converge before the
 * next transition began and will recover accordingly.
 */
public void onWakefulnessChangeStarted(final int wakefulness, int reason, long eventTime) {
    final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
    if (DEBUG) {
        Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness
                + ", reason=" + reason + ", interactive=" + interactive);
    }

    // Tell the activity manager about changes in wakefulness, not just interactivity.
    // It needs more granularity than other components.
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            mActivityManagerInternal.onWakefulnessChanged(wakefulness);
        }
    });

    // Handle any early interactive state changes.
    // Finish pending incomplete ones from a previous cycle.
    if (mInteractive != interactive) {
        // Finish up late behaviors if needed.
        if (mInteractiveChanging) {
            handleLateInteractiveChange();
        }

        // Start input as soon as we start waking up or going to sleep.
        mInputManagerInternal.setInteractive(interactive);
        mInputMethodManagerInternal.setInteractive(interactive);

        // Notify battery stats.
        try {
            mBatteryStats.noteInteractive(interactive);
        } catch (RemoteException ex) { }
        FrameworkStatsLog.write(FrameworkStatsLog.INTERACTIVE_STATE_CHANGED,
                interactive ? FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON :
                        FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__OFF);

        // Handle early behaviors.
        mInteractive = interactive;
        mInteractiveChangeReason = reason;
        mInteractiveChangeStartTime = eventTime;
        mInteractiveChanging = true;
        handleEarlyInteractiveChange();
    }
}

继续看 handleErarlyInteractiveChange 方法

/**
 * Handle early interactive state changes such as getting applications or the lock
 * screen running and ready for the user to see (such as when turning on the screen).
 */
private void handleEarlyInteractiveChange() {
    synchronized (mLock) {
        if (mInteractive) {
            // Waking up...
            mHandler.post(() -> mPolicy.startedWakingUp(mInteractiveChangeReason));

            // Send interactive broadcast.
            mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
            mPendingWakeUpBroadcast = true;
            updatePendingBroadcastLocked();
        } else {
            // Going to sleep...
            // Tell the policy that we started going to sleep.
            mHandler.post(() -> mPolicy.startedGoingToSleep(mInteractiveChangeReason));
        }
    }
}

再分析 updatePowerStateLocked(), 唤醒的时候,也会走到这里

/**
 * Updates the global power state based on dirty bits recorded in mDirty.
 *
 * This is the main function that performs power state transitions.
 * We centralize them here so that we can recompute the power state completely
 * each time something important changes, and ensure that we do it the same
 * way each time.  The point is to gather all of the transition logic here.
 */
private void updatePowerStateLocked() {
    if (!mSystemReady || mDirty == 0) {
        return;
    }
    if (!Thread.holdsLock(mLock)) {
        Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked");
    }

    Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState");
    try {
        // Phase 0: Basic state updates.
        updateIsPoweredLocked(mDirty);
        updateStayOnLocked(mDirty);
        updateScreenBrightnessBoostLocked(mDirty);

        // Phase 1: Update wakefulness.
        // Loop because the wake lock and user activity computations are influenced
        // by changes in wakefulness.
        final long now = mClock.uptimeMillis();
        int dirtyPhase2 = 0;
        for (;;) {
            int dirtyPhase1 = mDirty;
            dirtyPhase2 |= dirtyPhase1;
            mDirty = 0;

            updateWakeLockSummaryLocked(dirtyPhase1);
            updateUserActivitySummaryLocked(now, dirtyPhase1);
            updateAttentiveStateLocked(now, dirtyPhase1);
            if (!updateWakefulnessLocked(dirtyPhase1)) {
                break;
            }
        }

        // Phase 2: Lock profiles that became inactive/not kept awake.
        updateProfilesLocked(now);

        // Phase 3: Update display power state.
        final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);

        // Phase 4: Update dream state (depends on display ready signal).
        updateDreamLocked(dirtyPhase2, displayBecameReady);

        // Phase 5: Send notifications, if needed.
        finishWakefulnessChangeIfNeededLocked();

        // Phase 6: Update suspend blocker.
        // Because we might release the last suspend blocker here, we need to make sure
        // we finished everything else first!
        updateSuspendBlockerLocked();
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_POWER);
    }
}

当息屏执行结束,我们分析finishWakefulnessChangeIfNeededLocked方法

private void finishWakefulnessChangeIfNeededLocked() {
    if (mWakefulnessChanging && mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) {
        if (getWakefulnessLocked() == WAKEFULNESS_DOZING
                && (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {
            return; // wait until dream has enabled dozing
        } else {
            // Doze wakelock acquired (doze started) or device is no longer dozing.
            mDozeStartInProgress = false;
        }
        if (getWakefulnessLocked() == WAKEFULNESS_DOZING
                || getWakefulnessLocked() == WAKEFULNESS_ASLEEP) {
            logSleepTimeoutRecapturedLocked();
        }
        mWakefulnessChanging = false;
        mNotifier.onWakefulnessChangeFinished();
    }
}

我们继续来分析Notifier类的onWakefulnessChangeFinished方法

/**
 * Notifies that the device has finished changing wakefulness.
 */
public void onWakefulnessChangeFinished() {
    if (DEBUG) {
        Slog.d(TAG, "onWakefulnessChangeFinished");
    }

    if (mInteractiveChanging) {
        mInteractiveChanging = false;
        handleLateInteractiveChange();
    }
}
/**
 * Handle late interactive state changes once they are finished so that the system can
 * finish pending transitions (such as turning the screen off) before causing
 * applications to change state visibly.
 */
private void handleLateInteractiveChange() {
    synchronized (mLock) {
        final int interactiveChangeLatency =
                (int) (SystemClock.uptimeMillis() - mInteractiveChangeStartTime);
        if (mInteractive) {
            // Finished waking up...
            mHandler.post(() -> {
                LogMaker log = new LogMaker(MetricsEvent.SCREEN);
                log.setType(MetricsEvent.TYPE_OPEN);
                log.setSubtype(WindowManagerPolicyConstants.translateWakeReasonToOnReason(
                        mInteractiveChangeReason));
                log.setLatency(interactiveChangeLatency);
                log.addTaggedData(
                        MetricsEvent.FIELD_SCREEN_WAKE_REASON, mInteractiveChangeReason);
                MetricsLogger.action(log);
                EventLogTags.writePowerScreenState(1, 0, 0, 0, interactiveChangeLatency);
                mPolicy.finishedWakingUp(mInteractiveChangeReason);
            });
        } else {
            // Finished going to sleep...
            // This is a good time to make transitions that we don't want the user to see,
            // such as bringing the key guard to focus.  There's no guarantee for this
            // however because the user could turn the device on again at any time.
            // Some things may need to be protected by other mechanisms that defer screen on.

            // Cancel pending user activity.
            if (mUserActivityPending) {
                mUserActivityPending = false;
                mHandler.removeMessages(MSG_USER_ACTIVITY);
            }

            // Tell the policy we finished going to sleep.
            final int offReason = WindowManagerPolicyConstants.translateSleepReasonToOffReason(
                    mInteractiveChangeReason);
            mHandler.post(() -> {
                LogMaker log = new LogMaker(MetricsEvent.SCREEN);
                log.setType(MetricsEvent.TYPE_CLOSE);
                log.setSubtype(offReason);
                log.setLatency(interactiveChangeLatency);
                log.addTaggedData(
                        MetricsEvent.FIELD_SCREEN_SLEEP_REASON, mInteractiveChangeReason);
                MetricsLogger.action(log);
                EventLogTags.writePowerScreenState(
                        0, offReason, 0, 0, interactiveChangeLatency);
                mPolicy.finishedGoingToSleep(mInteractiveChangeReason);
            });

            // Send non-interactive broadcast.
            mPendingInteractiveState = INTERACTIVE_STATE_ASLEEP;
            mPendingGoToSleepBroadcast = true;
            //发送息屏广播,和唤醒类似,就不再赘述了
            updatePendingBroadcastLocked();
        }
    }
}

好了,从我们分析源码得知
唤醒的时候,唤醒广播是在handleEarlyInteractiveChange方法,就是在通知PhoneWindowManager 去执行startedWakingUp方法,紧跟着就执行updatePendingBroadcastLocked方法,去发送唤醒广播
而在息屏的时候,在handleEarlyInteractiveChange里,我们只是通知PhoneWindowManager去执行startedGoingToSleep方法,最后在updatePowerStateLocked==>finishWakefulnessChangeIfNeededLocked==>Notifier==>onWakefulnessChangeFinished==》handleLateInteractiveChange方法里,在通知PhoneWindowManager执行finishedGoingToSleep()之后,才执行updatePendingBroadcastLocked()方法去发送息屏广播(Intent.ACTION_SCREEN_OFF),这个时机需要特别注意一下。

参考 : https://blog.csdn.net/jwg1988/article/details/123899706

2、长按Power事件

2.1、PhoneWindowManager.java -> powerLongPress() 方法
private void powerLongPress(long eventTime) {
    final int behavior = getResolvedLongPressOnPowerBehavior();
    Slog.d(TAG, "powerLongPress: eventTime=" + eventTime
            + " mLongPressOnPowerBehavior=" + mLongPressOnPowerBehavior);

    switch (behavior) {
        case LONG_PRESS_POWER_NOTHING:
            break;
        case LONG_PRESS_POWER_GLOBAL_ACTIONS:
            mPowerKeyHandled = true;
            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                    "Power - Long Press - Global Actions");
            showGlobalActions();
            break;
        case LONG_PRESS_POWER_SHUT_OFF:
        case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
            mPowerKeyHandled = true;
            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                    "Power - Long Press - Shut Off");
            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
            mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
            break;
        case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
            mPowerKeyHandled = true;
            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                    "Power - Long Press - Go To Voice Assist");
            // Some devices allow the voice assistant intent during setup (and use that intent
            // to launch something else, like Settings). So we explicitly allow that via the
            // config_allowStartActivityForLongPressOnPowerInSetup resource in config.xml.
            launchVoiceAssist(mAllowStartActivityForLongPressOnPowerDuringSetup);
            break;
        case LONG_PRESS_POWER_ASSISTANT:
            mPowerKeyHandled = true;
            performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON, false,
                    "Power - Long Press - Go To Assistant");
            final int powerKeyDeviceId = Integer.MIN_VALUE;
            launchAssistAction(null, powerKeyDeviceId, eventTime,
                    AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS);
            break;
    }
}
2.2、getResolvedLongPressOnPowerBehavior()

解析长按电源键行为的方法

private int getResolvedLongPressOnPowerBehavior() {
    if (FactoryTest.isLongPressOnPowerOffEnabled()) {
        return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
    }

    // If the config indicates the assistant behavior but the device isn't yet provisioned, show
    // global actions instead.
    if (mLongPressOnPowerBehavior == LONG_PRESS_POWER_ASSISTANT && !isDeviceProvisioned()) {
        return LONG_PRESS_POWER_GLOBAL_ACTIONS;
    }

    return mLongPressOnPowerBehavior; //最后返回这个行为
}

mLongPressOnPowerBehavior 在init()方法中调用的是资源文件

mLongPressOnPowerBehavior = mContext.getResources().getInteger(
        com.android.internal.R.integer.config_longPressOnPowerBehavior);

默认配置文件中

    <!-- Control the behavior when the user long presses the power button.
            0 - Nothing
            1 - Global actions menu
            2 - Power off (with confirmation)
            3 - Power off (without confirmation)
            4 - Go to voice assist
            5 - Go to assistant (Settings.Secure.ASSISTANT)
    -->
    <integer name="config_longPressOnPowerBehavior">5</integer>  //可以看到默认值为5

但在overlay中设置的内容默认值为1

 /os/la.qssi12/vendor/qcom/proprietary/commonsys/resource-overlay/common/Frameworks/res/values/config.xml 
<integer name="config_longPressOnPowerBehavior">1</integer>
2.3、showGlobalActions()

设置的 config_longPressOnPowerBehavior = 1;在powerLongPress方法中走 LONG_PRESS_POWER_GLOBAL_ACTIONS,调用 showGlobalActions()

@Override
public void showGlobalActions() {
    mHandler.removeMessages(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS);
    mHandler.sendEmptyMessage(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS);
}
2.4、showGlobalActionsInternal()

MSG_DISPATCH_SHOW_GLOBAL_ACTIONS -> showGlobalActionsInternal()

void showGlobalActionsInternal() {
    if (mGlobalActions == null) {
        mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
    }
    final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
    mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
    // 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);
}
2.5、showDialog()
//com.android.server.policy.GlobalActions
public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
    if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned);
    if (mGlobalActionsProvider != null && mGlobalActionsProvider.isGlobalActionsDisabled()) {
        return;
    }
    mKeyguardShowing = keyguardShowing;
    mDeviceProvisioned = deviceProvisioned;
    mShowing = true;
    if (mGlobalActionsAvailable) {
        mHandler.postDelayed(mShowTimeout, 5000);
        mGlobalActionsProvider.showGlobalActions();
    } else {
        // SysUI isn't alive, show legacy menu.
        ensureLegacyCreated();
        mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned);
    }
}
2.6、mLegacyGlobalActions.showDialog()
//com.android.server.policy.LegacyGlobalActions
/**
 * 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();
    }
}
private void handleShow() {
    awakenIfNecessary();
    mDialog = createDialog();
    prepareDialog();

    // If we only have 1 item and it's a simple press action, just do this action.
    if (mAdapter.getCount() == 1
            && mAdapter.getItem(0) instanceof SinglePressAction
            && !(mAdapter.getItem(0) instanceof LongPressAction)) {
        ((SinglePressAction) mAdapter.getItem(0)).onPress();
    } else {
        if (mDialog != null) {
            WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
            attrs.setTitle("LegacyGlobalActions");
            mDialog.getWindow().setAttributes(attrs);
            mDialog.show();
            mDialog.getWindow().getDecorView().setSystemUiVisibility(
                    View.STATUS_BAR_DISABLE_EXPAND);
        }
    }
}
2.7、createDialog() 创建dialog
/**
 * Create the global actions dialog.
 * @return A new dialog.
 */
private ActionsDialog 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) {

        @Override
        public void onToggle(boolean on) {
            if (mHasTelephony && TelephonyProperties.in_ecm_mode().orElse(false)) {
                mIsWaitingForEcmExit = true;
                // Launch ECM exit dialog
                Intent ecmDialogIntent =
                        new Intent(TelephonyManager.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 (!TelephonyProperties.in_ecm_mode().orElse(false)) {
                mState = buttonOn ? State.TurningOn : State.TurningOff;
                mAirplaneState = mState;
            }
        }

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

        @Override
        public boolean showBeforeProvisioning() {
            return false;
        }
    };
    onAirplaneModeChanged();

    mItems = new ArrayList<Action>();
    String[] defaultActions = mContext.getResources().getStringArray(
            com.android.internal.R.array.config_globalActionsList);

    ArraySet<String> addedKeys = new ArraySet<String>();
    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(mContext, mWindowManagerFuncs));
        } 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(new BugReportAction());
            }
        } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
            if (mShowSilentToggle) {
                mItems.add(mSilentModeAction);
            }
        } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
            if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
                addUsersToMenu(mItems);
            }
        } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
            mItems.add(getSettingsAction());
        } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
            mItems.add(getLockdownAction());
        } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
            mItems.add(getVoiceAssistAction());
        } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
            mItems.add(getAssistAction());
        } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
            mItems.add(new RestartAction(mContext, mWindowManagerFuncs));
        } else {
            Log.e(TAG, "Invalid global action key " + actionKey);
        }
        // Add here so we don't add more than one.
        addedKeys.add(actionKey);
    }

    if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
        mItems.add(getEmergencyAction());
    }

    mAdapter = new ActionsAdapter(mContext, mItems,
            () -> mDeviceProvisioned, () -> mKeyguardShowing);

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

    ActionsDialog dialog = new ActionsDialog(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);
    // Don't acquire soft keyboard focus, to avoid destroying state when capturing bugreports
    dialog.getWindow().setFlags(FLAG_ALT_FOCUSABLE_IM, FLAG_ALT_FOCUSABLE_IM);

    dialog.setOnDismissListener(this);

    return dialog;
}

通过 String[] defaultActions = mContext.getResources().getStringArray(
com.android.internal.R.array.config_globalActionsList); 来查看配置文件

   <!-- Defines the default set of global actions. Actions may still be disabled or hidden based
         on the current state of the device.
         Each item must be one of the following strings:
         "power" = Power off
         "settings" = An action to launch settings
         "airplane" = Airplane mode toggle
         "bugreport" = Take bug report, if available
         "silent" = silent mode
         "users" = list of users
         "restart" = restart device
         "emergency" = Launch emergency dialer
         "lockdown" = Lock down device until the user authenticates
         "logout" =  Logout the current user
         -->
    <string-array translatable="false" name="config_globalActionsList">
        <item>emergency</item>
        <item>lockdown</item>
        <item>power</item>
        <item>restart</item>
        <item>logout</item>
        <item>screenshot</item>
        <item>bugreport</item>
    </string-array>

最后展示出来,那么既然得到了关机界面,假如我们点击了其中的关机键,那么会如何执行呢

我们发现在构造弹框的时候

if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
                mItems.add(new PowerAction(mContext, mWindowManagerFuncs));
            }

那么点击item,就会执行PowerAction 的onPress()方法

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

mWindowManagerFuncs 由 PowerAction构造传进来的,往上追 ==> LegacyGlobalActions构造传入的 ==> GlobalActions构造传入 ==> PhoneWindowManager init()方法初始化的,最终我们是在WindowManagerService里找到PhoneWindowManager初始化的地方

//com.android.server.wm.WindowManagerService
private void initPolicy() {
    UiThread.getHandler().runWithScissors(new Runnable() {
        @Override
        public void run() {
            WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
            mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
        }
    }, 0);
}

我们也知道其实mWindowManagerFuncs就是一个WindowManagerService对象,那么上面的shutdown其实是执行的是WindowManagerService的shutdown

// Called by window manager policy.  Not exposed externally.
@Override
public void shutdown(boolean confirm) {
    // Pass in the UI context, since ShutdownThread requires it (to show UI).
    ShutdownThread.shutdown(ActivityThread.currentActivityThread().getSystemUiContext(),
            PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
}

继续往下追 ShutdownThread 类里的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. This must be a context
 *                suitable for displaying UI (aka Themable).
 * @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);
}

继续执行shutdownInner 方法

 private static void shutdownInner(final Context context, boolean confirm) {
     // ShutdownThread is called from many places, so best to verify here that the context passed
     // in is themed.
     context.assertRuntimeOverlayThemable();

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

     // Add checkpoint for this shutdown attempt. The user might still cancel the dialog, but
     // this point preserves the system trace of the trigger point of the ShutdownThread.
     ShutdownCheckPoints.recordCheckPoint(/* reason= */ null);

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

是否需要弹出关机确认框,如果需要则弹出,通过上面我们传入的是false,所以直接执行

2.8、beginShutdownSequence(context);
private static void beginShutdownSequence(Context context) {
    synchronized (sIsStartedGuard) {
        if (sIsStarted) {
            Log.d(TAG, "Shutdown sequence already running, returning.");
            return;
        }
        sIsStarted = true;
    }

    sInstance.mProgressDialog = showShutdownDialog(context);
    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;
        }
    }

    if (SecurityLog.isLoggingEnabled()) {
        SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
    }

    // start the thread that initiates shutdown
    sInstance.mHandler = new Handler() {
    };
    sInstance.start();
}

首先初始化一个关机页面showShutdownDialog,弹出页面,然后sInstance.start();执行真正的关机操作

/**
 * Makes sure we handle the shutdown gracefully.
 * Shuts off power regardless of radio state if the allotted time has passed.
 */
public void run() {
    TimingsTraceLog shutdownTimingLog = newTimingsLog();
    shutdownTimingLog.traceBegin("SystemServerShutdown");
    metricShutdownStart();
    metricStarted(METRIC_SYSTEM_SERVER);

    // Start dumping check points for this shutdown in a separate thread.
    Thread dumpCheckPointsThread = ShutdownCheckPoints.newDumpThread(
            new File(CHECK_POINTS_FILE_BASENAME));
    dumpCheckPointsThread.start();

    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") + (mReason != null ? mReason : "");
        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");
    }

    shutdownTimingLog.traceBegin("DumpPreRebootInfo");
    try {
        Slog.i(TAG, "Logging pre-reboot information...");
        PreRebootLogger.log(mContext);
    } catch (Exception e) {
        Slog.e(TAG, "Failed to log pre-reboot information", e);
    }
    shutdownTimingLog.traceEnd(); // DumpPreRebootInfo

    metricStarted(METRIC_SEND_BROADCAST);
    shutdownTimingLog.traceBegin("SendShutdownBroadcast");
    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 | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
    mContext.sendOrderedBroadcastAsUser(intent,
            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;
            } else if (mRebootHasProgressBar) {
                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, ACTION_DONE_POLL_WAIT_MS));
            } catch (InterruptedException e) {
            }
        }
    }
    if (mRebootHasProgressBar) {
        sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
    }
    shutdownTimingLog.traceEnd(); // SendShutdownBroadcast
    metricEnded(METRIC_SEND_BROADCAST);

    Log.i(TAG, "Shutting down activity manager...");
    shutdownTimingLog.traceBegin("ShutdownActivityManager");
    metricStarted(METRIC_AM);

    final IActivityManager am =
            IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
    if (am != null) {
        try {
            am.shutdown(MAX_BROADCAST_TIME);
        } catch (RemoteException e) {
        }
    }
    if (mRebootHasProgressBar) {
        sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
    }
    shutdownTimingLog.traceEnd();// ShutdownActivityManager
    metricEnded(METRIC_AM);

    Log.i(TAG, "Shutting down package manager...");
    shutdownTimingLog.traceBegin("ShutdownPackageManager");
    metricStarted(METRIC_PM);

    final PackageManagerService pm = (PackageManagerService)
        ServiceManager.getService("package");
    if (pm != null) {
        pm.shutdown();
    }
    if (mRebootHasProgressBar) {
        sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
    }
    shutdownTimingLog.traceEnd(); // ShutdownPackageManager
    metricEnded(METRIC_PM);

    // Shutdown radios.
    shutdownTimingLog.traceBegin("ShutdownRadios");
    metricStarted(METRIC_RADIOS);
    shutdownRadios(MAX_RADIO_WAIT_TIME);
    if (mRebootHasProgressBar) {
        sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
    }
    shutdownTimingLog.traceEnd(); // ShutdownRadios
    metricEnded(METRIC_RADIOS);

    if (mRebootHasProgressBar) {
        sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);

        // If it's to reboot to install an update and uncrypt hasn't been
        // done yet, trigger it now.
        uncrypt();
    }

    // Wait for the check points dump thread to finish, or kill it if not finished in time.
    shutdownTimingLog.traceBegin("ShutdownCheckPointsDumpWait");
    try {
        dumpCheckPointsThread.join(MAX_CHECK_POINTS_DUMP_WAIT_TIME);
    } catch (InterruptedException ex) {
    }
    shutdownTimingLog.traceEnd(); // ShutdownCheckPointsDumpWait

    shutdownTimingLog.traceEnd(); // SystemServerShutdown
    metricEnded(METRIC_SYSTEM_SERVER);
    saveMetrics(mReboot, mReason);
    // Remaining work will be done by init, including vold shutdown
    rebootOrShutdown(mContext, mReboot, mReason);
}

可以看到发出Intent.ACTION_SHUTDOWN广播,并且执行关机操作。

总结:

点击电源键,执行PhoneWindowManager里的dispatchUnhandledKey方法

然后在dispatchUnhandledKey进行处理,当判断是长按事件时,会执行powerLongPress方法

然后在执行showGlobalActionsInternal()弹出(飞行模式,关机,重启等)

在我们点击关机的时候,会执行PowerAction的onPress方法,执行WindowManagerService的shutdown方法,进而执行ShutdownThread的shutdown方法,在里面会执行真正的关机操作,并且可以看到,系统在关机之前会发出一个Intent.ACTION_SHUTDOWN广播后执行关机。

链接:https://blog.csdn.net/jwg1988/article/details/123631476?spm=1001.2014.3001.5501文章来源地址https://www.toymoban.com/news/detail-463453.html

到了这里,关于Android 电源键事件流程分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android系统启动流程分析

           当按下Android系统的开机电源按键时候,硬件会触发引导芯片,执行预定义的代码,然后加载引导程序(BootLoader)到RAM,Bootloader是Android系统起来前第一个程序,主要用来拉起Android系统程序,Android系统被拉起首先肯定会启动Linux内核。 备注: 我们再刷机时候,经常看到

    2024年02月15日
    浏览(32)
  • Android分屏流程分析

    本文基于Android 11。 SystemUI模块中的Divider管理着所有关于分屏的对象: DividerView(分屏分割线,分屏显示界面) SplitScreenTaskOrganizer(分屏Task组织者,分屏逻辑) 这里重点关注分屏逻辑实现SplitScreenTaskOrganizer。 Devider类实现了DisplayController.OnDisplaysChangedListener,系统启动后回调

    2024年02月10日
    浏览(32)
  • Android SystemServer 启动流程分析

    和你一起终身学 习,这里是程序员Android 经典好文推荐,通过阅读本文,您将收获以下知识点: 一、SystemServer 启动的服务有哪些 二、SystemServer启动总体流程概述 三、SystemServer 如何启动,是谁启动的? 四、 SystemServer 启动入门 main 方法 五、SystemServer Run 方法初始与启动 六、

    2024年02月13日
    浏览(32)
  • Android Audio音量设置原理流程分析

    本篇文章主要介绍Android音量设置从App应用层到framework层执行流程,以及相关的细节和原理分析,建议在阅读此文章前去看博主的混音理论篇的声音的音量属性和声音相关公式的推导章节,这对阅读时理解音量等级、音量分贝计算有着很大的益处;如果阅读时发现文章有错误,

    2024年02月15日
    浏览(37)
  • Android10.0 人脸解锁流程分析

    人脸解锁概述 人脸解锁即用户通过注视设备的正面方便地解锁手机或平板。Android 10 为支持人脸解锁的设备在人脸认证期间添加了一个新的可以安全处理相机帧、保持隐私与安全的人脸认证栈的支持,也为安全合规地启用集成交易的应用(网上银行或其他服务)提供了一种容

    2024年02月03日
    浏览(36)
  • android framework之Applicataion启动流程分析

    Application启动流程框架分析 启动方式一:通过Launcher启动app 启动方式二:在某一个app里启动第二个app的Activity. 以上两种方式均可触发app进程的启动。但无论哪种方式,最终通过通过调用AMS的startActivity()来启动application的。    根据上图分析, 要启动一个Application,需要涉及五

    2024年02月11日
    浏览(35)
  • systemserver的inputdispatcher直接产生CANCEL事件原理分析-讨厌的android触摸面试题

    上一个blog已经重点讲解了app层面自己产生的Cancel触摸事件,大概产生的原理如下: 上一个blog地址:https://blog.csdn.net/learnframework/article/details/124086882 即可以看出来,在服务端systemserver其实传递的触摸事件依然是move,只是move事件到了app端后,由于app端自己的业务把这个传递的

    2024年02月09日
    浏览(30)
  • 【Android12】Monkey压力测试源码执行流程分析

    Monkey是Android提供的用于应用程序自动化测试、压力测试的测试工具。 其源码路径(Android12)位于 部署形式为Java Binary 通过Monkey,可以模拟用户的Touch(单指、多指、手势)、按键(key)事件等,检测应用程序发生的ANR、Crash事件,并收集相关Debug信息等。 例如测试应用com.packa

    2024年03月22日
    浏览(31)
  • Android 13 网络 Adb相关流程深入分析研究

    通过代码分析发现Android13 上对 网络adb 进行了限制! Android13原生代码要求:必现连接上某个wifi,才能进行adb ,并且切换wifi或者关闭wifi都是会停止adb。 并且Android13 上 wifi adb 端口号每次是变化的,这个也是很不方便的! 如果要做成Android11 或者之前一样,设备连接WiFi/有线网

    2024年02月09日
    浏览(37)
  • Android9.0 系统Framework发送通知流程分析

      在android 9.0的系统rom定制化开发中,在systemui中一个重要的内容就是系统通知的展示,在状态栏展示系统发送通知的图标,而在 系统下拉通知栏中展示接收到的系统发送过来的通知,所以说对系统framework中发送通知的流程分析很重要,接下来就来分析下系统 通知从framework到

    2024年02月02日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包