Android PowerManagerService 打开省电模式

目录
  • 概要
  • 打开省电模式
  • BatterySaverStateMachine状态管理
  • BatterySaverController切换省电模式
    • BattterySaverPolicy控制省电策略
    • 处理省电模式状态改变
  • battery saver sticky 模式
  • 结束

概要

初识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 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2022-08-22

Android省电的秘密之JobScheduler

JobScheduler是Android L版本新引入的API,JobScheduler,顾名思义,是用来调度工作.工作被调度的条件包括网络变化,充电插拔,周期执行等.使用场景包括wifi条件下数据下载上传等等.谷歌为什么要引入这个新的API呢?是为了省电而制定的一种规范.想想如果每个开发者都利用这个API进行wifi网络下数据上传,数据上传的操作将会被统一到同一个时间点,批量处理,这样比许多应用单独唤醒要省电的多. 下面展示一个小例子 主MainActivity builder.setRequ

Android图片三级缓存策略(网络、本地、内存缓存)

一.简介 现在的Android应用程序中,不可避免的都会使用到图片,如果每次加载图片的时候都要从网络重新拉取,这样不但很耗费用户的流量,而且图片加载的也会很慢,用户体验很不好.所以一个应用的图片缓存策略是很重要的.通常情况下,Android应用程序中图片的缓存策略采用"内存-本地-网络"三级缓存策略,首先应用程序访问网络拉取图片,分别将加载的图片保存在本地SD卡中和内存中,当程序再一次需要加载图片的时候,先判断内存中是否有缓存,有则直接从内存中拉取,否则查看本地SD卡中是否有缓存,SD

Android&nbsp;PowerManagerService省电模式策略控制

目录 前言 监听策略改变 更新策略 通知监听者 如何配置策略 结束 前言 初识Android PowerManagerService省电模式 让我们省电模式的概念有了初步的认识, Android PowerManagerService 打开省电模式 对打开省电模式的代码进行了分析. 有了前面两篇文章的基础,现在我们开始分析如何控制省电模式策略,请读者务必仔细. 本文涉及的文件如下: frameworks/base/services/core/java/com/android/server/pow

Android分包MultiDex策略详解

1.分包背景 这里首先介绍下MultiDex的产生背景. 当Android系统安装一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOpt.DexOpt的执行过程是在第一次加载Dex文件的时候执行的.这个过程会生成一个ODEX文件,即Optimised Dex.执行ODex的效率会比直接执行Dex文件的效率要高很多. 但是在早期的Android系统中,DexOpt有一个问题,DexOpt会把每一个类的方法id检索起来,存在一个链表结构里面.但是这个链表的长度是用一

android中图片的三级缓存cache策略(内存/文件/网络)

1.简介 现在android应用中不可避免的要使用图片,有些图片是可以变化的,需要每次启动时从网络拉取,这种场景在有广告位的应用以及纯图片应用(比如百度美拍)中比较多. 现在有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量.在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响.当然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略.总之,图片缓存是很重要而且是必须的. 2.图片缓存的原理 实现图片缓存也不难,需要有相

Android 后台调度任务与省电详解

I. Handler: 在进程存活的期间有效使用, Google官方推荐使用. 简单易用. 稳定高效. II. AlarmManager: 利用系统层级的闹钟服务(持有Wake lock). 如果需要精确的定时任务,这个是最佳选择. 1. 功能 在大概的时间间隔 运行/重复执行 指定任务. 指定精确的时间间隔执行任务. 2. 特征 注册以后,无论是自己的应用进程是否存在/组件是否存在,都会正常执行. 所有注册的闹钟服务都会在系统重启后复位,因此如果需要保证任务,就需要注册RECEIVE_BOOT

初识Android PowerManagerService省电模式

目录 前言 功能介绍 环境 结束 前言 最近遇到一些关于省电模式.电量消耗.Doze模式,等等相关问题.于是,我决定对它们进行彻底分析,那就先从省电模式开启. 功能介绍 可以在 Settings->Battery->Battery Saver 界面进行省电模式的操作,如下图: 界面中有三个开关,它们的意思如下: Use Battery Saver : 打开/关闭省电模式. Set a Schedule : 设置一个电量百分比阈值,当电量低于这个阈值的时候,就会触发省电模式.设置阈值的界面如下图

深入理解Android中的建造者模式

前言 在Android开发过程中,我发现很多安卓源代码里应用了设计模式,比较常用的有适配器模式(各种adapter),建造者模式(Alert Dialog的构建)等等.虽然我们对大多数设计模式都有所了解,但是在应用设计模式的这个方面,感觉很多人在这方面有所不足.所以这篇文章我们一起深入的理解Android中的建造者模式. 建造者模式(Builder Pattern)也叫生成器模式,其定义如下: separate the construction of a complex object from

详解Android更改APP语言模式的实现过程

一.效果图 二.描述 更改Android项目中的语言,这个作用于只用于此APP,不会作用于整个系统 三.解决方案 (一)布局文件 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" a

Android 中使用ContentObserver模式获取短信用正则自动填充验证码

最近做注册的时候看到很多app在手机接受到短信的时候直接填写验证码到界面省略用户自动输入,感觉这样确实蛮人性化的呵呵,于是自己也做了一个 步骤: 首先我使用了ContentObserver监听短信,(最好知道您的验证码从那个号码发过来) 然后从短信中用正则的分组去拿到验证码(当然验证码必须是什么格式) 贴出关键代码: 注册监听短信数据库的  ContentObserver c=new ContentObserver(han) { @Override public void onChange(bo

Android 两种启动模式的实例详解

Android 两种启动模式的实例详解 Intent的FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_REORDER_TO_FRONT Activity的两种启动模式:FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_REORDER_TO_FRONT 1. 如果已经启动了四个Activity:A,B,C和D.在D Activity里,我们要跳到B Activity,同时希望C finish掉,可以在startActivity(intent)里

详谈Android ListView的选择模式

效果图: ListView 定义了choiceMode属性,描述是这样的: 用于为视图定义选择行为.默认情况下,列表时没有任何选择行为的.如果把choiceMode设置为singleChoice,列表允许有一个列表项处于被选状态.如果把choiceMode设置为multipleChoice,那么列表允许有任意数量的列表项处于被选状态 ListView以某种方式通过Checkable接口处理视图的选择状态,LIstView源码中有这么一段: if (mChoiceMode != CHOICE_MO

Android编程实现夜间模式的方法小结

本文实例讲述了Android编程实现夜间模式的方法.分享给大家供大家参考,具体如下: 随着APP实现的功能越来越丰富, 看小说看视频上网等等, 现在不少人花在手机平板等移动终端上的时间越来越长了. 但手机和平板的屏幕并不像Kindle那类电纸书的水墨屏那么耐看, 由于自发光的屏幕特性, 我们长期盯着屏幕看容易眼睛酸痛疲倦, 因此各种护目模式, 夜间模式在移动APP上得到广泛应用, 这的确也是一个贴心的小功能. 所以这次我们探讨下几种实现方式, 一起学习总结下: 1. 利用屏幕亮度 当夜间使用手机

Android 简单服务定位器模式实现

依赖注入(Dependency Injection)和服务定位器(Service Locator)是实现控制反转(Inversion of Control)的两种主要手段. Android的主流依赖注入框架有:Dagger 和 Kion 这些依赖注入框架都感觉比较重. 服务定位器比如少见,这里提供一个一个简单的服务定位器模式实现. 引入 项目地址:github.com/czy1121/ser- repositories { maven { url "https://gitee.com/ezy/r