Android PowerManagerService省电模式策略控制

目录
  • 前言
  • 监听策略改变
  • 更新策略
    • 通知监听者
  • 如何配置策略
  • 结束

前言

初识Android PowerManagerService省电模式 让我们省电模式的概念有了初步的认识,

Android PowerManagerService 打开省电模式 对打开省电模式的代码进行了分析。

有了前面两篇文章的基础,现在我们开始分析如何控制省电模式策略,请读者务必仔细。

本文涉及的文件如下:

  • frameworks/base/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
  • frameworks/base/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java

监听策略改变

// BatterySaverPolicy.java

    public void systemReady() {
        ConcurrentUtils.wtfIfLockHeld(TAG, mLock);

        // 1. 监听 Global 数据
        // 当数据改变,回调 onChange()
        mContentResolver.registerContentObserver(Settings.Global.getUriFor(
                Settings.Global.BATTERY_SAVER_CONSTANTS), false, this);
        mContentResolver.registerContentObserver(Settings.Global.getUriFor(
                Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS), false, this);

        // 无障碍模式相关
        final AccessibilityManager acm = mContext.getSystemService(AccessibilityManager.class);
        acm.addAccessibilityStateChangeListener(enabled -> mAccessibilityEnabled.update(enabled));
        mAccessibilityEnabled.initialize(acm.isEnabled());

        // 车载相关
        UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
        uiModeManager.addOnProjectionStateChangedListener(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE,
                mContext.getMainExecutor(), mOnProjectionStateChangedListener);
        mAutomotiveProjectionActive.initialize(
                uiModeManager.getActiveProjectionTypes() != UiModeManager.PROJECTION_TYPE_NONE);

        // 2. 监听 Config 表中,命名空间DeviceConfig.NAMESPACE_BATTERY_SAVER下的所有数据
        // 当数据改变时,回调 onPropertiesChanged()
        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_BATTERY_SAVER,
                mContext.getMainExecutor(), this);

        // 3. 读取 Config 表中,命名空间DeviceConfig.NAMESPACE_BATTERY_SAVER下的所有数据
        mLastDeviceConfigProperties =
                DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BATTERY_SAVER);

        // 4. 获取 Global 表中的数据,并执行更新操作
        onChange(true, null);
    }

前两步是监听数据,只不过回调的方式不同,但是最终都是根据数据更新省电模式策略,然后通知监听者。

因此本文只分析其中一个回调 onChange(),而另外一个回调 onPropertiesChanged() 请读者自行分析。

DeviceConfig 就是获取 SettingsProvider 中 Config 表中的数据,这些数据的 KEY 以命名空间开头,然后把所有这些数据封装成一个 DeviceConfig.Properties 对象。

后两步,是主动获取一次数据,然后主动触发一次 onChange() 回调。

// BatterySaverPolicy.java

    public void onChange(boolean selfChange, Uri uri) {
        refreshSettings();
    }
    private void refreshSettings() {
        synchronized (mLock) {
            // 1. 获取与设备无关的省电模式策略
            // 例如,vibration_disabled=true,adjust_brightness_factor=0.5
            final String setting = getGlobalSetting(Settings.Global.BATTERY_SAVER_CONSTANTS);

            // 2. 获取与设备相关的省电模式策略
            // 格式为, cpufreq-i=core-number:frequency/...,cpufreq-n=core-number:frequency/...
            String deviceSpecificSetting = getGlobalSetting(
                    Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS);

            // 保存与设备相关的省电模式策略的KEY值
            mDeviceSpecificSettingsSource =
                    Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS;

            // 3. 如果与设备相关的省电策略为空,那么加载 framework-res 的 config.xml 中的配置 config_batterySaverDeviceSpecificConfig
            if (TextUtils.isEmpty(deviceSpecificSetting) || "null".equals(deviceSpecificSetting)) {
                // 配置默认也为空
                deviceSpecificSetting =
                        mContext.getString(getDeviceSpecificConfigResId());
                // 表示是从配置文件中配置的
                mDeviceSpecificSettingsSource = "(overlay)";
            }

            // 4. 更新策略
            if (!updateConstantsLocked(setting, deviceSpecificSetting)) {
                // 没有变化,就不去执行后面的通知监听者的操作
                return;
            }
        }

        // 5. 如果策略改变,通知监听者
        maybeNotifyListenersOfPolicyChange();
    }

