Android 9.0 Battery Settings 分析

文章详细解析了Android9.0中的电池设置,包括电池用量显示、省电模式的实现机制以及电池百分比、上次完全充电时间等各项功能的工作原理。涉及到的关键组件有PowerUsageSummary、BatteryHeaderPreferenceController、BatterySaverButtonPreferenceController等,以及它们如何与PowerManagerService、BatterySaverStateMachine等系统服务协同工作来控制和显示电池状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

1. 主界面显示

PowerUsageSummary.java 布局文件是 power_usage_summary.xml

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:key="power_usage_summary_screen"
    android:title="@string/power_usage_summary_title"
    settings:keywords="@string/keywords_battery">

    <com.android.settings.applications.LayoutPreference
        android:key="battery_header"
        android:selectable="false"
        android:layout="@layout/battery_header" />

    <PreferenceCategory
        android:key="battery_tip"
        android:layout="@layout/preference_category_no_title" />

    <Preference
        android:fragment="com.android.settings.fuelgauge.batterysaver.BatterySaverSettings"
        android:key="battery_saver_summary"
        android:title="@string/battery_saver"
        settings:controller="com.android.settings.fuelgauge.BatterySaverController"
        settings:platform_slice="true"
        settings:allowDividerAbove="true" />

    <Preference
        android:fragment="com.android.settings.fuelgauge.SmartBatterySettings"
        android:key="smart_battery_manager"
        android:title="@string/smart_battery_manager_title"
        settings:controller="com.android.settings.fuelgauge.batterytip.BatteryManagerPreferenceController" />

    <SwitchPreference
        android:key="battery_percentage"
        android:title="@string/battery_percentage"
        android:summary="@string/battery_percentage_description" />


    <com.android.settings.fuelgauge.PowerGaugePreference
        android:key="last_full_charge"
        android:title="@string/battery_last_full_charge"
        android:selectable="false"
        settings:allowDividerAbove="true" />

    <com.android.settings.fuelgauge.PowerGaugePreference
        android:key="screen_usage"
        android:title="@string/device_screen_usage"
        android:selectable="false" />

</PreferenceScreen>

1.1 battery_header

在这里插入图片描述

布局文件是 battery_header.xml

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/battery_entity_header"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:orientation="horizontal"
    android:paddingTop="24dp"
    android:paddingBottom="24dp"
    style="@style/EntityHeader">

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_marginStart="56dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/battery_percent"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="12dp"
            android:fontFamily="@*android:string/config_headlineFontFamily"
            android:textAppearance="@android:style/TextAppearance.Material.Display1"/>

        <TextView
            android:id="@+id/summary1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:minLines="2"
            android:textAppearance="@android:style/TextAppearance.Material.Small"/>

        <TextView
            android:id="@+id/summary2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAppearance="@android:style/TextAppearance.Material.Small"/>

    </LinearLayout>

    <com.android.settings.fuelgauge.BatteryMeterView
        android:id="@+id/battery_header_icon"
        android:layout_width="@dimen/battery_meter_width"
        android:layout_height="@dimen/battery_meter_height"
        android:layout_marginEnd="16dp"/>

</LinearLayout>

在这里插入图片描述

对应 id为 battery_percent 的TextView
在这里插入图片描述
对应 id为 summary1 的TextView
在这里插入图片描述
对应 id为 summary2 的TextView
在这里插入图片描述
对应id为 battery_header_icon 的 BatteryMeterView,电池图标是由BatteryMeterView绘制而成


在 PowerUsageSummary.java 通过 key 获取到 对应的 LayoutPreference ,再使用mBatteryLayoutPref.findViewById 获取对应的控件,设置初始化参数。此时设置的是旧的值,从battery info获取。

    protected void updateViews(List<BatteryInfo> batteryInfos) {
        final BatteryMeterView batteryView = mBatteryLayoutPref
            .findViewById(R.id.battery_header_icon);
        final TextView percentRemaining =
            mBatteryLayoutPref.findViewById(R.id.battery_percent);
        final TextView summary1 = mBatteryLayoutPref.findViewById(R.id.summary1);
        final TextView summary2 = mBatteryLayoutPref.findViewById(R.id.summary2);
        BatteryInfo oldInfo = batteryInfos.get(0);
        BatteryInfo newInfo = batteryInfos.get(1);
        percentRemaining.setText(Utils.formatPercentage(oldInfo.batteryLevel));

        // set the text to the old estimate (copied from battery info). Note that this
        // can sometimes say 0 time remaining because battery stats requires the phone
        // be unplugged for a period of time before being willing ot make an estimate.
        summary1.setText(mPowerFeatureProvider.getOldEstimateDebugString(
            Formatter.formatShortElapsedTime(getContext(),
                PowerUtil.convertUsToMs(oldInfo.remainingTimeUs))));

        // for this one we can just set the string directly
        summary2.setText(mPowerFeatureProvider.getEnhancedEstimateDebugString(
            Formatter.formatShortElapsedTime(getContext(),
                PowerUtil.convertUsToMs(newInfo.remainingTimeUs))));

        batteryView.setBatteryLevel(oldInfo.batteryLevel);
        batteryView.setCharging(!oldInfo.discharging);
    }

