概要

初识Android PowerManagerService省电模式对省电模式进行了一个初步认识,介绍了一些概念,以及对省电模式环境初始化的代码进行了简单分析。读者需要仔细阅读第一篇文章,再来看这一篇文章。

打开省电模式,有三种方式:

  • 手动模式,也就是用户手动打开省电模式。
  • 自动模式,用户设置一个电量百分比阈值,当电量低于这个阈值,自动触发省电模式。
  • 动态模式,这种模式其实就是自动模式。根据文档,这个模式是提供给应用,根据情况自动调整触发省电模式的阈值。

本文只关注如下内容:

  • 省电模式的打开过程。
  • 什么是 battery saver sticky 模式。

只要掌握了上面2点内容,自动模式、动态模式,都可以自行分析。

打开省电模式

现在以手动打开省电模式为例,分析省电模式的打开过程。

从初识Android PowerManagerService省电模式可知,在 Settings->Battery->Battery Saver 界面,可以手动打开省电模式,调用代码如下

最终会调用 PowerManagerService 对应的方法:

public boolean setPowerSaveModeEnabled(boolean enabled) {
    if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER)
            != PackageManager.PERMISSION_GRANTED) {
        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.DEVICE_POWER, null);
    }
    final long ident = Binder.clearCallingIdentity();
    try {
        return setLowPowerModeInternal(enabled);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}


private boolean setLowPowerModeInternal(boolean enabled) {
    synchronized (mLock) {
        // 充电状态下,不允许打开/关闭省电模式
        if (mIsPowered) {
            return false;
        }

        mBatterySaverStateMachine.setBatterySaverEnabledManually(enabled);

        return true;
    }
}

在 AOSP 的设计中,省电模式和充电状态是冲突的。如果设备处于省电模式状态,此时插入充电器,那么一定会关闭省电模式。如果设备处于充电状态,那么是不允许打开省电模式的。

说实话,我不是很认同这种设计。我认为省电模式是用户的强烈个人意愿,只能由用户自己决定打开或者关闭。

BatterySaverStateMachine状态管理

从上面代码可知,打开省电模式时,通过 BatterySaverStateMachine#setBatterySaverEnabledManually() 方法,把指令传给状态机

public void setBatterySaverEnabledManually(boolean enabled) {
    synchronized (mLock) {
        updateStateLocked(true, enabled);
    }
}

状态机通过 updateStateLocked() 更新内部状态,然后根据状态执行相应的操作。 注意,这里的第一个参数表示是否是用户手动打开省电模式,值为 true,第二个参数表示是否打开省电模式,根据我们分析的例子,这里的值为 true。

    private void updateStateLocked(boolean manual, boolean enable) {
        if (!manual && !(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) {
            return; // Not fully initialized yet.
        }

        switch (mState) {
            case STATE_OFF: {
                if (!mIsPowered) { // 非充电模式,才允许操作省电模式
                    if (manual) { // 手动操作
                        if (!enable) {
                            return;
                        }
                        // 用户手动打开省电模式
                        enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
                                BatterySaverController.REASON_MANUAL_ON);
                        hideStickyDisabledNotification();
                        // 状态切换为 STATE_MANUAL_ON
                        mState = STATE_MANUAL_ON;
                    } else if (isAutomaticModeActiveLocked() && isInAutomaticLowZoneLocked()) {
                        // ... 自动模式
                    } else if (isDynamicModeActiveLocked() && isInDynamicLowZoneLocked()) { 
                        // ... 动态模式
                    }
                }
                break;
            }

            // ...
        }
    }

状态机里的默认状态是 STATE_OFF,表示省电模式默认关闭。

通过 enableBatterySaverLocked(/*enable*/ true, /*manual*/ true, BatterySaverController.REASON_MANUAL_ON); 打开省电模式,然后把状态切换为 STATE_MANUAL_ON。对于每一次状态切换,我们都要注意,因此这会影响下一次状态切换。

private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason) {
    enableBatterySaverLocked(enable, manual, intReason, reasonToString(intReason));
}

