欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Android Q 电量使用图分析 show app usage

程序员文章站 2022-03-23 12:18:49
这个界面就是packages/apps/Settings/src/com/android/settings/fuelgauge/PowerUsageAdcanced.java,然后查看它加载的布局文件@Override protected int getPreferenceScreenResId() { return R.xml.power_usage_advanced; }布局代码如下

Android Q 电量使用图分析 show app usage

 

这个界面就是packages/apps/Settings/src/com/android/settings/fuelgauge/PowerUsageAdcanced.java,然后查看它加载的布局文件

@Override
    protected int getPreferenceScreenResId() {
        return R.xml.power_usage_advanced;
    }

布局代码如下 

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

    <com.android.settings.fuelgauge.BatteryHistoryPreference
        android:key="battery_graph"/>

    <PreferenceCategory
        android:key="app_list"
        android:title="@string/power_usage_list_summary"/>

</PreferenceScreen>

从布局上看,我们可以发现,我们要找的那个曲线图就是BatteryHistoryPreference,回到PowerUsageAdvanced中看一下

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        final Context context = getContext();

        mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_GRAPH);
        mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
                .getPowerUsageFeatureProvider(context);
        mBatteryUtils = BatteryUtils.getInstance(context);

        // init the summary so other preferences won't have unnecessary move
        updateHistPrefSummary(context);
        restoreSavedInstance(icicle);
    }

    @Override
    protected void refreshUi(@BatteryUpdateType int refreshType) {
        final Context context = getContext();
        if (context == null) {
            return;
        }
        updatePreference(mHistPref);
        updateHistPrefSummary(context);

        mBatteryAppListPreferenceController.refreshAppListGroup(mStatsHelper, mShowAllApps);
    }

    private void updateHistPrefSummary(Context context) {
        Intent batteryIntent =
                context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        final boolean plugged = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0;

        if (mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(context) && !plugged) {
            mHistPref.setBottomSummary(
                    mPowerUsageFeatureProvider.getAdvancedUsageScreenInfoString());
        } else {
            mHistPref.hideBottomSummary();
        }
    }

我们发现BatteryHistoryPreference没有做什么特别的时候,只有一个updatePreference(),下面我们追一下看看去做什么了。这个updatePreference()在父类中有实现。

看一下packages/apps/Settings/src/com/android/settings/fuelgauge/PowerUsageBase.java,很简单,就是调用了BatteryHistoryPreference中的setStats()。

    protected void updatePreference(BatteryHistoryPreference historyPref) {
        final long startTime = System.currentTimeMillis();
        historyPref.setStats(mStatsHelper);
        BatteryUtils.logRuntime(TAG, "updatePreference", startTime);
    }

然后我们看一下BatteryHistoryPreference,setStats就是去获取一下batteryinfo,主要看onBindViewHolder,它会调用BatteryInfo的bindHistory去画图。

    public void setStats(BatteryStatsHelper batteryStats) {
        BatteryInfo.getBatteryInfo(getContext(), info -> {
            mBatteryInfo = info;
            notifyChanged();
        }, batteryStats, false);
    }


    @Override
    public void onBindViewHolder(PreferenceViewHolder view) {
        super.onBindViewHolder(view);
        final long startTime = System.currentTimeMillis();
        if (mBatteryInfo == null) {
            return;
        }

            ((TextView) view.findViewById(R.id.charge)).setText(mBatteryInfo.batteryPercentString);

        mSummaryView = (TextView) view.findViewById(R.id.bottom_summary);
        if (mSummary != null) {
            mSummaryView.setText(mSummary);
        }
        if (hideSummary) {
            mSummaryView.setVisibility(View.GONE);
        }
        UsageView usageView = (UsageView) view.findViewById(R.id.battery_usage);
        usageView.findViewById(R.id.label_group).setAlpha(.7f);
        mBatteryInfo.bindHistory(usageView);
        BatteryUtils.logRuntime(TAG, "onBindViewHolder", startTime);
    }

下面就去看packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryInfo.java,bindHistory就是去获取曲线图要画的每个点的信息。 

parse(mStats, parserList);