使用Loader之后,只要数据源发生变化,就会自动调用onLoadFinished(),数据源是


    LoaderManager.LoaderCallbacks<List<BatteryInfo>> mBatteryInfoDebugLoaderCallbacks =
            new LoaderCallbacks<List<BatteryInfo>>() {
                @Override
                //创建新的Loader,i为LoaderID,如果已经有相同ID的Loader就会复用该Loader,而不会重新创建。 args为初始化时传递的参数。该方法开始异步查询,并返回一个泛型类,如果是查询数据库可以返回一个CursorLoader
                public Loader<List<BatteryInfo>> onCreateLoader(int i, Bundle bundle) {
                    return new DebugEstimatesLoader(getContext(), mStatsHelper);
                }

                @Override
                //异步查询结束的会调用这个方法,并返回查询结果 data
                public void onLoadFinished(Loader<List<BatteryInfo>> loader,
                        List<BatteryInfo> batteryInfos) {
                    updateViews(batteryInfos);
                }

                @Override
                public void onLoaderReset(Loader<List<BatteryInfo>> loader) {
                }
            };

battery_header 绑定的Controller是 BatteryHeaderPreferenceController.java

    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
        ...
        mBatteryHeaderPreferenceController = new BatteryHeaderPreferenceController(
                context, activity, this /* host */, lifecycle);
        controllers.add(mBatteryHeaderPreferenceController);

在 PowerUsageSummary.java#oncreate() & refreshUi() 方法中都去更新了这几个view的状态

PowerUsageSummary.java # mBatteryInfoLoaderCallbacks 调用
       public void updateHeaderPreference(BatteryInfo info) {
        mBatteryPercentText.setText(Utils.formatPercentage(info.batteryLevel));
        if (info.remainingLabel == null) {
            mSummary1.setText(info.statusLabel);
        } else {
            mSummary1.setText(info.remainingLabel);
        }
        // Clear this just to be sure we don't get UI jank on re-entering this view from another
        // activity.
        mSummary2.setText("");

        mBatteryMeterView.setBatteryLevel(info.batteryLevel);
        mBatteryMeterView.setCharging(!info.discharging);
    }

    public void quickUpdateHeaderPreference() {
        Intent batteryBroadcast = mContext.registerReceiver(null,
                new IntentFilter(Intent.ACTION_BATTERY_CHANGED));//注册广播获取电池信息
        final int batteryLevel = Utils.getBatteryLevel(batteryBroadcast);
        final boolean discharging =
                batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) == 0;

        // Set battery level and charging status
        mBatteryMeterView.setBatteryLevel(batteryLevel);
        mBatteryMeterView.setCharging(!discharging);
        mBatteryPercentText.setText(Utils.formatPercentage(batteryLevel));

        // clear all the summaries
        mSummary1.setText("");
        mSummary2.setText("");
    }

2.2 battery_tip

在 PowerUsageSummary.java 通过key,将 BatteryTipPreferenceController 加入到 controller 集合中,用来控制显示。
在displayPreference 方法中,加入了SummaryTip这个preference。

    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        mPrefContext = screen.getContext();
        mPreferenceGroup = (PreferenceGroup) screen.findPreference(getPreferenceKey());

        // Add summary tip in advance to avoid UI flakiness
        final SummaryTip summaryTip = new SummaryTip(BatteryTip.StateType.NEW,
                Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
        mPreferenceGroup.addPreference(summaryTip.buildPreference(mPrefContext));
    }

在 PowerUsageSummary# LoaderManager.LoaderCallbacks中,更新SummaryTip的状态


mBatteryTipPreferenceController.updateBatteryTips(data);

