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 ? "" : 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);

默认配置文件中

    
    <integer name="config_longPressOnPowerBehavior">5integer>  //可以看到默认值为5

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

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

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); 来查看配置文件

   
    <string-array translatable="false" name="config_globalActionsList">
        <item>emergencyitem>
        <item>lockdownitem>
        <item>poweritem>
        <item>restartitem>
        <item>logoutitem>
        <item>screenshotitem>
        <item>bugreportitem>
    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

你可能感兴趣的:(android,java,开发语言)