1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 package com.android.settings.fuelgauge; 15 16 import android.content.ContentValues; 17 import android.database.Cursor; 18 import android.os.BatteryConsumer; 19 import android.util.Log; 20 21 import java.time.Duration; 22 23 /** A container class to carry data from {@link ContentValues}. */ 24 public class BatteryHistEntry { 25 private static final boolean DEBUG = false; 26 private static final String TAG = "BatteryHistEntry"; 27 28 /** Keys for accessing {@link ContentValues} or {@link Cursor}. */ 29 public static final String KEY_UID = "uid"; 30 public static final String KEY_USER_ID = "userId"; 31 public static final String KEY_APP_LABEL = "appLabel"; 32 public static final String KEY_PACKAGE_NAME = "packageName"; 33 public static final String KEY_IS_HIDDEN = "isHidden"; 34 // Device booting elapsed time from SystemClock.elapsedRealtime(). 35 public static final String KEY_BOOT_TIMESTAMP = "bootTimestamp"; 36 public static final String KEY_TIMESTAMP = "timestamp"; 37 public static final String KEY_ZONE_ID = "zoneId"; 38 public static final String KEY_TOTAL_POWER = "totalPower"; 39 public static final String KEY_CONSUME_POWER = "consumePower"; 40 public static final String KEY_PERCENT_OF_TOTAL = "percentOfTotal"; 41 public static final String KEY_FOREGROUND_USAGE_TIME = "foregroundUsageTimeInMs"; 42 public static final String KEY_BACKGROUND_USAGE_TIME = "backgroundUsageTimeInMs"; 43 public static final String KEY_DRAIN_TYPE = "drainType"; 44 public static final String KEY_CONSUMER_TYPE = "consumerType"; 45 public static final String KEY_BATTERY_LEVEL = "batteryLevel"; 46 public static final String KEY_BATTERY_STATUS = "batteryStatus"; 47 public static final String KEY_BATTERY_HEALTH = "batteryHealth"; 48 49 public final long mUid; 50 public final long mUserId; 51 public final String mAppLabel; 52 public final String mPackageName; 53 // Whether the data is represented as system component or not? 54 public final boolean mIsHidden; 55 // Records the timestamp relative information. 56 public final long mBootTimestamp; 57 public final long mTimestamp; 58 public final String mZoneId; 59 // Records the battery usage relative information. 60 public final double mTotalPower; 61 public final double mConsumePower; 62 public final double mPercentOfTotal; 63 public final long mForegroundUsageTimeInMs; 64 public final long mBackgroundUsageTimeInMs; 65 @BatteryConsumer.PowerComponent 66 public final int mDrainType; 67 @ConvertUtils.ConsumerType 68 public final int mConsumerType; 69 // Records the battery intent relative information. 70 public final int mBatteryLevel; 71 public final int mBatteryStatus; 72 public final int mBatteryHealth; 73 74 private String mKey = null; 75 private boolean mIsValidEntry = true; 76 BatteryHistEntry(ContentValues values)77 public BatteryHistEntry(ContentValues values) { 78 mUid = getLong(values, KEY_UID); 79 mUserId = getLong(values, KEY_USER_ID); 80 mAppLabel = getString(values, KEY_APP_LABEL); 81 mPackageName = getString(values, KEY_PACKAGE_NAME); 82 mIsHidden = getBoolean(values, KEY_IS_HIDDEN); 83 mBootTimestamp = getLong(values, KEY_BOOT_TIMESTAMP); 84 mTimestamp = getLong(values, KEY_TIMESTAMP); 85 mZoneId = getString(values, KEY_ZONE_ID); 86 mTotalPower = getDouble(values, KEY_TOTAL_POWER); 87 mConsumePower = getDouble(values, KEY_CONSUME_POWER); 88 mPercentOfTotal = getDouble(values, KEY_PERCENT_OF_TOTAL); 89 mForegroundUsageTimeInMs = getLong(values, KEY_FOREGROUND_USAGE_TIME); 90 mBackgroundUsageTimeInMs = getLong(values, KEY_BACKGROUND_USAGE_TIME); 91 mDrainType = getInteger(values, KEY_DRAIN_TYPE); 92 mConsumerType = getInteger(values, KEY_CONSUMER_TYPE); 93 mBatteryLevel = getInteger(values, KEY_BATTERY_LEVEL); 94 mBatteryStatus = getInteger(values, KEY_BATTERY_STATUS); 95 mBatteryHealth = getInteger(values, KEY_BATTERY_HEALTH); 96 } 97 BatteryHistEntry(Cursor cursor)98 public BatteryHistEntry(Cursor cursor) { 99 mUid = getLong(cursor, KEY_UID); 100 mUserId = getLong(cursor, KEY_USER_ID); 101 mAppLabel = getString(cursor, KEY_APP_LABEL); 102 mPackageName = getString(cursor, KEY_PACKAGE_NAME); 103 mIsHidden = getBoolean(cursor, KEY_IS_HIDDEN); 104 mBootTimestamp = getLong(cursor, KEY_BOOT_TIMESTAMP); 105 mTimestamp = getLong(cursor, KEY_TIMESTAMP); 106 mZoneId = getString(cursor, KEY_ZONE_ID); 107 mTotalPower = getDouble(cursor, KEY_TOTAL_POWER); 108 mConsumePower = getDouble(cursor, KEY_CONSUME_POWER); 109 mPercentOfTotal = getDouble(cursor, KEY_PERCENT_OF_TOTAL); 110 mForegroundUsageTimeInMs = getLong(cursor, KEY_FOREGROUND_USAGE_TIME); 111 mBackgroundUsageTimeInMs = getLong(cursor, KEY_BACKGROUND_USAGE_TIME); 112 mDrainType = getInteger(cursor, KEY_DRAIN_TYPE); 113 mConsumerType = getInteger(cursor, KEY_CONSUMER_TYPE); 114 mBatteryLevel = getInteger(cursor, KEY_BATTERY_LEVEL); 115 mBatteryStatus = getInteger(cursor, KEY_BATTERY_STATUS); 116 mBatteryHealth = getInteger(cursor, KEY_BATTERY_HEALTH); 117 } 118 BatteryHistEntry( BatteryHistEntry fromEntry, long bootTimestamp, long timestamp, double totalPower, double consumePower, long foregroundUsageTimeInMs, long backgroundUsageTimeInMs, int batteryLevel)119 private BatteryHistEntry( 120 BatteryHistEntry fromEntry, 121 long bootTimestamp, 122 long timestamp, 123 double totalPower, 124 double consumePower, 125 long foregroundUsageTimeInMs, 126 long backgroundUsageTimeInMs, 127 int batteryLevel) { 128 mUid = fromEntry.mUid; 129 mUserId = fromEntry.mUserId; 130 mAppLabel = fromEntry.mAppLabel; 131 mPackageName = fromEntry.mPackageName; 132 mIsHidden = fromEntry.mIsHidden; 133 mBootTimestamp = bootTimestamp; 134 mTimestamp = timestamp; 135 mZoneId = fromEntry.mZoneId; 136 mTotalPower = totalPower; 137 mConsumePower = consumePower; 138 mPercentOfTotal = fromEntry.mPercentOfTotal; 139 mForegroundUsageTimeInMs = foregroundUsageTimeInMs; 140 mBackgroundUsageTimeInMs = backgroundUsageTimeInMs; 141 mDrainType = fromEntry.mDrainType; 142 mConsumerType = fromEntry.mConsumerType; 143 mBatteryLevel = batteryLevel; 144 mBatteryStatus = fromEntry.mBatteryStatus; 145 mBatteryHealth = fromEntry.mBatteryHealth; 146 } 147 148 /** Whether this {@link BatteryHistEntry} is valid or not? */ isValidEntry()149 public boolean isValidEntry() { 150 return mIsValidEntry; 151 } 152 153 /** Whether this {@link BatteryHistEntry} is user consumer or not. */ isUserEntry()154 public boolean isUserEntry() { 155 return mConsumerType == ConvertUtils.CONSUMER_TYPE_USER_BATTERY; 156 } 157 158 /** Whether this {@link BatteryHistEntry} is app consumer or not. */ isAppEntry()159 public boolean isAppEntry() { 160 return mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY; 161 } 162 163 /** Whether this {@link BatteryHistEntry} is system consumer or not. */ isSystemEntry()164 public boolean isSystemEntry() { 165 return mConsumerType == ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY; 166 } 167 168 /** Gets an identifier to represent this {@link BatteryHistEntry}. */ getKey()169 public String getKey() { 170 if (mKey == null) { 171 switch (mConsumerType) { 172 case ConvertUtils.CONSUMER_TYPE_UID_BATTERY: 173 mKey = Long.toString(mUid); 174 break; 175 case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY: 176 mKey = "S|" + mDrainType; 177 break; 178 case ConvertUtils.CONSUMER_TYPE_USER_BATTERY: 179 mKey = "U|" + mUserId; 180 break; 181 } 182 } 183 return mKey; 184 } 185 186 @Override toString()187 public String toString() { 188 final String recordAtDateTime = 189 ConvertUtils.utcToLocalTime(/*context=*/ null, mTimestamp); 190 final StringBuilder builder = new StringBuilder() 191 .append("\nBatteryHistEntry{") 192 .append(String.format("\n\tpackage=%s|label=%s|uid=%d|userId=%d|isHidden=%b", 193 mPackageName, mAppLabel, mUid, mUserId, mIsHidden)) 194 .append(String.format("\n\ttimestamp=%s|zoneId=%s|bootTimestamp=%d", 195 recordAtDateTime, mZoneId, Duration.ofMillis(mBootTimestamp).getSeconds())) 196 .append(String.format("\n\tusage=%f|total=%f|consume=%f|elapsedTime=%d|%d", 197 mPercentOfTotal, mTotalPower, mConsumePower, 198 Duration.ofMillis(mForegroundUsageTimeInMs).getSeconds(), 199 Duration.ofMillis(mBackgroundUsageTimeInMs).getSeconds())) 200 .append(String.format("\n\tdrainType=%d|consumerType=%d", 201 mDrainType, mConsumerType)) 202 .append(String.format("\n\tbattery=%d|status=%d|health=%d\n}", 203 mBatteryLevel, mBatteryStatus, mBatteryHealth)); 204 return builder.toString(); 205 } 206 getInteger(ContentValues values, String key)207 private int getInteger(ContentValues values, String key) { 208 if (values != null && values.containsKey(key)) { 209 return values.getAsInteger(key); 210 }; 211 mIsValidEntry = false; 212 return 0; 213 } 214 getInteger(Cursor cursor, String key)215 private int getInteger(Cursor cursor, String key) { 216 final int columnIndex = cursor.getColumnIndex(key); 217 if (columnIndex >= 0) { 218 return cursor.getInt(columnIndex); 219 } 220 mIsValidEntry = false; 221 return 0; 222 } 223 getLong(ContentValues values, String key)224 private long getLong(ContentValues values, String key) { 225 if (values != null && values.containsKey(key)) { 226 return values.getAsLong(key); 227 } 228 mIsValidEntry = false; 229 return 0L; 230 } 231 getLong(Cursor cursor, String key)232 private long getLong(Cursor cursor, String key) { 233 final int columnIndex = cursor.getColumnIndex(key); 234 if (columnIndex >= 0) { 235 return cursor.getLong(columnIndex); 236 } 237 mIsValidEntry = false; 238 return 0L; 239 } 240 getDouble(ContentValues values, String key)241 private double getDouble(ContentValues values, String key) { 242 if (values != null && values.containsKey(key)) { 243 return values.getAsDouble(key); 244 } 245 mIsValidEntry = false; 246 return 0f; 247 } 248 getDouble(Cursor cursor, String key)249 private double getDouble(Cursor cursor, String key) { 250 final int columnIndex = cursor.getColumnIndex(key); 251 if (columnIndex >= 0) { 252 return cursor.getDouble(columnIndex); 253 } 254 mIsValidEntry = false; 255 return 0f; 256 } 257 getString(ContentValues values, String key)258 private String getString(ContentValues values, String key) { 259 if (values != null && values.containsKey(key)) { 260 return values.getAsString(key); 261 } 262 mIsValidEntry = false; 263 return null; 264 } 265 getString(Cursor cursor, String key)266 private String getString(Cursor cursor, String key) { 267 final int columnIndex = cursor.getColumnIndex(key); 268 if (columnIndex >= 0) { 269 return cursor.getString(columnIndex); 270 } 271 mIsValidEntry = false; 272 return null; 273 } 274 getBoolean(ContentValues values, String key)275 private boolean getBoolean(ContentValues values, String key) { 276 if (values != null && values.containsKey(key)) { 277 return values.getAsBoolean(key); 278 } 279 mIsValidEntry = false; 280 return false; 281 } 282 getBoolean(Cursor cursor, String key)283 private boolean getBoolean(Cursor cursor, String key) { 284 final int columnIndex = cursor.getColumnIndex(key); 285 if (columnIndex >= 0) { 286 // Use value == 1 to represent boolean value in the database. 287 return cursor.getInt(columnIndex) == 1; 288 } 289 mIsValidEntry = false; 290 return false; 291 } 292 293 /** Creates new {@link BatteryHistEntry} from interpolation. */ interpolate( long slotTimestamp, long upperTimestamp, double ratio, BatteryHistEntry lowerHistEntry, BatteryHistEntry upperHistEntry)294 public static BatteryHistEntry interpolate( 295 long slotTimestamp, 296 long upperTimestamp, 297 double ratio, 298 BatteryHistEntry lowerHistEntry, 299 BatteryHistEntry upperHistEntry) { 300 final double totalPower = interpolate( 301 lowerHistEntry == null ? 0 : lowerHistEntry.mTotalPower, 302 upperHistEntry.mTotalPower, 303 ratio); 304 final double consumePower = interpolate( 305 lowerHistEntry == null ? 0 : lowerHistEntry.mConsumePower, 306 upperHistEntry.mConsumePower, 307 ratio); 308 final double foregroundUsageTimeInMs = interpolate( 309 lowerHistEntry == null ? 0 : lowerHistEntry.mForegroundUsageTimeInMs, 310 upperHistEntry.mForegroundUsageTimeInMs, 311 ratio); 312 final double backgroundUsageTimeInMs = interpolate( 313 lowerHistEntry == null ? 0 : lowerHistEntry.mBackgroundUsageTimeInMs, 314 upperHistEntry.mBackgroundUsageTimeInMs, 315 ratio); 316 // Checks whether there is any abnoaml cases! 317 if (upperHistEntry.mConsumePower < consumePower 318 || upperHistEntry.mForegroundUsageTimeInMs < foregroundUsageTimeInMs 319 || upperHistEntry.mBackgroundUsageTimeInMs < backgroundUsageTimeInMs) { 320 if (DEBUG) { 321 Log.w(TAG, String.format( 322 "abnormal interpolation:\nupper:%s\nlower:%s", 323 upperHistEntry, lowerHistEntry)); 324 } 325 } 326 final double batteryLevel = 327 lowerHistEntry == null 328 ? upperHistEntry.mBatteryLevel 329 : interpolate( 330 lowerHistEntry.mBatteryLevel, 331 upperHistEntry.mBatteryLevel, 332 ratio); 333 return new BatteryHistEntry( 334 upperHistEntry, 335 /*bootTimestamp=*/ upperHistEntry.mBootTimestamp 336 - (upperTimestamp - slotTimestamp), 337 /*timestamp=*/ slotTimestamp, 338 totalPower, 339 consumePower, 340 Math.round(foregroundUsageTimeInMs), 341 Math.round(backgroundUsageTimeInMs), 342 (int) Math.round(batteryLevel)); 343 } 344 interpolate(double v1, double v2, double ratio)345 private static double interpolate(double v1, double v2, double ratio) { 346 return v1 + ratio * (v2 - v1); 347 } 348 } 349