private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason,
        String strReason) {
    final boolean wasEnabled = mBatterySaverController.isFullEnabled();
    // 已经处于省电模式状态
    if (wasEnabled == enable) {
        return;
    }

    // 充电中,是不允许打开省电模式的
    if (enable && mIsPowered) {
        return;
    }

    mLastChangedIntReason = intReason;
    mLastChangedStrReason = strReason;

    mSettingBatterySaverEnabled = enable;
    // 1. 保存省电模式的状态
    putGlobalSetting(Settings.Global.LOW_POWER_MODE, enable ? 1 : 0);

    // 2. 打开 battery saver sticky 模式
    if (manual) { // 用户手动操作省电模式
        // mBatterySaverStickyBehaviourDisabled 默认为 false,表示支持 battery saver sticky 模式
        setStickyActive(!mBatterySaverStickyBehaviourDisabled && enable);
    }

    // 3. 通过 BatterySaverController 打开省电模式
    mBatterySaverController.enableBatterySaver(enable, intReason);

    // 动态省电模式相关
    if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) {
        triggerDynamicModeNotification();
    } else if (!enable) {
        hideDynamicModeNotification();
    }
}

在打开省电模式之前,首先把数据库 Settings.Global.LOW_POWER_MODE 字段的值保存为 1。

low power 应该翻译为低功耗,俗称省电模式,而 low battery 才应该翻译为低电量,不要混淆了。 源码中 BatteryManagerService#getBatteryLevelLow() 表示电量是否低于自动省电模式的电量百分比,这个函数的命名非常差劲,一度让我误以为是低电量(电量低于15%),其实它表示是否触发了自动省电模式。

第二步,我们要注意了,这里涉及了 battery saver sticky 功能。根据判断条件可知,只有在用户手动操作省电模式的情况下,才会触发 battery saver sticky 功能,来看下 setStickyActive()

private void setStickyActive(boolean active) {
    // 表示 battery saver sticky 模式已经打开
    mSettingBatterySaverEnabledSticky = active;
    // Settings.Global.LOW_POWER_MODE_STICKY 代表 battery saver sticky功能的状态
    putGlobalSetting(Settings.Global.LOW_POWER_MODE_STICKY,
            mSettingBatterySaverEnabledSticky ? 1 : 0);
}

很简单,就是保存状态,表示 battery saver sticky 功能已经打开。

第三步,把打开省电模式的实际操作,交给了省电模式控制器 BatterySaverController

BatterySaverController切换省电模式

现在来看下 BatterySaverController#enableBatterySaver() 如何打开省电模式

public void enableBatterySaver(boolean enable, int reason) {
    synchronized (mLock) {
        if (getFullEnabledLocked() == enable) {
            return;
        }
        // 1. 保存省电模式的状态
        setFullEnabledLocked(enable);

        // 2. 更新省电模式策略
        if (updatePolicyLevelLocked()) {
            // 3. 处理省电模式状态的改变
            mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
        }
    }
}

private boolean getFullEnabledLocked() {
    return mFullEnabledRaw;
}
private void setFullEnabledLocked(boolean value) {
    if (mFullEnabledRaw == value) {
        return;
    }
    // 刷新省电模式的缓存,客户端可以通过 PowerManager 获取省电模式状态
    PowerManager.invalidatePowerSaveModeCaches();
    mFullEnabledRaw = value;
}

首先使用 mFullEnabledRaw 保存省电模式状态。

然后,更新省电模式的策略。省电模式会影响很多模块的功能,例如,最直观的就是影响屏幕亮度。因此打开省电模式,必须得有一个策略,这些策略影响某些模块的功能。

最后,处理省电模式状态的改变。其中包括切换省电模式,通知省电模式的监听者。

mFullEnabledRaw 表示 full battery saver,其实就是用户用到的省电模式。其实还有一种省电模式 adaptive battery saver,这种省电模式,是通过命令行设置的,应该是与测试相关,执行 adb shell power set-adaptive-power-saver-enabled true 来开启,具体可以参考 PowerManagerShellCommand 类。

