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 17 package com.android.settings.fuelgauge.batteryusage; 18 19 import android.app.usage.UsageEvents; 20 import android.content.Context; 21 import android.os.AsyncTask; 22 import android.os.BatteryUsageStats; 23 import android.util.Log; 24 25 import androidx.annotation.VisibleForTesting; 26 27 import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action; 28 import com.android.settings.fuelgauge.PowerUsageFeatureProvider; 29 import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils; 30 import com.android.settings.overlay.FeatureFactory; 31 32 import java.io.IOException; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.function.Supplier; 36 37 /** Load battery usage data in the background. */ 38 public final class BatteryUsageDataLoader { 39 private static final String TAG = "BatteryUsageDataLoader"; 40 41 // For testing only. 42 @VisibleForTesting static Supplier<List<BatteryEntry>> sFakeBatteryEntryListSupplier; 43 @VisibleForTesting static Supplier<Map<Long, UsageEvents>> sFakeAppUsageEventsSupplier; 44 @VisibleForTesting static Supplier<List<AppUsageEvent>> sFakeUsageEventsListSupplier; 45 BatteryUsageDataLoader()46 private BatteryUsageDataLoader() {} 47 enqueueWork(final Context context, final boolean isFullChargeStart)48 static void enqueueWork(final Context context, final boolean isFullChargeStart) { 49 AsyncTask.execute( 50 () -> { 51 Log.d(TAG, "loadUsageDataSafely() in the AsyncTask"); 52 loadUsageDataSafely(context.getApplicationContext(), isFullChargeStart); 53 }); 54 } 55 56 @VisibleForTesting loadBatteryStatsData(final Context context, final boolean isFullChargeStart)57 static void loadBatteryStatsData(final Context context, final boolean isFullChargeStart) { 58 BatteryUsageLogUtils.writeLog(context, Action.FETCH_USAGE_DATA, ""); 59 final long currentTime = System.currentTimeMillis(); 60 try (BatteryUsageStats batteryUsageStats = DataProcessor.getBatteryUsageStats(context)) { 61 final List<BatteryEntry> batteryEntryList = 62 sFakeBatteryEntryListSupplier != null 63 ? sFakeBatteryEntryListSupplier.get() 64 : DataProcessor.generateBatteryEntryListFromBatteryUsageStats( 65 context, batteryUsageStats); 66 if (batteryEntryList == null || batteryEntryList.isEmpty()) { 67 Log.w(TAG, "getBatteryEntryList() returns null or empty content"); 68 } 69 final long elapsedTime = System.currentTimeMillis() - currentTime; 70 Log.d(TAG, String.format("getBatteryUsageStats() in %d/ms", elapsedTime)); 71 if (isFullChargeStart) { 72 DatabaseUtils.recordDateTime(context, DatabaseUtils.KEY_LAST_LOAD_FULL_CHARGE_TIME); 73 DatabaseUtils.sendBatteryEventData( 74 context, 75 ConvertUtils.convertToBatteryEvent( 76 currentTime, BatteryEventType.FULL_CHARGED, 100)); 77 DatabaseUtils.removeDismissedPowerAnomalyKeys(context); 78 } 79 80 // Uploads the BatteryEntry data into database. 81 DatabaseUtils.sendBatteryEntryData( 82 context, currentTime, batteryEntryList, batteryUsageStats, isFullChargeStart); 83 } catch (IOException e) { 84 Log.e(TAG, "loadBatteryStatsData:", e); 85 } 86 } 87 88 @VisibleForTesting loadAppUsageData(final Context context, final UserIdsSeries userIdsSeries)89 static void loadAppUsageData(final Context context, final UserIdsSeries userIdsSeries) { 90 final long start = System.currentTimeMillis(); 91 final Map<Long, UsageEvents> appUsageEvents = 92 sFakeAppUsageEventsSupplier != null 93 ? sFakeAppUsageEventsSupplier.get() 94 : DataProcessor.getAppUsageEvents(context, userIdsSeries); 95 if (appUsageEvents == null) { 96 Log.w(TAG, "loadAppUsageData() returns null"); 97 return; 98 } 99 final List<AppUsageEvent> appUsageEventList = 100 sFakeUsageEventsListSupplier != null 101 ? sFakeUsageEventsListSupplier.get() 102 : DataProcessor.generateAppUsageEventListFromUsageEvents( 103 context, appUsageEvents); 104 if (appUsageEventList == null || appUsageEventList.isEmpty()) { 105 Log.w(TAG, "loadAppUsageData() returns null or empty content"); 106 return; 107 } 108 final long elapsedTime = System.currentTimeMillis() - start; 109 Log.d( 110 TAG, 111 String.format( 112 "loadAppUsageData() size=%d in %d/ms", 113 appUsageEventList.size(), elapsedTime)); 114 // Uploads the AppUsageEvent data into database. 115 DatabaseUtils.sendAppUsageEventData(context, appUsageEventList); 116 } 117 preprocessBatteryUsageSlots( final Context context, final UserIdsSeries userIdsSeries)118 private static void preprocessBatteryUsageSlots( 119 final Context context, final UserIdsSeries userIdsSeries) { 120 final long start = System.currentTimeMillis(); 121 final BatteryLevelData batteryLevelData = 122 DataProcessManager.getBatteryLevelData( 123 context, 124 null, 125 userIdsSeries, 126 /* isFromPeriodJob= */ true, 127 batteryDiffDataMap -> { 128 final PowerUsageFeatureProvider featureProvider = 129 FeatureFactory.getFeatureFactory() 130 .getPowerUsageFeatureProvider(); 131 DatabaseUtils.sendBatteryUsageSlotData( 132 context, 133 ConvertUtils.convertToBatteryUsageSlotList( 134 context, 135 batteryDiffDataMap, 136 featureProvider.isAppOptimizationModeLogged())); 137 if (batteryDiffDataMap.values().stream() 138 .anyMatch( 139 data -> 140 data != null 141 && (!data.getSystemDiffEntryList() 142 .isEmpty() 143 || !data.getAppDiffEntryList() 144 .isEmpty()))) { 145 featureProvider.detectPowerAnomaly( 146 context, 147 /* displayDrain= */ 0, 148 DetectRequestSourceType.TYPE_DATA_LOADER); 149 } 150 }); 151 if (batteryLevelData == null) { 152 Log.d(TAG, "preprocessBatteryUsageSlots() no new battery usage data."); 153 return; 154 } 155 156 DatabaseUtils.sendBatteryEventData( 157 context, ConvertUtils.convertToBatteryEventList(batteryLevelData)); 158 Log.d( 159 TAG, 160 String.format( 161 "preprocessBatteryUsageSlots() batteryLevelData=%s in %d/ms", 162 batteryLevelData, System.currentTimeMillis() - start)); 163 } 164 loadUsageDataSafely( final Context context, final boolean isFullChargeStart)165 private static void loadUsageDataSafely( 166 final Context context, final boolean isFullChargeStart) { 167 try { 168 final long start = System.currentTimeMillis(); 169 loadBatteryStatsData(context, isFullChargeStart); 170 AppOptModeSharedPreferencesUtils.resetExpiredAppOptModeBeforeTimestamp( 171 context, System.currentTimeMillis()); 172 if (!isFullChargeStart) { 173 // No app usage data or battery diff data at this time. 174 final UserIdsSeries userIdsSeries = 175 new UserIdsSeries(context, /* isNonUIRequest= */ true); 176 if (!userIdsSeries.isCurrentUserLocked()) { 177 loadAppUsageData(context, userIdsSeries); 178 preprocessBatteryUsageSlots(context, userIdsSeries); 179 } 180 } 181 Log.d( 182 TAG, 183 String.format( 184 "loadUsageDataSafely() in %d/ms", System.currentTimeMillis() - start)); 185 } catch (RuntimeException e) { 186 Log.e(TAG, "loadUsageData:", e); 187 } 188 } 189 } 190