Settings.Global.BATTERY_SAVER_CONSTANTS 保存的是与设备无关的省电策略。例如,这个字段的值可以为 vibration_disabled=true,adjust_brightness_factor=0.5,不同的策略通过逗号进行分隔。从名字可以猜测出,前一个省电策略表示关闭振动,后一个省电策略表示屏幕亮度降低一半。

什么叫与设备无关策略?如果这个省电策略不会因设备不同而不同的话,那这个策略就是与设备无关,反之就是与设备有关策略。

Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS 保存的是与设备相关的省点策略,目前只保存 CPU 限频策略。 这个字段的值可能为 cpufreq-i=core-number:frequency,cpufreq-n=core-number:frequency,其中 cpufreq-i 表示交互状态下的CPU限频策略,cpufreq-n 表示非交互状态下的CPU限频策略,core-number 表示 CPU 编号,frequency 表示需要限制的频率。交互状态和非交互状态的限频策略以逗号进行分隔。

当然,在省电模式下,不一定只限制一个CPU的频率,我们可以使用 / 来分隔不同的 CPU 限频策略,例如 cpufreq-i=core-number:frequency/core-number:frequency/core-number:frequency.

通常,亮屏状态下为交互模式,灭屏状态下为非交互模式。

从第三步中可以看出,可以在 framework-res 模块的 config.xml 中配置 CPU 限频策略。记住这里,不要看完了我一系列的省电模式的文章,最终连 CPU 限频策略还不会配置哦!

第四步,会根据这些数据来更新省电模式策略。并且,如果省电模式策略改变了,那么还会执行第五步,通知监听者。

下面,重点分析第四步和第五步。

更新策略

现在,假设 Settings.Global.BATTERY_SAVER_CONSTANTS 和 Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS 保存的数据改变了,那么会调用 updateConstantsLocked() 更新省电模式策略

// BatterySaverPolicy.java

    boolean updateConstantsLocked(String setting, String deviceSpecificSetting) {
        // 如果是null,返回""
        setting = TextUtils.emptyIfNull(setting);
        deviceSpecificSetting = TextUtils.emptyIfNull(deviceSpecificSetting);
        // 没有变化,直接返回
        if (setting.equals(mSettings)
                && deviceSpecificSetting.equals(mDeviceSpecificSettings)) {
            return false;
        }
        // 1. 保存设置
        mSettings = setting;
        mDeviceSpecificSettings = deviceSpecificSetting;
        // 2. 根据配置,创建新的策略
        Poilcy p = Policy.fromSettings(setting, deviceSpecificSetting, mLastDeviceConfigProperties, null, DEFAULT_FULL_POLICY);

        // 3. 更新默认的省电模式策略
        boolean changed = maybeUpdateDefaultFullPolicy(p);

        // 忽略 adaptive battery save 功能
        mDefaultAdaptivePolicy = Policy.fromSettings("", "",
                mLastDeviceConfigProperties, KEY_SUFFIX_ADAPTIVE, DEFAULT_ADAPTIVE_POLICY);
        if (mPolicyLevel == POLICY_LEVEL_ADAPTIVE
                && !mAdaptivePolicy.equals(mDefaultAdaptivePolicy)) {
            // adaptive policy changed
            changed = true;
        }
        mAdaptivePolicy = mDefaultAdaptivePolicy;

        // 4. 更新有效的省电模式策略
        updatePolicyDependenciesLocked();

        // 5. 返回状态,表示省电模式策略是否改变
        return changed;
    }

第二步,根据配置的数据创建一个策略,注意最后一个参数 DEFAULT_FULL_POLICY,它表示默认的省电模式策略