BattterySaverPolicy控制省电策略

现在来看下 BatterySaverController#updatePolicyLevelLocked() 如何更新省电模式策略

private boolean updatePolicyLevelLocked() {
    if (getFullEnabledLocked()) {
        // 设置省电模式 policy level
        return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_FULL);
    } else if (getAdaptiveEnabledLocked()) {
        return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_ADAPTIVE);
    } else {
        return mBatterySaverPolicy.setPolicyLevel(BatterySaverPolicy.POLICY_LEVEL_OFF);
    }
}

原来是给 BatterySaverPolicy 设置了 policy level,值为 BatterySaverPolicy.POLICY_LEVEL_FULL

boolean setPolicyLevel(@PolicyLevel int level) {
    synchronized (mLock) {
        if (mPolicyLevel == level) {
            return false;
        }
        if (mPolicyLevel == POLICY_LEVEL_FULL) {
            mFullPolicy = mDefaultFullPolicy;
        }
        switch (level) {
            case POLICY_LEVEL_FULL:
            case POLICY_LEVEL_ADAPTIVE:
            case POLICY_LEVEL_OFF:
                // 1. 保存 level
                mPolicyLevel = level;
                break;
            default:
                Slog.wtf(TAG, "setPolicyLevel invalid level given: "   level);
                return false;
        }
        // 2. 根据 level,更新有效的 policy
        updatePolicyDependenciesLocked();
        return true;
    }
}

BatterSaverPolicy 保存了 policy level,并且调用 updatePolicyDependenciesLocked() 来更新有效的 battery saver policy

private void updatePolicyDependenciesLocked() {
    // 1. 根据 policy level, 获取对应的 policy
    final Policy rawPolicy = getCurrentRawPolicyLocked();

    // 刷新省电模式缓存
    invalidatePowerSaveModeCaches();

    // 车载
    final int locationMode;
    if (mAutomotiveProjectionActive.get()
            && rawPolicy.locationMode != PowerManager.LOCATION_MODE_NO_CHANGE
            && rawPolicy.locationMode != PowerManager.LOCATION_MODE_FOREGROUND_ONLY) {
        // If car projection is enabled, ensure that navigation works.
        locationMode = PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
    } else {
        locationMode = rawPolicy.locationMode;
    }

    // 2. 根据获取的策略,来更新有效的策略
    // mEffectivePolicyRaw 表示实际生效的 policy
    // mEffectivePolicyRaw 的数据,基本上都是从 rawPolicy 中复制过来的
    // 只有几项是需要调整的,例如 车载 或者 无障碍,这两个特殊的情况,在使用时注意下即可
    mEffectivePolicyRaw = new Policy(
            rawPolicy.adjustBrightnessFactor,
            rawPolicy.advertiseIsEnabled,
            rawPolicy.cpuFrequenciesForInteractive,
            rawPolicy.cpuFrequenciesForNoninteractive,
            rawPolicy.deferFullBackup,
            rawPolicy.deferKeyValueBackup,
            rawPolicy.disableAnimation,
            rawPolicy.disableAod,
            rawPolicy.disableLaunchBoost,
            rawPolicy.disableOptionalSensors,
            // Don't disable vibration when accessibility is on.
            rawPolicy.disableVibration && !mAccessibilityEnabled.get(),
            rawPolicy.enableAdjustBrightness,
            rawPolicy.enableDataSaver,
            rawPolicy.enableFirewall,
            // Don't force night mode when car projection is enabled.
            rawPolicy.enableNightMode && !mAutomotiveProjectionActive.get(),
            rawPolicy.enableQuickDoze,
            rawPolicy.forceAllAppsStandby,
            rawPolicy.forceBackgroundCheck,
            locationMode,
            rawPolicy.soundTriggerMode
    );
    // ...
}

// 默认省电模式策略
private Policy mFullPolicy = DEFAULT_FULL_POLICY;

