PowerManagerService第三讲之灭屏

本文详细探讨了Android系统中PowerManagerService的灭屏机制,包括Power键灭屏、超时灭屏和Psensor灭屏。通过分析源码,解释了goToSleep方法在不同场景下的调用,并梳理了从用户操作到系统响应的完整流程。
摘要由CSDN通过智能技术生成

一.灭屏简述

先来讲灭屏,灭屏的方式一般有如下几种:

1.用户按Power键灭屏;
2.手机自动放置一段时间后超时灭屏;
3.Sensor灭屏。

灭屏的原因在PowerManager类中的sleepReasonToString方法有列出常见的:

    /**
     * @hide
     */
    public static String sleepReasonToString(int sleepReason) {
   
        switch (sleepReason) {
   
            case GO_TO_SLEEP_REASON_APPLICATION: return "application";
            case GO_TO_SLEEP_REASON_DEVICE_ADMIN: return "device_admin";
            case GO_TO_SLEEP_REASON_TIMEOUT: return "timeout";
            case GO_TO_SLEEP_REASON_LID_SWITCH: return "lid_switch";
            case GO_TO_SLEEP_REASON_POWER_BUTTON: return "power_button";
            case GO_TO_SLEEP_REASON_HDMI: return "hdmi";
            case GO_TO_SLEEP_REASON_SLEEP_BUTTON: return "sleep_button";
            case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility";
            case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend";
            default: return Integer.toString(sleepReason);
        }
    }

当触发灭屏的时候,其实是调用的PowerManager中的goToSleep方法。
这个方法共有两个:一个是只传参发出睡眠请求的时间time;另一是传三个参数:睡眠请求的时间time、灭屏原因reason、WakeLock的flag:

    public void goToSleep(long time) {
   
        goToSleep(time, GO_TO_SLEEP_REASON_APPLICATION, 0);
    }

    @UnsupportedAppUsage
    public void goToSleep(long time, int reason, int flags) {
   
        try {
   
            mService.goToSleep(time, reason, flags);
        } catch (RemoteException e) {
   
            throw e.rethrowFromSystemServer();
        }
    }

最终调用到PowerManagerSevice的goToSleep方法上:

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

            // 检查DEVICE_POWER权限
            mContext.enforceCallingOrSelfPermission(
                    android.Manifest.permission.DEVICE_POWER, null);

            // 线程id
            final int uid = Binder.getCallingUid();
            final long ident = Binder.clearCallingIdentity();
            try {
   
                goToSleepInternal(eventTime, reason, flags, uid);
            } finally {
   
                Binder.restoreCallingIdentity(ident);
            }
        }

接着看goToSleepInternal的实现:

    private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
   
        // 加上锁
        synchronized (mLock) {
   
            // 判断goToSleepNoUpdateLocked的返回值
            if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
   
                // 更新Powerstate
                updatePowerStateLocked();
            }
        }
    }

先看goToSleepNoUpdateLocked:

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

        if (eventTime < mLastWakeTime
                || mWakefulness == WAKEFULNESS_ASLEEP
                || mWakefulness == WAKEFULNESS_DOZING
                || !mBootCompleted || !mSystemReady) {
   
            return false;
        }

        Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep");
        try {
   
            // 获取灭屏的原因
            reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX,
                    Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN));
            Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason)
                    + " (uid " + uid + ")...");

            mLastSleepTime = eventTime;
            mLastSleepReason = reason;
            mSandmanSummoned = true;
            // 通知设备更改唤醒状态
            setWakefulnessLocked(WAKEFULNESS_DOZING, reason, eventTime);

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

            // Skip dozing if requested.
            // 跳过dozing状态
            if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
   
                // 调用reallyGoToSleepNoUpdateLocked函数
                reallyGoToSleepNoUpdateLocked(eventTime, uid);
            }
        } finally {
   
            Trace.traceEnd(Trace.TRACE_TAG_POWER);
        }
        return true;
    }