// BatterySaverPolicy.java

        private static Policy fromSettings(String settings, String deviceSpecificSettings,
                DeviceConfig.Properties properties, String configSuffix, Policy defaultPolicy) {
            // 以逗号为分隔符解析字符串
            final KeyValueListParser parser = new KeyValueListParser(',');
            configSuffix = TextUtils.emptyIfNull(configSuffix);

            // 1. 首先解析设备相关的策略参数
            try {
                parser.setString(deviceSpecificSettings == null ? "" : deviceSpecificSettings);
            } catch (IllegalArgumentException e) {
                Slog.wtf(TAG, "Bad device specific battery saver constants: "
                        + deviceSpecificSettings);
            }
            // 读取的值的格式为 core-number:frequency/core-number:frequency/...
            final String cpuFreqInteractive = parser.getString(KEY_CPU_FREQ_INTERACTIVE, "");
            final String cpuFreqNoninteractive = parser.getString(KEY_CPU_FREQ_NONINTERACTIVE, "");

            // 2. 再解析设备无关的策略参数
            try {
                parser.setString(settings == null ? "" : settings);
            } catch (IllegalArgumentException e) {
                Slog.wtf(TAG, "Bad battery saver constants: " + settings);
            }

            // 策略参数取值的优先级为: Settings > DeviceConfig > 默认省电策略
            final float adjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR,
                    properties.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR + configSuffix,
                            defaultPolicy.adjustBrightnessFactor));
            final boolean advertiseIsEnabled = parser.getBoolean(KEY_ADVERTISE_IS_ENABLED,
                    properties.getBoolean(KEY_ADVERTISE_IS_ENABLED + configSuffix,
                            defaultPolicy.advertiseIsEnabled));
            final boolean deferFullBackup = parser.getBoolean(KEY_DEFER_FULL_BACKUP,
                    properties.getBoolean(KEY_DEFER_FULL_BACKUP + configSuffix,
                            defaultPolicy.deferFullBackup));
            final boolean deferKeyValueBackup = parser.getBoolean(KEY_DEFER_KEYVALUE_BACKUP,
                    properties.getBoolean(KEY_DEFER_KEYVALUE_BACKUP + configSuffix,
                            defaultPolicy.deferKeyValueBackup));
            final boolean disableAnimation = parser.getBoolean(KEY_DISABLE_ANIMATION,
                    properties.getBoolean(KEY_DISABLE_ANIMATION + configSuffix,
                            defaultPolicy.disableAnimation));
            final boolean disableAod = parser.getBoolean(KEY_DISABLE_AOD,
                    properties.getBoolean(KEY_DISABLE_AOD + configSuffix,
                            defaultPolicy.disableAod));
            final boolean disableLaunchBoost = parser.getBoolean(KEY_DISABLE_LAUNCH_BOOST,
                    properties.getBoolean(KEY_DISABLE_LAUNCH_BOOST + configSuffix,
                            defaultPolicy.disableLaunchBoost));
            final boolean disableOptionalSensors = parser.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS,
                    properties.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS + configSuffix,
                            defaultPolicy.disableOptionalSensors));
            final boolean disableVibrationConfig = parser.getBoolean(KEY_DISABLE_VIBRATION,
                    properties.getBoolean(KEY_DISABLE_VIBRATION + configSuffix,
                            defaultPolicy.disableVibration));
            final boolean enableBrightnessAdjustment = parser.getBoolean(
                    KEY_ENABLE_BRIGHTNESS_ADJUSTMENT,
                    properties.getBoolean(KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + configSuffix,
                            defaultPolicy.enableAdjustBrightness));
            final boolean enableDataSaver = parser.getBoolean(KEY_ENABLE_DATASAVER,
                    properties.getBoolean(KEY_ENABLE_DATASAVER + configSuffix,
                            defaultPolicy.enableDataSaver));
            final boolean enableFirewall = parser.getBoolean(KEY_ENABLE_FIREWALL,
                    properties.getBoolean(KEY_ENABLE_FIREWALL + configSuffix,
                            defaultPolicy.enableFirewall));
            final boolean enableNightMode = parser.getBoolean(KEY_ENABLE_NIGHT_MODE,
                    properties.getBoolean(KEY_ENABLE_NIGHT_MODE + configSuffix,
                            defaultPolicy.enableNightMode));
            final boolean enableQuickDoze = parser.getBoolean(KEY_ENABLE_QUICK_DOZE,
                    properties.getBoolean(KEY_ENABLE_QUICK_DOZE + configSuffix,
                            defaultPolicy.enableQuickDoze));
            final boolean forceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY,
                    properties.getBoolean(KEY_FORCE_ALL_APPS_STANDBY + configSuffix,
                            defaultPolicy.forceAllAppsStandby));
            final boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK,
                    properties.getBoolean(KEY_FORCE_BACKGROUND_CHECK + configSuffix,
                            defaultPolicy.forceBackgroundCheck));
            final int locationMode = parser.getInt(KEY_LOCATION_MODE,
                    properties.getInt(KEY_LOCATION_MODE + configSuffix,
                            defaultPolicy.locationMode));
            final int soundTriggerMode = parser.getInt(KEY_SOUNDTRIGGER_MODE,
                    properties.getInt(KEY_SOUNDTRIGGER_MODE + configSuffix,
                            defaultPolicy.soundTriggerMode));

            // 3. 创建一个新的策略
            return new Policy(
                    adjustBrightnessFactor,
                    advertiseIsEnabled,
                    (new CpuFrequencies()).parseString(cpuFreqInteractive),
                    (new CpuFrequencies()).parseString(cpuFreqNoninteractive),
                    deferFullBackup,
                    deferKeyValueBackup,
                    disableAnimation,
                    disableAod,
                    disableLaunchBoost,
                    disableOptionalSensors,
                    /* disableVibration */
                    disableVibrationConfig,
                    enableBrightnessAdjustment,
                    enableDataSaver,
                    enableFirewall,
                    enableNightMode,
                    enableQuickDoze,
                    forceAllAppsStandby,
                    forceBackgroundCheck,
                    locationMode,
                    soundTriggerMode
            );
        }

