Settings中电池选项-Android13
- 1、设置中界面
- 2、电池计算
- 2.1 充电时间计算
- 2.1.1 BatteryUsageStats获取
- 2.1.2 BatteryStatsImpl计算
- 2.2 电池剩余使用时间
- 2.2.1 Estimate获取
- 2.2.2 BatteryStatsImpl计算
- 3、电池信息来源
- 4、命令模拟
- * 日志
[电池]Android 9.0 电池未充电与充电字符串提示信息[通俗易懂]
1、设置中界面
packages/apps/Settings/src/com/android/settings/fuelgauge/PowerUsageSummary.java
packages/apps/Settings/res/xml/power_usage_summary.xml
packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryUtils.java
packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryInfo.java
12-23 13:13:23.184 602 715 I ActivityTaskManager: START u0 {act=android.intent.action.MAIN cmp=com.android.settings/.SubSettings (has extras)} from uid 1000
12-23 13:13:23.285 994 2135 D BatteryTipLoader: BatteryInfoLoader post query: 5ms
12-23 13:13:23.356 994 2135 D BatteryInfo: time for getBatteryInfo: 1ms
12-23 13:13:23.357 994 2135 D BatteryTipLoader: BatteryInfoLoader.loadInBackground: 82ms
12-23 13:13:23.950 994 994 D SettingsActivity: Switching to fragment com.android.settings.fuelgauge.batteryusage.PowerUsageSummary
2、电池计算
packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryInfo.java
public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast,
@NonNull BatteryUsageStats batteryUsageStats, Estimate estimate,
long elapsedRealtimeUs, boolean shortString) {
final long startTime = System.currentTimeMillis();
final boolean isCompactStatus = context.getResources().getBoolean(
com.android.settings.R.bool.config_use_compact_battery_status);
BatteryInfo info = new BatteryInfo();
info.mBatteryUsageStats = batteryUsageStats;
info.batteryLevel = Utils.getBatteryLevel(batteryBroadcast);
info.batteryPercentString = Utils.formatPercentage(info.batteryLevel);
info.mCharging = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
info.averageTimeToDischarge = estimate.getAverageDischargeTime();
info.isOverheated = batteryBroadcast.getIntExtra(
BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN)
== BatteryManager.BATTERY_HEALTH_OVERHEAT;
info.statusLabel = Utils.getBatteryStatus(context, batteryBroadcast, isCompactStatus);
info.batteryStatus = batteryBroadcast.getIntExtra(
BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
if (!info.mCharging) {
updateBatteryInfoDischarging(context, shortString, estimate, info);
} else {
updateBatteryInfoCharging(context, batteryBroadcast, batteryUsageStats,
info, isCompactStatus);
}
BatteryUtils.logRuntime(LOG_TAG, "time for getBatteryInfo", startTime);
return info;
}
private static void updateBatteryInfoCharging(Context context, Intent batteryBroadcast,
BatteryUsageStats stats, BatteryInfo info, boolean compactStatus) {
final Resources resources = context.getResources();
final long chargeTimeMs = stats.getChargeTimeRemainingMs();
final int status = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
info.discharging = false;
info.suggestionLabel = null;
if (info.isOverheated && status != BatteryManager.BATTERY_STATUS_FULL) {
info.remainingLabel = null;
int chargingLimitedResId = R.string.power_charging_limited;
info.chargeLabel =
context.getString(chargingLimitedResId, info.batteryPercentString);
} else if (chargeTimeMs > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
info.remainingTimeUs = PowerUtil.convertMsToUs(chargeTimeMs);
final CharSequence timeString = StringUtil.formatElapsedTime(
context,
PowerUtil.convertUsToMs(info.remainingTimeUs),
false /* withSeconds */,
true /* collapseTimeUnit */);
int resId = R.string.power_charging_duration;
info.remainingLabel = context.getString(
R.string.power_remaining_charging_duration_only, timeString);
info.chargeLabel = context.getString(resId, info.batteryPercentString, timeString);
} else {
final String chargeStatusLabel =
Utils.getBatteryStatus(context, batteryBroadcast, compactStatus);
info.remainingLabel = null;
info.chargeLabel = info.batteryLevel == 100 ? info.batteryPercentString :
resources.getString(R.string.power_charging, info.batteryPercentString,
chargeStatusLabel.toLowerCase());
}
}
private static void updateBatteryInfoDischarging(Context context, boolean shortString,
Estimate estimate, BatteryInfo info) {
final long drainTimeUs = PowerUtil.convertMsToUs(estimate.getEstimateMillis());
if (drainTimeUs > 0) {
info.remainingTimeUs = drainTimeUs;
info.remainingLabel = PowerUtil.getBatteryRemainingStringFormatted(
context,
PowerUtil.convertUsToMs(drainTimeUs),
null /* percentageString */,
false /* basedOnUsage */
);
info.chargeLabel = PowerUtil.getBatteryRemainingStringFormatted(
context,
PowerUtil.convertUsToMs(drainTimeUs),
info.batteryPercentString,
estimate.isBasedOnUsage() && !shortString
);
info.suggestionLabel = PowerUtil.getBatteryTipStringFormatted(
context, PowerUtil.convertUsToMs(drainTimeUs));
} else {
info.remainingLabel = null;
info.suggestionLabel = null;
info.chargeLabel = info.batteryPercentString;
}
}
2.1 充电时间计算
2.1.1 BatteryUsageStats获取
updateBatteryInfoCharging
中获取BatteryUsageStats
中mChargeTimeRemainingMs
还需的充电时间;显示<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"还需<xliff:g id="TIME">%1$s</xliff:g>充满"</string>
BatteryUsageStats
对象是BatteryStatsService
通过BatteryUsageStatsProvider
获取BatteryUsageStatsProvider
中BatteryChargeCalculator.java#calculate
最终调用到batteryStats.computeChargeTimeRemaining(rawRealtimeUs)
计算
packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryUtils.java
public BatteryInfo getBatteryInfo(final String tag) {
final BatteryStatsManager systemService = mContext.getSystemService(
BatteryStatsManager.class);
BatteryUsageStats batteryUsageStats;
try {
batteryUsageStats = systemService.getBatteryUsageStats(
new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build());
} catch (RuntimeException e) {
Log.e(TAG, "getBatteryInfo() error from getBatteryUsageStats()", e);
// Use default BatteryUsageStats.
batteryUsageStats = new BatteryUsageStats.Builder(new String[0]).build();
}
final long startTime = System.currentTimeMillis();
// Stuff we always need to get BatteryInfo
final Intent batteryBroadcast = mContext.registerReceiver(null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
final long elapsedRealtimeUs = PowerUtil.convertMsToUs(
SystemClock.elapsedRealtime());
BatteryInfo batteryInfo;
Estimate estimate = getEnhancedEstimate();
// couldn't get estimate from cache or provider, use fallback
if (estimate == null) {
estimate = new Estimate(
batteryUsageStats.getBatteryTimeRemainingMs(),
false /* isBasedOnUsage */,
EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
}
BatteryUtils.logRuntime(tag, "BatteryInfoLoader post query", startTime);
batteryInfo = BatteryInfo.getBatteryInfo(mContext, batteryBroadcast,
batteryUsageStats, estimate, elapsedRealtimeUs, false /* shortString */);
BatteryUtils.logRuntime(tag, "BatteryInfoLoader.loadInBackground", startTime);
try {
batteryUsageStats.close();
} catch (Exception e) {
Log.e(TAG, "BatteryUsageStats.close() failed", e);
}
return batteryInfo;
}
frameworks/base/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
private BatteryUsageStats getCurrentBatteryUsageStats(BatteryUsageStatsQuery query,
long currentTimeMs) {
final long realtimeUs = elapsedRealtime() * 1000;
final long uptimeUs = uptimeMillis() * 1000;
final boolean includePowerModels = (query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
final boolean includeProcessStateData = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0)
&& mStats.isProcessStateDataAvailable();
final boolean includeVirtualUids = ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS) != 0);
final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder(
mStats.getCustomEnergyConsumerNames(), includePowerModels,
includeProcessStateData);
// TODO(b/188068523): use a monotonic clock to ensure resilience of order and duration
// of stats sessions to wall-clock adjustments
batteryUsageStatsBuilder.setStatsStartTimestamp(mStats.getStartClockTime());
batteryUsageStatsBuilder.setStatsEndTimestamp(currentTimeMs);
SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats();
for (int i = uidStats.size() - 1; i >= 0; i--) {
final BatteryStats.Uid uid = uidStats.valueAt(i);
if (!includeVirtualUids && uid.getUid() == Process.SDK_SANDBOX_VIRTUAL_UID) {
continue;
}
batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid)
.setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND,
getProcessBackgroundTimeMs(uid, realtimeUs))
.setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND,
getProcessForegroundTimeMs(uid, realtimeUs));
}
final int[] powerComponents = query.getPowerComponents();
final List<PowerCalculator> powerCalculators = getPowerCalculators();
for (int i = 0, count = powerCalculators.size(); i < count; i++) {
PowerCalculator powerCalculator = powerCalculators.get(i);
if (powerComponents != null) {
boolean include = false;
for (int j = 0; j < powerComponents.length; j++) {
if (powerCalculator.isPowerComponentSupported(powerComponents[j])) {
include = true;
break;
}
}
if (!include) {
continue;
}
}
powerCalculator.calculate(batteryUsageStatsBuilder, mStats, realtimeUs, uptimeUs,
query);
}
if ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY) != 0) {
if (!(mStats instanceof BatteryStatsImpl)) {
throw new UnsupportedOperationException(
"History cannot be included for " + getClass().getName());
}
BatteryStatsImpl batteryStatsImpl = (BatteryStatsImpl) mStats;
// Make a copy of battery history to avoid concurrent modification.
Parcel historyBuffer = Parcel.obtain();
historyBuffer.appendFrom(batteryStatsImpl.mHistoryBuffer, 0,
batteryStatsImpl.mHistoryBuffer.dataSize());
final File systemDir =
batteryStatsImpl.mBatteryStatsHistory.getHistoryDirectory().getParentFile();
final BatteryStatsHistory batteryStatsHistory =
new BatteryStatsHistory(batteryStatsImpl, systemDir, historyBuffer);
batteryUsageStatsBuilder.setBatteryHistory(batteryStatsHistory);
}
BatteryUsageStats stats = batteryUsageStatsBuilder.build();
if (includeProcessStateData) {
verify(stats);
}
return stats;
}
// STOPSHIP(b/229906525): remove verification before shipping
private static boolean sErrorReported;
private void verify(BatteryUsageStats stats) {
if (sErrorReported) {
return;
}
final double precision = 2.0; // Allow rounding errors up to 2 mAh
final int[] components =
{BatteryConsumer.POWER_COMPONENT_CPU,
BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
BatteryConsumer.POWER_COMPONENT_WIFI,
BatteryConsumer.POWER_COMPONENT_BLUETOOTH};
final int[] states =
{BatteryConsumer.PROCESS_STATE_FOREGROUND,
BatteryConsumer.PROCESS_STATE_BACKGROUND,
BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE,
BatteryConsumer.PROCESS_STATE_CACHED};
for (UidBatteryConsumer ubc : stats.getUidBatteryConsumers()) {
for (int component : components) {
double consumedPower = ubc.getConsumedPower(ubc.getKey(component));
double sumStates = 0;
for (int state : states) {
sumStates += ubc.getConsumedPower(ubc.getKey(component, state));
}
if (sumStates > consumedPower + precision) {
String error = "Sum of states exceeds total. UID = " + ubc.getUid() + " "
+ BatteryConsumer.powerComponentIdToString(component)
+ " total = " + consumedPower + " states = " + sumStates;
if (!sErrorReported) {
Slog.wtf(TAG, error);
sErrorReported = true;
} else {
Slog.e(TAG, error);
}
return;
}
}
}
}
frameworks/base/core/java/com/android/internal/os/BatteryChargeCalculator.java
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
builder.setDischargePercentage(
batteryStats.getDischargeAmount(BatteryStats.STATS_SINCE_CHARGED));
int batteryCapacityMah = batteryStats.getLearnedBatteryCapacity() / 1000;
if (batteryCapacityMah <= 0) {
batteryCapacityMah = batteryStats.getMinLearnedBatteryCapacity() / 1000;
if (batteryCapacityMah <= 0) {
batteryCapacityMah = batteryStats.getEstimatedBatteryCapacity();
}
}
builder.setBatteryCapacity(batteryCapacityMah);
final double dischargedPowerLowerBoundMah =
batteryStats.getLowDischargeAmountSinceCharge() * batteryCapacityMah / 100.0;
final double dischargedPowerUpperBoundMah =
batteryStats.getHighDischargeAmountSinceCharge() * batteryCapacityMah / 100.0;
builder.setDischargePercentage(
batteryStats.getDischargeAmount(BatteryStats.STATS_SINCE_CHARGED))
.setDischargedPowerRange(dischargedPowerLowerBoundMah,
dischargedPowerUpperBoundMah)
.setDischargeDurationMs(batteryStats.getBatteryRealtime(rawRealtimeUs) / 1000);
final long batteryTimeRemainingMs = batteryStats.computeBatteryTimeRemaining(rawRealtimeUs);
if (batteryTimeRemainingMs != -1) {
builder.setBatteryTimeRemainingMs(batteryTimeRemainingMs / 1000);
}
final long chargeTimeRemainingMs = batteryStats.computeChargeTimeRemaining(rawRealtimeUs);
if (chargeTimeRemainingMs != -1) {
builder.setChargeTimeRemainingMs(chargeTimeRemainingMs / 1000);
}
long dischargeMah = batteryStats.getUahDischarge(BatteryStats.STATS_SINCE_CHARGED) / 1000;
if (dischargeMah == 0) {
dischargeMah = (long) ((dischargedPowerLowerBoundMah + dischargedPowerUpperBoundMah) / 2
+ 0.5);
}
builder.getAggregateBatteryConsumerBuilder(
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
.setConsumedPower(dischargeMah);
}
2.1.2 BatteryStatsImpl计算
mBatteryTimeToFullSeconds
这个是底层支持计算,由底层health
上报。- 计算逻辑
final LevelStepTracker mChargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS);
:
- 电池状态信息变化调用**
setBatteryStateLocked
**,这里mChargeStepTracker
会init()
初始化或level增大变化记录计算addLevelSteps
mChargeStepTracker.mNumStepDurations < 1
充满一格电不会显示,至少连续充满两格电mChargeStepTracker.computeTimePerLevel()
平均addLevelSteps
记录充电每个格电的总时间(msPerLevel * (100 - mCurrentBatteryLevel)) * 1000;
连续充电的平均得到的每格电充电时间 * 需要充电level
frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
@Override
public long computeChargeTimeRemaining(long curTime) {
if (mOnBattery) {
// Not yet working.
return -1;
}
if (mBatteryTimeToFullSeconds >= 0) {
return mBatteryTimeToFullSeconds * (1000 * 1000); // s to us
}
// Else use algorithmic approach
if (mChargeStepTracker.mNumStepDurations < 1) {
return -1;
}
long msPerLevel = mChargeStepTracker.computeTimePerLevel();
if (msPerLevel <= 0) {
return -1;
}
return (msPerLevel * (100 - mCurrentBatteryLevel)) * 1000;
}
frameworks/base/core/java/android/os/BatteryStats.java
public static final class LevelStepTracker {
public long mLastStepTime = -1;
public int mNumStepDurations;
public final long[] mStepDurations;
public LevelStepTracker(int maxLevelSteps) {
mStepDurations = new long[maxLevelSteps];
}
public LevelStepTracker(int numSteps, long[] steps) {
mNumStepDurations = numSteps;
mStepDurations = new long[numSteps];
System.arraycopy(steps, 0, mStepDurations, 0, numSteps);
}
// ... ...
public void init() {
mLastStepTime = -1;
mNumStepDurations = 0;
}
// ... ...
public long computeTimePerLevel() {
final long[] steps = mStepDurations;
final int numSteps = mNumStepDurations;
// For now we'll do a simple average across all steps.
if (numSteps <= 0) {
return -1;
}
long total = 0;
for (int i=0; i<numSteps; i++) {
total += steps[i] & STEP_LEVEL_TIME_MASK;
}
return total / numSteps;
}
// ... ...
public void addLevelSteps(int numStepLevels, long modeBits, long elapsedRealtime) {
int stepCount = mNumStepDurations;
final long lastStepTime = mLastStepTime;
if (lastStepTime >= 0 && numStepLevels > 0) {
final long[] steps = mStepDurations;
long duration = elapsedRealtime - lastStepTime;
for (int i=0; i<numStepLevels; i++) {
System.arraycopy(steps, 0, steps, 1, steps.length-1);
long thisDuration = duration / (numStepLevels-i);
duration -= thisDuration;
if (thisDuration > STEP_LEVEL_TIME_MASK) {
thisDuration = STEP_LEVEL_TIME_MASK;
}
steps[0] = thisDuration | modeBits;
}
stepCount += numStepLevels;
if (stepCount > steps.length) {
stepCount = steps.length;
}
}
mNumStepDurations = stepCount;
mLastStepTime = elapsedRealtime;
}
// ... ...
}
2.2 电池剩余使用时间
2.2.1 Estimate获取
updateBatteryInfoDischarging
获取Estimate
中estimateMillis
,由2.1.1BatteryUsageStats
获取中查看实际获取batteryUsageStats.getBatteryTimeRemainingMs()
BatteryUsageStats
中mBatteryTimeRemainingMs
查看上面BatteryChargeCalculator.java#calculate
最终调用到batteryStats.computeBatteryTimeRemaining(rawRealtimeUs)
计算Estimate.kt
如果缓存的估计值可用,则返回该估计值。如果估计不可用或超过2分钟,将返回null。相关属性Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME、Settings.Global.TIME_REMAINING_ESTIMATE_MILLIS、Settings.Global.TIME_REMAINING_ESTIMATE_BASED_ON_USAGE、Settings.Global.AVERAGE_TIME_TO_DISCHARGE
frameworks/base/packages/SettingsLib/src/com/android/settingslib/fuelgauge/Estimate.kt
packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryUtils.java
Estimate getEnhancedEstimate() {
Estimate estimate = null;
// Get enhanced prediction if available
if (Duration.between(Estimate.getLastCacheUpdateTime(mContext), Instant.now())
.compareTo(Duration.ofSeconds(10)) < 0) {
estimate = Estimate.getCachedEstimateIfAvailable(mContext);
} else if (mPowerUsageFeatureProvider != null &&
mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(mContext)) {
estimate = mPowerUsageFeatureProvider.getEnhancedBatteryPrediction(mContext);
if (estimate != null) {
Estimate.storeCachedEstimate(mContext, estimate);
}
}
return estimate;
}
2.2.2 BatteryStatsImpl计算
- 计算逻辑
final LevelStepTracker mDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS);
:
- 电池状态信息变化调用**
setBatteryStateLocked
**,这里mDischargeStepTracker
会init()
初始化或level减小变化记录计算addLevelSteps
mDischargeStepTracker.mNumStepDurations < 1
耗电一格电不会显示,至少连续耗电两格电mChargeStepTracker.computeTimePerLevel()
平均addLevelSteps
记录耗电每个格电的总时间(msPerLevel * mCurrentBatteryLevel) * 1000
连续耗电的平均得到的每格电充电时间 * 当前电量level
frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
public long computeBatteryTimeRemaining(long curTime) {
if (!mOnBattery) {
return -1;
}
if (mDischargeStepTracker.mNumStepDurations < 1) {
return -1;
}
long msPerLevel = mDischargeStepTracker.computeTimePerLevel();
if (msPerLevel <= 0) {
return -1;
}
return (msPerLevel * mCurrentBatteryLevel) * 1000;
}
3、电池信息来源
mHealthServiceWrapper = HealthServiceWrapper.create(this::update);
注册监听IHealthInfoCallback.hal
,回调执行update > processValuesLocked
android.hardware.health@2.0-service
中 轮询更新Health::update()
,其中battery_monitor_->logValues()
输出日志healthd : battery l=
BatteryService.java#processValuesLocked
通过BatteryStatsService.java
最终通知到BatteryStatsImpl.java#setBatteryStateLocked
;这里输出Event日志battery_level、battery_status、battery_discharge、BatteryService
frameworks/base/services/core/java/com/android/server/BatteryService.java
private void registerHealthCallback() {
traceBegin("HealthInitWrapper");
// IHealth is lazily retrieved.
try {
mHealthServiceWrapper = HealthServiceWrapper.create(this::update);
} catch (RemoteException ex) {
Slog.e(TAG, "health: cannot register callback. (RemoteException)");
throw ex.rethrowFromSystemServer();
} catch (NoSuchElementException ex) {
Slog.e(TAG, "health: cannot register callback. (no supported health HAL service)");
throw ex;
} finally {
traceEnd();
}
traceBegin("HealthInitWaitUpdate");
// init register for new service notifications, and IServiceManager should return the
// existing service in a near future. Wait for this.update() to instantiate
// the initial mHealthInfo.
long beforeWait = SystemClock.uptimeMillis();
synchronized (mLock) {
while (mHealthInfo == null) {
Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait) +
"ms for callbacks. Waiting another " + HEALTH_HAL_WAIT_MS + " ms...");
try {
mLock.wait(HEALTH_HAL_WAIT_MS);
} catch (InterruptedException ex) {
Slog.i(TAG, "health: InterruptedException when waiting for update. "
+ " Continuing...");
}
}
}
Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait)
+ "ms and received the update.");
traceEnd();
}
private void update(android.hardware.health.HealthInfo info) {
traceBegin("HealthInfoUpdate");
Trace.traceCounter(
Trace.TRACE_TAG_POWER, "BatteryChargeCounter", info.batteryChargeCounterUah);
Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent", info.batteryCurrentMicroamps);
Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType", plugType(info));
Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus", info.batteryStatus);
synchronized (mLock) {
if (!mUpdatesStopped) {
mHealthInfo = info;
// Process the new values.
processValuesLocked(false);
mLock.notifyAll(); // for any waiters on new info
} else {
copyV1Battery(mLastHealthInfo, info);
}
}
traceEnd();
}
private void processValuesLocked(boolean force) {
boolean logOutlier = false;
long dischargeDuration = 0;
mBatteryLevelCritical =
mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
&& mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
mPlugType = plugType(mHealthInfo);
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.batteryTemperatureTenthsCelsius,
mHealthInfo.batteryVoltageMillivolts,
mHealthInfo.batteryChargeCounterUah,
mHealthInfo.batteryFullChargeUah,
mHealthInfo.batteryChargeTimeToFullNowSeconds);
} catch (RemoteException e) {
// Should never happen.
}
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
if (force
|| (mHealthInfo.batteryStatus != mLastBatteryStatus
|| mHealthInfo.batteryHealth != mLastBatteryHealth
|| mHealthInfo.batteryPresent != mLastBatteryPresent
|| mHealthInfo.batteryLevel != mLastBatteryLevel
|| mPlugType != mLastPlugType
|| mHealthInfo.batteryVoltageMillivolts != mLastBatteryVoltage
|| mHealthInfo.batteryTemperatureTenthsCelsius != mLastBatteryTemperature
|| mHealthInfo.maxChargingCurrentMicroamps != mLastMaxChargingCurrent
|| mHealthInfo.maxChargingVoltageMicrovolts != mLastMaxChargingVoltage
|| mHealthInfo.batteryChargeCounterUah != mLastChargeCounter
|| mInvalidCharger != mLastInvalidCharger)) {
if (mPlugType != mLastPlugType) {
if (mLastPlugType == BATTERY_PLUGGED_NONE) {
// discharging -> charging
mChargeStartLevel = mHealthInfo.batteryLevel;
mChargeStartTime = SystemClock.elapsedRealtime();
final LogMaker builder = new LogMaker(MetricsEvent.ACTION_CHARGE);
builder.setType(MetricsEvent.TYPE_ACTION);
builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mPlugType);
builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_START,
mHealthInfo.batteryLevel);
mMetricsLogger.write(builder);
// There's no value in this data unless we've discharged at least once and the
// battery level has changed; so don't log until it does.
if (mDischargeStartTime != 0 && mDischargeStartLevel != mHealthInfo.batteryLevel) {
dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
logOutlier = true;
EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
mDischargeStartLevel, mHealthInfo.batteryLevel);
// make sure we see a discharge event before logging again
mDischargeStartTime = 0;
}
} else if (mPlugType == BATTERY_PLUGGED_NONE) {
// charging -> discharging or we just powered up
mDischargeStartTime = SystemClock.elapsedRealtime();
mDischargeStartLevel = mHealthInfo.batteryLevel;
long chargeDuration = SystemClock.elapsedRealtime() - mChargeStartTime;
if (mChargeStartTime != 0 && chargeDuration != 0) {
final LogMaker builder = new LogMaker(MetricsEvent.ACTION_CHARGE);
builder.setType(MetricsEvent.TYPE_DISMISS);
builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mLastPlugType);
builder.addTaggedData(MetricsEvent.FIELD_CHARGING_DURATION_MILLIS,
chargeDuration);
builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_START,
mChargeStartLevel);
builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_END,
mHealthInfo.batteryLevel);
mMetricsLogger.write(builder);
}
mChargeStartTime = 0;
}
}
if (mHealthInfo.batteryStatus != mLastBatteryStatus ||
mHealthInfo.batteryHealth != mLastBatteryHealth ||
mHealthInfo.batteryPresent != mLastBatteryPresent ||
mPlugType != mLastPlugType) {
EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
mHealthInfo.batteryStatus, mHealthInfo.batteryHealth, mHealthInfo.batteryPresent ? 1 : 0,
mPlugType, mHealthInfo.batteryTechnology);
}
if (mHealthInfo.batteryLevel != mLastBatteryLevel) {
// Don't do this just from voltage or temperature changes, that is
// too noisy.
EventLog.writeEvent(
EventLogTags.BATTERY_LEVEL,
mHealthInfo.batteryLevel,
mHealthInfo.batteryVoltageMillivolts,
mHealthInfo.batteryTemperatureTenthsCelsius);
}
if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
mPlugType == BATTERY_PLUGGED_NONE) {
// We want to make sure we log discharge cycle outliers
// if the battery is about to die.
dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
logOutlier = true;
}
if (!mBatteryLevelLow) {
// Should we now switch in to low battery mode?
if (mPlugType == BATTERY_PLUGGED_NONE
&& mHealthInfo.batteryStatus !=
BatteryManager.BATTERY_STATUS_UNKNOWN
&& mHealthInfo.batteryLevel <= mLowBatteryWarningLevel) {
mBatteryLevelLow = true;
}
} else {
// Should we now switch out of low battery mode?
if (mPlugType != BATTERY_PLUGGED_NONE) {
mBatteryLevelLow = false;
} else if (mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
mBatteryLevelLow = false;
} else if (force && mHealthInfo.batteryLevel >= mLowBatteryWarningLevel) {
// If being forced, the previous state doesn't matter, we will just
// absolutely check to see if we are now above the warning level.
mBatteryLevelLow = false;
}
}
mSequence++;
// Separate broadcast is sent for power connected / not connected
// since the standard intent will not wake any applications and some
// applications may want to have smart behavior based on this.
if (mPlugType != 0 && mLastPlugType == 0) {
final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
mHandler.post(new Runnable() {
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
}
else if (mPlugType == 0 && mLastPlugType != 0) {
final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
mHandler.post(new Runnable() {
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
}
if (shouldSendBatteryLowLocked()) {
mSentLowBatteryBroadcast = true;
final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
mHandler.post(new Runnable() {
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
} else if (mSentLowBatteryBroadcast &&
mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
mSentLowBatteryBroadcast = false;
final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
mHandler.post(new Runnable() {
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
}
// We are doing this after sending the above broadcasts, so anything processing
// them will get the new sequence number at that point. (See for example how testing
// of JobScheduler's BatteryController works.)
sendBatteryChangedIntentLocked();
if (mLastBatteryLevel != mHealthInfo.batteryLevel || mLastPlugType != mPlugType) {
sendBatteryLevelChangedIntentLocked();
}
// Update the battery LED
mLed.updateLightsLocked();
// This needs to be done after sendIntent() so that we get the lastest battery stats.
if (logOutlier && dischargeDuration != 0) {
logOutlierLocked(dischargeDuration);
}
mLastBatteryStatus = mHealthInfo.batteryStatus;
mLastBatteryHealth = mHealthInfo.batteryHealth;
mLastBatteryPresent = mHealthInfo.batteryPresent;
mLastBatteryLevel = mHealthInfo.batteryLevel;
mLastPlugType = mPlugType;
mLastBatteryVoltage = mHealthInfo.batteryVoltageMillivolts;
mLastBatteryTemperature = mHealthInfo.batteryTemperatureTenthsCelsius;
mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrentMicroamps;
mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltageMicrovolts;
mLastChargeCounter = mHealthInfo.batteryChargeCounterUah;
mLastBatteryLevelCritical = mBatteryLevelCritical;
mLastInvalidCharger = mInvalidCharger;
}
}
hardware/interfaces/health/2.0/utils/libhealthservice/HealthServiceCommon.cpp
void healthd_mode_service_2_0_battery_update(struct android::BatteryProperties* prop) {
HealthInfo info;
convertToHealthInfo(prop, info.legacy);
Health::getImplementation()->notifyListeners(&info);
}
static struct healthd_mode_ops healthd_mode_service_2_0_ops = {
.init = healthd_mode_service_2_0_init,
.preparetowait = healthd_mode_service_2_0_preparetowait,
.heartbeat = healthd_mode_service_2_0_heartbeat,
.battery_update = healthd_mode_service_2_0_battery_update,
};
int health_service_main(const char* instance) {
gInstanceName = instance;
if (gInstanceName.empty()) {
gInstanceName = "default";
}
healthd_mode_ops = &healthd_mode_service_2_0_ops;
LOG(INFO) << LOG_TAG << gInstanceName << ": Hal starting main loop...";
return healthd_main();
}
hardware/interfaces/health/2.0/default/healthd_common_adapter.cpp
// Adapter of HealthLoop to use legacy healthd_mode_ops.
class HealthLoopAdapter : public HealthLoop {
public:
// Expose internal functions, assuming clients calls them in the same thread
// where StartLoop is called.
int RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
return HealthLoop::RegisterEvent(fd, func, wakeup);
}
void AdjustWakealarmPeriods(bool charger_online) {
return HealthLoop::AdjustWakealarmPeriods(charger_online);
}
protected:
void Init(healthd_config* config) override { healthd_mode_ops->init(config); }
void Heartbeat() override { healthd_mode_ops->heartbeat(); }
int PrepareToWait() override { return healthd_mode_ops->preparetowait(); }
void ScheduleBatteryUpdate() override { Health::getImplementation()->update(); }
};
hardware/interfaces/health/utils/libhealthloop/HealthLoop.cpp
void HealthLoop::PeriodicChores() {
ScheduleBatteryUpdate();
}
void HealthLoop::MainLoop(void) {
int nevents = 0;
while (1) {
reject_event_register_ = true;
size_t eventct = event_handlers_.size();
struct epoll_event events[eventct];
int timeout = awake_poll_interval_;
int mode_timeout;
/* Don't wait for first timer timeout to run periodic chores */
if (!nevents) PeriodicChores();
Heartbeat();
mode_timeout = PrepareToWait();
if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
nevents = epoll_wait(epollfd_, events, eventct, timeout);
if (nevents == -1) {
if (errno == EINTR) continue;
KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
break;
}
for (int n = 0; n < nevents; ++n) {
if (events[n].data.ptr) {
auto* event_handler = reinterpret_cast<EventHandler*>(events[n].data.ptr);
event_handler->func(event_handler->object, events[n].events);
}
}
}
return;
}
hardware/interfaces/health/2.0/default/Health.cpp
Return<Result> Health::update() {
if (!healthd_mode_ops || !healthd_mode_ops->battery_update) {
LOG(WARNING) << "health@2.0: update: not initialized. "
<< "update() should not be called in charger";
return Result::UNKNOWN;
}
// Retrieve all information and call healthd_mode_ops->battery_update, which calls
// notifyListeners.
battery_monitor_->updateValues();
const HealthInfo_1_0& health_info = battery_monitor_->getHealthInfo_1_0();
struct BatteryProperties props;
convertFromHealthInfo(health_info, &props);
bool log = (healthd_board_battery_update(&props) == 0);
if (log) {
battery_monitor_->logValues();
}
healthd_mode_ops->battery_update(&props);
bool chargerOnline = battery_monitor_->isChargerOnline();
// adjust uevent / wakealarm periods
healthd_battery_update_internal(chargerOnline);
return Result::SUCCESS;
}
void Health::notifyListeners(HealthInfo* healthInfo) {
std::vector<StorageInfo> info;
get_storage_info(info);
std::vector<DiskStats> stats;
get_disk_stats(stats);
int32_t currentAvg = 0;
struct BatteryProperty prop;
status_t ret = battery_monitor_->getProperty(BATTERY_PROP_CURRENT_AVG, &prop);
if (ret == OK) {
currentAvg = static_cast<int32_t>(prop.valueInt64);
}
healthInfo->batteryCurrentAverage = currentAvg;
healthInfo->diskStats = stats;
healthInfo->storageInfos = info;
std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
for (auto it = callbacks_.begin(); it != callbacks_.end();) {
auto ret = (*it)->healthInfoChanged(*healthInfo);
if (!ret.isOk() && ret.isDeadObject()) {
it = callbacks_.erase(it);
} else {
++it;
}
}
}
4、命令模拟
frameworks/base/services/core/java/com/android/server/BatteryService.java
C:\Users\Administrator\Desktop>adb shell cmd battery
Battery service (battery) commands:
help
Print this help text.
get [-f] [ac|usb|wireless|status|level|temp|present|counter|invalid]
set [-f] [ac|usb|wireless|status|level|temp|present|counter|invalid] <value>
Force a battery property value, freezing battery state.
-f: force a battery change broadcast be sent, prints new sequence.
unplug [-f]
Force battery unplugged, freezing battery state.
-f: force a battery change broadcast be sent, prints new sequence.
reset [-f]
Unfreeze battery state, returning to current hardware values.
-f: force a battery change broadcast be sent, prints new sequence.
suspend_input
Suspend charging even if plugged in.
* 日志
- Event日志
# ---------------------------
# BatteryService.java
# ---------------------------
2722 battery_level (level|1|6),(voltage|1|1),(temperature|1|1)
2723 battery_status (status|1|5),(health|1|5),(present|1|5),(plugged|1|5),(technology|3)
# This is logged when battery goes from discharging to charging.
# It lets us count the total amount of time between charges and the discharge level
2730 battery_discharge (duration|2|3),(minLevel|1|6),(maxLevel|1|6)
12-23 05:47:45.420 602 764 I battery_status: [2,2,1,1,Li-ion]
12-23 13:13:43.762 0 0 W healthd : battery l=57 v=5000 t=25.0 h=2 st=2 c=900000 fc=3000000 cc=10 chg=a
12-23 13:13:51.756 602 623 I battery_level: [58,5000,250]
12-23 13:13:52.156 994 2461 D BatteryInfoLoader: BatteryInfoLoader post query: 5ms
12-23 13:13:52.168 994 2461 D BatteryInfo: time for getBatteryInfo: 1ms
12-23 13:13:52.170 994 2461 D BatteryInfoLoader: BatteryInfoLoader.loadInBackground: 19ms
12-23 13:13:52.300 994 2131 D BatteryTipLoader: BatteryInfoLoader post query: 6ms
12-23 13:13:52.308 994 2131 D BatteryInfo: time for getBatteryInfo: 1ms
12-23 13:13:52.308 994 2131 D BatteryTipLoader: BatteryInfoLoader.loadInBackground: 15ms
12-23 13:13:57.300 602 623 I battery_level: [59,5000,250]
12-23 13:13:57.706 994 2135 D BatteryTipLoader: BatteryInfoLoader post query: 14ms
12-23 13:13:57.713 994 2135 D BatteryInfo: time for getBatteryInfo: 7ms
12-23 13:13:57.714 994 2135 D BatteryTipLoader: BatteryInfoLoader.loadInBackground: 21ms
12-23 13:13:57.789 994 2461 D BatteryInfoLoader: BatteryInfoLoader post query: 4ms
12-23 13:13:57.792 994 2461 D BatteryInfo: time for getBatteryInfo: 1ms
12-23 13:13:57.793 994 2461 D BatteryInfoLoader: BatteryInfoLoader.loadInBackground: 8ms