• 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.annotation.IntDef;
19 import android.annotation.Nullable;
20 import android.app.usage.IUsageStatsManager;
21 import android.app.usage.UsageEvents.Event;
22 import android.app.usage.UsageStatsManager;
23 import android.content.ContentValues;
24 import android.content.Context;
25 import android.content.pm.PackageManager;
26 import android.database.Cursor;
27 import android.os.BatteryUsageStats;
28 import android.os.Build;
29 import android.os.LocaleList;
30 import android.os.UserHandle;
31 import android.text.TextUtils;
32 import android.text.format.DateFormat;
33 import android.util.Base64;
34 import android.util.Log;
35 
36 import androidx.annotation.NonNull;
37 import androidx.annotation.VisibleForTesting;
38 
39 import com.android.settings.fuelgauge.BatteryUtils;
40 import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
41 import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
42 import com.android.settings.fuelgauge.batteryusage.db.BatteryUsageSlotEntity;
43 
44 import java.lang.annotation.Retention;
45 import java.lang.annotation.RetentionPolicy;
46 import java.util.ArrayList;
47 import java.util.List;
48 import java.util.Locale;
49 import java.util.Map;
50 import java.util.Set;
51 import java.util.TimeZone;
52 
53 /** A utility class to convert data into another types. */
54 public final class ConvertUtils {
55     private static final String TAG = "ConvertUtils";
56 
57     /** A fake package name to represent no BatteryEntry data. */
58     public static final String FAKE_PACKAGE_NAME = "fake_package";
59 
60     @IntDef(prefix = {"CONSUMER_TYPE"}, value = {
61             CONSUMER_TYPE_UNKNOWN,
62             CONSUMER_TYPE_UID_BATTERY,
63             CONSUMER_TYPE_USER_BATTERY,
64             CONSUMER_TYPE_SYSTEM_BATTERY,
65     })
66     @Retention(RetentionPolicy.SOURCE)
67     public @interface ConsumerType {
68     }
69 
70     public static final int CONSUMER_TYPE_UNKNOWN = 0;
71     public static final int CONSUMER_TYPE_UID_BATTERY = 1;
72     public static final int CONSUMER_TYPE_USER_BATTERY = 2;
73     public static final int CONSUMER_TYPE_SYSTEM_BATTERY = 3;
74 
75     public static final int DEFAULT_USAGE_SOURCE = UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY;
76     public static final int EMPTY_USAGE_SOURCE = -1;
77 
78     @VisibleForTesting
79     static int sUsageSource = EMPTY_USAGE_SOURCE;
80 
ConvertUtils()81     private ConvertUtils() {
82     }
83 
84     /** Whether {@code consumerType} is app consumer or not. */
isUidConsumer(final int consumerType)85     public static boolean isUidConsumer(final int consumerType) {
86         return consumerType == CONSUMER_TYPE_UID_BATTERY;
87     }
88 
89     /** Whether {@code consumerType} is user consumer or not. */
isUserConsumer(final int consumerType)90     public static boolean isUserConsumer(final int consumerType) {
91         return consumerType == CONSUMER_TYPE_USER_BATTERY;
92     }
93 
94     /** Whether {@code consumerType} is system consumer or not. */
isSystemConsumer(final int consumerType)95     public static boolean isSystemConsumer(final int consumerType) {
96         return consumerType == CONSUMER_TYPE_SYSTEM_BATTERY;
97     }
98 
99     /** Converts {@link BatteryEntry} to {@link ContentValues} */
convertBatteryEntryToContentValues( final BatteryEntry entry, final BatteryUsageStats batteryUsageStats, final int batteryLevel, final int batteryStatus, final int batteryHealth, final long bootTimestamp, final long timestamp, final boolean isFullChargeStart)100     public static ContentValues convertBatteryEntryToContentValues(
101             final BatteryEntry entry,
102             final BatteryUsageStats batteryUsageStats,
103             final int batteryLevel,
104             final int batteryStatus,
105             final int batteryHealth,
106             final long bootTimestamp,
107             final long timestamp,
108             final boolean isFullChargeStart) {
109         final ContentValues values = new ContentValues();
110         if (entry != null && batteryUsageStats != null) {
111             values.put(BatteryHistEntry.KEY_UID, Long.valueOf(entry.getUid()));
112             values.put(BatteryHistEntry.KEY_USER_ID,
113                     Long.valueOf(UserHandle.getUserId(entry.getUid())));
114             values.put(BatteryHistEntry.KEY_PACKAGE_NAME,
115                     entry.getDefaultPackageName());
116             values.put(BatteryHistEntry.KEY_CONSUMER_TYPE,
117                     Integer.valueOf(entry.getConsumerType()));
118         } else {
119             values.put(BatteryHistEntry.KEY_PACKAGE_NAME, FAKE_PACKAGE_NAME);
120         }
121         values.put(BatteryHistEntry.KEY_TIMESTAMP, Long.valueOf(timestamp));
122         values.put(BatteryHistEntry.KEY_IS_FULL_CHARGE_CYCLE_START,
123                 Boolean.valueOf(isFullChargeStart));
124         final BatteryInformation batteryInformation =
125                 constructBatteryInformation(
126                         entry,
127                         batteryUsageStats,
128                         batteryLevel,
129                         batteryStatus,
130                         batteryHealth,
131                         bootTimestamp);
132         values.put(BatteryHistEntry.KEY_BATTERY_INFORMATION,
133                 convertBatteryInformationToString(batteryInformation));
134         // Save the BatteryInformation unencoded string into database for debugging.
135         if (Build.TYPE.equals("userdebug")) {
136             values.put(
137                     BatteryHistEntry.KEY_BATTERY_INFORMATION_DEBUG, batteryInformation.toString());
138         }
139         return values;
140     }
141 
142     /** Converts {@link AppUsageEvent} to {@link ContentValues} */
convertAppUsageEventToContentValues(final AppUsageEvent event)143     public static ContentValues convertAppUsageEventToContentValues(final AppUsageEvent event) {
144         final ContentValues values = new ContentValues();
145         values.put(AppUsageEventEntity.KEY_UID, event.getUid());
146         values.put(AppUsageEventEntity.KEY_USER_ID, event.getUserId());
147         values.put(AppUsageEventEntity.KEY_TIMESTAMP, event.getTimestamp());
148         values.put(AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE, event.getType().getNumber());
149         values.put(AppUsageEventEntity.KEY_PACKAGE_NAME, event.getPackageName());
150         values.put(AppUsageEventEntity.KEY_INSTANCE_ID, event.getInstanceId());
151         values.put(AppUsageEventEntity.KEY_TASK_ROOT_PACKAGE_NAME, event.getTaskRootPackageName());
152         return values;
153     }
154 
155     /** Converts {@link BatteryEvent} to {@link ContentValues} */
convertBatteryEventToContentValues(final BatteryEvent event)156     public static ContentValues convertBatteryEventToContentValues(final BatteryEvent event) {
157         final ContentValues values = new ContentValues();
158         values.put(BatteryEventEntity.KEY_TIMESTAMP, event.getTimestamp());
159         values.put(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE, event.getType().getNumber());
160         values.put(BatteryEventEntity.KEY_BATTERY_LEVEL, event.getBatteryLevel());
161         return values;
162     }
163 
164     /** Converts {@link BatteryUsageSlot} to {@link ContentValues} */
convertBatteryUsageSlotToContentValues( final BatteryUsageSlot batteryUsageSlot)165     public static ContentValues convertBatteryUsageSlotToContentValues(
166             final BatteryUsageSlot batteryUsageSlot) {
167         final ContentValues values = new ContentValues(2);
168         values.put(BatteryUsageSlotEntity.KEY_TIMESTAMP, batteryUsageSlot.getStartTimestamp());
169         values.put(BatteryUsageSlotEntity.KEY_BATTERY_USAGE_SLOT,
170                 Base64.encodeToString(batteryUsageSlot.toByteArray(), Base64.DEFAULT));
171         return values;
172     }
173 
174     /** Gets the encoded string from {@link BatteryInformation} instance. */
convertBatteryInformationToString( final BatteryInformation batteryInformation)175     public static String convertBatteryInformationToString(
176             final BatteryInformation batteryInformation) {
177         return Base64.encodeToString(batteryInformation.toByteArray(), Base64.DEFAULT);
178     }
179 
180     /** Gets the {@link BatteryInformation} instance from {@link ContentValues}. */
getBatteryInformation( final ContentValues values, final String key)181     public static BatteryInformation getBatteryInformation(
182             final ContentValues values, final String key) {
183         final BatteryInformation defaultInstance = BatteryInformation.getDefaultInstance();
184         if (values != null && values.containsKey(key)) {
185             return BatteryUtils.parseProtoFromString(values.getAsString(key), defaultInstance);
186         }
187         return defaultInstance;
188     }
189 
190     /** Gets the {@link BatteryInformation} instance from {@link Cursor}. */
getBatteryInformation(final Cursor cursor, final String key)191     public static BatteryInformation getBatteryInformation(final Cursor cursor, final String key) {
192         final BatteryInformation defaultInstance = BatteryInformation.getDefaultInstance();
193         final int columnIndex = cursor.getColumnIndex(key);
194         if (columnIndex >= 0) {
195             return BatteryUtils.parseProtoFromString(
196                     cursor.getString(columnIndex), defaultInstance);
197         }
198         return defaultInstance;
199     }
200 
201     /** Converts to {@link BatteryHistEntry} */
convertToBatteryHistEntry( BatteryEntry entry, BatteryUsageStats batteryUsageStats)202     public static BatteryHistEntry convertToBatteryHistEntry(
203             BatteryEntry entry,
204             BatteryUsageStats batteryUsageStats) {
205         return new BatteryHistEntry(
206                 convertBatteryEntryToContentValues(
207                         entry,
208                         batteryUsageStats,
209                         /*batteryLevel=*/ 0,
210                         /*batteryStatus=*/ 0,
211                         /*batteryHealth=*/ 0,
212                         /*bootTimestamp=*/ 0,
213                         /*timestamp=*/ 0,
214                         /*isFullChargeStart=*/ false));
215     }
216 
217     /** Converts from {@link Event} to {@link AppUsageEvent} */
218     @Nullable
convertToAppUsageEvent( Context context, IUsageStatsManager usageStatsManager, final Event event, final long userId)219     public static AppUsageEvent convertToAppUsageEvent(
220             Context context, IUsageStatsManager usageStatsManager, final Event event,
221             final long userId) {
222         final String packageName = event.getPackageName();
223         if (packageName == null) {
224             // See b/190609174: Event package names should never be null, but sometimes they are.
225             // Note that system events like device shutting down should still come with the android
226             // package name.
227             Log.w(TAG, String.format(
228                     "Ignoring a usage event with null package name (timestamp=%d, type=%d)",
229                     event.getTimeStamp(), event.getEventType()));
230             return null;
231         }
232 
233         final AppUsageEvent.Builder appUsageEventBuilder = AppUsageEvent.newBuilder();
234         appUsageEventBuilder
235                 .setTimestamp(event.getTimeStamp())
236                 .setType(getAppUsageEventType(event.getEventType()))
237                 .setPackageName(packageName)
238                 .setUserId(userId);
239 
240         final String taskRootPackageName = getTaskRootPackageName(event);
241         if (taskRootPackageName != null) {
242             appUsageEventBuilder.setTaskRootPackageName(taskRootPackageName);
243         }
244 
245         final String effectivePackageName =
246                 getEffectivePackageName(
247                         context, usageStatsManager, packageName, taskRootPackageName);
248         try {
249             final long uid = context
250                     .getPackageManager()
251                     .getPackageUidAsUser(effectivePackageName, (int) userId);
252             appUsageEventBuilder.setUid(uid);
253         } catch (PackageManager.NameNotFoundException e) {
254             Log.w(TAG, String.format(
255                     "Fail to get uid for package %s of user %d)", event.getPackageName(), userId));
256             return null;
257         }
258 
259         try {
260             appUsageEventBuilder.setInstanceId(event.getInstanceId());
261         } catch (NoClassDefFoundError | NoSuchMethodError e) {
262             Log.w(TAG, "UsageEvent instance ID API error");
263         }
264 
265         return appUsageEventBuilder.build();
266     }
267 
268     /** Converts from {@link Cursor} to {@link AppUsageEvent} */
convertToAppUsageEvent(final Cursor cursor)269     public static AppUsageEvent convertToAppUsageEvent(final Cursor cursor) {
270         final AppUsageEvent.Builder eventBuilder = AppUsageEvent.newBuilder();
271         eventBuilder.setTimestamp(getLongFromCursor(cursor, AppUsageEventEntity.KEY_TIMESTAMP));
272         eventBuilder.setType(
273                 AppUsageEventType.forNumber(
274                         getIntegerFromCursor(
275                                 cursor, AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE)));
276         eventBuilder.setPackageName(
277                 getStringFromCursor(cursor, AppUsageEventEntity.KEY_PACKAGE_NAME));
278         eventBuilder.setInstanceId(
279                 getIntegerFromCursor(cursor, AppUsageEventEntity.KEY_INSTANCE_ID));
280         eventBuilder.setTaskRootPackageName(
281                 getStringFromCursor(cursor, AppUsageEventEntity.KEY_TASK_ROOT_PACKAGE_NAME));
282         eventBuilder.setUserId(getLongFromCursor(cursor, AppUsageEventEntity.KEY_USER_ID));
283         eventBuilder.setUid(getLongFromCursor(cursor, AppUsageEventEntity.KEY_UID));
284         return eventBuilder.build();
285     }
286 
287     /** Converts from {@link BatteryEventType} to {@link BatteryEvent} */
convertToBatteryEvent( long timestamp, BatteryEventType type, int batteryLevel)288     public static BatteryEvent convertToBatteryEvent(
289             long timestamp, BatteryEventType type, int batteryLevel) {
290         final BatteryEvent.Builder eventBuilder = BatteryEvent.newBuilder();
291         eventBuilder.setTimestamp(timestamp);
292         eventBuilder.setType(type);
293         eventBuilder.setBatteryLevel(batteryLevel);
294         return eventBuilder.build();
295     }
296 
297     /** Converts from {@link Cursor} to {@link BatteryEvent} */
convertToBatteryEvent(final Cursor cursor)298     public static BatteryEvent convertToBatteryEvent(final Cursor cursor) {
299         final BatteryEvent.Builder eventBuilder = BatteryEvent.newBuilder();
300         eventBuilder.setTimestamp(getLongFromCursor(cursor, BatteryEventEntity.KEY_TIMESTAMP));
301         eventBuilder.setType(
302                 BatteryEventType.forNumber(
303                         getIntegerFromCursor(
304                                 cursor, BatteryEventEntity.KEY_BATTERY_EVENT_TYPE)));
305         eventBuilder.setBatteryLevel(
306                 getIntegerFromCursor(cursor, BatteryEventEntity.KEY_BATTERY_LEVEL));
307         return eventBuilder.build();
308     }
309 
310     /** Converts from {@link BatteryLevelData} to {@link List<BatteryEvent>} */
convertToBatteryEventList( final BatteryLevelData batteryLevelData)311     public static List<BatteryEvent> convertToBatteryEventList(
312             final BatteryLevelData batteryLevelData) {
313         final List<BatteryEvent> batteryEventList = new ArrayList<>();
314         final List<BatteryLevelData.PeriodBatteryLevelData> levelDataList =
315                 batteryLevelData.getHourlyBatteryLevelsPerDay();
316         for (BatteryLevelData.PeriodBatteryLevelData oneDayData : levelDataList) {
317             for (int hourIndex = 0; hourIndex < oneDayData.getLevels().size() - 1; hourIndex++) {
318                 batteryEventList.add(convertToBatteryEvent(
319                         oneDayData.getTimestamps().get(hourIndex),
320                         BatteryEventType.EVEN_HOUR,
321                         oneDayData.getLevels().get(hourIndex)));
322             }
323         }
324         return batteryEventList;
325     }
326 
327     /** Converts from {@link Cursor} to {@link BatteryUsageSlot} */
convertToBatteryUsageSlot(final Cursor cursor)328     public static BatteryUsageSlot convertToBatteryUsageSlot(final Cursor cursor) {
329         final BatteryUsageSlot defaultInstance = BatteryUsageSlot.getDefaultInstance();
330         final int columnIndex =
331                 cursor.getColumnIndex(BatteryUsageSlotEntity.KEY_BATTERY_USAGE_SLOT);
332         return columnIndex < 0 ? defaultInstance : BatteryUtils.parseProtoFromString(
333                 cursor.getString(columnIndex), defaultInstance);
334     }
335 
336     /** Converts from {@link Map<Long, BatteryDiffData>} to {@link List<BatteryUsageSlot>} */
convertToBatteryUsageSlotList( final Map<Long, BatteryDiffData> batteryDiffDataMap)337     public static List<BatteryUsageSlot> convertToBatteryUsageSlotList(
338             final Map<Long, BatteryDiffData> batteryDiffDataMap) {
339         List<BatteryUsageSlot> batteryUsageSlotList = new ArrayList<>();
340         for (BatteryDiffData batteryDiffData : batteryDiffDataMap.values()) {
341             batteryUsageSlotList.add(convertToBatteryUsageSlot(batteryDiffData));
342         }
343         return batteryUsageSlotList;
344     }
345 
346     /** Converts UTC timestamp to local time string for logging only, so use the US locale for
347      *  better readability in debugging. */
utcToLocalTimeForLogging(long timestamp)348     public static String utcToLocalTimeForLogging(long timestamp) {
349         final Locale locale = Locale.US;
350         final String pattern =
351                 DateFormat.getBestDateTimePattern(locale, "MMM dd,yyyy HH:mm:ss");
352         return DateFormat.format(pattern, timestamp).toString();
353     }
354 
355     /** Converts UTC timestamp to local time hour data. */
utcToLocalTimeHour(final Context context, final long timestamp, final boolean is24HourFormat, final boolean showMinute)356     public static String utcToLocalTimeHour(final Context context, final long timestamp,
357             final boolean is24HourFormat, final boolean showMinute) {
358         final Locale locale = getLocale(context);
359         // e.g. for 12-hour format: 9 PM
360         // e.g. for 24-hour format: 09:00
361         final String skeleton = is24HourFormat ? "HHm" : (showMinute ? "hma" : "ha");
362         final String pattern = DateFormat.getBestDateTimePattern(locale, skeleton);
363         return DateFormat.format(pattern, timestamp).toString();
364     }
365 
366     /** Converts UTC timestamp to local time day of week data. */
utcToLocalTimeDayOfWeek( final Context context, final long timestamp, final boolean isAbbreviation)367     public static String utcToLocalTimeDayOfWeek(
368             final Context context, final long timestamp, final boolean isAbbreviation) {
369         final Locale locale = getLocale(context);
370         final String pattern = DateFormat.getBestDateTimePattern(locale,
371                 isAbbreviation ? "E" : "EEEE");
372         return DateFormat.format(pattern, timestamp).toString();
373     }
374 
375     @VisibleForTesting
getLocale(Context context)376     static Locale getLocale(Context context) {
377         if (context == null) {
378             return Locale.getDefault();
379         }
380         final LocaleList locales =
381                 context.getResources().getConfiguration().getLocales();
382         return locales != null && !locales.isEmpty() ? locales.get(0)
383                 : Locale.getDefault();
384     }
385 
386     /**
387      * Returns the package name the app usage should be attributed to.
388      *
389      * <ul>
390      *   <li>If {@link UsageStatsManager#getUsageSource()} returns {@link
391      *       UsageStatsManager#USAGE_SOURCE_CURRENT_ACTIVITY}, this method will return packageName.
392      *   <li>If {@link UsageStatsManager#getUsageSource()} returns {@link
393      *       UsageStatsManager#USAGE_SOURCE_TASK_ROOT_ACTIVITY}, this method will return
394      *       taskRootPackageName if it exists, or packageName otherwise.
395      * </ul>
396      */
397     @VisibleForTesting
getEffectivePackageName( Context context, IUsageStatsManager usageStatsManager, final String packageName, final String taskRootPackageName)398     static String getEffectivePackageName(
399             Context context, IUsageStatsManager usageStatsManager, final String packageName,
400             final String taskRootPackageName) {
401         final int usageSource = getUsageSource(context, usageStatsManager);
402         switch (usageSource) {
403             case UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY:
404                 return !TextUtils.isEmpty(taskRootPackageName)
405                         ? taskRootPackageName
406                         : packageName;
407             case UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY:
408                 return packageName;
409             default:
410                 Log.e(TAG, "Unexpected usage source: " + usageSource);
411                 return packageName;
412         }
413     }
414 
415     /**
416      * Returns the package name of the task root when this event was reported when {@code event} is
417      * one of:
418      *
419      * <ul>
420      *   <li>{@link Event#ACTIVITY_RESUMED}
421      *   <li>{@link Event#ACTIVITY_STOPPED}
422      * </ul>
423      */
424     @Nullable
getTaskRootPackageName(Event event)425     private static String getTaskRootPackageName(Event event) {
426         int eventType = event.getEventType();
427         if (eventType != Event.ACTIVITY_RESUMED && eventType != Event.ACTIVITY_STOPPED) {
428             // Task root is only relevant for ACTIVITY_* events.
429             return null;
430         }
431 
432         try {
433             String taskRootPackageName = event.getTaskRootPackageName();
434             if (taskRootPackageName == null) {
435                 Log.w(TAG, String.format(
436                         "Null task root in event with timestamp %d, type=%d, package %s",
437                         event.getTimeStamp(), event.getEventType(), event.getPackageName()));
438             }
439             return taskRootPackageName;
440         } catch (NoSuchMethodError e) {
441             Log.w(TAG, "Failed to call Event#getTaskRootPackageName()");
442             return null;
443         }
444     }
445 
getUsageSource(Context context, IUsageStatsManager usageStatsManager)446     private static int getUsageSource(Context context, IUsageStatsManager usageStatsManager) {
447         if (sUsageSource == EMPTY_USAGE_SOURCE) {
448             sUsageSource = DatabaseUtils.getUsageSource(context, usageStatsManager);
449         }
450         return sUsageSource;
451     }
452 
getAppUsageEventType(final int eventType)453     private static AppUsageEventType getAppUsageEventType(final int eventType) {
454         switch (eventType) {
455             case Event.ACTIVITY_RESUMED:
456                 return AppUsageEventType.ACTIVITY_RESUMED;
457             case Event.ACTIVITY_STOPPED:
458                 return AppUsageEventType.ACTIVITY_STOPPED;
459             case Event.DEVICE_SHUTDOWN:
460                 return AppUsageEventType.DEVICE_SHUTDOWN;
461             default:
462                 return AppUsageEventType.UNKNOWN;
463         }
464     }
465 
convertToBatteryUsageDiff(BatteryDiffEntry batteryDiffEntry)466     private static BatteryUsageDiff convertToBatteryUsageDiff(BatteryDiffEntry batteryDiffEntry) {
467         BatteryUsageDiff.Builder builder = BatteryUsageDiff.newBuilder()
468                 .setUid(batteryDiffEntry.mUid)
469                 .setUserId(batteryDiffEntry.mUserId)
470                 .setIsHidden(batteryDiffEntry.mIsHidden)
471                 .setComponentId(batteryDiffEntry.mComponentId)
472                 .setConsumerType(batteryDiffEntry.mConsumerType)
473                 .setConsumePower(batteryDiffEntry.mConsumePower)
474                 .setForegroundUsageConsumePower(batteryDiffEntry.mForegroundUsageConsumePower)
475                 .setBackgroundUsageConsumePower(batteryDiffEntry.mBackgroundUsageConsumePower)
476                 .setForegroundServiceUsageConsumePower(
477                         batteryDiffEntry.mForegroundServiceUsageConsumePower)
478                 .setCachedUsageConsumePower(batteryDiffEntry.mCachedUsageConsumePower)
479                 .setForegroundUsageTime(batteryDiffEntry.mForegroundUsageTimeInMs)
480                 .setBackgroundUsageTime(batteryDiffEntry.mBackgroundUsageTimeInMs)
481                 .setScreenOnTime(batteryDiffEntry.mScreenOnTimeInMs);
482         if (batteryDiffEntry.mKey != null) {
483             builder.setKey(batteryDiffEntry.mKey);
484         }
485         if (batteryDiffEntry.mLegacyPackageName != null) {
486             builder.setPackageName(batteryDiffEntry.mLegacyPackageName);
487         }
488         if (batteryDiffEntry.mLegacyLabel != null) {
489             builder.setLabel(batteryDiffEntry.mLegacyLabel);
490         }
491         return builder.build();
492     }
493 
convertToBatteryUsageSlot( final BatteryDiffData batteryDiffData)494     private static BatteryUsageSlot convertToBatteryUsageSlot(
495             final BatteryDiffData batteryDiffData) {
496         if (batteryDiffData == null) {
497             return BatteryUsageSlot.getDefaultInstance();
498         }
499         final BatteryUsageSlot.Builder builder = BatteryUsageSlot.newBuilder()
500                 .setStartTimestamp(batteryDiffData.getStartTimestamp())
501                 .setEndTimestamp(batteryDiffData.getEndTimestamp())
502                 .setStartBatteryLevel(batteryDiffData.getStartBatteryLevel())
503                 .setEndBatteryLevel(batteryDiffData.getEndBatteryLevel())
504                 .setScreenOnTime(batteryDiffData.getScreenOnTime());
505         for (BatteryDiffEntry batteryDiffEntry : batteryDiffData.getAppDiffEntryList()) {
506             builder.addAppUsage(convertToBatteryUsageDiff(batteryDiffEntry));
507         }
508         for (BatteryDiffEntry batteryDiffEntry : batteryDiffData.getSystemDiffEntryList()) {
509             builder.addSystemUsage(convertToBatteryUsageDiff(batteryDiffEntry));
510         }
511         return builder.build();
512     }
513 
convertToBatteryDiffEntry( Context context, final BatteryUsageDiff batteryUsageDiff)514     private static BatteryDiffEntry convertToBatteryDiffEntry(
515             Context context, final BatteryUsageDiff batteryUsageDiff) {
516         return new BatteryDiffEntry(
517                 context,
518                 batteryUsageDiff.getUid(),
519                 batteryUsageDiff.getUserId(),
520                 batteryUsageDiff.getKey(),
521                 batteryUsageDiff.getIsHidden(),
522                 batteryUsageDiff.getComponentId(),
523                 batteryUsageDiff.getPackageName(),
524                 batteryUsageDiff.getLabel(),
525                 batteryUsageDiff.getConsumerType(),
526                 batteryUsageDiff.getForegroundUsageTime(),
527                 batteryUsageDiff.getBackgroundUsageTime(),
528                 batteryUsageDiff.getScreenOnTime(),
529                 batteryUsageDiff.getConsumePower(),
530                 batteryUsageDiff.getForegroundUsageConsumePower(),
531                 batteryUsageDiff.getForegroundServiceUsageConsumePower(),
532                 batteryUsageDiff.getBackgroundUsageConsumePower(),
533                 batteryUsageDiff.getCachedUsageConsumePower());
534     }
535 
convertToBatteryDiffData( Context context, final BatteryUsageSlot batteryUsageSlot, @NonNull final Set<String> systemAppsPackageNames, @NonNull final Set<Integer> systemAppsUids)536     static BatteryDiffData convertToBatteryDiffData(
537             Context context,
538             final BatteryUsageSlot batteryUsageSlot,
539             @NonNull final Set<String> systemAppsPackageNames,
540             @NonNull final Set<Integer> systemAppsUids) {
541         final List<BatteryDiffEntry> appDiffEntries = new ArrayList<>();
542         final List<BatteryDiffEntry> systemDiffEntries = new ArrayList<>();
543         for (BatteryUsageDiff batteryUsageDiff : batteryUsageSlot.getAppUsageList()) {
544             appDiffEntries.add(convertToBatteryDiffEntry(context, batteryUsageDiff));
545         }
546         for (BatteryUsageDiff batteryUsageDiff : batteryUsageSlot.getSystemUsageList()) {
547             systemDiffEntries.add(convertToBatteryDiffEntry(context, batteryUsageDiff));
548         }
549         return new BatteryDiffData(
550                 context,
551                 batteryUsageSlot.getStartTimestamp(),
552                 batteryUsageSlot.getEndTimestamp(),
553                 batteryUsageSlot.getStartBatteryLevel(),
554                 batteryUsageSlot.getEndBatteryLevel(),
555                 batteryUsageSlot.getScreenOnTime(),
556                 appDiffEntries,
557                 systemDiffEntries,
558                 systemAppsPackageNames,
559                 systemAppsUids,
560                 /*isAccumulated=*/ false);
561     }
562 
constructBatteryInformation( final BatteryEntry entry, final BatteryUsageStats batteryUsageStats, final int batteryLevel, final int batteryStatus, final int batteryHealth, final long bootTimestamp)563     private static BatteryInformation constructBatteryInformation(
564             final BatteryEntry entry,
565             final BatteryUsageStats batteryUsageStats,
566             final int batteryLevel,
567             final int batteryStatus,
568             final int batteryHealth,
569             final long bootTimestamp) {
570         final DeviceBatteryState deviceBatteryState =
571                 DeviceBatteryState
572                         .newBuilder()
573                         .setBatteryLevel(batteryLevel)
574                         .setBatteryStatus(batteryStatus)
575                         .setBatteryHealth(batteryHealth)
576                         .build();
577         final BatteryInformation.Builder batteryInformationBuilder =
578                 BatteryInformation
579                         .newBuilder()
580                         .setDeviceBatteryState(deviceBatteryState)
581                         .setBootTimestamp(bootTimestamp)
582                         .setZoneId(TimeZone.getDefault().getID());
583         if (entry != null && batteryUsageStats != null) {
584             batteryInformationBuilder
585                     .setIsHidden(entry.isHidden())
586                     .setAppLabel(entry.getLabel() != null ? entry.getLabel() : "")
587                     .setTotalPower(batteryUsageStats.getConsumedPower())
588                     .setConsumePower(entry.getConsumedPower())
589                     .setForegroundUsageConsumePower(entry.getConsumedPowerInForeground())
590                     .setForegroundServiceUsageConsumePower(
591                             entry.getConsumedPowerInForegroundService())
592                     .setBackgroundUsageConsumePower(entry.getConsumedPowerInBackground())
593                     .setCachedUsageConsumePower(entry.getConsumedPowerInCached())
594                     .setPercentOfTotal(entry.mPercent)
595                     .setDrainType(entry.getPowerComponentId())
596                     .setForegroundUsageTimeInMs(entry.getTimeInForegroundMs())
597                     .setBackgroundUsageTimeInMs(entry.getTimeInBackgroundMs());
598         }
599 
600         return batteryInformationBuilder.build();
601     }
602 
getIntegerFromCursor(final Cursor cursor, final String key)603     private static int getIntegerFromCursor(final Cursor cursor, final String key) {
604         final int columnIndex = cursor.getColumnIndex(key);
605         if (columnIndex >= 0) {
606             return cursor.getInt(columnIndex);
607         }
608         return 0;
609     }
610 
getLongFromCursor(final Cursor cursor, final String key)611     private static long getLongFromCursor(final Cursor cursor, final String key) {
612         final int columnIndex = cursor.getColumnIndex(key);
613         if (columnIndex >= 0) {
614             return cursor.getLong(columnIndex);
615         }
616         return 0L;
617     }
618 
getStringFromCursor(final Cursor cursor, final String key)619     private static String getStringFromCursor(final Cursor cursor, final String key) {
620         final int columnIndex = cursor.getColumnIndex(key);
621         if (columnIndex >= 0) {
622             return cursor.getString(columnIndex);
623         }
624         return "";
625     }
626 }
627