与设备相关的省电策略,也就是 CPU 限频策略,如果空缺,也不会被任何配置取代。

与设备无关的省电策略,如果某一项空缺,会依次被 DeviceConfig 和 默认的省电策略 DEFAULT_FULL_POLICY 取代。

最终通过解析的数据,创建一个策略 Policy 对象。

新的策略已经创建出来,之后调用 maybeUpdateDefaultFullPolicy() 更新默认的省电策略。

// BatterySaverPolicy.java

    private boolean maybeUpdateDefaultFullPolicy(Policy p) {
        boolean fullPolicyChanged = false;
        if (!mDefaultFullPolicy.equals(p)) {
            // mFullPolicy 会被 setFullPolicyLocked() 修改
            // 如果 mFullPolicy 与 mDefaultFullPolicy 不同, 那么表示 mFullPolicy 被覆盖
            // 如果相同,表示没有被覆盖
            boolean isDefaultFullPolicyOverridden = !mDefaultFullPolicy.equals(mFullPolicy);
            if (!isDefaultFullPolicyOverridden) {
                // mFullPolicy 没有被覆盖,就要同步进行更新
                mFullPolicy = p;
                // 现在处于省电模式中,需要通知监听者
                fullPolicyChanged = (mPolicyLevel == POLICY_LEVEL_FULL);
            }
            // 更新默认的省电模式策略
            mDefaultFullPolicy = p;
        }

        return fullPolicyChanged;
    }

mDefaultFullPolicy 代表的就是默认的省电策略,这里会更新它,但是同时,如果 mFullPolicy 没有被 setFullPolicyLocked() 修改(源码设计中称之为 overridden),那么也会同步更新它。

现在默认的省点策略已经更新,但是要应用的最终策略还不是它,需要调用 updatePolicyDependenciesLocked() 来根据情况,更新一个有效的省点策略

// BatterySaverPolicy.java
    private void updatePolicyDependenciesLocked() {
        final Policy rawPolicy = getCurrentRawPolicyLocked();
        final int locationMode;
        invalidatePowerSaveModeCaches();
        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;
        }
        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
        );
        // ...
    }

很简单,根据是否是车载项目,以及是否打开无障碍模式,更新有效的省电模式策略 mEffectivePolicyRaw

通知监听者

现在省电策略已经更新完毕,如果策略改变了,那么需要通知监听者

// BatterySaverPolicy.java

    private void maybeNotifyListenersOfPolicyChange() {
        final BatterySaverPolicyListener[] listeners;
        synchronized (mLock) {
            if (mPolicyLevel == POLICY_LEVEL_OFF) {
                // 省电模式没有打开
                return;
            }
            // Don't call out to listeners with the lock held.
            listeners = mListeners.toArray(new BatterySaverPolicyListener[mListeners.size()]);
        }

        mHandler.post(() -> {
            for (BatterySaverPolicyListener listener : listeners) {
                // 通知监听者
                listener.onBatterySaverPolicyChanged(this);
            }
        });
    }

目前,省电策略的监听者只有一个 BatterySaverController

// BatterySaverController.java

    public void onBatterySaverPolicyChanged(BatterySaverPolicy policy) {
        if (!isPolicyEnabled()) {
            return; // No need to send it if not enabled.
        }
        mHandler.postStateChanged(/*sendBroadcast=*/ true, REASON_POLICY_CHANGED);
    }

最终会调用 handleBatterySaverStateChanged()