会去循环获取HistoryItem,它储存了每个时间点的电量,用来画图的时候用的。所以我们要去看HistoryItem是怎么获取的。


    public void bindHistory(final UsageView view, BatteryDataParser... parsers) {
        final Context context = view.getContext();
        BatteryDataParser parser = new BatteryDataParser() {
            SparseIntArray points = new SparseIntArray();
            long startTime;
            int lastTime = -1;
            byte lastLevel;

            @Override
            public void onParsingStarted(long startTime, long endTime) {
                this.startTime = startTime;
                timePeriod = endTime - startTime;
                view.clearPaths();
                // Initially configure the graph for history only.
                view.configureGraph((int) timePeriod, 100);
            }

            @Override
            public void onDataPoint(long time, HistoryItem record) {
                lastTime = (int) time;
                lastLevel = record.batteryLevel;
                points.put(lastTime, lastLevel);
           
            }

            @Override
            public void onDataGap() {
                if (points.size() > 1) {
                    view.addPath(points);
                }
                points.clear();
            }

            @Override
            public void onParsingDone() {
                onDataGap();

                // Add projection if we have an estimate.
                if (remainingTimeUs != 0) {
                    PowerUsageFeatureProvider provider = FeatureFactory.getFactory(context)
                            .getPowerUsageFeatureProvider(context);
                    if (!mCharging && provider.isEnhancedBatteryPredictionEnabled(context)) {
                        points = provider.getEnhancedBatteryPredictionCurve(context, startTime);
                    } else {
                        // Linear extrapolation.
                        if (lastTime >= 0) {
                            points.put(lastTime, lastLevel);
                            points.put((int) (timePeriod +
                                            PowerUtil.convertUsToMs(remainingTimeUs)),
                                    mCharging ? 100 : 0);
                        }
                    }
                }

                // If we have a projection, reconfigure the graph to show it.
                if (points != null && points.size() > 0) {
                    int maxTime = points.keyAt(points.size() - 1);
                    view.configureGraph(maxTime, 100);
                    view.addProjectedPath(points);
                }
            }
        };
        BatteryDataParser[] parserList = new BatteryDataParser[parsers.length + 1];
        for (int i = 0; i < parsers.length; i++) {
            parserList[i] = parsers[i];
        }
        parserList[parsers.length] = parser;
        parse(mStats, parserList);
        String timeString = context.getString(R.string.charge_length_format,
                Formatter.formatShortElapsedTime(context, timePeriod));
        String remaining = "";
        if (remainingTimeUs != 0) {
            remaining = context.getString(R.string.remaining_length_format,
                    Formatter.formatShortElapsedTime(context, remainingTimeUs / 1000));
        }
        view.setBottomLabels(new CharSequence[] {timeString, remaining});
    }

从代码上看HistoryItem是通过BatteryStats对象获取的,下面看BatteryStats对象怎么获取的。

