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 /** 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 
31     public static final String KEY_USER_ID = "userId";
32     public static final String KEY_PACKAGE_NAME = "packageName";
33     public static final String KEY_TIMESTAMP = "timestamp";
34     public static final String KEY_CONSUMER_TYPE = "consumerType";
35     public static final String KEY_IS_FULL_CHARGE_CYCLE_START = "isFullChargeCycleStart";
36     public static final String KEY_BATTERY_INFORMATION = "batteryInformation";
37     public static final String KEY_BATTERY_INFORMATION_DEBUG = "batteryInformationDebug";
38 
39     public final long mUid;
40     public final long mUserId;
41     public final String mAppLabel;
42     public final String mPackageName;
43     // Whether the data is represented as system component or not?
44     public final boolean mIsHidden;
45     // Records the timestamp relative information.
46     public final long mBootTimestamp;
47     public final long mTimestamp;
48     public final String mZoneId;
49     // Records the battery usage relative information.
50     public final double mTotalPower;
51     public final double mConsumePower;
52     public final double mForegroundUsageConsumePower;
53     public final double mForegroundServiceUsageConsumePower;
54     public final double mBackgroundUsageConsumePower;
55     public final double mCachedUsageConsumePower;
56     public final double mPercentOfTotal;
57     public final long mForegroundUsageTimeInMs;
58     public final long mForegroundServiceUsageTimeInMs;
59     public final long mBackgroundUsageTimeInMs;
60     @BatteryConsumer.PowerComponent public final int mDrainType;
61     @ConvertUtils.ConsumerType public final int mConsumerType;
62     // Records the battery intent relative information.
63     public final int mBatteryLevel;
64     public final int mBatteryStatus;
65     public final int mBatteryHealth;
66 
67     private String mKey = null;
68     private boolean mIsValidEntry = true;
69 
BatteryHistEntry(ContentValues values)70     public BatteryHistEntry(ContentValues values) {
71         mUid = getLong(values, KEY_UID);
72         mUserId = getLong(values, KEY_USER_ID);
73         mPackageName = getString(values, KEY_PACKAGE_NAME);
74         mTimestamp = getLong(values, KEY_TIMESTAMP);
75         mConsumerType = getInteger(values, KEY_CONSUMER_TYPE);
76         final BatteryInformation batteryInformation =
77                 ConvertUtils.getBatteryInformation(values, KEY_BATTERY_INFORMATION);
78         mAppLabel = batteryInformation.getAppLabel();
79         mIsHidden = batteryInformation.getIsHidden();
80         mBootTimestamp = batteryInformation.getBootTimestamp();
81         mZoneId = batteryInformation.getZoneId();
82         mTotalPower = batteryInformation.getTotalPower();
83         mConsumePower = batteryInformation.getConsumePower();
84         mForegroundUsageConsumePower = batteryInformation.getForegroundUsageConsumePower();
85         mForegroundServiceUsageConsumePower =
86                 batteryInformation.getForegroundServiceUsageConsumePower();
87         mBackgroundUsageConsumePower = batteryInformation.getBackgroundUsageConsumePower();
88         mCachedUsageConsumePower = batteryInformation.getCachedUsageConsumePower();
89         mPercentOfTotal = batteryInformation.getPercentOfTotal();
90         mForegroundUsageTimeInMs = batteryInformation.getForegroundUsageTimeInMs();
91         mForegroundServiceUsageTimeInMs = batteryInformation.getForegroundServiceUsageTimeInMs();
92         mBackgroundUsageTimeInMs = batteryInformation.getBackgroundUsageTimeInMs();
93         mDrainType = batteryInformation.getDrainType();
94         final DeviceBatteryState deviceBatteryState = batteryInformation.getDeviceBatteryState();
95         mBatteryLevel = deviceBatteryState.getBatteryLevel();
96         mBatteryStatus = deviceBatteryState.getBatteryStatus();
97         mBatteryHealth = deviceBatteryState.getBatteryHealth();
98     }
99 
BatteryHistEntry(Cursor cursor)100     public BatteryHistEntry(Cursor cursor) {
101         mUid = getLong(cursor, KEY_UID);
102         mUserId = getLong(cursor, KEY_USER_ID);
103         mPackageName = getString(cursor, KEY_PACKAGE_NAME);
104         mTimestamp = getLong(cursor, KEY_TIMESTAMP);
105         mConsumerType = getInteger(cursor, KEY_CONSUMER_TYPE);
106         final BatteryInformation batteryInformation =
107                 ConvertUtils.getBatteryInformation(cursor, KEY_BATTERY_INFORMATION);
108         mAppLabel = batteryInformation.getAppLabel();
109         mIsHidden = batteryInformation.getIsHidden();
110         mBootTimestamp = batteryInformation.getBootTimestamp();
111         mZoneId = batteryInformation.getZoneId();
112         mTotalPower = batteryInformation.getTotalPower();
113         mConsumePower = batteryInformation.getConsumePower();
114         mForegroundUsageConsumePower = batteryInformation.getForegroundUsageConsumePower();
115         mForegroundServiceUsageConsumePower =
116                 batteryInformation.getForegroundServiceUsageConsumePower();
117         mBackgroundUsageConsumePower = batteryInformation.getBackgroundUsageConsumePower();
118         mCachedUsageConsumePower = batteryInformation.getCachedUsageConsumePower();
119         mPercentOfTotal = batteryInformation.getPercentOfTotal();
120         mForegroundUsageTimeInMs = batteryInformation.getForegroundUsageTimeInMs();
121         mForegroundServiceUsageTimeInMs = batteryInformation.getForegroundServiceUsageTimeInMs();
122         mBackgroundUsageTimeInMs = batteryInformation.getBackgroundUsageTimeInMs();
123         mDrainType = batteryInformation.getDrainType();
124         final DeviceBatteryState deviceBatteryState = batteryInformation.getDeviceBatteryState();
125         mBatteryLevel = deviceBatteryState.getBatteryLevel();
126         mBatteryStatus = deviceBatteryState.getBatteryStatus();
127         mBatteryHealth = deviceBatteryState.getBatteryHealth();
128     }
129 
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)130     private BatteryHistEntry(
131             BatteryHistEntry fromEntry,
132             long bootTimestamp,
133             long timestamp,
134             double totalPower,
135             double consumePower,
136             double foregroundUsageConsumePower,
137             double foregroundServiceUsageConsumePower,
138             double backgroundUsageConsumePower,
139             double cachedUsageConsumePower,
140             long foregroundUsageTimeInMs,
141             long foregroundServiceUsageTimeInMs,
142             long backgroundUsageTimeInMs,
143             int batteryLevel) {
144         mUid = fromEntry.mUid;
145         mUserId = fromEntry.mUserId;
146         mAppLabel = fromEntry.mAppLabel;
147         mPackageName = fromEntry.mPackageName;
148         mIsHidden = fromEntry.mIsHidden;
149         mBootTimestamp = bootTimestamp;
150         mTimestamp = timestamp;
151         mZoneId = fromEntry.mZoneId;
152         mTotalPower = totalPower;
153         mConsumePower = consumePower;
154         mForegroundUsageConsumePower = foregroundUsageConsumePower;
155         mForegroundServiceUsageConsumePower = foregroundServiceUsageConsumePower;
156         mBackgroundUsageConsumePower = backgroundUsageConsumePower;
157         mCachedUsageConsumePower = cachedUsageConsumePower;
158         mPercentOfTotal = fromEntry.mPercentOfTotal;
159         mForegroundUsageTimeInMs = foregroundUsageTimeInMs;
160         mForegroundServiceUsageTimeInMs = foregroundServiceUsageTimeInMs;
161         mBackgroundUsageTimeInMs = backgroundUsageTimeInMs;
162         mDrainType = fromEntry.mDrainType;
163         mConsumerType = fromEntry.mConsumerType;
164         mBatteryLevel = batteryLevel;
165         mBatteryStatus = fromEntry.mBatteryStatus;
166         mBatteryHealth = fromEntry.mBatteryHealth;
167     }
168 
169     /** Whether this {@link BatteryHistEntry} is valid or not? */
isValidEntry()170     public boolean isValidEntry() {
171         return mIsValidEntry;
172     }
173 
174     /** Gets an identifier to represent this {@link BatteryHistEntry}. */
getKey()175     public String getKey() {
176         if (mKey == null) {
177             switch (mConsumerType) {
178                 case ConvertUtils.CONSUMER_TYPE_UID_BATTERY:
179                     mKey = Long.toString(mUid);
180                     break;
181                 case ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY:
182                     mKey = "S|" + mDrainType;
183                     break;
184                 case ConvertUtils.CONSUMER_TYPE_USER_BATTERY:
185                     mKey = "U|" + mUserId;
186                     break;
187             }
188         }
189         return mKey;
190     }
191 
192     @Override
toString()193     public String toString() {
194         final String recordAtDateTime = ConvertUtils.utcToLocalTimeForLogging(mTimestamp);
195         return new StringBuilder()
196                 .append("\nBatteryHistEntry{")
197                 .append(
198                         String.format(
199                                 "\n\tpackage=%s|label=%s|uid=%d|userId=%d|isHidden=%b",
200                                 mPackageName, mAppLabel, mUid, mUserId, mIsHidden))
201                 .append(
202                         String.format(
203                                 "\n\ttimestamp=%s|zoneId=%s|bootTimestamp=%d",
204                                 recordAtDateTime,
205                                 mZoneId,
206                                 TimestampUtils.getSeconds(mBootTimestamp)))
207                 .append(
208                         String.format(
209                                 "\n\tusage=%f|total=%f|consume=%f",
210                                 mPercentOfTotal, mTotalPower, mConsumePower))
211                 .append(
212                         String.format(
213                                 "\n\tforeground=%f|foregroundService=%f",
214                                 mForegroundUsageConsumePower, mForegroundServiceUsageConsumePower))
215                 .append(
216                         String.format(
217                                 "\n\tbackground=%f|cached=%f",
218                                 mBackgroundUsageConsumePower, mCachedUsageConsumePower))
219                 .append(
220                         String.format(
221                                 "\n\telapsedTime,fg=%d|fgs=%d|bg=%d",
222                                 TimestampUtils.getSeconds(mForegroundUsageTimeInMs),
223                                 TimestampUtils.getSeconds(mForegroundServiceUsageTimeInMs),
224                                 TimestampUtils.getSeconds(mBackgroundUsageTimeInMs)))
225                 .append(
226                         String.format(
227                                 "\n\tdrainType=%d|consumerType=%d", mDrainType, mConsumerType))
228                 .append(
229                         String.format(
230                                 "\n\tbattery=%d|status=%d|health=%d\n}",
231                                 mBatteryLevel, mBatteryStatus, mBatteryHealth))
232                 .toString();
233     }
234 
getInteger(ContentValues values, String key)235     private int getInteger(ContentValues values, String key) {
236         if (values != null && values.containsKey(key)) {
237             return values.getAsInteger(key);
238         }
239         mIsValidEntry = false;
240         return 0;
241     }
242 
getInteger(Cursor cursor, String key)243     private int getInteger(Cursor cursor, String key) {
244         final int columnIndex = cursor.getColumnIndex(key);
245         if (columnIndex >= 0) {
246             return cursor.getInt(columnIndex);
247         }
248         mIsValidEntry = false;
249         return 0;
250     }
251 
getLong(ContentValues values, String key)252     private long getLong(ContentValues values, String key) {
253         if (values != null && values.containsKey(key)) {
254             return values.getAsLong(key);
255         }
256         mIsValidEntry = false;
257         return 0L;
258     }
259 
getLong(Cursor cursor, String key)260     private long getLong(Cursor cursor, String key) {
261         final int columnIndex = cursor.getColumnIndex(key);
262         if (columnIndex >= 0) {
263             return cursor.getLong(columnIndex);
264         }
265         mIsValidEntry = false;
266         return 0L;
267     }
268 
getString(ContentValues values, String key)269     private String getString(ContentValues values, String key) {
270         if (values != null && values.containsKey(key)) {
271             return values.getAsString(key);
272         }
273         mIsValidEntry = false;
274         return null;
275     }
276 
getString(Cursor cursor, String key)277     private String getString(Cursor cursor, String key) {
278         final int columnIndex = cursor.getColumnIndex(key);
279         if (columnIndex >= 0) {
280             return cursor.getString(columnIndex);
281         }
282         mIsValidEntry = false;
283         return null;
284     }
285 
286     /** Creates new {@link BatteryHistEntry} from interpolation. */
interpolate( long slotTimestamp, long upperTimestamp, double ratio, BatteryHistEntry lowerHistEntry, BatteryHistEntry upperHistEntry)287     public static BatteryHistEntry interpolate(
288             long slotTimestamp,
289             long upperTimestamp,
290             double ratio,
291             BatteryHistEntry lowerHistEntry,
292             BatteryHistEntry upperHistEntry) {
293         final double totalPower =
294                 interpolate(
295                         lowerHistEntry == null ? 0 : lowerHistEntry.mTotalPower,
296                         upperHistEntry.mTotalPower,
297                         ratio);
298         final double consumePower =
299                 interpolate(
300                         lowerHistEntry == null ? 0 : lowerHistEntry.mConsumePower,
301                         upperHistEntry.mConsumePower,
302                         ratio);
303         final double foregroundUsageConsumePower =
304                 interpolate(
305                         lowerHistEntry == null ? 0 : lowerHistEntry.mForegroundUsageConsumePower,
306                         upperHistEntry.mForegroundUsageConsumePower,
307                         ratio);
308         final double foregroundServiceUsageConsumePower =
309                 interpolate(
310                         lowerHistEntry == null
311                                 ? 0
312                                 : lowerHistEntry.mForegroundServiceUsageConsumePower,
313                         upperHistEntry.mForegroundServiceUsageConsumePower,
314                         ratio);
315         final double backgroundUsageConsumePower =
316                 interpolate(
317                         lowerHistEntry == null ? 0 : lowerHistEntry.mBackgroundUsageConsumePower,
318                         upperHistEntry.mBackgroundUsageConsumePower,
319                         ratio);
320         final double cachedUsageConsumePower =
321                 interpolate(
322                         lowerHistEntry == null ? 0 : lowerHistEntry.mCachedUsageConsumePower,
323                         upperHistEntry.mCachedUsageConsumePower,
324                         ratio);
325         final double foregroundUsageTimeInMs =
326                 interpolate(
327                         (lowerHistEntry == null ? 0 : lowerHistEntry.mForegroundUsageTimeInMs),
328                         upperHistEntry.mForegroundUsageTimeInMs,
329                         ratio);
330         final double foregroundServiceUsageTimeInMs =
331                 interpolate(
332                         (lowerHistEntry == null
333                                 ? 0
334                                 : lowerHistEntry.mForegroundServiceUsageTimeInMs),
335                         upperHistEntry.mForegroundServiceUsageTimeInMs,
336                         ratio);
337         final double backgroundUsageTimeInMs =
338                 interpolate(
339                         (lowerHistEntry == null ? 0 : lowerHistEntry.mBackgroundUsageTimeInMs),
340                         upperHistEntry.mBackgroundUsageTimeInMs,
341                         ratio);
342         // Checks whether there is any abnormal cases!
343         if (upperHistEntry.mConsumePower < consumePower
344                 || upperHistEntry.mForegroundUsageConsumePower < foregroundUsageConsumePower
345                 || upperHistEntry.mForegroundServiceUsageConsumePower
346                         < foregroundServiceUsageConsumePower
347                 || upperHistEntry.mBackgroundUsageConsumePower < backgroundUsageConsumePower
348                 || upperHistEntry.mCachedUsageConsumePower < cachedUsageConsumePower
349                 || upperHistEntry.mForegroundUsageTimeInMs < foregroundUsageTimeInMs
350                 || upperHistEntry.mForegroundServiceUsageTimeInMs < foregroundServiceUsageTimeInMs
351                 || upperHistEntry.mBackgroundUsageTimeInMs < backgroundUsageTimeInMs) {
352             if (DEBUG) {
353                 Log.w(
354                         TAG,
355                         String.format(
356                                 "abnormal interpolation:\nupper:%s\nlower:%s",
357                                 upperHistEntry, lowerHistEntry));
358             }
359         }
360         final double batteryLevel =
361                 lowerHistEntry == null
362                         ? upperHistEntry.mBatteryLevel
363                         : interpolate(
364                                 lowerHistEntry.mBatteryLevel, upperHistEntry.mBatteryLevel, ratio);
365         return new BatteryHistEntry(
366                 upperHistEntry,
367                 /* bootTimestamp= */ upperHistEntry.mBootTimestamp
368                         - (upperTimestamp - slotTimestamp),
369                 /* timestamp= */ slotTimestamp,
370                 totalPower,
371                 consumePower,
372                 foregroundUsageConsumePower,
373                 foregroundServiceUsageConsumePower,
374                 backgroundUsageConsumePower,
375                 cachedUsageConsumePower,
376                 Math.round(foregroundUsageTimeInMs),
377                 Math.round(foregroundServiceUsageTimeInMs),
378                 Math.round(backgroundUsageTimeInMs),
379                 (int) Math.round(batteryLevel));
380     }
381 
interpolate(double v1, double v2, double ratio)382     private static double interpolate(double v1, double v2, double ratio) {
383         return v1 + ratio * (v2 - v1);
384     }
385 }
386