// BatterySaverController.java

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

            mFullPreviouslyEnabled = getFullEnabledLocked();
            mAdaptivePreviouslyEnabled = getAdaptiveEnabledLocked();

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

            mIsInteractive = isInteractive;

            // 1. 获取CPU限频策略
            if (enabled) {
                fileValues = mBatterySaverPolicy.getFileValues(isInteractive);
            } else {
                fileValues = null;
            }
        }

        final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
        if (pmi != null) {
            pmi.setPowerMode(Mode.LOW_POWER, isEnabled());
        }

        updateBatterySavingStats();

        // 2. 应用CPU限频策略
        if (ArrayUtils.isEmpty(fileValues)) {
            // 如果策略为空,那肯定是关闭了省电模式,此时需要恢复正常的CPU频率
            mFileUpdater.restoreDefault();
        } else {
            // 策略不为空,首先保存节点中原本的值到 /data/system/battery-saver/default-values.xml,然后向节点写值
            mFileUpdater.writeFiles(fileValues);
        }

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

            // Send the broadcast to a manifest-registered receiver that is specified in the config.
            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);
            }

            // Send internal version that requires signature permission.
            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);

            // 4. 通知监听者,省电策略已经改变
            for (LowPowerModeListener listener : listeners) {
                final PowerSaveState result =
                        mBatterySaverPolicy.getBatterySaverPolicy(listener.getServiceType());
                listener.onLowPowerModeChanged(result);
            }
        }
    }

这段代码,我只分析如何应用省电策略。

第一步,是获取 CPU 限频策略,以一个 Map 表示,其中 KEY 为 CPU 限频的节点路径,VALUE 为被限制的频率。

然后第二步是应用这个 CPU 限频策略,其实就是向节点写值。 不过在写值之前,会先保存节点中原本的值到 /data/system/battery-saver/default-values.xml 文件中,这个文件的作用是,当关闭省电模式,会恢复 CPU 原本的频率。

最后,通知监听者,策略已经改变,你们需要做相应的适配。这些监听者都是系统服务,例如WindowManagerService 就是一个监听者,它会根据省电模式策略,决定是否关闭动画,关键代码如下:

                case NEW_ANIMATOR_SCALE: {
                    // 省电模式下,scale 为 0
                    float scale = getCurrentAnimatorScale();
                    // 关闭 system_server 进程的动画
                    ValueAnimator.setDurationScale(scale);
                    Session session = (Session)msg.obj;
                    if (session != null) {
                        try {
                            session.mCallback.onAnimatorScaleChanged(scale);
                        } catch (RemoteException e) {
                        }
                    } else {
                        ArrayList<IWindowSessionCallback> callbacks
                                = new ArrayList<IWindowSessionCallback>();
                        synchronized (mGlobalLock) {
                            for (int i=0; i<mSessions.size(); i++) {
                                callbacks.add(mSessions.valueAt(i).mCallback);
                            }

                        }
                        for (int i=0; i<callbacks.size(); i++) {
                            try {
                                // 关闭 app 进程的动画
                                callbacks.get(i).onAnimatorScaleChanged(scale);
                            } catch (RemoteException e) {
                            }
                        }
                    }
                    break;
                }

如何配置策略

看完了策略控制的源码分析,总结下如何控制策略

  • 修改 Settings.Global.BATTERY_SAVER_CONSTANTS 字段值,可以设置的字参考注释文档,或者参考源码的解析部分。例如 vibration_disabled=true,adjust_brightness_factor=0.5
  • 修改 Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS字段值,但是这个只能修改 CPU 限频策略。例如 cpufreq-i=0:1804810/1:1804900,cpufreq-n=0:1804700/1:1804600
  • 可以通过 framework-res 的 config.xml 的config_batterySaverDeviceSpecificConfig 配置默认的 CPU 限频策略。但是这个会被 Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS 覆盖,在有值的情况下。

结束

省电模式的文章,到此就结束了。本来我还准备分析省电模式影响的功能,但是由于影响的功能有点多,但是我又无法精通所有的功能,因此我就不献丑了去分析了。

在工作中,如果你熟悉的某个功能模块,例如 WindowManagerService,它受省电模式影响,我相信,如果你看完我的文章,应该能自行分析受影响的功能。

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

时间: 2022-08-22

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 PowerManagerService 打开省电模式

目录 概要 打开省电模式 BatterySaverStateMachine状态管理 BatterySaverController切换省电模式 BattterySaverPolicy控制省电策略 处理省电模式状态改变 battery saver sticky 模式 结束 概要 初识Android PowerManagerService省电模式对省电模式进行了一个初步认识,介绍了一些概念,以及对省电模式环境初始化的代码进行了简单分析.读者需要仔细阅读第一篇文章,再来看这一篇文章. 打开省电模式,有三

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

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