private Policy getCurrentRawPolicyLocked() {
    switch (mPolicyLevel) {
        case POLICY_LEVEL_FULL:
            return mFullPolicy;
        case POLICY_LEVEL_ADAPTIVE:
            return mAdaptivePolicy;
        case POLICY_LEVEL_OFF:
        default:
            return OFF_POLICY;
    }
}

getCurrentRawPolicyLocked() 会获取默认的省电模式策略 DEFAULT_FULL_POLICY,然后根据一些情况调整省电模式策略,最后形成有效的省电模式策略 mEffectivePolicyRaw

现在让我们看看这个默认的省电策略 DEFAULT_FULL_POLICY 到底是何方神圣

private static final Policy DEFAULT_FULL_POLICY = new Policy(
        0.5f,  /* adjustBrightnessFactor */
        true,  /* advertiseIsEnabled */
        new CpuFrequencies(), /* cpuFrequenciesForInteractive */
        new CpuFrequencies(), /* cpuFrequenciesForNoninteractive */
        true,  /* deferFullBackup */
        true,  /* deferKeyValueBackup */
        false, /* disableAnimation */
        true,  /* disableAod */
        true,  /* disableLaunchBoost */
        true,  /* disableOptionalSensors */
        true,  /* disableVibration */
        false, /* enableAdjustBrightness */
        false, /* enableDataSaver */
        true,  /* enableFirewall */
        true, /* enableNightMode */
        true, /* enableQuickDoze */
        true, /* forceAllAppsStandby */
        true, /* forceBackgroundCheck */
        PowerManager.LOCATION_MODE_FOREGROUND_ONLY, /* locationMode */
        PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY /* soundTriggerMode */
);

Policy 就是一个数据封装类,看下它构造函数的参数,我们就能大致猜测出省电模式影响哪些模块的功能。

这里注意下第三个和第四个参数,它表示省电模式下,需要限制频率的 CPU 的编号以及限制的频率值,这里默认是空,后面会用到。

处理省电模式状态改变

现在让我们回到打开省电模式的代码

public void enableBatterySaver(boolean enable, int reason) {
    synchronized (mLock) {
        if (getFullEnabledLocked() == enable) {
            return;
        }
        // 1. 保存省电模式的状态
        setFullEnabledLocked(enable);

        // 2. 更新省电模式策略
        if (updatePolicyLevelLocked()) {
            // 3. 处理省电模式状态的改变
            mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
        }
    }
}

前两步已经分析完毕,现在来看看第三步,它最终会调用 BatterySaverController#handleBatterySaverStateChanged() 来处理省电模式状态改变

void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) {
    final LowPowerModeListener[] listeners;

    final boolean enabled;
    // 获取设备是否处于交互状态
    // 一般来说,如果屏幕熄灭,设备处于非交互状态,屏幕电量,设备处于交互状态
    final boolean isInteractive = getPowerManager().isInteractive();
    final ArrayMap<String, String> fileValues;

    synchronized (mLock) {
        // 获取省电模式的状态
        enabled = getFullEnabledLocked() || getAdaptiveEnabledLocked();

        // 保存前一个 full battery saver状态
        mFullPreviouslyEnabled = getFullEnabledLocked();
        // 保存前一个adaptive battery saver状态
        mAdaptivePreviouslyEnabled = getAdaptiveEnabledLocked();

        listeners = mListeners.toArray(new LowPowerModeListener[0]);

        mIsInteractive = isInteractive;

        if (enabled) {
            // 1. 打开省电模式情况下,获取频率受限的CPU的编号以及受限的值
            fileValues = mBatterySaverPolicy.getFileValues(isInteractive);
        } else {
            fileValues = null;
        }
    }

    // 2. 通过 PowerManagerService 向底层设置省电模式
    final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
    if (pmi != null) {
        pmi.setPowerMode(Mode.LOW_POWER, isEnabled());
    }

    // 用 BatterySavingStats 记录数据
    updateBatterySavingStats();

    // 3. 根据策略,限制或恢复CPU频率
    if (ArrayUtils.isEmpty(fileValues)) {
        // CPU 策略为空,表示需要恢复 CPU 之前的频率
        mFileUpdater.restoreDefault();
    } else {
        // CPU 频率策略不为空,表示需要限制 CPU 频率
        mFileUpdater.writeFiles(fileValues);
    }

    if (sendBroadcast) {
        // 4. 发送广播
        Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);

        // 可以在 frameworks-res 的配置文件中配置一个应用的包名
        // 这个应用可以在manifest.xml中注册广播接收器,接收省电模式状态改变
        if (getPowerSaveModeChangedListenerPackage().isPresent()) {
            intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)
                    .setPackage(getPowerSaveModeChangedListenerPackage().get())
                    .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
                            | Intent.FLAG_RECEIVER_FOREGROUND);
            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
        }

        // 发送一个内部版本的广播,但是接收者需要权限
        intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
                Manifest.permission.DEVICE_POWER);

        // 5. 通知监听者
        for (LowPowerModeListener listener : listeners) {
            final PowerSaveState result =
                    mBatterySaverPolicy.getBatterySaverPolicy(listener.getServiceType());
            listener.onLowPowerModeChanged(result);
        }
    }
}