BatteryTipPreferenceController.java
    public void updateBatteryTips(List<BatteryTip> batteryTips) {
        if (batteryTips == null) {
            return;
        }
        if (mBatteryTips == null) {
            mBatteryTips = batteryTips;
        } else {
            // mBatteryTips and batteryTips always have the same length and same sequence.
            for (int i = 0, size = batteryTips.size(); i < size; i++) {
                mBatteryTips.get(i).updateState(batteryTips.get(i));
            }
        }

        mPreferenceGroup.removeAll();
        for (int i = 0, size = batteryTips.size(); i < size; i++) {
            final BatteryTip batteryTip = mBatteryTips.get(i);
            if (batteryTip.getState() != BatteryTip.StateType.INVISIBLE) {
                final Preference preference = batteryTip.buildPreference(mPrefContext);
                mBatteryTipMap.put(preference.getKey(), batteryTip);
                mPreferenceGroup.addPreference(preference);
                batteryTip.log(mContext, mMetricsFeatureProvider);
                mNeedUpdate = batteryTip.needUpdate();
                break;
            }
        }
    }

还对SummaryTip 添加了点击事件,用来弹出dialog

    public boolean handlePreferenceTreeClick(Preference preference) {
        final BatteryTip batteryTip = mBatteryTipMap.get(preference.getKey());
        if (batteryTip != null) {
            if (batteryTip.shouldShowDialog()) {//显示dialog
                BatteryTipDialogFragment dialogFragment = BatteryTipDialogFragment.newInstance(
                        batteryTip, mFragment.getMetricsCategory());
                dialogFragment.setTargetFragment(mFragment, REQUEST_ANOMALY_ACTION);
                dialogFragment.show(mFragment.getFragmentManager(), TAG);
            } else {
                final BatteryTipAction action = BatteryTipUtils.getActionForBatteryTip(batteryTip,
                        mSettingsActivity, mFragment);
                if (action != null) {
                    action.handlePositiveAction(mFragment.getMetricsCategory());
                }
                if (mBatteryTipListener != null) {
                    mBatteryTipListener.onBatteryTipHandled(batteryTip);
                }
            }

            return true;
        }
        return super.handlePreferenceTreeClick(preference);
    }

2.3 battery_saver

省电模式主要做什么?

DisplayPowerController -> 屏幕亮度减半
VibratorService -> 关闭触摸震动和来电震动
NetworkPolicyManagerService -> 限制 Doze 非白名单应用联网(WiFi与数据流量)使用
WindowManagerService -> 关闭动画
PowerManagerService -> 省电模式-CPU降频
UiModeManagerService -> 开启暗色主题模式(Q平台版本新增)
GnssLocationProvider -> 灭屏后开启GPS待机省电模式
SoundTriggerHelper -> 拦截语音交互功能
DeviceStateMonitor -> 设置Modem为省电模式
PowerUI -> 取消低电量通知提醒

    <Preference
        <!--绑定的fragment-->
        android:fragment="com.android.settings.fuelgauge.batterysaver.BatterySaverSettings"
        android:key="battery_saver_summary"
        android:title="@string/battery_saver"
        <!--绑定的controller-->
        settings:controller="com.android.settings.fuelgauge.BatterySaverController"
        settings:platform_slice="true"
        settings:allowDividerAbove="true" />

BatterySaverController #onStart() 控制 summary的显示,

  1. 注册了一个ContentObserveryong用来监测LOW_POWER_MODE_TRIGGER_LEVEL 的改变,
  2. 利用mBatteryStateChangeReceiver来接收
    PowerManager.ACTION_POWER_SAVE_MODE_CHANGING
    Intent.ACTION_BATTERY_CHANGED
    的广播状态。
  3. 最后更新summary
    @Override
    public void onStart() {
        mContext.getContentResolver().registerContentObserver(
                Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL)
                , true, mObserver);

        mBatteryStateChangeReceiver.setListening(true);
        updateSummary();
    }
...
    private final ContentObserver mObserver = new ContentObserver(
            new Handler(Looper.getMainLooper())) {
        @Override
        public void onChange(boolean selfChange) {
            updateSummary();
        }
    };

updateSummary() 会调用 getSummary()

    @Override
    public CharSequence getSummary() {
        final boolean isPowerSaveOn = mPowerManager.isPowerSaveMode();
        final int percent = Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
        if (isPowerSaveOn) {
            return mContext.getString(R.string.battery_saver_on_summary);
        } else if (percent != 0) {
            return mContext.getString(R.string.battery_saver_off_scheduled_summary,
                    Utils.formatPercentage(percent));
        } else {
            return mContext.getString(R.string.battery_saver_off_summary);
        }
    }

利用mPowerManager 获取是否打开了savemode,还有获取 LOW_POWER_MODE_TRIGGER_LEVEL的值,来确定最终的summary字符串是什么。
isPowerSaveMode() 获取流程

进入设置项