最后来看PowerManagerService的核心方法updatePowerStateLocked方法,关键的地方Google都有注释说明了,来看源码实现:

    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.
            // 阶段0:基础状态的更新
            // 更新mIsPowered的值,ture表示设备插入电源
            updateIsPoweredLocked(mDirty);
            // 更新mStayOn的值,true表示设备应保持打开状态
            updateStayOnLocked(mDirty);
            // 更新屏幕亮度
            updateScreenBrightnessBoostLocked(mDirty);

            // Phase 1: Update wakefulness.
            // Loop because the wake lock and user activity computations are influenced
            // by changes in wakefulness.
            // 阶段1:更新唤醒状态
            final long now = SystemClock.uptimeMillis();
            int dirtyPhase2 = 0;
            // 死循环
            for (;;) {
   
                int dirtyPhase1 = mDirty;
                dirtyPhase2 |= dirtyPhase1;
                mDirty = 0;

                // 更新mWakeLockSummary的值以汇总所有活动唤醒锁的状态
                updateWakeLockSummaryLocked(dirtyPhase1);
                // 更新mUserActivitySummary的值以汇总请求的用户
                updateUserActivitySummaryLocked(now, dirtyPhase1);
                // 更新设备的唤醒状态,如果返回true表示wakefulness有变化
                if (!updateWakefulnessLocked(dirtyPhase1)) {
   
                    break;
                }
            }

            // Phase 2: Lock profiles that became inactive/not kept awake.
            // 阶段2:锁定已停用/未唤醒的配置文件
            // 检查配置文件超时并通知应锁定的配置文件
            updateProfilesLocked(now);

            // Phase 3: Update display power state.
            // 阶段3:更新显示状态
            // 异步更新显示状态
            // 更新完成之后,mDisplayReady将设置为true。方法返回为true,表示显示准备就绪
            final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);

            // Phase 4: Update dream state (depends on display ready signal).
            // 阶段4:更新dream状态(取决于显示就绪信号)
            // 异步更新
            updateDreamLocked(dirtyPhase2, displayBecameReady);

            // Phase 5: Send notifications, if needed.
            // 阶段5:如果需要发送通知
            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!
            // 更新暂停阻止程序
            // 更新使CPU保持活动状态的暂停阻塞程序
            updateSuspendBlockerLocked();
        } finally {
   
            Trace.traceEnd(Trace.TRACE_TAG_POWER);
        }
    }

二.常见的灭屏方式

1.Power键灭屏

Power键的按键实现从native层到java层的整个流程我们后续会介绍。这里从java层之上开始看。

流程图如下:

在这里插入图片描述

具体的代码实现分析如下:

InputManagerService类中interceptKeyBeforeQueueing方法:

    // Native callback.
    private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
   
        return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
    }

调用WindowManagerCallbacks接口的interceptKeyBeforeQueueing方法,来看接口实现类InputMonitor:

    @Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
   
        return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
    }

调用的接口WindowManagerPolicy的interceptKeyBeforeQueueing方法,来看实现类是PhoneWindowManager类:

    @Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
   
        if (!mSystemBooted) {
   
            // If we have not yet booted, don't let key events do anything.
            return 0;
        }
        
        ......
        // Handle special keys.
        switch (keyCode) {
   
            case KeyEvent.KEYCODE_BACK: {
   
                if (down) {
   
                    interceptBackKeyDown();
                } else {
   
                    boolean handled = interceptBackKeyUp(event);

                    // Don't pass back press to app if we've already handled it via long press
                    if (handled) {
   
                        result &= ~ACTION_PASS_TO_USER;
                    }
                }
                break;
            }

            ......

            case KeyEvent.KEYCODE_POWER: {
   
                EventLogTags.writeInterceptPower(
                        KeyEvent.actionToString(event.getAction()),
                        mPowerKeyHandled ? 1 : 0, mPowerKeyPressCounter);
                // Any activity on the power button stops the accessibility shortcut
                cancelPendingAccessibilityShortcutAction();
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
   
                    interceptPowerKeyDown(event, interactive)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值