第一步和第三步,是在省电模式下限制 CPU 频率的。根据前面分析可知,目前默认策略是没有配置CPU频率的,因此这两步不分析了。我将在后面的文章中,分析如何控制省电模式策略,到时候再来分析这里的代码逻辑。

第二步,通过 PowerManagerService 向底层设置省电模式,底层称之为低功耗模式(low power mode)。

第四步,发送省电模式状态改变的广播。

第五步,通知监听者。谁会是监听者呢?当然是那些受省电模式影响的模块。

让我们看下返回给监听者的数据到底是什么?看下mBatterySaverPolicy.getBatterySaverPolicy(listener.getServiceType())

    public PowerSaveState getBatterySaverPolicy(@ServiceType int type) {
        synchronized (mLock) {
            final Policy currPolicy = getCurrentPolicyLocked();
            final PowerSaveState.Builder builder = new PowerSaveState.Builder()
                    .setGlobalBatterySaverEnabled(currPolicy.advertiseIsEnabled);
            switch (type) {
                case ServiceType.LOCATION:
                    boolean isEnabled = currPolicy.advertiseIsEnabled
                            || currPolicy.locationMode != PowerManager.LOCATION_MODE_NO_CHANGE;
                    return builder.setBatterySaverEnabled(isEnabled)
                            .setLocationMode(currPolicy.locationMode)
                            .build();
                case ServiceType.ANIMATION:
                    return builder.setBatterySaverEnabled(currPolicy.disableAnimation)
                            .build();
                // ...

                case ServiceType.VIBRATION:
                    return builder.setBatterySaverEnabled(currPolicy.disableVibration)
                            .build();
                case ServiceType.FORCE_ALL_APPS_STANDBY:
                    return builder.setBatterySaverEnabled(currPolicy.forceAllAppsStandby)
                            .build();
                case ServiceType.FORCE_BACKGROUND_CHECK:
                    return builder.setBatterySaverEnabled(currPolicy.forceBackgroundCheck)
                            .build();
                // ...
                default:
                    return builder.setBatterySaverEnabled(currPolicy.advertiseIsEnabled)
                            .build();
            }
        }
    }

原来,根据监听者的类型,返回一个 PowerSaveState 对象,这个对象中只包含了监听者关心的数据。

从这里,我们应该有所领悟,如果我们自己开发了一个功能模块

  • 如果受省电模式策略影响,必须注册一个监听器,获取省电模式下策略,然后调整模块的功能。
  • 如果这个模块是个耗电大户,那么必须监听省电模式,在省电模式下执行相应的操作。

现在很多项目都关注电量消耗问题,省电模式到底能让手机待机多长时间,也是一个考核的指标。

battery saver sticky 模式

根据前面的分析,只有在用户手动操作省电模式的时候,才会相应的打开或者关闭 battery saver sticky 模式。

我先总结下什么是 battery saver sticky 模式? 当手机已经处于省电模式,插入电源,系统会默认关闭省电模式,如果此时拔掉电源或者手机重启,当 battery saver sticky 功能已经打开的情况下,系统会重新打开省电模式。