final HistoryItem rec = new HistoryItem();
            while (stats.getNextHistoryLocked(rec)) {

是通过pars(mStats,parserList)中传过去的,接着看mStats怎么获取的。它是通过getBatteryInfo获取的。

 public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast,
            BatteryStats stats, Estimate estimate, long elapsedRealtimeUs, boolean shortString) {
        final long startTime = System.currentTimeMillis();
        BatteryInfo info = new BatteryInfo();
        info.mStats = stats;

然后这个getBatteryInfo又是通过别的getBatteryInfo获取的,你会发现BatteryStats是通过BatteryStatsHelper获得的。而这个最上面的getBatteryInfo刚好是BatteryHistoryPreference中setStats的调用的,然后传了一个BatteryStatsHelper对象,所以我们要往回追。

    public static void getBatteryInfo(final Context context, final Callback callback,
            final BatteryStatsHelper statsHelper, boolean shortString) {
        new AsyncTask<Void, Void, BatteryInfo>() {
            @Override
            protected BatteryInfo doInBackground(Void... params) {
                return getBatteryInfo(context, statsHelper, shortString);
            }

            @Override
            protected void onPostExecute(BatteryInfo batteryInfo) {
                final long startTime = System.currentTimeMillis();
                callback.onBatteryInfoLoaded(batteryInfo);
                BatteryUtils.logRuntime(LOG_TAG, "time for callback", startTime);
            }
        }.execute();
    }

    public static BatteryInfo getBatteryInfo(final Context context,
            final BatteryStatsHelper statsHelper, boolean shortString) {
        final BatteryStats stats;
        final long batteryStatsTime = System.currentTimeMillis();
        if (statsHelper == null) {
            final BatteryStatsHelper localStatsHelper = new BatteryStatsHelper(context,
                    true);
            localStatsHelper.create((Bundle) null);
            stats = localStatsHelper.getStats();
        } else {
            stats = statsHelper.getStats();
        }
        BatteryUtils.logRuntime(LOG_TAG, "time for getStats", batteryStatsTime);

        final long startTime = System.currentTimeMillis();
        PowerUsageFeatureProvider provider =
                FeatureFactory.getFactory(context).getPowerUsageFeatureProvider(context);
        final long elapsedRealtimeUs =
                PowerUtil.convertMsToUs(SystemClock.elapsedRealtime());

        final Intent batteryBroadcast = context.registerReceiver(null,
                new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        // 0 means we are discharging, anything else means charging
        final boolean discharging =
                batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) == 0;

        if (discharging && provider != null
                && provider.isEnhancedBatteryPredictionEnabled(context)) {
            Estimate estimate = provider.getEnhancedBatteryPrediction(context);
            if (estimate != null) {
                Estimate.storeCachedEstimate(context, estimate);
                BatteryUtils
                        .logRuntime(LOG_TAG, "time for enhanced BatteryInfo", startTime);
                return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats,
                        estimate, elapsedRealtimeUs, shortString);
            }
        }
        final long prediction = discharging
                ? stats.computeBatteryTimeRemaining(elapsedRealtimeUs) : 0;
        final Estimate estimate = new Estimate(
                PowerUtil.convertUsToMs(prediction),
                false, /* isBasedOnUsage */
                EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
        BatteryUtils.logRuntime(LOG_TAG, "time for regular BatteryInfo", startTime);
        return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats,
                estimate, elapsedRealtimeUs, shortString);
    }

 

下面回去看BatteryHistoryPreference中的setStats中的BatteryStatsHelper怎么获取的,继续往回就回到了PowerUsageBase了。

    protected void updatePreference(BatteryHistoryPreference historyPref) {
        final long startTime = System.currentTimeMillis();
        historyPref.setStats(mStatsHelper);
        BatteryUtils.logRuntime(TAG, "updatePreference", startTime);
    }

然后接着看这个mStatsHelper怎么获取的。

  @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        mUm = (UserManager) activity.getSystemService(Context.USER_SERVICE);
        mStatsHelper = new BatteryStatsHelper(activity, true);
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        mStatsHelper.create(icicle);
        setHasOptionsMenu(true);

        mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(getContext());
        mBatteryBroadcastReceiver.setBatteryVoltageTempListener(mBatteryVoltageTempListener);
        mBatteryBroadcastReceiver.setBatteryChangedListener(type -> {
            restartBatteryStatsLoader(type);
        });
    }

public class PowerLoaderCallback implements LoaderManager.LoaderCallbacks<BatteryStatsHelper> {
        private int mRefreshType;

        @Override
        public Loader<BatteryStatsHelper> onCreateLoader(int id,
                Bundle args) {
            mRefreshType = args.getInt(KEY_REFRESH_TYPE);
            return new BatteryStatsHelperLoader(getContext());
        }

        @Override
        public void onLoadFinished(Loader<BatteryStatsHelper> loader,
                BatteryStatsHelper statsHelper) {
            mStatsHelper = statsHelper;
            refreshUi(mRefreshType);
        }

        @Override
        public void onLoaderReset(Loader<BatteryStatsHelper> loader) {

        }
    }

 

 

后面我就追不下去了,没什么思路了。后来发现,既然这边追不下去,那么我是不是可以去看系统是怎么去设置HistroyItem的。后来不知道看到哪里了,因为我要找的是电量,所以直接去BatteryStatsImpl(BatteryStats是接口),中去看它怎么给HistoryItem赋值的。然后这边看看,那边看看。去里面搜索batterylevel,然后无意中发现了

setBatteryStateLocked