按钮打开和关闭的显示页面
布局文件如下:

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:title="@string/battery_saver"
    android:key="battery_saver_page">

    <!-- Turn on automatically -->
    <SwitchPreference
        android:key="auto_battery_saver"
        android:title="@string/battery_saver_auto_title"
        settings:controller="com.android.settings.fuelgauge.batterysaver.AutoBatterySaverPreferenceController" />

    <com.android.settings.widget.SeekBarPreference
        android:key="battery_saver_seek_bar"
        android:title="@string/battery_saver_seekbar_title_placeholder"
        android:max="75"
        android:min="5" />

    <com.android.settings.widget.TwoStateButtonPreference
        android:key="battery_saver"
        android:title="@string/battery_saver"
        android:selectable="false"
        settings:textOn="@string/battery_saver_button_turn_on"
        settings:textOff="@string/battery_saver_button_turn_off"
        settings:platform_slice="true"
        settings:controller="com.android.settings.fuelgauge.batterysaver.BatterySaverButtonPreferenceController" />

    <PreferenceCategory
        android:key="battery_saver_footer">
        <com.android.settingslib.widget.FooterPreference
            android:key="battery_saver_footer_preference"
            android:title="@*android:string/battery_saver_description"
            android:selectable="false" />
    </PreferenceCategory>

</PreferenceScreen>

2.3.1 auto_battery_saver

布局文件 battery_saver_settings.xml

    <SwitchPreference
        android:key="auto_battery_saver"
        android:title="@string/battery_saver_auto_title"
        settings:controller="com.android.settings.fuelgauge.batterysaver.AutoBatterySaverPreferenceController" />

AutoBatterySaverPreferenceController 控制控件的状态显示,获取状态值,确定按钮是打开还是关闭

    ...
    mDefaultTriggerLevelForOn = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_lowBatteryWarningLevel);//15
    ...
    @Override
    public boolean isChecked() {
        return Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, DEFAULT_TRIGGER_LEVEL) != 0;
    }
    @Override
    public boolean setChecked(boolean isChecked) {
        BatterySaverUtils.setAutoBatterySaverTriggerLevel(mContext,
                isChecked ? mDefaultTriggerLevelForOn : 0);
        return true;
    }
    ...

打开自动开启:BatterySaverUtils.setAutoBatterySaverTriggerLevel,更新 SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION
LOW_POWER_MODE_TRIGGER_LEVEL
的值

**
     * Don't show the automatic battery suggestion notification in the future.
     */
    public static void suppressAutoBatterySaver(Context context) {
        Secure.putInt(context.getContentResolver(),
                Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 1);
    }

    /**
     * Set the automatic battery saver trigger level to {@code level}.
     */
    public static void setAutoBatterySaverTriggerLevel(Context context, int level) {
        if (level > 0) {
            suppressAutoBatterySaver(context);
        }
        Global.putInt(context.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL, level);
    }

2.3.2 battery_saver_seek_bar

    <com.android.settings.widget.SeekBarPreference
        android:key="battery_saver_seek_bar"
        android:title="@string/battery_saver_seekbar_title_placeholder"
        android:max="75"
        android:min="5" />

在 BatterySaverSettings.java 中加入了控制它的controller : AutoBatterySeekBarPreferenceController.java

 /**
     * Observer that listens to change from {@link Settings.Global#LOW_POWER_MODE_TRIGGER_LEVEL}
     */
    private final class AutoBatterySaverSettingObserver extends ContentObserver {
        private final Uri mUri = Settings.Global.getUriFor(
                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL);
        private final ContentResolver mContentResolver;

        public AutoBatterySaverSettingObserver(Handler handler) {
            super(handler);
            mContentResolver = mContext.getContentResolver();
        }

        public void registerContentObserver() {
            mContentResolver.registerContentObserver(mUri, false, this);
        }

        public void unRegisterContentObserver() {
            mContentResolver.unregisterContentObserver(this);
        }

        @Override
        public void onChange(boolean selfChange, Uri uri, int userId) {
            if (mUri.equals(uri)) {
                updatePreference(mPreference);
            }
        }
    }

