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