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