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