精华内容
下载资源
问答
  • Android 8.1 Doze模式分析(三)Light Doze模式流程分析
    千次阅读
    2019-01-22 11:38:11

    在第一篇Android 8.1 Doze模式分析(一)我们知道,如果设备处于未充电且屏幕关闭状态,就会进入Light Doze模式,在LightDoze模式中,会定期进行维护,这种维护会持续N分钟,在维护状态(maintenance)时,会进行网络的访问,和同步、JobScheduler的操作,然后又会进入Idle状态,持续多次。之后如果设备仍旧保持静止,则会进入Deep Doze模式,因此,如果没有运动传感器,则无法检测设备是否处于静止,因此无法进入Deep Doze模式。

    Light Doze模式相关值:

    //表示轻度doze模式
    private int mLightState;
    //mLightState状态值,表示设备处于活动状态
    private static final int LIGHT_STATE_ACTIVE = 0;
    //mLightState状态值,表示设备处于不活动状态
    private static final int LIGHT_STATE_INACTIVE = 1;
    //mLightState状态值,表示设备进入空闲状态前,需要等待完成必要操作
    private static final int LIGHT_STATE_PRE_IDLE = 3;
    //mLightState状态值,表示设备处于空闲状态,该状态内将进行优化
    private static final int LIGHT_STATE_IDLE = 4;
    //mLightState状态值,表示设备处于空闲状态,要进入维护状态,先等待网络连接
    private static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;
    //mLightState状态值,表示设备处于维护状态
    private static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;
    //mLightState状态值,表示设备处轻度Doze被覆盖,开始进入深度Doze模式
    private static final int LIGHT_STATE_OVERRIDE = 7;
    

    下面开始讲解模式流程

    Android 8.1 Doze模式分析(一)讲到当屏幕灭屏或者充电结束时,就会调用becomeInactiveIfAppropriateLocked()

    DeviceIdleController.java-->becomeInactiveIfAppropriateLocked()

        void becomeInactiveIfAppropriateLocked() {
            if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
            //仅当屏幕灭屏且没充电且mVoWifiCalling为faslse时,才能进入Doze模式,mForceIdle是shell设置相关
            if ((!mScreenOn && !mCharging && !mVoWifiCalling) || mForceIdle) {
                // Screen has turned off; we are now going to become inactive and start
                // waiting to see if we will ultimately go idle.
                if (mState == STATE_ACTIVE && mDeepEnabled) {
                    mState = STATE_INACTIVE;
                    if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
                     //重置
                    resetIdleManagementLocked();
                     //设置DeepAlarm,时间为30mins
                    scheduleAlarmLocked(mInactiveTimeout, false);
                    EventLogTags.writeDeviceIdle(mState, "no activity");
                }
                if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) {
                    mLightState = LIGHT_STATE_INACTIVE;
                    if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");
                     //重置
                    resetLightIdleManagementLocked();
                     //设置LightDoze,时间为5mins
                    scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
                    EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
                }
            }
        }

    当LightDoze处于LIGHT_STATE_ACTIVE状态,且mLightEnabled为true,就会将mLightState变为LIGHT_STATE_INACTIVE状态,表示不可交互状态(灭屏),调用resetLightIdleManagementLocked()重置,最后调用scheduleLightAlarmLocked()发送一个5分钟的alarm,

    下面我们重点看scheduleLightAlarmLocked()

        void scheduleLightAlarmLocked(long delay) {
            if (DEBUG) Slog.d(TAG, "scheduleLightAlarmLocked(" + delay + ")");
            mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay;
            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
        }

    5分钟后到达mLightAlarmListener的回调方法onAlarm()

        private final AlarmManager.OnAlarmListener mLightAlarmListener
                = new AlarmManager.OnAlarmListener() {
            @Override
            public void onAlarm() {
                synchronized (DeviceIdleController.this) {
                    stepLightIdleStateLocked("s:alarm");
                }
            }
        };

    调用stepLightIdleStateLocked(),stepLightIdleStateLocked()方法负责LightDoze模式的改变

    void stepLightIdleStateLocked(String reason) {
            //LIGHT_STATE_OVERRIDE表示已经进入深度睡眠了,直接退出
            if (mLightState == LIGHT_STATE_OVERRIDE) {
                // If we are already in deep device idle mode, then
                // there is nothing left to do for light mode.
                return;
            }
    
            if (DEBUG) Slog.d(TAG, "stepLightIdleStateLocked: mLightState=" + mLightState);
            EventLogTags.writeDeviceIdleLightStep();
    
            switch (mLightState) {
                case LIGHT_STATE_INACTIVE:
                    mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
                    // Reset the upcoming idle delays.
                    //mNextLightIdleDelay 表示LightDoze 进入空闲(Idle)状态的时间,5分钟
                    mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
                    //LightDoze进入维护状态(maintenance)的开始时间
                    mMaintenanceStartTime = 0;
                    //表示还有活动进行,给它们5分钟时间处理,如果没有活动进行的话,就直接进入到idle
                    if (!isOpsInactiveLocked()) {
                        // We have some active ops going on...  give them a chance to finish
                        // before going in to our first idle.
                        mLightState = LIGHT_STATE_PRE_IDLE;
                        EventLogTags.writeDeviceIdleLight(mLightState, reason);
                        scheduleLightAlarmLocked(mConstants.LIGHT_PRE_IDLE_TIMEOUT);
                        break;
                    }
                    // Nothing active, fall through to immediately idle.
                case LIGHT_STATE_PRE_IDLE:
                case LIGHT_STATE_IDLE_MAINTENANCE:
                    if (mMaintenanceStartTime != 0) {
                        long duration = SystemClock.elapsedRealtime() - mMaintenanceStartTime;
                        if (duration < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
                            // We didn't use up all of our minimum budget; add this to the reserve.
                            mCurIdleBudget += (mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET-duration);
                        } else {
                            // We used more than our minimum budget; this comes out of the reserve.
                            mCurIdleBudget -= (duration-mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);
                        }
                    }
                    mMaintenanceStartTime = 0;
                    scheduleLightAlarmLocked(mNextLightIdleDelay);
                    mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
                            (long)(mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
                    if (mNextLightIdleDelay < mConstants.LIGHT_IDLE_TIMEOUT) {
                        mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
                    }
                    if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
                    //LightDoze状态设置为LIGHT_STATE_IDLE
                    mLightState = LIGHT_STATE_IDLE;
                    EventLogTags.writeDeviceIdleLight(mLightState, reason);
                    addEvent(EVENT_LIGHT_IDLE);
                    //申请一个wakelock锁,保持CPU唤醒
                    mGoingIdleWakeLock.acquire();
                    //处理LightDoze进入Idle状态前的操作
                    mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);
                    break;
                case LIGHT_STATE_IDLE:
                case LIGHT_STATE_WAITING_FOR_NETWORK:
                    if (mNetworkConnected || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {
                        // We have been idling long enough, now it is time to do some work.
                        mActiveIdleOpCount = 1;
                        mActiveIdleWakeLock.acquire();
                        mMaintenanceStartTime = SystemClock.elapsedRealtime();
                        if (mCurIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
                            mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
                        } else if (mCurIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
                            mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
                        }
                        scheduleLightAlarmLocked(mCurIdleBudget);
                        if (DEBUG) Slog.d(TAG,
                                "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");
                        mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
                        EventLogTags.writeDeviceIdleLight(mLightState, reason);
                        addEvent(EVENT_LIGHT_MAINTENANCE);
                        mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
                    } else {
                        // We'd like to do maintenance, but currently don't have network
                        // connectivity...  let's try to wait until the network comes back.
                        // We'll only wait for another full idle period, however, and then give up.
                        scheduleLightAlarmLocked(mNextLightIdleDelay);
                        if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
                        mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
                        EventLogTags.writeDeviceIdleLight(mLightState, reason);
                    }
                    break;
            }
        }

    从这里可以看出从LIGHT_STATE_PRE_IDLE/LIGHT_STATE_IDLE_MAINTENANCE进入到LIGHT_STATE_IDLE/LIGHT_STATE_WAITING_FOR_NETWORK,或者从LIGHT_STATE_IDLE/LIGHT_STATE_WAITING_FOR_NETWORK进入到LIGHT_STATE_PRE_IDLE/LIGHT_STATE_IDLE_MAINTENANCE都会发送handler

    final class MyHandler extends Handler {
            MyHandler(Looper looper) {
                super(looper);
            }
    
            @Override public void handleMessage(Message msg) {
                if (DEBUG) Slog.d(TAG, "handleMessage(" + msg.what + ")");
                switch (msg.what) {
                    case MSG_WRITE_CONFIG: {
                        // Does not hold a wakelock. Just let this happen whenever.
                        handleWriteConfigFile();
                    } break;
                    case MSG_REPORT_IDLE_ON:
                    case MSG_REPORT_IDLE_ON_LIGHT: {
                        // mGoingIdleWakeLock is held at this point
                        EventLogTags.writeDeviceIdleOnStart();
                        final boolean deepChanged;
                        final boolean lightChanged;
                        if (msg.what == MSG_REPORT_IDLE_ON) {
                            deepChanged = mLocalPowerManager.setDeviceIdleMode(true);
                            lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);
                        } else {
                            deepChanged = mLocalPowerManager.setDeviceIdleMode(false);
                            lightChanged = mLocalPowerManager.setLightDeviceIdleMode(true);
                        }
                        try {
                            mNetworkPolicyManager.setDeviceIdleMode(true);
                            mBatteryStats.noteDeviceIdleMode(msg.what == MSG_REPORT_IDLE_ON
                                    ? BatteryStats.DEVICE_IDLE_MODE_DEEP
                                    : BatteryStats.DEVICE_IDLE_MODE_LIGHT, null, Process.myUid());
                        } catch (RemoteException e) {
                        }
                        if (deepChanged) {
                            getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
                        }
                        if (lightChanged) {
                            getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);
                        }
                        EventLogTags.writeDeviceIdleOnComplete();
                        mGoingIdleWakeLock.release();
                    } break;
                    case MSG_REPORT_IDLE_OFF: {
                        // mActiveIdleWakeLock is held at this point
                        EventLogTags.writeDeviceIdleOffStart("unknown");
                        final boolean deepChanged = mLocalPowerManager.setDeviceIdleMode(false);
                        final boolean lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);
                        try {
                            mNetworkPolicyManager.setDeviceIdleMode(false);
                            mBatteryStats.noteDeviceIdleMode(BatteryStats.DEVICE_IDLE_MODE_OFF,
                                    null, Process.myUid());
                        } catch (RemoteException e) {
                        }
                        if (deepChanged) {
                            incActiveIdleOps();
                            getContext().sendOrderedBroadcastAsUser(mIdleIntent, UserHandle.ALL,
                                    null, mIdleStartedDoneReceiver, null, 0, null, null);
                        }
                        if (lightChanged) {
                            incActiveIdleOps();
                            getContext().sendOrderedBroadcastAsUser(mLightIdleIntent, UserHandle.ALL,
                                    null, mIdleStartedDoneReceiver, null, 0, null, null);
                        }
                        // Always start with one active op for the message being sent here.
                        // Now we are done!
                        decActiveIdleOps();
                        EventLogTags.writeDeviceIdleOffComplete();
                    } break;
                    case MSG_REPORT_ACTIVE: {
                        // The device is awake at this point, so no wakelock necessary.
                        String activeReason = (String)msg.obj;
                        int activeUid = msg.arg1;
                        EventLogTags.writeDeviceIdleOffStart(
                                activeReason != null ? activeReason : "unknown");
                        final boolean deepChanged = mLocalPowerManager.setDeviceIdleMode(false);
                        final boolean lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);
                        try {
                            mNetworkPolicyManager.setDeviceIdleMode(false);
                            mBatteryStats.noteDeviceIdleMode(BatteryStats.DEVICE_IDLE_MODE_OFF,
                                    activeReason, activeUid);
                        } catch (RemoteException e) {
                        }
                        if (deepChanged) {
                            getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
                        }
                        if (lightChanged) {
                            getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);
                        }
                        EventLogTags.writeDeviceIdleOffComplete();
                    } break;
                    case MSG_TEMP_APP_WHITELIST_TIMEOUT: {
                        // TODO: What is keeping the device awake at this point? Does it need to be?
                        int uid = msg.arg1;
                        checkTempAppWhitelistTimeout(uid);
                    } break;
                    case MSG_REPORT_MAINTENANCE_ACTIVITY: {
                        // TODO: What is keeping the device awake at this point? Does it need to be?
                        boolean active = (msg.arg1 == 1);
                        final int size = mMaintenanceActivityListeners.beginBroadcast();
                        try {
                            for (int i = 0; i < size; i++) {
                                try {
                                    mMaintenanceActivityListeners.getBroadcastItem(i)
                                            .onMaintenanceActivityChanged(active);
                                } catch (RemoteException ignored) {
                                }
                            }
                        } finally {
                            mMaintenanceActivityListeners.finishBroadcast();
                        }
                    } break;
                    case MSG_FINISH_IDLE_OP: {
                        // mActiveIdleWakeLock is held at this point
                        decActiveIdleOps();
                    } break;
                }
            }
        }
    

    从这段代码可以看出在LightDoze进入IDLE状态或退出IDLE状态时时,首先通知PMS、NetworkPolicyManager设置DeviceIdleMode为true或false,然后发送一个Intent为mLightIdleIntent的广播

    当LightDoze进入IDLE/MAINTENANCE状态时,在Handler中:

    • 1.通知NetworkPolicyManagerService限制/打开网络;
    • 2.发送广播,DeviceIdleJobsController中进行接受,限制/运行JobService.

    如何限制网络?

    DeviceIdleController中仅仅调用NetworkPolicyManagerService提供的接口,具体如何限制在NetworkPolicyManagerService内部实现:

    mNetworkPolicyManager.setDeviceIdleMode(true)

    后续流程流程请参看Android 8.1 Doze模式分析(二)对网络的限制

    如何延迟Job?

    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            //当DeepDoze或LightDoze的IDLE状态改变时,都会执行updateIdleMode()
            if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE
                    _CHANGED.equals(action)|| PowerManager.ACTION_DEVICE
                    _IDLE_MODE_CHANGED.equals(action)) {
                updateIdleMode(mPowerManager != null
                        ? (mPowerManager.isDeviceIdleMode()
                                || mPowerManager.isLightDeviceIdleMode())
                        : false);
            } else if (PowerManager.ACTION_POWER_SAVE_WHITELIST_
                CHANGED.equals(action)) {
                updateWhitelist();
            }
        }
    };
    

    DeviceIdleJobsController.java-->updateIdleMode()

    void updateIdleMode(boolean enabled) {
        boolean changed = false;
        // Need the whitelist to be ready when going into idle
        if (mDeviceIdleWhitelistAppIds == null) {
            updateWhitelist();//更新白名单列表
        }
        synchronized (mLock) {
            if (mDeviceIdleMode != enabled) {
                changed = true;
            }
            mDeviceIdleMode = enabled;
            //遍历所有的Job
            mJobSchedulerService.getJobStore().forEachJob(mUpdateFunctor);
        }
        if (changed) {
            //回调到JobSchedulerService中,停止或开始Job
            mStateChangedListener.onDeviceIdleStateChanged(enabled);
        }
    }
    

     

    更多相关内容
  • 主要介绍了Android Doze模式启用和恢复功能,非常不错,具有参考借鉴价值,需要的朋友可以参考下
  • Doze模式简介

    千次阅读 2021-02-17 23:29:34
    Doze模式是自Android 6.0开始引入的两项省电功能的其中之一,还有一个就是appstandby,通过管理应用在设备未连接至电源时的行为方式,帮助用户延长电池寿命。当用户长时间未使用设备时,低电耗模式会延迟应用的后台 ...

    Doze模式是自Android 6.0开始引入的两项省电功能的其中之一,还有一个就是appstandby,通过管理应用在设备未连接至电源时的行为方式,帮助用户延长电池寿命。当用户长时间未使用设备时,低电耗模式会延迟应用的后台 CPU 和网络活动,从而降低耗电量。应用待机模式会延迟用户近期未与之交互的应用的后台网络活动。

    当设备处于低电耗模式时,应用对某些高耗电量资源的访问会延迟到维护期。如果用户未插接设备的电源,在屏幕关闭的情况下,让设备在一段时间内保持不活动状态,那么设备就会进入低电耗模式。在低电耗模式下,系统会尝试通过限制应用访问占用大量网络和 CPU 资源的服务来节省电量。它还会阻止应用访问网络,并延迟其作业、同步和标准闹钟。系统会定期退出低电耗模式一小段时间,让应用完成其延迟的活动。在此维护期内,系统会运行所有待处理的同步、作业和闹钟,并允许应用访问网络。

    在每个维护期结束时,系统会再次进入低电耗模式,暂停网络访问并推迟作业、同步和闹钟。随着时间的推移,系统安排维护期的次数越来越少,这有助于在设备未连接至充电器的情况下长期处于不活动状态时降低耗电量。

    一旦用户通过移动设备、打开屏幕或连接至充电器唤醒设备,系统就会立即退出低电耗模式,并且所有应用都会恢复正常活动。

    在低电耗模式下,您的应用会受到一下限制:

    1. 暂停访问网络
    2. 系统忽略唤醒锁定
    3. 标准 Ala'r'm'Manager闹钟(包括 setExact() 和 setWindow())推迟到下一个维护期  如果您需要设置在设备处于低电耗模式时触发的闹钟,请使用 setAndAllowWhileIdle() 或 setExactAndAllowWhileIdle()使用 setAlarmClock() 设置的闹钟将继续正常触发,系统会在这些闹钟触发之前不久退出低电耗模式)
    4. 系统不执行 WLAN 扫描
    5. 系统不允许运行同步适配器
    6. 系统不允许运行 JobScheduler

    测试相关:

    运行以下命令,强制系统进入doze模式

    adb shell dumpsys deviceidle force-idle

    准备就绪后,运行以下命令,使系统退出闲置模式

    adb shell dumpsys deviceidle unforce

    执行以下命令,重新激活设备

    adb shell dumpsys battery reset

    以上介绍内容来自:

    https://developer.android.com/topic/performance?hl=zh-cn

     

    代码实现简介(基于android R):

    Doze模式的控制代码都在DeviceIdleController中,DeviceIdleController作为一个系统服务在systemserver中启动,启动后监听电池广播/应用卸载广播/屏幕亮灭广播,还通过注册传感器监听来获知当前设备是否在请求未知信息,是否有运动,来确定当前设备状态,是否需要进入doze模式。进入doze模式后通过NetworkPolicyManagerService来修改联网策略,通过BatteryStatsService修改电池省电策略,通过PowerManagerService来修改持锁策略,且通过发送广播DEVICE_IDLE_MODE_CHANGED来通知对应的省电模块和jobschedule模块做出相应调整。

    Doze模式状态图如下:

    Doze模式状态流转如下图:

    DeviceIdleController根据不同状态做不同的检测,设置不同的闹钟时间间隔,在最后强制设备进入doze睡眠模式,且会在进入后一段时间自动的退出doze模式,短暂退出后会再次进入,且距离下次退出doze模式时间会逐次拉长。在任意一个非STATA_ACTIVE状态下,如果接收到亮屏或者充电广播,或者是注册的传感器监听到运动信息,都会直接将状态回退到STATE_ACTIVE状态,所以进入doze模式的条件就是在灭屏不充电状态下经过一段时间的静止后才会进入,这个时间通过系统设置。

    DeviceIdleController的运行机制主要依赖一下四个模块:

    1. 数据模块Constants,提供了基础数据,比如各个不同状态直接闹钟的间隔时间
    2. 广播监听,监听电池变化,亮灭屏,应用卸载,网络变化广播,来确定进入或者退出doze状态模式流转的时机
    3. 传感器监听,通过监听传感器,获知当前设备活动情况
    4. 闹钟,各状态间切换,doze模式的开放窗口期进入都是通过闹钟来操作

     

    几个比较重要的方法如下:

    初始化:

    主要的初始化工作都在onBootPhase和onStart方法中完成,这里主要对一些需要用大的服务做初始化,广播注册,数据初始化,白名单初始化

        @Override
        public void onBootPhase(int phase) {
            Slog.d(TAG, "onBootPhase phase = " + phase);
            if (phase == PHASE_SYSTEM_SERVICES_READY) {
                synchronized (this) {
                    //各种需要用的的服务管理类的初始化
                    mAlarmManager = mInjector.getAlarmManager();
                    mLocalAlarmManager = getLocalService(AlarmManagerInternal.class);
                    mBatteryStats = BatteryStatsService.getService();
                    mLocalActivityManager = getLocalService(ActivityManagerInternal.class);
                    mLocalActivityTaskManager = getLocalService(ActivityTaskManagerInternal.class);
                    mLocalPowerManager = getLocalService(PowerManagerInternal.class);
                    mPowerManager = mInjector.getPowerManager();
                    mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                            "deviceidle_maint");
                    mActiveIdleWakeLock.setReferenceCounted(false);
                    mGoingIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                            "deviceidle_going_idle");
                    mGoingIdleWakeLock.setReferenceCounted(true);
                    mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
                            ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
                    mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);
                    mSensorManager = mInjector.getSensorManager();
    
                    if (mUseMotionSensor) {
                        mMotionSensor = mInjector.getMotionSensor();
                    }
    
                    
                    mAppStateTracker.onSystemServicesReady();
                    ......
                    //广播和广播接收器的初始化和注册
                    mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
                    mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                            | Intent.FLAG_RECEIVER_FOREGROUND);
                    mLightIdleIntent = new Intent(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
                    mLightIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                            | Intent.FLAG_RECEIVER_FOREGROUND);
    
                    IntentFilter filter = new IntentFilter();
                    filter.addAction(Intent.ACTION_BATTERY_CHANGED);
                    getContext().registerReceiver(mReceiver, filter);
    
                    filter = new IntentFilter();
                    filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
                    filter.addDataScheme("package");
                    getContext().registerReceiver(mReceiver, filter);
    
                    filter = new IntentFilter();
                    filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
                    getContext().registerReceiver(mReceiver, filter);
    
                    filter = new IntentFilter();
                    filter.addAction(Intent.ACTION_SCREEN_OFF);
                    filter.addAction(Intent.ACTION_SCREEN_ON);
                    getContext().registerReceiver(mInteractivityReceiver, filter);
    
                    mLocalActivityManager.setDeviceIdleWhitelist(
                            mPowerSaveWhitelistAllAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray);
                    mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
    
                    mLocalPowerManager.registerLowPowerModeObserver(ServiceType.QUICK_DOZE,
                            state -> {
                                synchronized (DeviceIdleController.this) {
                                    updateQuickDozeFlagLocked(state.batterySaverEnabled);
                                }
                            });
                    updateQuickDozeFlagLocked(
                            mLocalPowerManager.getLowPowerState(
                                    ServiceType.QUICK_DOZE).batterySaverEnabled);
    
                    mLocalActivityTaskManager.registerScreenObserver(mScreenObserver);
    
                    passWhiteListsToForceAppStandbyTrackerLocked();
                    updateInteractivityLocked();
                }
                //根据联网状态判断light模式状态
                updateConnectivityState(null);
            }
            
        }
        @Override
        public void onStart() {
            final PackageManager pm = getContext().getPackageManager();
    
            synchronized (this) {
                mLightEnabled = mDeepEnabled = getContext().getResources().getBoolean(
                        com.android.internal.R.bool.config_enableAutoPowerModes);
                SystemConfig sysConfig = SystemConfig.getInstance();
                ArraySet<String> allowPowerExceptIdle = sysConfig.getAllowInPowerSaveExceptIdle();
                for (int i=0; i<allowPowerExceptIdle.size(); i++) {
                    String pkg = allowPowerExceptIdle.valueAt(i);
                    try {
                        ApplicationInfo ai = pm.getApplicationInfo(pkg,
                                PackageManager.MATCH_SYSTEM_ONLY);
                        int appid = UserHandle.getAppId(ai.uid);
                        mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
                        mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
                    } catch (PackageManager.NameNotFoundException e) {
                    }
                }
                ArraySet<String> allowPower = sysConfig.getAllowInPowerSave();
                for (int i=0; i<allowPower.size(); i++) {
                    String pkg = allowPower.valueAt(i);
                    try {
                        ApplicationInfo ai = pm.getApplicationInfo(pkg,
                                PackageManager.MATCH_SYSTEM_ONLY);
                        int appid = UserHandle.getAppId(ai.uid);
                        // These apps are on both the whitelist-except-idle as well
                        // as the full whitelist, so they apply in all cases.
                        mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
                        mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
                        mPowerSaveWhitelistApps.put(ai.packageName, appid);
                        mPowerSaveWhitelistSystemAppIds.put(appid, true);
                    } catch (PackageManager.NameNotFoundException e) {
                    }
                }
    
                mConstants = mInjector.getConstants(this, mHandler, getContext().getContentResolver());
                
                readConfigFileLocked();
                updateWhitelistAppIdsLocked();
    
                mNetworkConnected = true;
                mScreenOn = true;
                mScreenLocked = false;
                // Start out assuming we are charging.  If we aren't, we will at least get
                // a battery update the next time the level drops.
                mCharging = true;
                mActiveReason = ACTIVE_REASON_UNKNOWN;
                mState = STATE_ACTIVE;
                mLightState = LIGHT_STATE_ACTIVE;
                mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
                mPreIdleFactor = 1.0f;
                mLastPreIdleFactor = 1.0f;
            }
    
            mBinderService = new BinderService();
            publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);
            mLocalService = new LocalService();
            publishLocalService(DeviceIdleInternal.class, mLocalService);
        }

    电池与卸载广播接收器:

        private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
            @Override public void onReceive(Context context, Intent intent) {
                switch (intent.getAction()) {
                    case ConnectivityManager.CONNECTIVITY_ACTION: {
                        updateConnectivityState(intent);
                    } break;
                    case Intent.ACTION_BATTERY_CHANGED: {
                        boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
                        boolean plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
                        synchronized (DeviceIdleController.this) {
                            updateChargingLocked(present && plugged);
                        }
                    } break;
                    case Intent.ACTION_PACKAGE_REMOVED: {
                        if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                            Uri data = intent.getData();
                            String ssp;
                            if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
                                removePowerSaveWhitelistAppInternal(ssp);
                            }
                        }
                    } break;
                }
            }
        };

    两灭屏广播:

        private final BroadcastReceiver mInteractivityReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                synchronized (DeviceIdleController.this) {
                    updateInteractivityLocked();
                }
            }
        };

    不管是电池变化的广播中调用的updateChargingLocked方法还是亮灭屏广播中的updateInteractivityLocked方法,最终都调用了两个方法:

    进入不活跃状态becomeInactiveIfAppropriateLocked,进入活跃状态becomeActiveLocked

        void becomeInactiveIfAppropriateLocked() {
            verifyAlarmStateLocked();
    
            final boolean isScreenBlockingInactive =
                    mScreenOn && (!mConstants.WAIT_FOR_UNLOCK || !mScreenLocked);
            
            if (!mForceIdle && (mCharging || isScreenBlockingInactive)) {
                return;
            }
            // Become inactive and determine if we will ultimately go idle.
            if (mDeepEnabled) {
                if (mQuickDozeActivated) {
                    if (mState == STATE_QUICK_DOZE_DELAY || mState == STATE_IDLE
                            || mState == STATE_IDLE_MAINTENANCE) {
                        // Already "idling". Don't want to restart the process.
                        // mLightState can't be LIGHT_STATE_ACTIVE if mState is any of these 3
                        // values, so returning here is safe.
                        return;
                    }
                    if (DEBUG) {
                        Slog.d(TAG, "Moved from "
                                + stateToString(mState) + " to STATE_QUICK_DOZE_DELAY");
                    }
                    mState = STATE_QUICK_DOZE_DELAY;
                    // Make sure any motion sensing or locating is stopped.
                    resetIdleManagementLocked();
                    if (isUpcomingAlarmClock()) {
                        
                        scheduleAlarmLocked(
                                mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime()
                                        + mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
                    } else {
                        
                        scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
                    }
                    EventLogTags.writeDeviceIdle(mState, "no activity");
                } else if (mState == STATE_ACTIVE) {
                    mState = STATE_INACTIVE;
                    if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
                    resetIdleManagementLocked();
                    long delay = mInactiveTimeout;
                    if (shouldUseIdleTimeoutFactorLocked()) {
                        delay = (long) (mPreIdleFactor * delay);
                    }
                    if (isUpcomingAlarmClock()) {
                        
                        scheduleAlarmLocked(
                                mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime()
                                        + delay, false);
                    } else {
                        scheduleAlarmLocked(delay, false);
                    }
                    EventLogTags.writeDeviceIdle(mState, "no activity");
                }
            }
            if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) {
                mLightState = LIGHT_STATE_INACTIVE;
                if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");
                resetLightIdleManagementLocked();
                scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
                
                EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
            }
        }

    becomeInactiveIfAppropriateLocked方法主要功能就是设置进入下一状态的闹钟

        private void becomeActiveLocked(String activeReason, int activeUid,
                long newInactiveTimeout, boolean changeLightIdle) {
           
            if (mState != STATE_ACTIVE || mLightState != STATE_ACTIVE) {
                EventLogTags.writeDeviceIdle(STATE_ACTIVE, activeReason);
                mState = STATE_ACTIVE;
                mInactiveTimeout = newInactiveTimeout;
                resetIdleManagementLocked();
                // Don't reset maintenance window start time if we're in a light idle maintenance window
                // because its used in the light idle budget calculation.
                if (mLightState != LIGHT_STATE_IDLE_MAINTENANCE) {
                    mMaintenanceStartTime = 0;
                }
    
                if (changeLightIdle) {
                    EventLogTags.writeDeviceIdleLight(LIGHT_STATE_ACTIVE, activeReason);
                    mLightState = LIGHT_STATE_ACTIVE;
                    resetLightIdleManagementLocked();
                    // Only report active if light is also ACTIVE.
                    scheduleReportActiveLocked(activeReason, activeUid);
                    addEvent(EVENT_NORMAL, activeReason);
                }
            }
        }

    becomeActiveLocked的功能是将状态设置回active状态,并且取消所有设置的进入下一状态的闹钟,取消行为监听

    doze模式light与deep两种模式之间各个状态之间转换在stepIdleStateLocked与stepLightIdleStateLocked方法中实现,这里详细介绍下stepIdleStateLocked

        @VisibleForTesting
        void stepIdleStateLocked(String reason) {
    
            ......
    
            switch (mState) {
                case STATE_INACTIVE:
                    // We have now been inactive long enough, it is time to start looking
                    // for motion and sleep some more while doing so.
                    startMonitoringMotionLocked();
                    long delay = mConstants.IDLE_AFTER_INACTIVE_TIMEOUT;
                    if (shouldUseIdleTimeoutFactorLocked()) {
                        delay = (long) (mPreIdleFactor * delay);
                    }
                    scheduleAlarmLocked(delay, false);
                    
                    moveToStateLocked(STATE_IDLE_PENDING, reason);
                    break;
                case STATE_IDLE_PENDING:
                    
                    moveToStateLocked(STATE_SENSING, reason);
                    cancelLocatingLocked();
                    mLocated = false;
                    mLastGenericLocation = null;
                    mLastGpsLocation = null;
                    updateActiveConstraintsLocked();
    
                    // Wait for open constraints and an accelerometer reading before moving on.
                    if (mUseMotionSensor && mAnyMotionDetector.hasSensor()) {
                        scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);
                        mNotMoving = false;
                        mAnyMotionDetector.checkForAnyMotion();
                        break;
                    } else if (mNumBlockingConstraints != 0) {
                        cancelAlarmLocked();
                        break;
                    }
    
                    mNotMoving = true;
                    // Otherwise, fall through and check this off the list of requirements.
                case STATE_SENSING:
                    cancelSensingTimeoutAlarmLocked();
                    moveToStateLocked(STATE_LOCATING, reason);
                    scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);
                    
                    LocationManager locationManager = mInjector.getLocationManager();
                    if (locationManager != null
                            && locationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
                        locationManager.requestLocationUpdates(mLocationRequest,
                                mGenericLocationListener, mHandler.getLooper());
                        mLocating = true;
                    } else {
                        mHasNetworkLocation = false;
                    }
                    if (locationManager != null
                            && locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
                        mHasGps = true;
                        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,
                                mGpsLocationListener, mHandler.getLooper());
                        mLocating = true;
                    } else {
                        mHasGps = false;
                    }
                    
                    // If we have a location provider, we're all set, the listeners will move state
                    // forward.
                    if (mLocating) {
                        break;
                    }
    
                    // Otherwise, we have to move from locating into idle maintenance.
                case STATE_LOCATING:
                    cancelAlarmLocked();
                    cancelLocatingLocked();
                    mAnyMotionDetector.stop();
    
                    // Intentional fallthrough -- time to go into IDLE state.
                case STATE_QUICK_DOZE_DELAY:
                    // Reset the upcoming idle delays.
                    mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
                    mNextIdleDelay = mConstants.IDLE_TIMEOUT;
    
                    // Everything is in place to go into IDLE state.
                case STATE_IDLE_MAINTENANCE:
                    
                    scheduleAlarmLocked(mNextIdleDelay, true);
                    if (DEBUG) Slog.d(TAG, "Moved to STATE_IDLE. Next alarm in " + mNextIdleDelay +
                            " ms.");
                    mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);
                    if (DEBUG) Slog.d(TAG, "Setting mNextIdleDelay = " + mNextIdleDelay);
                    mIdleStartTime = SystemClock.elapsedRealtime();
                    mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT);
                    if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {
                        mNextIdleDelay = mConstants.IDLE_TIMEOUT;
                    }
                    moveToStateLocked(STATE_IDLE, reason);
                    if (mLightState != LIGHT_STATE_OVERRIDE) {
                        mLightState = LIGHT_STATE_OVERRIDE;
                        cancelLightAlarmLocked();
                    }
                    addEvent(EVENT_DEEP_IDLE, null);
                    mGoingIdleWakeLock.acquire();
                    mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
                    break;
                case STATE_IDLE:
                    // We have been idling long enough, now it is time to do some work.
                    mActiveIdleOpCount = 1;
                    mActiveIdleWakeLock.acquire();
                    scheduleAlarmLocked(mNextIdlePendingDelay, false);
                    if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE. " +
                            "Next alarm in " + mNextIdlePendingDelay + " ms.");
                    mMaintenanceStartTime = SystemClock.elapsedRealtime();
                    mNextIdlePendingDelay = Math.min(mConstants.MAX_IDLE_PENDING_TIMEOUT,
                            (long)(mNextIdlePendingDelay * mConstants.IDLE_PENDING_FACTOR));
                    if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) {
                        mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
                    }
                    moveToStateLocked(STATE_IDLE_MAINTENANCE, reason);
                    addEvent(EVENT_DEEP_MAINTENANCE, null);
                    mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
                    break;
            }
        }
    

    从这里可以清晰的看到doze模式是怎么一步步深入检测的,从息屏断电到进入doze模式,需要经过多次闹钟等待,这期间不能有gps请求或者设备活动,否则会直接回退到最初的active状态,进入doze模式后通过mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);

            @Override public void handleMessage(Message msg) {
               
                switch (msg.what) {
                    
                    case MSG_REPORT_IDLE_ON:
                    case MSG_REPORT_IDLE_ON_LIGHT: {
                        // mGoingIdleWakeLock is held at this point
                        EventLogTags.writeDeviceIdleOnStart();
                        final boolean deepChanged;
                        final boolean lightChanged;
                        if (msg.what == MSG_REPORT_IDLE_ON) {
                            deepChanged = mLocalPowerManager.setDeviceIdleMode(true);
                            lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);
                            
                        } else {
                            deepChanged = mLocalPowerManager.setDeviceIdleMode(false);
                            lightChanged = mLocalPowerManager.setLightDeviceIdleMode(true);
                            
                        }
                        try {
                            mNetworkPolicyManager.setDeviceIdleMode(true);
                            mBatteryStats.noteDeviceIdleMode(msg.what == MSG_REPORT_IDLE_ON
                                    ? BatteryStats.DEVICE_IDLE_MODE_DEEP
                                    : BatteryStats.DEVICE_IDLE_MODE_LIGHT, null, Process.myUid());
                        } catch (RemoteException e) {
                        }
                        if (deepChanged) {
                            getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
                        }
                        if (lightChanged) {
                            getContext().sendBroadcastAsUser(mLightIdleIntent, UserHandle.ALL);
                        }
                        EventLogTags.writeDeviceIdleOnComplete();
                        
                        mGoingIdleWakeLock.release();
                    } break;

    进入后通过设置NetworkPolicyManagerService和BatteryStatsService的标志位来修改应用的联网策略和电视省电策略,并且发送广播通知其他模块进入了doze模式,

    其中广播会在DeviceIdleJobsController中对待执行的jobschedler进行延迟操作。

                    case MSG_REPORT_IDLE_OFF: {
                        // mActiveIdleWakeLock is held at this point
                        EventLogTags.writeDeviceIdleOffStart("unknown");
                        final boolean deepChanged = mLocalPowerManager.setDeviceIdleMode(false);
                        final boolean lightChanged = mLocalPowerManager.setLightDeviceIdleMode(false);
                        
                        try {
                            mNetworkPolicyManager.setDeviceIdleMode(false);
                            mBatteryStats.noteDeviceIdleMode(BatteryStats.DEVICE_IDLE_MODE_OFF,
                                    null, Process.myUid());
                        } catch (RemoteException e) {
                        }
                        if (deepChanged) {
                            incActiveIdleOps();
                            getContext().sendOrderedBroadcastAsUser(mIdleIntent, UserHandle.ALL,
                                    null, mIdleStartedDoneReceiver, null, 0, null, null);
                        }
                        if (lightChanged) {
                            incActiveIdleOps();
                            getContext().sendOrderedBroadcastAsUser(mLightIdleIntent, UserHandle.ALL,
                                    null, mIdleStartedDoneReceiver, null, 0, null, null);
                        }
                        // Always start with one active op for the message being sent here.
                        // Now we are done!
                        decActiveIdleOps();
                        EventLogTags.writeDeviceIdleOffComplete();
                    } break;

    进入maintenance状态所进行的操作和进入idle状态的操作正好相反,将NetworkPolicyManagerService和BatteryStatsService的标志位设置为false,发送广播通知idle状态短暂的结束。被延迟的job会被执行,此时应用也可以访问网络。

    至此DeviceIdleController的主要功能就大体分析完成,从谷歌官网提供的信息来看doze模式功能十分强大,但是DeviceIdleController却只是起到一个负责doze的进入与退出,通过一些列的条件判断是否需要进入doze模式,一旦进入真正的各种省电策略都是通过其他模块来具体协助实现,比如联网策略控制,电池状态控制,job任务控制,以进一步减少设备在不活动期间后台电量消耗,达到省电的目地。

     

    展开全文
  • Android中的Doze模式

    2021-02-07 15:01:34
    概述 Android 6.0引入的Doze机制在于节省系统耗电量,保护电池,延长电池的使用时间。当设备未连接至电源,且长时间处于闲置状态时,系统会将应用进入... 在进入Doze模式后,每间隔一段时间,会进入一段时长为30s的m

    概述

    Android 6.0引入的Doze机制在于节省系统耗电量,保护电池,延长电池的使用时间。当设备未连接至电源,且长时间处于闲置状态时,系统会将应用进入Doze,置于App Standby模式。而最终的效果,能提升30%的电量续航能力。

    Doze模式的状态

    该状态与API版本无关,未适配API23以上的应用只要运行在6.0以上的系统上就会受到Doze模式的影响。

    • 在屏幕熄灭30分钟、没有晃动并且在不充电的时候,会进入Doze模式
    • 在进入Doze模式后,每间隔一段时间,会进入一段时长为30s的maintenance window的窗口期,可以唤醒系统,进行网络交互等等
    • 进入Doze模式后,如果没有退出的话,系统唤醒的间隔时长会越来越长

    当系统处于Doze模式下,系统和白名单之外的应用将受到以下限制:

    • 无法访问网络
    • Wake Locks被忽略
    • AlarmManager闹铃会被推迟到下一个maintenance window响应
      • 使用setAndAllowWhileIdleSetExactAndAllowWhileIdle设置闹铃的闹钟则不会受到Doze模式的影响
      • setAlarmClock设置的闹铃在Doze模式下仍然生效,但系统会在闹铃生效前退出Doze
    • 系统不执行Wi-Fi/GPS扫描;
    • 系统不允许同步适配器运行;
    • 系统不允许JobScheduler运行;

    而位于白名单中的应用可以:

    • 继续使用网络并保留部分wake lock
    • Job和同步仍然会被推迟
    • 常规的AlarmManager闹铃也不会被触发

    应用申请加入白名单

    App可以通过PowerManager.isIgnoringBatteryOptimizations检查本App是否在系统的白名单列表中。

    如果不在,则可以通过在AndroidManifest.xml中添加REQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限,并且通过发送ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS的Intent来向用户申请该权限

    原理

    Doze的原理是在框架层对资源加入了一层资源的调度。在监听系统硬件或者屏幕亮暗的中断信号所发出来的广播,然后对于JobScheduler以及AlarmManager中任务进行统一调度。

    而Doze的源码在于链接:
    /frameworks/base/services/core/java/com/android/server/DeviceIdleController.java

    DeviceIdleController中存在一个mState变量来保存当前设备的状态,状态值如下:



    作者:None_Ling
    链接:https://www.jianshu.com/p/d62d58d6ba5a
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

      /** Device is currently active. */
        private static final int STATE_ACTIVE = 0;
        /** Device is inactve (screen off, no motion) and we are waiting to for idle. */
        private static final int STATE_INACTIVE = 1;
        /** Device is past the initial inactive period, and waiting for the next idle period. */
        private static final int STATE_IDLE_PENDING = 2;
        /** Device is currently sensing motion. */
        private static final int STATE_SENSING = 3;
        /** Device is currently finding location (and may still be sensing). */
        private static final int STATE_LOCATING = 4;
        /** Device is in the idle state, trying to stay asleep as much as possible. */
        private static final int STATE_IDLE = 5;
        /** Device is in the idle state, but temporarily out of idle to do regular maintenance. */
        private static final int STATE_IDLE_MAINTENANCE = 6;

    DeviceIdleController继承自SystemService,在SystemServer初始化的时候,会初始化该对象,并且将它添加到ServiceManager

    而在onBootPhase,即设备Boot初始化阶段,也就是所有的SystemService都初始化完毕后,DeviceIdleController会初始化需要用到的AlarmManager、LocationManager等,并且会调用updateDisplayLoced

    @Override
        public void onBootPhase(int phase) {
            if (phase == PHASE_SYSTEM_SERVICES_READY) {
                synchronized (this) {
                    mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
                    mBatteryStats = BatteryStatsService.getService();
                    mLocalPowerManager = getLocalService(PowerManagerInternal.class);
                    mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
                    mDisplayManager = (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE);
                    mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
                    mSigMotionSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
                    mLocationManager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
                    mLocationRequest = new LocationRequest().setQuality(LocationRequest.ACCURACY_FINE).setInterval(0).setFastestInterval(0).setNumUpdates(1);
                    mAnyMotionDetector = new AnyMotionDetector(
                            (PowerManager) getContext().getSystemService(Context.POWER_SERVICE),
                            mHandler, mSensorManager, this);
                    Intent intent = new Intent(ACTION_STEP_IDLE_STATE)
                            .setPackage("android")
                            .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                    mAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
    
                    Intent intentSensing = new Intent(ACTION_STEP_IDLE_STATE)
                            .setPackage("android")
                            .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                    mSensingAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intentSensing, 0);
                    mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
                    mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY| Intent.FLAG_RECEIVER_FOREGROUND);
    
                    IntentFilter filter = new IntentFilter();
                    filter.addAction(Intent.ACTION_BATTERY_CHANGED);
                    filter.addAction(ACTION_STEP_IDLE_STATE);
                    getContext().registerReceiver(mReceiver, filter);
    
                    mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
                    mDisplayManager.registerDisplayListener(mDisplayListener, null);
                    updateDisplayLocked();
                }
            }
        }

    而在updateDisplayLockedupdateChargingLocked函数中会判断当前屏幕是否亮着,或者是否在充电,如果屏幕熄灭或者没在充电的话,则会调用becomeInactiveIfAppropriateLocked开始准备进入Doze状态。
    PS:后者是在收到ACTION_BATTERY_CHANGED的时候调用的,代表充电的变化

    void updateDisplayLocked() {
            mCurDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
            // We consider any situation where the display is showing something to be it on,
            // because if there is anything shown we are going to be updating it at some
            // frequency so can't be allowed to go into deep sleeps.
            boolean screenOn = mCurDisplay.getState() == Display.STATE_ON;
            if (DEBUG) Slog.d(TAG, "updateDisplayLocked: screenOn=" + screenOn);
            if (!screenOn && mScreenOn) {
                mScreenOn = false;
                if (!mForceIdle) {
                    becomeInactiveIfAppropriateLocked();
                }
            } else if (screenOn) {
                mScreenOn = true;
                if (!mForceIdle) {
                    becomeActiveLocked("screen", Process.myUid());
                }
            }
        }
    
        void updateChargingLocked(boolean charging) {
            if (DEBUG) Slog.i(TAG, "updateChargingLocked: charging=" + charging);
            if (!charging && mCharging) {
                mCharging = false;
                if (!mForceIdle) {
                    becomeInactiveIfAppropriateLocked();
                }
            } else if (charging) {
                mCharging = charging;
                if (!mForceIdle) {
                    becomeActiveLocked("charging", Process.myUid());
                }
            }
        }

    becomeInactiveIfAppropriateLocked函数中:

    • 将状态设置成STATE_INACTIVE
    • 取消定位、传感器监听的闹钟
    • 重新设置mInactiveTimeout时长的闹钟,也就是30分钟或者3分钟
    • 在闹钟的Intent中,会发送一个广播ACTION_STEP_IDLE_STATE
    void becomeInactiveIfAppropriateLocked() {
            if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
            if (((!mScreenOn && !mCharging) || mForceIdle) && mEnabled && mState == STATE_ACTIVE) {
                // Screen has turned off; we are now going to become inactive and start
                // waiting to see if we will ultimately go idle.
                mState = STATE_INACTIVE;
                if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
                resetIdleManagementLocked();
                scheduleAlarmLocked(mInactiveTimeout, false);
                EventLogTags.writeDeviceIdle(mState, "no activity");
            }
        }
    
        void resetIdleManagementLocked() {
            mNextIdlePendingDelay = 0;
            mNextIdleDelay = 0;
            cancelAlarmLocked();
            cancelSensingAlarmLocked();
            cancelLocatingLocked();
            stopMonitoringSignificantMotion();
            mAnyMotionDetector.stop();
        }

    在接收到ACTION_STEP_IDLE_STATE的广播后,会调用stepIdleStateLocked,在该函数中,处理所有的状态变化,而在状态处理的过程中还会有几个Alarm被设置。在该函数中,主要涉及一些状态变化,以及闹钟的设置,借图说明:

    最终,在进入Doze模式后,会通过mHandler发送一个MSG_REPORT_IDLE_ON的消息,在该消息中,通过mNetworkPolicyManager.setDeviceIdleMode禁止网络连接,通过PowerManager来限制WakeLock

    case MSG_REPORT_IDLE_ON: {
                        EventLogTags.writeDeviceIdleOnStart();
                        mLocalPowerManager.setDeviceIdleMode(true);
                        try {
                            mNetworkPolicyManager.setDeviceIdleMode(true);
                            mBatteryStats.noteDeviceIdleMode(true, null, Process.myUid());
                        } catch (RemoteException e) {
                        }
                        getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
                        EventLogTags.writeDeviceIdleOnComplete();

     

    展开全文
  • Doze的退出,说的更严格一点,就是当Doze模式的状态由其他状态变为ACTIVE状态。简而言之,退出Doze模式有三种情况:屏幕亮屏、插入充电器、设备有移动。下面就这三种情况进行下分析。 在前面的分析中我们有见到过...

    概述

    Doze的退出,说的更严格一点,就是当Doze模式的状态由其他状态变为ACTIVE状态。简而言之,退出Doze模式有三种情况:屏幕亮屏、插入充电器、设备有移动。下面就这三种情况进行下分析。

    在前面的分析中我们有见到过becomeActiveLocked()方法,这个方法当时没有进行分析,这个方法就是用来退出Doze的,严格来说,是将Doze状态置为了ACTIVE状态,从而退出IDLE状态或MAINTENANCE状态。因此,不管是何种方式退出Doze,都会调用这个方法。该方法如下:

    void becomeActiveLocked(String activeReason, int activeUid) {
        if (mState != STATE_ACTIVE || mLightState != STATE_ACTIVE) {
            //设置一个Handler,在Handler中通知PMS、发送idle changed 广播
            scheduleReportActiveLocked(activeReason, activeUid);
            //将DeepDoze状态值置为STATE_ACTIVE
            mState = STATE_ACTIVE;
            //将LightDoze状态值置为LIGHT_STATE_ACTIVE
            mLightState = LIGHT_STATE_ACTIVE;
            //多久后进入INACTIVE状态
            mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
            mCurIdleBudget = 0;
            mMaintenanceStartTime = 0;
            //重置DeepDoze相关属性值、alarm、listener等
            resetIdleManagementLocked();
            //重置LightDoze相关属性值、alarm、listener等
            resetLightIdleManagementLocked();
        }
    }
    

    1.屏幕亮灭屏

    当亮屏或灭屏时,PMS发送亮灭屏广播,DIC中监听了亮灭屏的广播,负责接受该广播并进行屏幕状态改变后的操作:
    注册广播:

    filter = new IntentFilter();
    filter.addAction(Intent.ACTION_SCREEN_OFF);
    filter.addAction(Intent.ACTION_SCREEN_ON);
    getContext().registerReceiver(mInteractivityReceiver, filter);
    

    接收到广播后进行处理:

    private final BroadcastReceiver mInteractivityReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            synchronized (DeviceIdleController.this) {
                updateInteractivityLocked();
            }
        }
    };
    

    updateInteractivityLocked()方法中更新LightDoze和DeepDoze的状态,这个方法在第一篇文章中分析过了。

    2.充电状态改变

    当电池状态时,在BatteryService中会发送广播,DIC也对该广播进行了监听:

    IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_BATTERY_CHANGED);
    getContext().registerReceiver(mReceiver, filter);
    

    接收到广播后进行处理:

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override public void onReceive(Context context, Intent intent) {
            switch (intent.getAction()) {
                ............
                case Intent.ACTION_BATTERY_CHANGED: {
                    synchronized (DeviceIdleController.this) {
                        int plugged = intent.getIntExtra("plugged", 0);
                        updateChargingLocked(plugged != 0);
                    }
                } break;
                ....................
            }
        }
    };
    

    当接收到该广播后,调用updateChargingLocked(plugged != 0)来更新状态,参数表示是否插有充电线,该方法如下:

    void updateChargingLocked(boolean charging) {
        //由充电转变为放电
        if (!charging && mCharging) {
            mCharging = false;
            if (!mForceIdle) {
                //进入INACTIVE状态,开始Doze模式
                becomeInactiveIfAppropriateLocked();
            }
        } else if (charging) {//由放电转变为充电
            mCharging = charging;
            if (!mForceIdle) {
                //Doze状态退出ACTIVE状态,退出Doze模式
                becomeActiveLocked("charging", Process.myUid());
            }
        }
    }
    

    最终,也会调用becomeActiveLocked()退出Doze模式。

    3.其他服务通过Binder调用

    DeviceIdleController内部Binder也提供了用于退出Doze的接口:

    @Override public void exitIdle(String reason) {
        getContext().enforceCallingOrSelfPermission(Manifest.permission.DEVICE_POWER,
                null);
        long ident = Binder.clearCallingIdentity();
        try {
            exitIdleInternal(reason);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }
    

    其内部也是通过调用becomeActiveLocked()方法退出Doze的:

    
    public void exitIdleInternal(String reason) {
        synchronized (this) {
            becomeActiveLocked(reason, Binder.getCallingUid());
        }
    }
    

    4.MotionSensor检测状态发生

    这种情况和以上几种情况略不同,这种情况下Doze的退出是瞬时的,会将两个状态值都转换为STATE_ACTIVE后,又会立即调用becomeInactiveIfAppropriateLocked()方法进入INACTIVE状态,可以说是个“瞬时”退出。当MotionSensor检测到有移动时,则会退出Doze,处理逻辑在handleMotionDetectedLocked()中:

    void handleMotionDetectedLocked(long timeout, String type) {
        boolean becomeInactive = false;
        if (mState != STATE_ACTIVE) {
            //通过Handler通知PMS、NetworkPolicy、发送IDLE_CHANGED广播等
            scheduleReportActiveLocked(type, Process.myUid());
            mState = STATE_ACTIVE;//将DeepDoze状态变为ACTIVE状态
            mInactiveTimeout = timeout;
            mCurIdleBudget = 0;
            mMaintenanceStartTime = 0;
            becomeInactive = true;
        }
        if (mLightState == LIGHT_STATE_OVERRIDE) {
            mLightState = STATE_ACTIVE;//将LightDoze状态变为ACTIVE状态
            becomeInactive = true;
        }
        if (becomeInactive) {
            //进入INACTIVE状态
            becomeInactiveIfAppropriateLocked();
        }
    }
    
    展开全文
  • 在Android M中,Google就引入了Doze模式。它定义了一种全新的、低能耗的状态。  在该状态,后台只有部分任务被允许运行,其它任务都被强制停止。 在之前的博客中分析过Doze模式,就是device idle状态。可能有的...
  • Doze模式时序调研

    2022-01-13 21:39:35
    Doze模式 机型 Light idle Deepidle 时序 定制白名单 AppStandby BLE Scanning管控 华为P20 Pro 开启 64.5分钟...
  • While the device is dozing, syncs and jobs are turned off globally, ... When the device is in idle maintenance mode, all of the doze restrictions are removed (so AllowWhileIdle can happen once a minute).
  • Deep Doze,也就是Android的Doze模式了,表示深度Doze,比起LightDoze,它将进行更多的限制:无法进行网络访问和 GPS/WLAN 扫描、唤醒被忽略、闹钟和作业/同步被延迟。当然,它的触发条件也将更加苛刻:灭屏、未充电...
  • Android 8.1 Doze模式分析(一)

    千次阅读 2018-12-27 17:56:37
    Doze模式(低电耗模式),是Andoriod6.0增加的一项系统服务,主要目的是为了优化电池性能,增加电池续航时间,Doze模式又分两种模式:深度Doze模式(Deep Doze)和轻度Doze模式(Light Doze),如果用户长时...
  • Android7.0 Doze模式

    万次阅读 2016-11-07 15:30:56
    在Android M中,Google就引入了Doze模式。它定义了一种全新的、低能耗的状态。 在该状态,后台只有部分任务被允许运行,其它任务都被强制停止。本篇博客中,我们就来分析一下Android 7.0中Doze模式相关的流程。
  • 我认为这是Doze功能,它不允许我的警报发射.我使用sharedpreferences来处理选项://ENABLE NIGHT MODE TIMERint sHour = blockerTimerPreferences.getInt("sHour", 00);int sMinute = blockerTimerPreferences.getIn.....
  • Android Doze模式分析

    万次阅读 2016-09-12 15:01:23
    Android 6.0 Doze模式分析 Doze模式是Android6.0上新出的一种模式,是一种全新的、低能耗的状态,在后台只有部分任务允许运行,其他都被强制停止。当用户一段时间没有使用手机的时候,Doze模式通过延缓app后台的...
  • 【Android】Doze模式识别与检测

    千次阅读 2019-05-27 10:33:56
    从 Android 6.0(API 级别 23)开始,Android 引入了两个省电功能:Doze模式(官方翻译为低电耗模式)和 App Standby模式(官方翻译为应用待机模式),可通过管理应用在设备未连接至电源时的行为方式为用户延长电池...
  • Doze模式,官方翻译为低电耗模式,是Andoriod6.0增加的一项系统服务,主要目的是为了优化电池性能,增加电池续航时间,Doze模式又分两种模式:深度Doze模式(Deep Doze)和轻度Doze模式(Light Doze),如果用户长时间...
  • 进入到IDLE状态(可用adb shell dumpsys deviceidle命令查看:mState=IDLE) 至此,手机进入到深度休眠模式。 如果手机有TYPE_SIGNIFICANT_MOTION运动触发传感器,用力左右晃动,手机会进入到INACTIVE状态。即退出...
  • Android的Doze模式

    2019-06-24 00:57:57
    也是偶然之间听到这个词的 Doze模式 Doze模式 Doze 翻译为打盹, 那么Android的Doze模式呢 , 就是让手机进入了类似打盹的一个状态 , 在这个半梦半醒的状态下 , 手机的后台、服务、广播等都会发生相应的延迟。 Doze...
  • 1. adb 命令模拟进入doze模式 设置未充电状态 方便连接logcat查看实时日志,正常情况下如果连接 USB 是无法进入doze模式,这个步骤是欺骗系统当前没有连接USB,虽然实际连接得好好的 adb shell dumpsys ...
  • Android7.0 Doze模式分析(三)alarm

    千次阅读 2019-01-22 15:09:37
    上篇博客分析了Doze模式下WakeLock,这篇我们分析Alarm。 白名单 首先我们从白名单开始,在DeviceIdleController中会设置alarm的白名单。   public final class LocalService { public void ...
  • Android Doze模式模拟 从Android 6.0(API level 23)开始,Android提出了两个延长电池使用时间的省电特性给用户。用户管理可以在没有充电的情况下管理app的行为。当用户一段时间没有使用手机的时候,Doze模式通过...
  • Android Doze模式适配实验记录

    千次阅读 2019-12-02 15:28:47
    QQ交流群:122717260 课题:想定时执行一些代码,如每隔4分钟向服务器发送一个数据包。 实验1:用TimerTask. 机型:vivo android 9.0 V1911A vivoZ5...自从Android6.0引入低功耗与待机模式之后,TimerTask就更不能...
  • android 7.0低电耗Doze模式

    千次阅读 2017-04-13 16:01:46
    从 Android 6.0(API 级别 23)开始,Android 引入了两个省电功能,可通过管理应用在设备未连接至电源时的行为方式为用户延长电池寿命。 低电耗(Doze)模式通过在设备长时间处于闲置状态时...低电耗模式(Doze模式)...
  • 电信设备-Doze模式切换方法、装置、移动终端及可读存储介质.zip
  • Android Doze模式下的AlarmManager策略

    千次阅读 2017-08-30 11:53:46
    Doze 模式的定义Android 6.0引入了Doze模式,用户拔掉电源,在关闭手机屏幕并且不动的一段时间后,系统便会进入Doze模式。 此模式下通过延缓CPU和网络活动减少电量的消耗。阻止APP访问网络,推迟jobs,syncs,标准 ...
  • 激活Alarm的Doze模式 void scheduleAlarmLocked(long delay, boolean idleUntil) { if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")"); // NOTE: Bug #627645 low power ...
  • LightDoze表示轻度doze模式,如果设备处于未充电且屏幕关闭状态,但未处于静止状态时,就会进入Light Doze模式,在LightDoze模式中,会定期进行维护,这种维护会持续N分钟,在维护状态(maintenance)时,会进行网络的...
  • Doze模式adb shell命令集

    千次阅读 2017-11-20 15:38:14
    一、adb 指令1.1 让设备进入未连接充电的模式进行doze模式命令模拟的话,需要先下发该命令,因为只要存在充电状态,是无法从Acitve状态切换到InActive状态的adb shell dumpsys battery unplugZ90:/ # dumpsys ...
  • Android O 的Doze模式白名单路径

    千次阅读 2017-11-17 15:28:03
    Doze 模式列表上述备注规则如下if(powerWhitelist.isSysWhitelisted(pkg)) { // Summary of app which doesn't have a battery optimization setting show:Battery optimization not available } else { if...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,068
精华内容 827
关键字:

doze模式

友情链接: graf.zip