使用了ContentObserver来监测 LOW_POWER_MODE_TRIGGER_LEVEL 值的变化,如果是0,就不显示该设置项,如果是其他的值,就正常显示和调节进度值。由于 auto_battery_saver 的开关在关闭时,会设置 LOW_POWER_MODE_TRIGGER_LEVEL 的值为0,所以 battery_saver_seek_bar 会被隐藏。

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        final int progress = (int) newValue;
        Settings.Global.putInt(mContext.getContentResolver(),
                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, progress);//根据滑动设置seek bar的值
        return true;
    }

    @VisibleForTesting
    void updatePreference(Preference preference) {
        final ContentResolver contentResolver = mContext.getContentResolver();

        // Override the max value with LOW_POWER_MODE_TRIGGER_LEVEL_MAX, if set.
        final int maxLevel = Settings.Global.getInt(contentResolver,
                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, 0);
        if (maxLevel > 0) {
            if (!(preference instanceof SeekBarPreference)) {
                Log.e(TAG, "Unexpected preference class: " + preference.getClass());
            } else {
                final SeekBarPreference seekBarPreference = (SeekBarPreference) preference;
                if (maxLevel < seekBarPreference.getMin()) {
                    Log.e(TAG, "LOW_POWER_MODE_TRIGGER_LEVEL_MAX too low; ignored.");
                } else {
                    seekBarPreference.setMax(maxLevel);
                }
            }
        }
        // Set the current value.
        final int level = Settings.Global.getInt(contentResolver,
                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL,
                AutoBatterySaverPreferenceController.DEFAULT_TRIGGER_LEVEL);
        if (level == 0) {
            preference.setVisible(false);//隐藏seek bar
        } else {
            preference.setVisible(true);//显示seek bar
            preference.setTitle(mContext.getString(R.string.battery_saver_seekbar_title,
                    Utils.formatPercentage(level)));
            SeekBarPreference seekBarPreference = (SeekBarPreference) preference;
            seekBarPreference.setProgress(level);
            seekBarPreference.setSeekBarContentDescription(
                    mContext.getString(R.string.battery_saver_turn_on_automatically_title));
        }
    }

2.3.3 battery_saver Button

使用的是 TwoStateButtonPreference,在styles_preference.xml 定义了布局和样式

    <style name="TwoStateButtonPreference" parent="Preference.SettingsBase">
        <item name="android:layout">@layout/two_state_button</item>
    </style>
    
    <com.android.settings.widget.TwoStateButtonPreference
        android:key="battery_saver"
        android:title="@string/battery_saver"
        android:selectable="false"
        settings:textOn="@string/battery_saver_button_turn_on"
        settings:textOff="@string/battery_saver_button_turn_off"
        settings:platform_slice="true"
        settings:controller="com.android.settings.fuelgauge.batterysaver.BatterySaverButtonPreferenceController" />

控制这个控件的是 BatterySaverButtonPreferenceController.java
在构造方法里面 获取了PowerManager 和 BatterySaverReceiver 并且实现了 BatterySaverListener 的onBatteryChanged 方法。

    public BatterySaverButtonPreferenceController(Context context, String key) {
        super(context, key);
        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        mBatterySaverReceiver = new BatterySaverReceiver(context);
        mBatterySaverReceiver.setBatterySaverListener(this);
    }
...
    @Override
    public void onBatteryChanged(boolean pluggedIn) {
        if (mPreference != null) {
            //置灰
            mPreference.setButtonEnabled(!pluggedIn);//监测电源插入,数据来自atterySaverListener
        }
    }

在BatterySaverReceiver.java 中,实现了

    public void onReceive(Context context, Intent intent) {
        if (DEBUG) Log.d(TAG, "Received " + intent.getAction());
        String action = intent.getAction();
        if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGING.equals(action)) {
            if (mBatterySaverListener != null) {
                mBatterySaverListener.onPowerSaveModeChanged();
            }
        } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
            // disable BSM switch if phone is plugged in
            if (mBatterySaverListener != null) {
                final boolean pluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
                mBatterySaverListener.onBatteryChanged(pluggedIn);//实时监测电源插入
            }
        }
    }

在不插入电源的时候,加载页面会初始化按钮状态 使用 updateState方法

    @Override
    public boolean isChecked() {
        return mPowerManager.isPowerSaveMode();//调用流程和前面的 2.3 一样
    }
    ...
    @Override
    public void updateState(Preference preference) {
        super.updateState(preference);
        if (mPreference != null) {
            mPreference.setChecked(isChecked());
        }
    }

打开/关闭 battery saver按钮
代码调用流程:

2.3.3.1 BatterySaverButtonPreferenceController.java

使用 setChecked 方法

    @Override
    public boolean setChecked(boolean stateOn) {
        // This screen already shows a warning, so we don't need another warning.
        return BatterySaverUtils.setPowerSaveMode(mContext, stateOn,
                false /* needFirstTimeWarning */);
    }
