blob: 980aec196079b59b660ad35f5f80e8ce98c25efe [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.os;
import android.content.Context;
import android.hardware.SensorManager;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.SystemClock;
import android.os.UidBatteryConsumer;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Uses accumulated battery stats data and PowerCalculators to produce power
* usage data attributed to subsystems and UIDs.
*/
public class BatteryUsageStatsProvider {
private static final String TAG = "BatteryUsageStatsProv";
private final Context mContext;
private final BatteryStats mStats;
private final BatteryUsageStatsStore mBatteryUsageStatsStore;
private final PowerProfile mPowerProfile;
private final Object mLock = new Object();
private List<PowerCalculator> mPowerCalculators;
public BatteryUsageStatsProvider(Context context, BatteryStats stats) {
this(context, stats, null);
}
@VisibleForTesting
public BatteryUsageStatsProvider(Context context, BatteryStats stats,
BatteryUsageStatsStore batteryUsageStatsStore) {
mContext = context;
mStats = stats;
mBatteryUsageStatsStore = batteryUsageStatsStore;
mPowerProfile = stats instanceof BatteryStatsImpl
? ((BatteryStatsImpl) stats).getPowerProfile()
: new PowerProfile(context);
}
private List<PowerCalculator> getPowerCalculators() {
synchronized (mLock) {
if (mPowerCalculators == null) {
mPowerCalculators = new ArrayList<>();
// Power calculators are applied in the order of registration
mPowerCalculators.add(new BatteryChargeCalculator());
mPowerCalculators.add(new CpuPowerCalculator(mPowerProfile));
mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
if (!BatteryStatsHelper.checkWifiOnly(mContext)) {
mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile));
}
mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile));
mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile));
mPowerCalculators.add(new SensorPowerCalculator(
mContext.getSystemService(SensorManager.class)));
mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile));
mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile));
mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile));
mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile));
mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
mPowerCalculators.add(new CustomMeasuredPowerCalculator(mPowerProfile));
mPowerCalculators.add(new UserPowerCalculator());
// It is important that SystemServicePowerCalculator be applied last,
// because it re-attributes some of the power estimated by the other
// calculators.
mPowerCalculators.add(new SystemServicePowerCalculator(mPowerProfile));
}
}
return mPowerCalculators;
}
/**
* Returns true if the last update was too long ago for the tolerances specified
* by the supplied queries.
*/
public boolean shouldUpdateStats(List<BatteryUsageStatsQuery> queries,
long lastUpdateTimeStampMs) {
long allowableStatsAge = Long.MAX_VALUE;
for (int i = queries.size() - 1; i >= 0; i--) {
BatteryUsageStatsQuery query = queries.get(i);
allowableStatsAge = Math.min(allowableStatsAge, query.getMaxStatsAge());
}
return elapsedRealtime() - lastUpdateTimeStampMs > allowableStatsAge;
}
/**
* Returns snapshots of battery attribution data, one per supplied query.
*/
public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) {
ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size());
synchronized (mStats) {
mStats.prepareForDumpLocked();
final long currentTimeMillis = currentTimeMillis();
for (int i = 0; i < queries.size(); i++) {
results.add(getBatteryUsageStats(queries.get(i), currentTimeMillis));
}
}
return results;
}
/**
* Returns a snapshot of battery attribution data.
*/
@VisibleForTesting
public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
return getBatteryUsageStats(query, currentTimeMillis());
}
private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
long currentTimeMs) {
if (query.getToTimestamp() == 0) {
return getCurrentBatteryUsageStats(query, currentTimeMs);
} else {
return getAggregatedBatteryUsageStats(query);
}
}
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 BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder(
mStats.getCustomEnergyConsumerNames(), includePowerModels);
// 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);
batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid)
.setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND,
getProcessBackgroundTimeMs(uid, realtimeUs))
.setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND,
getProcessForegroundTimeMs(uid, realtimeUs));
}
final List<PowerCalculator> powerCalculators = getPowerCalculators();
for (int i = 0, count = powerCalculators.size(); i < count; i++) {
PowerCalculator powerCalculator = powerCalculators.get(i);
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;
ArrayList<BatteryStats.HistoryTag> tags = new ArrayList<>(
batteryStatsImpl.mHistoryTagPool.size());
for (Map.Entry<BatteryStats.HistoryTag, Integer> entry :
batteryStatsImpl.mHistoryTagPool.entrySet()) {
final BatteryStats.HistoryTag tag = entry.getKey();
tag.poolIdx = entry.getValue();
tags.add(tag);
}
batteryUsageStatsBuilder.setBatteryHistory(batteryStatsImpl.mHistoryBuffer, tags);
}
return batteryUsageStatsBuilder.build();
}
private long getProcessForegroundTimeMs(BatteryStats.Uid uid, long realtimeUs) {
final long topStateDurationUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP,
realtimeUs, BatteryStats.STATS_SINCE_CHARGED);
long foregroundActivityDurationUs = 0;
final BatteryStats.Timer foregroundActivityTimer = uid.getForegroundActivityTimer();
if (foregroundActivityTimer != null) {
foregroundActivityDurationUs = foregroundActivityTimer.getTotalTimeLocked(realtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
}
// Use the min value of STATE_TOP time and foreground activity time, since both of these
// times are imprecise
long totalForegroundDurationUs = Math.min(topStateDurationUs, foregroundActivityDurationUs);
totalForegroundDurationUs += uid.getProcessStateTime(
BatteryStats.Uid.PROCESS_STATE_FOREGROUND, realtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
totalForegroundDurationUs += uid.getProcessStateTime(
BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE, realtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
return totalForegroundDurationUs / 1000;
}
private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, long realtimeUs) {
return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND, realtimeUs,
BatteryStats.STATS_SINCE_CHARGED) / 1000;
}
private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryUsageStatsQuery query) {
final boolean includePowerModels = (query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
final String[] customEnergyConsumerNames = mStats.getCustomEnergyConsumerNames();
final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
customEnergyConsumerNames, includePowerModels);
if (mBatteryUsageStatsStore == null) {
Log.e(TAG, "BatteryUsageStatsStore is unavailable");
return builder.build();
}
final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
for (long timestamp : timestamps) {
if (timestamp > query.getFromTimestamp() && timestamp <= query.getToTimestamp()) {
final BatteryUsageStats snapshot =
mBatteryUsageStatsStore.loadBatteryUsageStats(timestamp);
if (snapshot != null) {
if (Arrays.equals(snapshot.getCustomPowerComponentNames(),
customEnergyConsumerNames)) {
builder.add(snapshot);
} else {
Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which has different "
+ "custom power components: "
+ Arrays.toString(snapshot.getCustomPowerComponentNames()));
}
}
}
}
return builder.build();
}
private long elapsedRealtime() {
if (mStats instanceof BatteryStatsImpl) {
return ((BatteryStatsImpl) mStats).mClocks.elapsedRealtime();
} else {
return SystemClock.elapsedRealtime();
}
}
private long uptimeMillis() {
if (mStats instanceof BatteryStatsImpl) {
return ((BatteryStatsImpl) mStats).mClocks.uptimeMillis();
} else {
return SystemClock.uptimeMillis();
}
}
private long currentTimeMillis() {
if (mStats instanceof BatteryStatsImpl) {
return ((BatteryStatsImpl) mStats).mClocks.currentTimeMillis();
} else {
return System.currentTimeMillis();
}
}
}