现在让我们从代码角度分析,继续使用上面的例子分析,假如现在已经打开了省电模式,此时插入了电源,来看下状态机的切换动作 BatterySaverStateMachine#updateStateLocked()

    private void updateStateLocked(boolean manual, boolean enable) {
        if (!manual && !(mBootCompleted && mSettingsLoaded && mBatteryStatusSet)) {
            return; // Not fully initialized yet.
        }

        switch (mState) {
            case STATE_OFF: {
                if (!mIsPowered) { // 充电状态下,不允许打开省电模式
                    if (manual) { // 手动模式
                        if (!enable) {
                            Slog.e(TAG, "Tried to disable BS when it's already OFF");
                            return;
                        }
                        enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
                                BatterySaverController.REASON_MANUAL_ON);
                        hideStickyDisabledNotification();
                        // 1. 用户打开省电模式,状态切换为 STATE_MANUAL_ON
                        mState = STATE_MANUAL_ON;
                    } else if (isAutomaticModeActiveLocked() && isInAutomaticLowZoneLocked()) {
                        // 自动模式 ...
                    } else if (isDynamicModeActiveLocked() && isInDynamicLowZoneLocked()) {
                        // 动态模式 ...
                    }
                }
                break;
            }

            case STATE_MANUAL_ON: {
                if (manual) {
                    // ...
                } else if (mIsPowered) { // 2. 插入电源
                    // 关闭省电模式
                    enableBatterySaverLocked(/*enable*/ false, /*manual*/ false,
                            BatterySaverController.REASON_PLUGGED_IN);
                    // 手动打开省电模式时,mSettingBatterySaverEnabledSticky 设置为 true
                    // mBatterySaverStickyBehaviourDisabled 默认为 false,表示支持这个 feature
                    if (mSettingBatterySaverEnabledSticky
                            && !mBatterySaverStickyBehaviourDisabled) {
                        // 插入电源,状态切换为 STATE_PENDING_STICKY_ON
                        mState = STATE_PENDING_STICKY_ON;
                    } else {
                        mState = STATE_OFF;
                    }
                }
                break;
            }

            // ...

            case STATE_PENDING_STICKY_ON: { // 3. battery saver sticky 模式操作
                if (manual) {
                    return;
                }
                // mSettingBatterySaverStickyAutoDisableEnabled 对应 Battery Saver界面下的 Turn off when charging 开关
                // mSettingBatterySaverStickyAutoDisableThreshold 默认值为 90
                final boolean shouldTurnOffSticky = mSettingBatterySaverStickyAutoDisableEnabled
                        && mBatteryLevel >= mSettingBatterySaverStickyAutoDisableThreshold;
                // 手动打开省电模式,再插入电源,此时 isStickyDisabled 值为 false
                final boolean isStickyDisabled =
                        mBatterySaverStickyBehaviourDisabled || !mSettingBatterySaverEnabledSticky;
                if (isStickyDisabled || shouldTurnOffSticky) {
                    // 3.2 如果Turn off when charging 开关被打开,并且电量大于90%,那么不会重新打开省电模式
                    mState = STATE_OFF;
                    setStickyActive(false);
                    triggerStickyDisabledNotification();
                } else if (!mIsPowered) {
                    // Re-enable BS.
                    // 3.1 断开电源,重新打开省电模式
                    enableBatterySaverLocked(/*enable*/ true, /*manual*/ true,
                            BatterySaverController.REASON_STICKY_RESTORE);
                    mState = STATE_MANUAL_ON;
                }
                break;
            }
            // ...
        }
    }

首先看下第一步,它打开了省电模式,并且状态切换为 STATE_MANUAL_ON

如果此时,插入电源,那么会进入第二步, 关闭省电模式, 并把状态切换为 STATE_PENDING_STICKY_ON

如果关闭了设置中 Battery Saver 界面的 Turn off when Charging 开关,此时拔掉电源,那么进入 3.1 步,又会再次打开省电模式,这就是 sticky 的含义。