Android省电的秘密之JobScheduler

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

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

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

Android分包MultiDex策略详解

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

Android系统对话框使用详解(最详细)

在实际应用开发中,用到系统对话框中的情况几乎是没有的.按开发流程来说,UI工程师都会给出每一个弹窗的样式,故而在实际开发中都是自定义弹窗的. 即使用到的地方不多,但是我们也是需要了解并且能熟练的运用它,下面为大家奉上各种系统对话框的实现. 目录 一.系统对话框的几种类型与实现 在项目的实际开发中,用到的系统对话框几乎是没有的.原因大概包含以下几点: 样式过于单一,不能满足大部分实际项目中的需求. 对话框的样式会根据手机系统版本的不同而变化.不能达到统一的样式. 能实现的功能过于简单. 在这里先附

Android HandlerThread使用方法详解

Android HandlerThread使用方法详解 HandlerThread 继承自Thread,内部封装了Looper. 首先Handler和HandlerThread的主要区别是:Handler与Activity在同一个线程中,HandlerThread与Activity不在同一个线程,而是别外新的线程中(Handler中不能做耗时的操作). 用法: import android.app.Activity; import android.os.Bundle; import androi

Android中menu使用详解

Menu(菜单)是Android中一定会使用的模块,每个Android项目都会用到Menu来给用户起到选择和导航的作用,提升用户体验,下面通过本文给大家分享android 中menu使用,需要的朋友一起看看吧 很多activity界面中都存在一个菜单栏,就是点击右上角的一个按钮的时候会出现一个下拉列表差不多的东西,这个功能的实现其实只需要下面的两步,每一个activity都可以拥有自己独一无二的menu,具体的格式可以自己进行定义,详细的创建步骤如下 ①在res下的menu中创建file_men

Android xml解析实例详解

Android  xml解析实例详解 实现效果图: XmlActivity package com.Android.xiong.gridlayoutTest; import android.app.Activity; import android.content.res.XmlResourceParser; import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; impo

Android AOP注解Annotation详解(一)

Android 注解Annotation 相关文章: Android AOP注解Annotation详解(一) Android AOP之注解处理解释器详解(二) Android AOP 注解详解及简单使用实例(三) Android AOP 等在Android上应用越来越广泛,例如框架ButterKnife,Dagger2,EventBus3等等,这里我自己总结了一个学习路程. - Java的注解Annotation - 注解处理解析器APT(Annotation Processing Tool)

Android Tab 控件详解及实例

Android Tab 控件详解及实例 在桌面应用中Tab控件使用得非常普遍,那么我们经常在Android中也见到以Tab进行布局的客户端.那么Android中的Tab是如何使用的呢? 1.Activity package com.wicresoft.activity; import com.wicresoft.myandroid.R; import android.app.TabActivity; import android.os.Bundle; import android.util.Lo

Android canvas drawBitmap方法详解及实例

 Android canvas drawBitmap方法详解及实例 之前自己在自定义view,用到canvas.drawBitmap(Bitmap, SrcRect, DesRect, Paint)的时候,对其中的第2和3个参数的含义含糊不清.看源码函数也没理解,然后看了一些其他的博客加上自己的理解,整理如下.首先,我们看一张图片,今天就要绘制这张图片. 然后将图片用红色的线条分成4个部分,如下: 我们自定义一个View,代码如下: public class PoterDuffLoadingVi

Android init.rc文件详解及简单实例

Android init.rc文件详解 本文主要来自$ANDROID_SOURCE/system/init/readme.txt的翻译. 1 简述 Android init.rc文件由系统第一个启动的init程序解析,此文件由语句组成,主要包含了四种类型的语句:Action,Commands,Services,Options.在init.rc文件中一条语句通常是占据一行.单词之间是通过空格符来相隔的.如果需要在单词内使用空格,那么得使用转义字符"\",如果在一行的末尾有一个反斜杠,那么

Android getevent用法实例详解

 Android getevent用法实例详解 最近在测试设备按键的常用命令,感觉这些命令都有的,但就是不知道怎么查找. 翻阅了几篇博文,才发现有一个getevent,就是指这样的命令. 首先需要说明的是getevent命令后面可以带上具体的input设备,列如getevent /dev/iput/event0,这样可以过滤掉一些不用显示的input的设备. 我在之前的使用中,还是有些找不到点子,也是一步一步使用起来的. 首先看-p 选项, -p选项用于输出input设备相关的一些信息,列如,