/* * Copyright (C) 2022 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.settings.fuelgauge.batteryusage; import android.content.ContentValues; import android.database.Cursor; import android.os.BatteryConsumer; import android.util.Log; /** A container class to carry data from {@link ContentValues}. */ public class BatteryHistEntry { private static final boolean DEBUG = false; private static final String TAG = "BatteryHistEntry"; /** Keys for accessing {@link ContentValues} or {@link Cursor}. */ public static final String KEY_UID = "uid"; public static final String KEY_USER_ID = "userId"; public static final String KEY_PACKAGE_NAME = "packageName"; public static final String KEY_TIMESTAMP = "timestamp"; public static final String KEY_CONSUMER_TYPE = "consumerType"; public static final String KEY_IS_FULL_CHARGE_CYCLE_START = "isFullChargeCycleStart"; public static final String KEY_BATTERY_INFORMATION = "batteryInformation"; public static final String KEY_BATTERY_INFORMATION_DEBUG = "batteryInformationDebug"; public final long mUid; public final long mUserId; public final String mAppLabel; public final String mPackageName; // Whether the data is represented as system component or not? public final boolean mIsHidden; // Records the timestamp relative information. public final long mBootTimestamp; public final long mTimestamp; public final String mZoneId; // Records the battery usage relative information. public final double mTotalPower; public final double mConsumePower; public final double mForegroundUsageConsumePower; public final double mForegroundServiceUsageConsumePower; public final double mBackgroundUsageConsumePower; public final double mCachedUsageConsumePower; public final double mPercentOfTotal; public final long mForegroundUsageTimeInMs; public final long mForegroundServiceUsageTimeInMs; public final long mBackgroundUsageTimeInMs; @BatteryConsumer.PowerComponent public final int mDrainType; @ConvertUtils.ConsumerType public final int mConsumerType; // Records the battery intent relative information. public final int mBatteryLevel; public final int mBatteryStatus; public final int mBatteryHealth; private String mKey = null; private boolean mIsValidEntry = true; public BatteryHistEntry(ContentValues values) { mUid = getLong(values, KEY_UID); mUserId = getLong(values, KEY_USER_ID); mPackageName = getString(values, KEY_PACKAGE_NAME); mTimestamp = getLong(values, KEY_TIMESTAMP); mConsumerType = getInteger(values, KEY_CONSUMER_TYPE); final BatteryInformation batteryInformation = ConvertUtils.getBatteryInformation(values, KEY_BATTERY_INFORMATION); mAppLabel = batteryInformation.getAppLabel(); mIsHidden = batteryInformation.getIsHidden(); mBootTimestamp = batteryInformation.getBootTimestamp(); mZoneId = batteryInformation.getZoneId(); mTotalPower = batteryInformation.getTotalPower(); mConsumePower = batteryInformation.getConsumePower(); mForegroundUsageConsumePower = batteryInformation.getForegroundUsageConsumePower(); mForegroundServiceUsageConsumePower = batteryInformation.getForegroundServiceUsageConsumePower(); mBackgroundUsageConsumePower = batteryInformation.getBackgroundUsageConsumePower(); mCachedUsageConsumePower = batteryInformation.getCachedUsageConsumePower(); mPercentOfTotal = batteryInformation.getPercentOfTotal(); mForegroundUsageTimeInMs = batteryInformation.getForegroundUsageTimeInMs(); mForegroundServiceUsageTimeInMs = batteryInformation.getForegroundServiceUsageTimeInMs(); mBackgroundUsageTimeInMs = batteryInformation.getBackgroundUsageTimeInMs(); mDrainType = batteryInformation.getDrainType(); final DeviceBatteryState deviceBatteryState = batteryInformation.getDeviceBatteryState(); mBatteryLevel = deviceBatteryState.getBatteryLevel(); mBatteryStatus = deviceBatteryState.getBatteryStatus(); mBatteryHealth = deviceBatteryState.getBatteryHealth(); } public BatteryHistEntry(Cursor cursor) { mUid = getLong(cursor, KEY_UID); mUserId = getLong(cursor, KEY_USER_ID); mPackageName = getString(cursor, KEY_PACKAGE_NAME); mTimestamp = getLong(cursor, KEY_TIMESTAMP); mConsumerType = getInteger(cursor, KEY_CONSUMER_TYPE); final BatteryInformation batteryInformation = ConvertUtils.getBatteryInformation(cursor, KEY_BATTERY_INFORMATION); mAppLabel = batteryInformation.getAppLabel(); mIsHidden = batteryInformation.getIsHidden(); mBootTimestamp = batteryInformation.getBootTimestamp(); mZoneId = batteryInformation.getZoneId(); mTotalPower = batteryInformation.getTotalPower(); mConsumePower = batteryInformation.getConsumePower(); mForegroundUsageConsumePower = batteryInformation.getForegroundUsageConsumePower(); mForegroundServiceUsageConsumePower = batteryInformation.getForegroundServiceUsageConsumePower(); mBackgroundUsageConsumePower = batteryInformation.getBackgroundUsageConsumePower(); mCachedUsageConsumePower = batteryInformation.getCachedUsageConsumePower(); mPercentOfTotal = batteryInformation.getPercentOfTotal(); mForegroundUsageTimeInMs = batteryInformation.getForegroundUsageTimeInMs(); mForegroundServiceUsageTimeInMs = batteryInformation.getForegroundServiceUsageTimeInMs(); mBackgroundUsageTimeInMs = batteryInformation.getBackgroundUsageTimeInMs(); mDrainType = batteryInformation.getDrainType(); final DeviceBatteryState deviceBatteryState = batteryInformation.getDeviceBatteryState(); mBatteryLevel = deviceBatteryState.getBatteryLevel(); mBatteryStatus = deviceBatteryState.getBatteryStatus(); mBatteryHealth = deviceBatteryState.getBatteryHealth(); } private BatteryHistEntry( BatteryHistEntry fromEntry, long bootTimestamp, long timestamp, double totalPower, double consumePower, double foregroundUsageConsumePower, double foregroundServiceUsageConsumePower, double backgroundUsageConsumePower, double cachedUsageConsumePower, long foregroundUsageTimeInMs, long foregroundServiceUsageTimeInMs, long backgroundUsageTimeInMs, int batteryLevel) { mUid = fromEntry.mUid; mUserId = fromEntry.mUserId; mAppLabel = fromEntry.mAppLabel; mPackageName = fromEntry.mPackageName; mIsHidden = fromEntry.mIsHidden; mBootTimestamp = bootTimestamp; mTimestamp = timestamp; mZoneId = fromEntry.mZoneId; mTotalPower = totalPower; mConsumePower = consumePower; mForegroundUsageConsumePower = foregroundUsageConsumePower; mForegroundServiceUsageConsumePower = foregroundServiceUsageConsumePower; mBackgroundUsageConsumePower = backgroundUsageConsumePower; mCachedUsageConsumePower = cachedUsageConsumePower; mPercentOfTotal = fromEntry.mPercentOfTotal; mForegroundUsageTimeInMs = foregroundUsageTimeInMs; mForegroundServiceUsageTimeInMs = foregroundServiceUsageTimeInMs; mBackgroundUsageTimeInMs = backgroundUsageTimeInMs; mDrainType = fromEntry.mDrainType; mConsumerType = fromEntry.mConsumerType; mBatteryLevel = batteryLevel; mBatteryStatus = fromEntry.mBatteryStatus; mBatteryHealth = fromEntry.mBatteryHealth; } /** Whether this {@link BatteryHistEntry} is valid or not? */ public boolean isValidEntry() { return mIsValidEntry; } /** Gets an identifier to represent this {@link BatteryHistEntry}. */ public String getKey() { if (mKey == null) { switch (mConsumerType) { case ConvertUtils.CONSUMER_TYPE_UID_BATTERY: mKey = Long.toString(mUid); break; case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY: mKey = "S|" + mDrainType; break; case ConvertUtils.CONSUMER_TYPE_USER_BATTERY: mKey = "U|" + mUserId; break; } } return mKey; } @Override public String toString() { final String recordAtDateTime = ConvertUtils.utcToLocalTimeForLogging(mTimestamp); return new StringBuilder() .append("\nBatteryHistEntry{") .append( String.format( "\n\tpackage=%s|label=%s|uid=%d|userId=%d|isHidden=%b", mPackageName, mAppLabel, mUid, mUserId, mIsHidden)) .append( String.format( "\n\ttimestamp=%s|zoneId=%s|bootTimestamp=%d", recordAtDateTime, mZoneId, TimestampUtils.getSeconds(mBootTimestamp))) .append( String.format( "\n\tusage=%f|total=%f|consume=%f", mPercentOfTotal, mTotalPower, mConsumePower)) .append( String.format( "\n\tforeground=%f|foregroundService=%f", mForegroundUsageConsumePower, mForegroundServiceUsageConsumePower)) .append( String.format( "\n\tbackground=%f|cached=%f", mBackgroundUsageConsumePower, mCachedUsageConsumePower)) .append( String.format( "\n\telapsedTime,fg=%d|fgs=%d|bg=%d", TimestampUtils.getSeconds(mForegroundUsageTimeInMs), TimestampUtils.getSeconds(mForegroundServiceUsageTimeInMs), TimestampUtils.getSeconds(mBackgroundUsageTimeInMs))) .append( String.format( "\n\tdrainType=%d|consumerType=%d", mDrainType, mConsumerType)) .append( String.format( "\n\tbattery=%d|status=%d|health=%d\n}", mBatteryLevel, mBatteryStatus, mBatteryHealth)) .toString(); } private int getInteger(ContentValues values, String key) { if (values != null && values.containsKey(key)) { return values.getAsInteger(key); } mIsValidEntry = false; return 0; } private int getInteger(Cursor cursor, String key) { final int columnIndex = cursor.getColumnIndex(key); if (columnIndex >= 0) { return cursor.getInt(columnIndex); } mIsValidEntry = false; return 0; } private long getLong(ContentValues values, String key) { if (values != null && values.containsKey(key)) { return values.getAsLong(key); } mIsValidEntry = false; return 0L; } private long getLong(Cursor cursor, String key) { final int columnIndex = cursor.getColumnIndex(key); if (columnIndex >= 0) { return cursor.getLong(columnIndex); } mIsValidEntry = false; return 0L; } private String getString(ContentValues values, String key) { if (values != null && values.containsKey(key)) { return values.getAsString(key); } mIsValidEntry = false; return null; } private String getString(Cursor cursor, String key) { final int columnIndex = cursor.getColumnIndex(key); if (columnIndex >= 0) { return cursor.getString(columnIndex); } mIsValidEntry = false; return null; } /** Creates new {@link BatteryHistEntry} from interpolation. */ public static BatteryHistEntry interpolate( long slotTimestamp, long upperTimestamp, double ratio, BatteryHistEntry lowerHistEntry, BatteryHistEntry upperHistEntry) { final double totalPower = interpolate( lowerHistEntry == null ? 0 : lowerHistEntry.mTotalPower, upperHistEntry.mTotalPower, ratio); final double consumePower = interpolate( lowerHistEntry == null ? 0 : lowerHistEntry.mConsumePower, upperHistEntry.mConsumePower, ratio); final double foregroundUsageConsumePower = interpolate( lowerHistEntry == null ? 0 : lowerHistEntry.mForegroundUsageConsumePower, upperHistEntry.mForegroundUsageConsumePower, ratio); final double foregroundServiceUsageConsumePower = interpolate( lowerHistEntry == null ? 0 : lowerHistEntry.mForegroundServiceUsageConsumePower, upperHistEntry.mForegroundServiceUsageConsumePower, ratio); final double backgroundUsageConsumePower = interpolate( lowerHistEntry == null ? 0 : lowerHistEntry.mBackgroundUsageConsumePower, upperHistEntry.mBackgroundUsageConsumePower, ratio); final double cachedUsageConsumePower = interpolate( lowerHistEntry == null ? 0 : lowerHistEntry.mCachedUsageConsumePower, upperHistEntry.mCachedUsageConsumePower, ratio); final double foregroundUsageTimeInMs = interpolate( (lowerHistEntry == null ? 0 : lowerHistEntry.mForegroundUsageTimeInMs), upperHistEntry.mForegroundUsageTimeInMs, ratio); final double foregroundServiceUsageTimeInMs = interpolate( (lowerHistEntry == null ? 0 : lowerHistEntry.mForegroundServiceUsageTimeInMs), upperHistEntry.mForegroundServiceUsageTimeInMs, ratio); final double backgroundUsageTimeInMs = interpolate( (lowerHistEntry == null ? 0 : lowerHistEntry.mBackgroundUsageTimeInMs), upperHistEntry.mBackgroundUsageTimeInMs, ratio); // Checks whether there is any abnormal cases! if (upperHistEntry.mConsumePower < consumePower || upperHistEntry.mForegroundUsageConsumePower < foregroundUsageConsumePower || upperHistEntry.mForegroundServiceUsageConsumePower < foregroundServiceUsageConsumePower || upperHistEntry.mBackgroundUsageConsumePower < backgroundUsageConsumePower || upperHistEntry.mCachedUsageConsumePower < cachedUsageConsumePower || upperHistEntry.mForegroundUsageTimeInMs < foregroundUsageTimeInMs || upperHistEntry.mForegroundServiceUsageTimeInMs < foregroundServiceUsageTimeInMs || upperHistEntry.mBackgroundUsageTimeInMs < backgroundUsageTimeInMs) { if (DEBUG) { Log.w( TAG, String.format( "abnormal interpolation:\nupper:%s\nlower:%s", upperHistEntry, lowerHistEntry)); } } final double batteryLevel = lowerHistEntry == null ? upperHistEntry.mBatteryLevel : interpolate( lowerHistEntry.mBatteryLevel, upperHistEntry.mBatteryLevel, ratio); return new BatteryHistEntry( upperHistEntry, /* bootTimestamp= */ upperHistEntry.mBootTimestamp - (upperTimestamp - slotTimestamp), /* timestamp= */ slotTimestamp, totalPower, consumePower, foregroundUsageConsumePower, foregroundServiceUsageConsumePower, backgroundUsageConsumePower, cachedUsageConsumePower, Math.round(foregroundUsageTimeInMs), Math.round(foregroundServiceUsageTimeInMs), Math.round(backgroundUsageTimeInMs), (int) Math.round(batteryLevel)); } private static double interpolate(double v1, double v2, double ratio) { return v1 + ratio * (v2 - v1); } }