如果打开了设置中 Battery Saver 界面的 Turn off when Charging 开关,那么进入 3.2 步,不会再次打开省电模式。

设置中 Battery Saver 界面的 Turn off when Charging 开关就是 battery saver sticky auto disable 功能。

结束

通读了整个省电模式的代码,给我的感觉是很多功能都非常鸡肋,限于偏于原因,我只分析了核心的代码,那就是如何切换省电模式。剩下的其他功能,留给读者自行分析。

到此这篇关于Android PowerManagerService 打开省电模式的文章就介绍到这了,更多相关Android PowerManagerService 内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Android PowerManagerService 打开省电模式的更多相关文章

  1. html5 canvas合成海报所遇问题及解决方案总结

    这篇文章主要介绍了html5 canvas合成海报所遇问题及解决方案总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  4. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  5. Ionic – Splash Screen适用于iOS,但不适用于Android

    我有一个离子应用程序,其中使用CLI命令离子资源生成的启动画面和图标iOS版本与正在渲染的启动画面完美配合,但在Android版本中,只有在加载应用程序时才会显示白屏.我检查了config.xml文件,所有路径看起来都是正确的,生成的图像出现在相应的文件夹中.(我使用了splash.psd模板来生成它们.我错过了什么?这是config.xml文件供参考,我觉得我在这里做错了–解决方法在config.xml中添加以下键:它对我有用!

  6. ios – 无法启动iPhone模拟器

    /Library/Developer/CoreSimulator/Devices/530A44CB-5978-4926-9E91-E9DBD5BFB105/data/Containers/Bundle/Application/07612A5C-659D-4C04-ACD3-D211D2830E17/ProductName.app/ProductName然后,如果您在Xcode构建设置中选择标准体系结构并再次构建和运行,则会产生以下结果:dyld:lazysymbolbindingFailed:Symbol

  7. Xamarin iOS图像在Grid内部重叠

    heyo,所以在Xamarin我有一个使用并在其中包含一对,所有这些都包含在内.这在Xamarin.Android中看起来完全没问题,但是在Xamarin.iOS中,图像与标签重叠.我不确定它的区别是什么–为什么它在Xamarin.Android中看起来不错但在iOS中它的全部都不稳定?

  8. 在iOS上向后播放HTML5视频

    我试图在iPad上反向播放HTML5视频.HTML5元素包括一个名为playbackRate的属性,它允许以更快或更慢的速率或相反的方式播放视频.根据Apple’sdocumentation,iOS不支持此属性.通过每秒多次设置currentTime属性,可以反复播放,而无需使用playbackRate.这种方法适用于桌面Safari,但似乎在iOS设备上的搜索限制为每秒1次更新–在我的情况下太慢了.有没有办法在iOS设备上向后播放HTML5视频?解决方法iOS6Safari现在支持playbackRat

  9. 使用 Swift 语言编写 Android 应用入门

    Swift标准库可以编译安卓armv7的内核,这使得可以在安卓移动设备上执行Swift语句代码。做梦,虽然Swift编译器可以胜任在安卓设备上编译Swift代码并运行。这需要的不仅仅是用Swift标准库编写一个APP,更多的是你需要一些框架来搭建你的应用用户界面,以上这些Swift标准库不能提供。简单来说,构建在安卓设备上使用的Swiftstdlib需要libiconv和libicu。通过命令行执行以下命令:gitclonegit@github.com:SwiftAndroid/libiconv-libi

  10. Android – 调用GONE然后VISIBLE使视图显示在错误的位置

    我有两个视图,A和B,视图A在视图B上方.当我以编程方式将视图A设置为GONE时,它将消失,并且它正下方的视图将转到视图A的位置.但是,当我再次将相同的视图设置为VISIBLE时,它会在视图B上显示.我不希望这样.我希望视图B回到原来的位置,这是我认为会发生的事情.我怎样才能做到这一点?编辑–代码}这里是XML:解决方法您可以尝试将两个视图放在RelativeLayout中并相对于彼此设置它们的位置.

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部