2.3.3.2 BatterySaverUtils.java#setPowerSaveMode
    public static synchronized boolean setPowerSaveMode(Context context,
            boolean enable, boolean needFirstTimeWarning) {
        if (DEBUG) {
            Log.d(TAG, "Battery saver turning " + (enable ? "ON" : "OFF"));
        }
        final ContentResolver cr = context.getContentResolver();
        //不进入 这个 if
        if (enable && needFirstTimeWarning && maybeShowBatterySaverConfirmation(context)) {
            return false;
        }
        if (enable && !needFirstTimeWarning) {
            setBatterySaverConfirmationAcknowledged(context);
        }

        if (context.getSystemService(PowerManager.class).setPowerSaveMode(enable)) {
            if (enable) {
                final int count =
                        Secure.getInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 0) + 1;
                Secure.putInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, count);

                final Parameters parameters = new Parameters(context);

                if ((count >= parameters.startNth)
                        && (count <= parameters.endNth)
                        && Global.getInt(cr, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) == 0
                        && Secure.getInt(cr,
                        Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0) == 0) {
                    showAutoBatterySaverSuggestion(context);
                }
            }

            return true;
        }
        return false;
    }
...
    private static void setBatterySaverConfirmationAcknowledged(Context context) {
        // 不显示“首次节省电量警告”对话框
        Secure.putInt(context.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1);
    }
