Android 手机灭屏流程分析详解

作者: 程序员Android1 | 来源:发表于2019-01-14 17:54 被阅读11次

    极力推荐Android 开发大总结文章:欢迎收藏
    程序员Android 力荐 ,Android 开发者需要的必备技能

    本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:

    1.前言
    2.Power键灭屏
    3.超时灭屏
    4.PSensor灭屏


    PowerManagerService 之前系列文章请参考如下
    1.PowerManagerService分析(一)之PMS启动
    2.PowerManagerService分析(二)之updatePowerStateLocked()核心
    3.PowerManagerService分析(三)之WakeLock机制
    4.Android手机亮屏流程分析

    前言

    在之前的PMS文章分析中知道,PMS中定义了四种屏幕状态:

    • Awake状态:表示唤醒状态
    • Dream状态:表示处于屏保状态
    • Doze状态:表示处于Doze状态
    • Asleep状态:表示处于休眠状态

    Power键灭屏

    power键灭屏时,会在PhoneWindowManager中处理按键事件后,调用到PMSgotoSleep()进行灭屏处理,下面直接看看PhoneWindowManger中对Power键灭屏的处理以及和PMS的交互。

    在按power后,PWS中如下:

    case KeyEvent.KEYCODE_POWER: {
        .......
        if (down) {//按下时
            //处理按下事件
            interceptPowerKeyDown(event, interactive);
        } else //抬起时
            //处理抬起事件
            interceptPowerKeyUp(event, interactive, canceled);
        }
        break;
    }
    
    

    在处理PowerinterceptPowerKeyUp抬起事件时,开始了灭屏流程:

        private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
       
        .......
            if (!handled) {
              
                // No other actions.  Handle it immediately.开始灭屏流程
                powerPress(eventTime, interactive, mPowerKeyPressCounter);
            }
    
            // Done.  Reset our state.
            finishPowerKeyPress();
        }
    

    powerPress灭屏流程

    private void powerPress(long eventTime, boolean interactive, int count) {
        if (mScreenOnEarly && !mScreenOnFully) {
            Slog.i(TAG, "Suppressed redundant power key press while "
                    + "already in the process of turning the screen on.");
            return;
        }
        if (count == 2) {
           ......
        } else if (interactive && !mBeganFromNonInteractive) {
            switch (mShortPressOnPowerBehavior) {
                //灭屏
                case SHORT_PRESS_POWER_GO_TO_SLEEP:
                    goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                    break;
                //灭屏,直接跳过Doze状态
                case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
                    goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                    break;
                    } else {
                        shortPressPowerGoHome();
                    }
                    break;
                }
            }
        }
    }
    
    

    在这里调用了goToSleep()方法,该方法如下:

    private void goToSleep(long eventTime, int reason, int flags) {
        mRequestedOrGoingToSleep = true;
        mPowerManager.goToSleep(eventTime, reason, flags);
    }
    
    

    最终,PhoneWindowManager中调用了PowerManagergoToSleep()方法来灭屏。

    现在我们进入到PowerManager.goToSleep()方法:

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

    可以看到,在PowerManger中开始向下调用到了PoweManagerService(以下简称PMS)中的goToSleep()中。
    我们进入PMS中,就需要详细分析其中的方法了,先来看看goToSleep()方法:

    /**
     * @param eventTime 时间
     * @param reason 原因,Power键灭屏则是PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON
     * @param flags 目前只有两个值:0和1(GO_TO_SLEEP_FLAG_NO_DOZE)
     */
    @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");
        }
        //检查权限
        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.DEVICE_POWER, null);
    
        final int uid = Binder.getCallingUid();
        final long ident = Binder.clearCallingIdentity();
        try {
            //调用gotToSleepInternal
            goToSleepInternal(eventTime, reason, flags, uid);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }
    
    

    这个方法的参数和PowerManager,PhoneWindowManager中的同名方法对应,需要注意的是第二个参数和第三个参数;
    第二个参数:表示灭屏原因,在PowerManager中定义了一些常量值来表示;
    第三个参数:是一个标识,用来表示是否直接进入灭屏,一般的灭屏流程,都会先进入Doze状态,然后才会进入Sleep状态,如果将flag设置为1,则将会直接进入Sleep状态,这部分会在下文中逐渐分析到。

    goToSleep()方法中,检查权限之后,开始调用了goToSleepInternal()方法,该方法如下:

    private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
        synchronized (mLock) {
            if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {
                updatePowerStateLocked();
            }
        }
    }
    
    

    这个方法逻辑很简单,首先是调用了goToSleepNoUpdateLocked()方法,并根据该方法返回值来决定是否调用updatePowerStateLocked()方法。

    一般来说,goToSleepNoUpdateLocked()都会返回true,现在看看该方法:

    @SuppressWarnings("deprecation")
    private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {
    
        if (eventTime < mLastWakeTime
                || mWakefulness == WAKEFULNESS_ASLEEP
                || mWakefulness == WAKEFULNESS_DOZING
                || !mBootCompleted || !mSystemReady) {
            return false;
        }
        try {
            switch (reason) {
                case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
                    Slog.i(TAG, "Going to sleep due to device administration policy "
                            + "(uid " + uid +")...");
                    break;
                case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
                    Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")...");
                    break;
                case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH:
                    Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")...");
                    break;
                case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:
                    Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")...");
                    break;
                case PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON:
                    Slog.i(TAG, "Going to sleep due to sleep button (uid " + uid +")...");
                    break;
                case PowerManager.GO_TO_SLEEP_REASON_HDMI:
                    Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")...");
                    break;
                case PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY:
                    Slog.i(TAG, "Going to sleep by an accessibility service request (uid "
                            + uid +")...");
                    break;
                default:
                    Slog.i(TAG, "Going to sleep by application request (uid " + uid +")...");
                    reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION;
                    break;
            }
            //标记最后一次灭屏时间
            mLastSleepTime = eventTime;
            //用于判定是否进入屏保
            mSandmanSummoned = true;
            //设置wakefulness值为WAKEFULNESS_DOZING,因此先进Doze状态
            setWakefulnessLocked(WAKEFULNESS_DOZING, reason);
    
            // Report the number of wake locks that will be cleared by going to sleep.
            //灭屏时,将清除以下三种使得屏幕保持亮屏的wakelock锁,numWakeLocksCleared统计下个数
            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;
                }
            }
            // Skip dozing if requested.
            //如果带有PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE的flag,则直接进入Sleep状态,不再进入Doze状态
            if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {
                //该方法才会真正地进入睡眠
                reallyGoToSleepNoUpdateLocked(eventTime, uid);
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_POWER);
        }
        return true;
    }
    
    

    在这个方法中:
    首先,是判断调用该方法的原因并打印log,该log在日常分析问题时非常有用;
    然后,通过setWakefulnessLocked()将当前wakefulness设置为Doze状态;
    最后,通过flag判断,如果flag为1,则调用reallyGoToSleepNoUpdateLocked()方法直接进入Sleep状态。
    因此,系统其他模块在调用PM.goToSleep()灭屏时,在除指定flag为PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE的情况外,都会首先进入Doze,再由Doze进入Sleep。

    setWakefulnessLocked()方法用来设置wakefulness值,同时将会调用Notifierwakefulness相关的逻辑,这部分在之前的流程分析中也分析过,这里再来看下:

    @VisibleForTesting
    void setWakefulnessLocked(int wakefulness, int reason) {
        if (mWakefulness != wakefulness) {
            //设置mWakefulness
            mWakefulness = wakefulness;
            mWakefulnessChanging = true;
            mDirty |= DIRTY_WAKEFULNESS;
            if (mNotifier != null) {
                //调用Notifier中的方法,做wakefulness改变开始时的工作
                mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
            }
        }
    }
    
    

    我们跟着执行流程来进行分析,NotifierPMS模块中用于进行“通知”的一个组件类,比如发送亮灭屏广播就是它来负责,具体详细的分析请点击这里 查看。这里针对于灭屏场景,再来看下其中的逻辑:

    public void onWakefulnessChangeStarted(final int wakefulness, int reason) {
        //由于wakefulness为Doze,故interactive为false
        final boolean interactive = PowerManagerInternal.isInteractive(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();
            }
            // Handle early behaviors.
            mInteractive = interactive;
            mInteractiveChangeReason = reason;
            mInteractiveChanging = true;
            //处理早期工作
            handleEarlyInteractiveChange();
        }
    }
    
    

    在这个方法中,首先根据wakefulness值判断了系统当前的交互状态,如果是处于Awake状态和Dream状态,则表示可交互;如果处于DozeAsleep状态,则表示不可交互;
    由于在setWakefulnessLocked()中已经设置了wakefulness为DOZE状态,因此此时处于不可交互状态,接下来开始执行handleEarlyInteractiveChange()方法:

    private void handleEarlyInteractiveChange() {
        synchronized (mLock) {
            //此时为false
            if (mInteractive) {
                // Waking up...
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        // Note a SCREEN tron event is logged in PowerManagerService.
                        mPolicy.startedWakingUp();
                    }
                });
                // Send interactive broadcast.
                mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
                mPendingWakeUpBroadcast = true;
                updatePendingBroadcastLocked();
            } else {
                final int why = translateOffReason(mInteractiveChangeReason);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        //通过PhoneWindowManager设置锁屏
                        mPolicy.startedGoingToSleep(why);
                    }
                });
            }
        }
    }
    
    

    在这个方法中,将调用mPolicy.startedGoingToSleep(why)进行锁屏流程(Keyguard的绘制)。

    回到PMS中,在处理完setWakefulnessLocked()方法后,由于没有PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE,所以不会立即执行reallyGoToSleepNoUpdateLocked()方法,此时goToSleepNoUpdateLocked()方法完毕并返回true。

    之后开始执行updatePowerStateLocked()方法了,这个方法对于熟悉PMS模块的人来说再熟悉不过了,它是整个PMS的核心,详细的分析请点击这里 , 在这里我们只看其灭屏时的一些处理。

    updatePowerStateLocked()方法中,和灭屏直接相关的有如下部分:

    // 更新屏幕状态
    boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
    //更新屏保信息
    updateDreamLocked(dirtyPhase2, displayBecameReady);
    // 收尾工作
    finishWakefulnessChangeIfNeededLocked();
    //释放锁
    updateSuspendBlockerLocked();
    
    
    • updateDisplayPowerStateLocked()将会向DisplayPowerController请求新的屏幕状态,完成屏幕的更新;

    • updateDreamLocked()方法用来更新屏保信息,除此之外还有一个任务
      调用reallyGoToSleep()方法进入休眠,即由DOZE状态进入Sleep状态。

    • finishWakefulnessChangeIfNeededLocked()方法用来做最后的收尾工作,当然,在这里会调用到Notifier中进行收尾。

    • updateSuspendBlockerLocked()方法将用来更新SuspendBlocker锁,会根据当前的WakeLock类型以及屏幕状态来决定是否需要申请SuspendBlocker锁。

    updateDreamLocked()中更新屏保状态时,如果此时处于Doze状态且没有进行屏保,则将调用reallyGoToSleepNoUpdateLocked()方法,将wakefulness值设置为了Sleep,部分代码如下:

    else if (wakefulness == WAKEFULNESS_DOZING) {
                    if (isDreaming) {
                        return; // continue dozing
                    }
    
                    // Doze has ended or will be stopped.  Update the power state.
                    reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
                    updatePowerStateLocked();
                }
    
    

    再来看看该方法:

    private boolean reallyGoToSleepNoUpdateLocked(long eventTime, int uid) {
        if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP
                || !mBootCompleted || !mSystemReady) {
            return false;
        }
    
        try {
            //设置为ASLEEP状态
            setWakefulnessLocked(WAKEFULNESS_ASLEEP, 
                      PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_POWER);
        }
        return true;
    }
    
    

    以上就是整个Power键灭屏PMS部分的流程,其时序图如下:

    超时灭屏

    经过上面的分析,我们知道了Power键灭屏PhoneWindowManager发起了goToSleep,现在来看看超时灭屏是如何实现的。

    超时灭屏主要有两个影响因素休眠时间用户活动。休眠时间在Settings中进行设置,用户活动是指当手机处于亮屏状态,都会调用userActivityNoUpdateLocked()方法去更新用户活动时间。接下来我们就从userActivityNoUpdateLocked()方法开始分析其超时灭屏的流程。

    首先来看该方法:

        private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {
    
            if (eventTime < mLastSleepTime || eventTime < mLastWakeTime
                    || !mBootCompleted || !mSystemReady) {
                return false;
            }
    
                mNotifier.onUserActivity(event, uid);
               
                if (mUserInactiveOverrideFromWindowManager) {
                    mUserInactiveOverrideFromWindowManager = false;
                    mOverriddenTimeout = -1;
                }
                //如果wakefulness为Asleep或Doze,不再计算超时时间,直接返回
                if (mWakefulness == WAKEFULNESS_ASLEEP
                        || mWakefulness == WAKEFULNESS_DOZING
                        || (flags & PowerManager.USER_ACTIVITY_FLAG_INDIRECT) != 0) {
                    return false;
                }
                //如果带有该flag,则会小亮一会儿再灭屏
                if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {
                    if (eventTime > mLastUserActivityTimeNoChangeLights
                            && eventTime > mLastUserActivityTime) {
                        //将当前时间赋值给mLastUserActivityTimeNoChangeLights
                        mLastUserActivityTimeNoChangeLights = eventTime;
                        mDirty |= DIRTY_USER_ACTIVITY;
                        if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
                            mDirty |= DIRTY_QUIESCENT;
                        }
    
                        return true;
                    }
                } else {
                    if (eventTime > mLastUserActivityTime) {
                        //将当前时间赋值给mLastUserActivityTime
                        mLastUserActivityTime = eventTime;
                        mDirty |= DIRTY_USER_ACTIVITY;
                        if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) {
                            mDirty |= DIRTY_QUIESCENT;
                        }
                        return true;
                    }
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_POWER);
            }
            return false;
        }
    
    

    在这个方法中,如果传入的参数flag为PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,则将事件时间赋值给mLastUserActivityTimeNoChangeLights,否则将事件时间赋值给mLastUserActivityTime。这个flag标志用于延长亮屏或Dim的时长一小会儿。

    当这个方法执行之后,就得到了mLastUserActivityTime或者mLastUserActivityTimeNoChangeLights的值,然后经过一些调用后,又会进入updatePowerStateLocked()方法中。在这个方法中,和超市灭屏直接相关的就是for循环部分:

                for (;;) {
                    int dirtyPhase1 = mDirty;
                    dirtyPhase2 |= dirtyPhase1;
                    mDirty = 0;
    
                    updateWakeLockSummaryLocked(dirtyPhase1);
                    updateUserActivitySummaryLocked(now, dirtyPhase1);
                    if (!updateWakefulnessLocked(dirtyPhase1)) {
                        break;
                    }
                }
    
    

    其中updateWakeLockSummaryLocked()用来统计WakeLock,这里就不分析该方法了,详细的分析请点击这里,现在从updateUserActivitySummaryLocked()方法开始分析,该方法如下:

        private void updateUserActivitySummaryLocked(long now, int dirty) {
            // Update the status of the user activity timeout timer.
            if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY
                    | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {
                mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
    
                long nextTimeout = 0;
                if (mWakefulness == WAKEFULNESS_AWAKE
                        || mWakefulness == WAKEFULNESS_DREAMING
                        || mWakefulness == WAKEFULNESS_DOZING) {
                    //获取睡眠时长,为Settings.Secure.SLEEP_TIMEOUT的值和最小休眠时间的最大值,Settings.Secure.SLEEP_TIMEOUT一般为-1,
                    //表示禁用,因此该值默认为-1
                    final int sleepTimeout = getSleepTimeoutLocked();
                    //获取休眠时长,在Settings中设置的值
                    final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
                    //获取Dim时长,由休眠时长剩Dim百分比得到
                    final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
                    //用户活动是否由Window覆盖
                    final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;
                    //该值用来统计用户活动状态,每次进入该方法,置为0
                    mUserActivitySummary = 0;
                    //上次用户活动时间>=上次唤醒时间
                    if (mLastUserActivityTime >= mLastWakeTime) {
                        //下次超时时间为上次用户活动时间+休眠时间-Dim时间,到达这个时间后,将进入Dim状态
                        nextTimeout = mLastUserActivityTime
                                + screenOffTimeout - screenDimDuration;
                        //如果当前时间<nextTimeout,则此时处于亮屏状态,标记mUserActivitySummary为USER_ACTIVITY_SCREEN_BRIGHT
                        if (now < nextTimeout) {
                            mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
                        } else {
                            //如果当前时间>nextTimeout,此时有两种情况,要么进入Dim要么进入Sleep
                            //将上次用户活动时间+灭屏时间赋值给nextTimeout,如果该值大于当前时间,则说明此时应该处于Dim状态
                            //因此将标记mUserActivitySummary为USER_ACTIVITY_SCREEN_DIM
                            nextTimeout = mLastUserActivityTime + screenOffTimeout;
                            if (now < nextTimeout) {
                                mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
                            }
                        }
                    }
                    //判断和USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS标记相关,如果带有此标记,才会进入该if
                    if (mUserActivitySummary == 0
                            && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
                        //下次超时时间=上次用户活动时间+灭屏时间
                        nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
                        //根据当前时间和nextTimeout设置mUserActivitySummary
                        if (now < nextTimeout) {
                            if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT
                                    || mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) {
                                mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
                            } else if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
                                mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
                            }
                        }
                    }
                    //不满足以上条件时,此时mUserActivitySummary为0,这种情况应该为当mUserActivitySummary经历了USER_ACTIVITY_SCREEN_BRIGHT
                    //和USER_ACTIVITY_SCREEN_DIM之后才会执行到这里
                    if (mUserActivitySummary == 0) {
                        if (sleepTimeout >= 0) {
                            //获取上次用户活动时间的最后一次时间
                            final long anyUserActivity = Math.max(mLastUserActivityTime,
                                    mLastUserActivityTimeNoChangeLights);
                            if (anyUserActivity >= mLastWakeTime) {
                                nextTimeout = anyUserActivity + sleepTimeout;
                                //将mUserActivitySummary值置为USER_ACTIVITY_SCREEN_DREAM,表示屏保
                                if (now < nextTimeout) {
                                    mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
                                }
                            }
                        } else {
                            //将mUserActivitySummary值置为USER_ACTIVITY_SCREEN_DREAM,表示屏保
                            mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
                            nextTimeout = -1;
                        }
                    }
    
                    if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) {
                        if ((mUserActivitySummary &
                                (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) {
                            // Device is being kept awake by recent user activity
                            if (nextTimeout >= now && mOverriddenTimeout == -1) {
                                // Save when the next timeout would have occurred
                                mOverriddenTimeout = nextTimeout;
                            }
                        }
                        mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
                        nextTimeout = -1;
                    }
                    if (mUserActivitySummary != 0 && nextTimeout >= 0) {
                        //发送一个异步Handler定时消息
                        Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
                        msg.setAsynchronous(true);
                        mHandler.sendMessageAtTime(msg, nextTimeout);
                    }
                } else {//当wakefulness=Sleep的时候,直接将mUserActivitySummary置为0
                    mUserActivitySummary = 0;
                }
            }
        }
    
    

    该方法用来更新用户活动状态,其中细节在代码中都进行了注释,该方法中来看,通过Handler多次再此进入updatePowerStateLocked()从而调用updateUserActivitySummaryLocked()方法,直到nextTime=-1mUserActivitySummary=0时将不再发送Handler,从而完成了mUserActivitySummary的更新。根据流程来看,当设备从亮屏到休眠时间到达灭屏,mUserActivitySummary的值的变化应为:
    USER_ACTIVITY_SCREEN_BRIGHT—>USER_ACTIVITY_SCREEN_DIM—>USER_ACTIVITY_SCREEN_DREAM—>0.
    Handler的调用处理逻辑如下:

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_USER_ACTIVITY_TIMEOUT:
                        handleUserActivityTimeout();
                        break;
                }
            }
    
        private void handleUserActivityTimeout() { // runs on handler thread
            synchronized (mLock) {
                mDirty |= DIRTY_USER_ACTIVITY;
                updatePowerStateLocked();
            }
        }
    
    

    当执行到这个方法后,现在就统计得到了mWakeLockSummary和mUserActivitySummary的值,现在我们看下一个方法——updateWakefulnessLocked(),在for循环中,会根据该方法返回值来决定是否进行循环,为何会如此设计呢?在分析完该方法后,就会有答案了,如下:

        private boolean updateWakefulnessLocked(int dirty) {
            boolean changed = false;
            if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED
                    | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE
                    | DIRTY_DOCK_STATE)) != 0) {
                //isItBedTimeYetLocked()判断是否需要"睡觉"了
                if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {
                    final long time = SystemClock.uptimeMillis();
                    if (shouldNapAtBedTimeLocked()) {//进入屏保
                        changed = napNoUpdateLocked(time, Process.SYSTEM_UID);
                    } else {//开始休眠
                        changed = goToSleepNoUpdateLocked(time,
                                PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
                    }
                }
            }
            return changed;
        }
    
    

    这个方法中可以看到,首先根据isItBedTimeYetLocked()和mWakefulness来决定是否执行,然后根据shouldNapAtBedTimeLocked()决定进入屏保还是休眠。
    该方法如果返回值为true,则说明此时屏幕状态发生改变(在goToSleepNoUpdateLocked()napNoUpdateLocked()中会分别设置mWakefulness为DREAM和ASLEEP),因此将不会跳出for循环,再次进行一次循环。这就是为何会设置一个死循环的目的,同时也说明只有超时灭屏才会循环两次,其他情况下都会只执行一次for循环就退出。

    回到该方法中,我们继续看看isItBedTimeYetLocked():

        private boolean isItBedTimeYetLocked() {
            return mBootCompleted && !isBeingKeptAwakeLocked();
        }
    
        private boolean isBeingKeptAwakeLocked() {
            return mStayOn//是否需要保持常亮
                    || mProximityPositive//PSensor是否靠近
                    || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0//当前是否有Wakelock类型为屏幕相关的锁
                    || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
                            | USER_ACTIVITY_SCREEN_DIM)) != 0//当前用户活动状态是否为Draem或者0
                    || mScreenBrightnessBoostInProgress;//是否处于亮度增强过程中
        }
    
    

    以上代码可以看出,如果有任意一个条件为true,那么就不能进入休眠或者屏保状态,因此只有全部为false时,才会返回false,从而说明需要“睡觉”了。

    仔细看这个方法,这里正是mWakeLockSummarymUserActivitySummary的作用体现之一。

    在平时分析问题时,如果存在无法超时灭屏问题,就需要查看mWakeLockSummarymUserActivitySummary的值了。前者查看是否存在亮屏锁,后者查看用户活动是否已经处于0了。
    现在继续分析updateWakfulnessLocked()方法中的下一个逻辑,当进入if语句后,就开始判断是要进入屏保呢?还是要直接休眠呢?

    如果shouldNapAtBedTimeLocked()返回true,则开始屏保,否则直接休眠,这里对于屏保相关就不再分析了,以后的时间中如果有机会,会单独进行分析。

    当开始休眠时,直接调用了goToSleepNoUpdateLocked()方法中了,于是开始走休眠流程,之后的逻辑和Power键灭屏一样了。

    整个超时灭屏的流程分析就到这里了,从以上流程中可以看到,mWakeLockSummarymUserActivitySummayr的作用相当重要,我之前在android4.4手机上遇到过一个问题就是到达休眠时间后不会灭屏,分析后发现有一个应用申请了一个PowerManager.SCREEN_BRIGHT_WAKE_LOCK锁,该锁导致mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0,从而没有灭屏。
    整个超时灭屏流程的时序图如下:

    PSensor灭屏

    什么是PSensor灭屏呢?
    Proximity Sensor,即距离传感器,当通话或微信时,如果脸部靠近屏幕,将会灭屏,这就是通过PSensor灭屏的。
    为何会有PSensor灭屏呢?
    为了防止脸部误触,有更好的用户体验。

    在原生的Android系统中,PSensor灭屏不同于Power键灭屏超时灭屏,前者仅仅是设置屏幕的状态和关闭背光,而后两者在设置屏幕的状态和关闭背光后,让CPU也进入了休眠状态(如果不持有PowerManger.PARTIAL_WAKE_LOCK)。

    PSensor灭屏涉及到更多的是DisplayPowerController中的内容,因此,将会在之后的文章中进行分析。

    长按识别二维码,领福利

    至此,本篇已结束,如有不对的地方,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

    如有侵权,请联系小编,小编对此深感抱歉,届时小编会删除文章,立即停止侵权行为,请您多多包涵。

    既然都看到这里,领个红包在走吧!
    以下两个红包每天都可以领取

    1.支付宝首页收缩红包口令522398497 即可免费领取最高99元红包

    支付宝首页收缩红包口令522398497 即可免费领取最高99元红包

    相关文章

      网友评论

        本文标题:Android 手机灭屏流程分析详解

        本文链接:https://www.haomeiwen.com/subject/yoeddqtx.html