后来加了log,debug,就是这个去设置HistoryItem的电量。然后去frameworks/base下面去搜索看看,setBatteryStateLocked有哪些地方用了。发现services/core/java/com/android/server/am/BatteryStatsService.java中有使用,


    @Override
    public void setBatteryState(final int status, final int health, final int plugType,
            final int level, final int temp, final int volt, final int chargeUAh,
            final int chargeFullUAh) {
        enforceCallingPermission();

        // BatteryService calls us here and we may update external state. It would be wrong
        // to block such a low level service like BatteryService on external stats like WiFi.
        mWorker.scheduleRunnable(() -> {
            synchronized (mStats) {
                final boolean onBattery = BatteryStatsImpl.isOnBattery(plugType, status);
                if (mStats.isOnBattery() == onBattery) {
                    // The battery state has not changed, so we don't need to sync external
                    // stats immediately.
                    mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
                            chargeUAh, chargeFullUAh);
                    return;
                }
            }

            // Sync external stats first as the battery has changed states. If we don't sync
            // before changing the state, we may not collect the relevant data later.
            // Order here is guaranteed since we're scheduling from the same thread and we are
            // using a single threaded executor.
            mWorker.scheduleSync("battery-state", BatteryExternalStatsWorker.UPDATE_ALL);
            mWorker.scheduleRunnable(() -> {
                synchronized (mStats) {
                    mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
                            chargeUAh, chargeFullUAh);
                }
            });
        });
    }

下面在去看看setBatteryState哪里有用。 其实它使用了AIDL,public final class BatteryStatsService extends IBatteryStats.Stub

,然后搜索发现,services/core/java/com/android/server/BatteryService.java中有使用这个。

private void processValuesLocked(boolean force) {
        boolean logOutlier = false;
        long dischargeDuration = 0;

        mBatteryLevelCritical =
            mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
            && mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
        if (mHealthInfo.chargerAcOnline) {
            mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
        } else if (mHealthInfo.chargerUsbOnline) {
            mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
        } else if (mHealthInfo.chargerWirelessOnline) {
            mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
        } else {
            mPlugType = BATTERY_PLUGGED_NONE;
        }

        if (DEBUG) {
            Slog.d(TAG, "Processing new values: "
                    + "info=" + mHealthInfo
                    + ", mBatteryLevelCritical=" + mBatteryLevelCritical
                    + ", mPlugType=" + mPlugType);
        }

        // Let the battery stats keep track of the current level.
        try {
                mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
                        mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature,
                        mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter,
                        mHealthInfo.batteryFullCharge);
            }
        } catch (RemoteException e) {
            // Should never happen.
        }

 

 

最后其实就是BatteryService中,当电池变化了,就去设置一下。

setBatteryState里面的电量就是图上显示的,当然它会有一些处理,并且保存下来,形成一个列表。然后用的时候就可以取出来了,每个时间点的电量。

 

 

 

------------------------------------------------------------------------------------------------------------

PS:Last full charge 和 Screen usage since full charge也是通过这个触发计时的。

BatteryService setBatteryState() -> IBatteryStats setBatteryState() -> BatteryStatsService setBatteryState() -> BatteryStatsImpl setBatteryStateLock() -> setOnBatteryLocked()

BatteryStatsImpl。 

从代码上可以看出,会更具电量等信息去处理,是否要重新计时。就是batteryservice setBatteryState那传过来的。

protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
            final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) {
        boolean doWrite = false;
        Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
        m.arg1 = onBattery ? 1 : 0;
        mHandler.sendMessage(m);

        final long uptime = mSecUptime * 1000;
        final long realtime = mSecRealtime * 1000;
        final int screenState = mScreenState;
        if (onBattery) {
            // We will reset our status if we are unplugging after the
            // battery was last full, or the level is at 100, or
            // we have gone through a significant charge (from a very low
            // level to a now very high level).
            boolean reset = false;
            if (!mNoAutoReset && (oldStatus == BatteryManager.BATTERY_STATUS_FULL
                    || level >= 90
                    || (mDischargeCurrentLevel < 20 && level >= 80))) {
                Slog.i(TAG, "Resetting battery stats: level=" + level + " status=" + oldStatus
                        + " dischargeLevel=" + mDischargeCurrentLevel
                        + " lowAmount=" + getLowDischargeAmountSinceCharge()
                        + " highAmount=" + getHighDischargeAmountSinceCharge());
                // Before we write, collect a snapshot of the final aggregated
                // stats to be reported in the next checkin.  Only do this if we have
                // a sufficient amount of data to make it interesting.

 

本文地址:https://blog.csdn.net/Aaron121314/article/details/107401227