2.3.3.3 PowerManager.java
  public boolean setPowerSaveMode(boolean mode) {
        try {
            return mService.setPowerSaveMode(mode);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
2.3.3.4 IPowerManager.aidl
boolean setPowerSaveMode(boolean mode);
2.3.3.5 PowerManagerService.java # BinderService
 @Override // Binder call
        public boolean setPowerSaveMode(boolean enabled) {
            //检查权限
            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 (DEBUG) {
                Slog.d(TAG, "setLowPowerModeInternal " + enabled + " mIsPowered=" + mIsPowered);
            }
            if (mIsPowered) {// 是否在充电
                return false;
            }

            mBatterySaverStateMachine.setBatterySaverEnabledManually(enabled);

            return true;
        }
    }
2.3.3.6 BatterySaverStateMachine.java # setBatterySaverEnabledManually
    public void setBatterySaverEnabledManually(boolean enabled) {
        if (DEBUG) {
            Slog.d(TAG, "setBatterySaverEnabledManually: enabled=" + enabled);
        }
        synchronized (mLock) {
            enableBatterySaverLocked(/*enable=*/ enabled, /*manual=*/ true,
                    (enabled ? BatterySaverController.REASON_MANUAL_ON //2
                            : BatterySaverController.REASON_MANUAL_OFF),
                    (enabled ? "Manual ON" : "Manual OFF"));
        }
    }
...

    private void enableBatterySaverLocked(boolean enable, boolean manual, int intReason,
            String strReason) {
        if (DEBUG) {
            Slog.d(TAG, "enableBatterySaver: enable=" + enable + " manual=" + manual
                    + " reason=" + strReason + "(" + intReason + ")");
        }
        //battery saver 是否启用
        final boolean wasEnabled = mBatterySaverController.isEnabled();

        if (wasEnabled == enable) {//已经启用
            if (DEBUG) {
                Slog.d(TAG, "Already " + (enable ? "enabled" : "disabled"));
            }
            return;
        }
        if (enable && mIsPowered) {//启用battery saver 并且在充电
            if (DEBUG) Slog.d(TAG, "Can't enable: isPowered");
            return;
        }
        mLastChangedIntReason = intReason;
        mLastChangedStrReason = strReason;

        if (manual) {//true
            if (enable) {
                // 手动禁用了BS
                updateSnoozingLocked(false, "Manual snooze OFF");
            } else {
                // When battery saver is disabled manually (while battery saver is enabled)
                // when the battery level is low, we "snooze" BS -- i.e. disable auto battery saver.
                // We resume auto-BS once the battery level is not low, or the device is plugged in.
                // 当电池电量低的时候,手动禁用电池节能器(当电池节能器启用时),我们“打盹”BS -即禁用自动电池节能器。一旦电池电量不低,或者设备被插入,我们就恢复自动- bs。
                if (isBatterySaverEnabled() && mIsBatteryLevelLow) {
                    updateSnoozingLocked(true, "Manual snooze");
                }
            }
        }

        mSettingBatterySaverEnabled = enable;
        // 启用低电量模式 (battery saver)
        putGlobalSetting(Global.LOW_POWER_MODE, enable ? 1 : 0);

        if (manual) {
            // 更新状态值
            mSettingBatterySaverEnabledSticky = enable;
            // 如果是1,当设备从充电器上拔出或重启后,电池节省器({@link #LOW_POWER_MODE})将被重新激活。
            putGlobalSetting(Global.LOW_POWER_MODE_STICKY, enable ? 1 : 0);
        }
        mBatterySaverController.enableBatterySaver(enable, intReason//2);

        if (DEBUG) {
            Slog.d(TAG, "Battery saver: Enabled=" + enable
                    + " manual=" + manual
                    + " reason=" + strReason + "(" + intReason + ")");
        }
    }
2.3.3.7 BatterySaverController.java # enableBatterySaver
 public void enableBatterySaver(boolean enable, int reason) {
        synchronized (mLock) {
            if (mEnabled == enable) {
                return;
            }
            mEnabled = enable;
            mHandler.postStateChanged(/*sendBroadcast=*/ true, reason);
        }
    }
...
    private class MyHandler extends Handler {
        private static final int MSG_STATE_CHANGED = 1;
        private static final int ARG_DONT_SEND_BROADCAST = 0;
        private static final int ARG_SEND_BROADCAST = 1;
        private static final int MSG_SYSTEM_READY = 2;
        ...
        public void postStateChanged(boolean sendBroadcast, int reason) {
            obtainMessage(MSG_STATE_CHANGED, sendBroadcast ?
                    ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, reason).sendToTarget();
        ...
      @Override
      public void dispatchMessage(Message msg) {
            switch (msg.what) {
                case MSG_STATE_CHANGED://获取到 message
                    handleBatterySaverStateChanged(
                            msg.arg1 == ARG_SEND_BROADCAST,
                            msg.arg2);
                    break;

                case MSG_SYSTEM_READY:
                    for (Plugin p : mPlugins) {
                        p.onSystemReady(BatterySaverController.this);
                    }
                    break;
            }
        }
        }


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

        final boolean enabled;
        final boolean isInteractive = getPowerManager().isInteractive();
        final ArrayMap<String, String> fileValues;

        synchronized (mLock) {
            EventLogTags.writeBatterySaverMode(
                    mPreviouslyEnabled ? 1 : 0, // Previously off or on.
                    mEnabled ? 1 : 0, // Now off or on.
                    isInteractive ?  1 : 0, // Device interactive state.
                    mEnabled ? mBatterySaverPolicy.toEventLogString() : "",
                    reason);
            mPreviouslyEnabled = mEnabled;

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

            enabled = mEnabled;
            mIsInteractive = isInteractive;

            if (enabled) {
                fileValues = mBatterySaverPolicy.getFileValues(isInteractive);
            } else {
                fileValues = null;
            }
        }

        final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
        if (pmi != null) {
            // CPU 调低频
            pmi.powerHint(PowerHint.LOW_POWER, enabled ? 1 : 0);
        }

        updateBatterySavingStats();

        if (ArrayUtils.isEmpty(fileValues)) {
            mFileUpdater.restoreDefault();
        } else {
            mFileUpdater.writeFiles(fileValues);
        }

        for (Plugin p : mPlugins) {
            p.onBatterySaverChanged(this);
        }

        if (sendBroadcast) {

            if (DEBUG) {
                Slog.i(TAG, "Sending broadcasts for mode: " + enabled);
            }

            // Send the broadcasts and notify the listeners. We only do this when the battery saver
            // mode changes, but not when only the screen state changes.
            // 省电模式变化广播 1
            // 普通 APP 使用动态广播
            Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)
                    .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, enabled)
                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
            
            // 省电模式变化广播 2
            // 普通 APP 可监听
            // 需要重点查看接受这个广播的系统服务做了什么
            intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
            
            // 省电模式变化广播 3
            // 需要注册 Manifest.permission.DEVICE_POWER 权限且需要系统签名才能收到
            // 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);
            // 系统其它服务,主要注册监听 LowPowerModeListener 变化进行省电模式的开启
            for (LowPowerModeListener listener : listeners) {
                final PowerSaveState result =
                        mBatterySaverPolicy.getBatterySaverPolicy(
                                listener.getServiceType(), enabled);
                // 通知所有注册 LowPowerModeListener 的服务会发生改变
                listener.onLowPowerModeChanged(result);
            }
        }
    }

2.3.4 battery_saver_footer

    <PreferenceCategory
        android:key="battery_saver_footer">
        <com.android.settingslib.widget.FooterPreference
            android:key="battery_saver_footer_preference"
            android:title="@*android:string/battery_saver_description"
            android:selectable="false" />
    </PreferenceCategory>

