1.PowerUsageSummary.getFakeStats()
Google的函数调试,实际用途不大,但是适合修改UI和算法,毕竟总不能静静等耗电数据产生吧。
故这里提供了非常好的模拟数据和方法,进行调试耗电详情列表
1.1 开启调试开关
Z:\8.1\vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\fuelgauge\PowerUsageSummary.java
/**
* Displays a list of apps and subsystems that consume power, ordered by how much power was
* consumed since the last time it was unplugged.
*/
public class PowerUsageSummary extends PowerUsageBase implements
AnomalyDialogListener, OnLongClickListener, OnClickListener {
private static final boolean USE_FAKE_DATA = true;
private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 10;
1.2 耗电详情类型
// frameworks/base/core/java/com/android/internal/os/BatterySipper.java
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatterySipper.DrainType;
public enum DrainType {
IDLE,
CELL,
PHONE,
WIFI,
BLUETOOTH,
FLASHLIGHT,
SCREEN,
APP,
USER,
UNACCOUNTED,
OVERCOUNTED,
CAMERA,
MEMORY
}
1.3 制造模拟耗电数据
PowerUsageSummary->getFakeStats() 主要进行 DrainType 中的所有类型的模拟数据填充
private static List<BatterySipper> getFakeStats() {
ArrayList<BatterySipper> stats = new ArrayList<>();
// 耗电量 单位是 mA
float use = 5;
// 遍历耗电类型,除了APP类型,其他类型数据填充模拟数据 use
for (DrainType type : DrainType.values()) {
// 应用类型的耗电数据
if (type == DrainType.APP) {
// 过滤掉
continue;
}
// 接口名称 BatterySipper(DrainType drainType, Uid uid, double value)
/**
05-30 12:27:56.464 1295 1295 D fadi: type = IDLE, use = 5.0 mA
05-30 12:27:56.464 1295 1295 D fadi: type = CELL, use = 10.0 mA
05-30 12:27:56.464 1295 1295 D fadi: type = PHONE, use = 15.0 mA
05-30 12:27:56.464 1295 1295 D fadi: type = WIFI, use = 20.0 mA
05-30 12:27:56.464 1295 1295 D fadi: type = BLUETOOTH, use = 25.0 mA
05-30 12:27:56.464 1295 1295 D fadi: type = FLASHLIGHT, use = 30.0 mA
05-30 12:27:56.465 1295 1295 D fadi: type = SCREEN, use = 35.0 mA
05-30 12:27:56.465 1295 1295 D fadi: type = USER, use = 40.0 mA
05-30 12:27:56.465 1295 1295 D fadi: type = UNACCOUNTED, use = 45.0 mA
05-30 12:27:56.465 1295 1295 D fadi: type = OVERCOUNTED, use = 50.0 mA
05-30 12:27:56.465 1295 1295 D fadi: type = CAMERA, use = 55.0 mA
05-30 12:27:56.465 1295 1295 D fadi: type = MEMORY, use = 60.0 mA
*/
stats.add(new BatterySipper(type, null, use));
use += 5;
}
// 将 UID为 10000 ~ 10099 的应用,填写上模拟耗电数据数据
for (int i = 0; i < 100; i++) {
// 具体UID 对应的应用可以使用命令导出查看 adb pull /data/system/packages.xml D://
/**
* 05-30 12:48:54.623 1297 1297 D fadi : DrainType.APP UID = 10000, use = 65.0 mA
05-30 12:48:54.624 1297 1297 D fadi : DrainType.APP UID = 10001, use = 65.0 mA
05-30 12:48:54.625 1297 1297 D fadi : DrainType.APP UID = 10002, use = 65.0 mA
... ...
05-30 12:48:54.649 1297 1297 D fadi : DrainType.APP UID = 10097, use = 65.0 mA
05-30 12:48:54.649 1297 1297 D fadi : DrainType.APP UID = 10098, use = 65.0 mA
05-30 12:48:54.649 1297 1297 D fadi : DrainType.APP UID = 10099, use = 65.0 mA
*/
stats.add(new BatterySipper(DrainType.APP, new FakeUid(Process.FIRST_APPLICATION_UID + i), use));
}
// 事实上没有 uid 为 0 的进程,可作为防错检测
// stats.add(new BatterySipper(DrainType.APP,new FakeUid(0), use));
// 模拟odex进程的耗电数据 Simulate dex2oat process.
// // 模拟UID 10000 的耗电数据
// android.auto_generated_rro__ 10000 0 /data/user/0/android.auto_generated_rro__ platform:targetSdkVersion=27 none
BatterySipper sipper = new BatterySipper(DrainType.APP,
new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID)), 10.0f);
sipper.packageWithHighestDrain = "dex2oat";
stats.add(sipper);
// 模拟UID 10001 的耗电数据
// com.android.systemui.theme.dark 10001 1 /data/user/0/com.android.systemui.theme.dark platform:targetSdkVersion=27 none
sipper = new BatterySipper(DrainType.APP,
new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID + 1)), 10.0f);
sipper.packageWithHighestDrain = "dex2oat";
stats.add(sipper);
// 模拟日志进程的耗电数据
sipper = new BatterySipper(DrainType.APP,
new FakeUid(UserHandle.getSharedAppGid(Process.LOG_UID)), 9.0f);
stats.add(sipper);
return stats;
}
1.4 显示耗电数据
PowerUsageSummary->refreshAppListGroup()
具体如下
private void refreshAppListGroup() {
final Context context = getContext();
// 获取电量校准文件
final PowerProfile powerProfile = mStatsHelper.getPowerProfile();
// 电量相关数据,我们也可以使用 adb shell dumpsys batterystats > batterystats.txt 导出
final BatteryStats stats = mStatsHelper.getStats();
// 屏幕最高亮度平均电流
final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
boolean addedSome = false;
// frameworks/base/core/res/res/values/attrs.xml:1095: <attr name="colorControlNormal" format="color" />
// TypedArray是存储资源数组的容器
TypedArray array = context.obtainStyledAttributes(
new int[]{android.R.attr.colorControlNormal});
final int colorControl = array.getColor(0, 0);
Log.d("fadi", "colorControl = " + colorControl);
// 使用完后释放
array.recycle();
// dichargeaAmount 相当于 usb 拔掉后这段时间的电量
final int dischargeAmount = USE_FAKE_DATA ? 5000
: stats != null ? stats.getDischargeAmount(mStatsType) : 0;
cacheRemoveAllPrefs(mAppListGroup);
// 被add进来的时候是否排序
mAppListGroup.setOrderingAsAdded(false);
Log.d("fadi", "averagePower = " + averagePower);
if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP || USE_FAKE_DATA) {
final List<BatterySipper> usageList = getCoalescedUsageList(
USE_FAKE_DATA ? getFakeStats() : mStatsHelper.getUsageList());
double hiddenPowerMah = mShowAllApps ? 0 /**完整设备用电量*/:
mBatteryUtils.removeHiddenBatterySippers(usageList)/**显示应用使用情况*/;
// 排序,耗电量从高到低
mBatteryUtils.sortUsageList(usageList);
final int numSippers = usageList.size();
for (int i = 0; i < numSippers; i++) {
final BatterySipper sipper = usageList.get(i);
double totalPower = USE_FAKE_DATA ? 4000 : mStatsHelper.getTotalPower();
// 耗电量百分比换算
final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
sipper.totalPowerMah/**该类型耗电量大小*/, totalPower/**总负载耗电量*/, hiddenPowerMah, dischargeAmount/**usb 拔掉后这段时间的电量*/);
...
主要是应用信息获取形成 list
1.5 耗电百分百换算
package com.android.settings.fuelgauge;
/**
* Utils for battery operation
*/
public class BatteryUtils {
/**
* Calculate the power usage percentage for an app
*
* @param powerUsageMah power used by the app
* @param totalPowerMah total power used in the system
* @param hiddenPowerMah power used by no-actionable app that we want to hide, i.e. Screen,
* Android OS.
* @param dischargeAmount The discharge amount calculated by {@link BatteryStats}
* @return A percentage value scaled by {@paramref dischargeAmount}
* @see BatteryStats#getDischargeAmount(int)
*/
public double calculateBatteryPercent(double powerUsageMah, double totalPowerMah,
double hiddenPowerMah, int dischargeAmount) {
if (totalPowerMah == 0) {
return 0;
}
//
return (powerUsageMah / (totalPowerMah - hiddenPowerMah)) * dischargeAmount;
}
1.6 结语
我们可以通过 getFakeStats() 函数进行模拟数据的修改和调试,快速看到功耗结果。
其中模拟数据 BatterySipper(DrainType drainType, Uid uid, double value),的三个参数,耗电类型,耗电应用UID,和耗电量构成了耗电列表的主要参数。