2.4 smart_battery_manager

    <Preference
        android:fragment="com.android.settings.fuelgauge.SmartBatterySettings"
        android:key="smart_battery_manager"
        android:title="@string/smart_battery_manager_title"
        settings:controller="com.android.settings.fuelgauge.batterytip.BatteryManagerPreferenceController" />
   @Override
    public void updateState(Preference preference) {
        super.updateState(preference);
        // 获取受限 app 列表
        final int num = BatteryTipUtils.getRestrictedAppsList(mAppOpsManager, mUserManager).size();
        final String setting = mPowerUsageFeatureProvider.isSmartBatterySupported()
                ? Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED
                : Settings.Global.APP_AUTO_RESTRICTION_ENABLED;
        // 获取开关状态
        final boolean featureOn =
                Settings.Global.getInt(mContext.getContentResolver(), setting, ON) == ON;
        //更新summary
        updateSummary(preference, featureOn, num);
    }

    @VisibleForTesting
    void updateSummary(Preference preference, boolean featureOn, int num) {
        if (num > 0) {
            preference.setSummary(mContext.getResources().getQuantityString(
                    R.plurals.battery_manager_app_restricted, num, num));
        } else if (featureOn) {
            preference.setSummary(R.string.battery_manager_on);
        } else {
            preference.setSummary(R.string.battery_manager_off);
        }
    }

2.5 battery_percentage

    <SwitchPreference
        android:key="battery_percentage"
        android:title="@string/battery_percentage"
        android:summary="@string/battery_percentage_description" />

BatteryPercentagePreferenceController.java

 @Override
    public boolean isAvailable() {//该项是否显示
        return mContext.getResources()
                .getBoolean(R.bool.config_battery_percentage_setting_available);
    }

    @Override
    public void updateState(Preference preference) {
        int setting = Settings.System.getInt(mContext.getContentResolver(),
                SHOW_BATTERY_PERCENT, 0);//获取状态值

        ((SwitchPreference) preference).setChecked(setting == 1);
    }

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        boolean showPercentage = (Boolean) newValue;
        Settings.System.putInt(mContext.getContentResolver(), SHOW_BATTERY_PERCENT,
                showPercentage ? 1 : 0);//写入属性值
        return true;
    }

在 frameworks/base/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java 中利用ContentObserver 区监测 SHOW_BATTERY_PERCENT 的改变,进而显示或隐藏电量百分比

getContext().getContentResolver().registerContentObserver(
    Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, newUserId);

2.6 last_full_charge

    <com.android.settings.fuelgauge.PowerGaugePreference
        android:key="last_full_charge"
        android:title="@string/battery_last_full_charge"
        android:selectable="false"
        settings:allowDividerAbove="true" />

代码调用关系

PowerUsageSummary.java
-> onCreate()
-> restartBatteryInfoLoader()
getLoaderManager().restartLoader(BATTERY_INFO_LOADER, Bundle.EMPTY,
mBatteryInfoLoaderCallbacks);
->mBatteryInfoLoaderCallbacks
->updateLastFullChargePreference()

    @VisibleForTesting
    void updateLastFullChargePreference() {
        if (mBatteryInfo != null && mBatteryInfo.averageTimeToDischarge//获取平均放电时间
                != Estimate.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN) {
            // 电池充满电后的大致使用时长
            mLastFullChargePref.setTitle(R.string.battery_full_charge_last);
            mLastFullChargePref.setSubtitle(
                    StringUtil.formatElapsedTime(getContext(), mBatteryInfo.averageTimeToDischarge,
                            false /* withSeconds */));
        } else {
            final long lastFullChargeTime = mBatteryUtils.calculateLastFullChargeTime(mStatsHelper,
                    System.currentTimeMillis());
            mLastFullChargePref.setTitle(R.string.battery_last_full_charge);
            mLastFullChargePref.setSubtitle(
                    StringUtil.formatRelativeTime(getContext(), lastFullChargeTime,
                            false /* withSeconds */));
        }
    }

2.7 screen_usage

    <com.android.settings.fuelgauge.PowerGaugePreference
        android:key="screen_usage"
        android:title="@string/device_screen_usage"
        android:selectable="false" />
...
        mScreenUsagePref.setSubtitle(StringUtil.formatElapsedTime(getContext(),
                mBatteryUtils.calculateScreenUsageTime(mStatsHelper), false));

调用 BatteryUtils的calculateScreenUsageTime方法,来获取 BatterySipper

    public long calculateScreenUsageTime(BatteryStatsHelper batteryStatsHelper) {
        final BatterySipper sipper = findBatterySipperByType(
                batteryStatsHelper.getUsageList(), BatterySipper.DrainType.SCREEN);
        return sipper != null ? sipper.usageTimeMs : 0;
    }
    ...
    public BatterySipper findBatterySipperByType(List<BatterySipper> usageList,
            BatterySipper.DrainType type) {
        for (int i = 0, size = usageList.size(); i < size; i++) {
            final BatterySipper sipper = usageList.get(i);
            if (sipper.drainType == type) {
                return sipper;
            }
        }
        return null;
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值