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.os.Handler; 24 import android.os.Looper; 25 import android.util.Log; 26 27 import androidx.annotation.VisibleForTesting; 28 29 import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action; 30 import com.android.settings.fuelgauge.PowerUsageFeatureProvider; 31 import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils; 32 import com.android.settings.overlay.FeatureFactory; 33 34 import java.util.List; 35 import java.util.Map; 36 import java.util.function.Supplier; 37 38 /** Load battery usage data in the background. */ 39 public final class BatteryUsageDataLoader { 40 private static final String TAG = "BatteryUsageDataLoader"; 41 42 // For testing only. 43 @VisibleForTesting static Supplier<List<BatteryEntry>> sFakeBatteryEntryListSupplier; 44 @VisibleForTesting static Supplier<Map<Long, UsageEvents>> sFakeAppUsageEventsSupplier; 45 @VisibleForTesting static Supplier<List<AppUsageEvent>> sFakeUsageEventsListSupplier; 46 BatteryUsageDataLoader()47 private BatteryUsageDataLoader() {} 48 enqueueWork(final Context context, final boolean isFullChargeStart)49 static void enqueueWork(final Context context, final boolean isFullChargeStart) { 50 AsyncTask.execute( 51 () -> { 52 Log.d(TAG, "loadUsageDataSafely() in the AsyncTask"); 53 loadUsageDataSafely(context.getApplicationContext(), isFullChargeStart); 54 }); 55 } 56 57 @VisibleForTesting loadBatteryStatsData(final Context context, final boolean isFullChargeStart)58 static void loadBatteryStatsData(final Context context, final boolean isFullChargeStart) { 59 BatteryUsageLogUtils.writeLog(context, Action.FETCH_USAGE_DATA, ""); 60 final long currentTime = System.currentTimeMillis(); 61 final BatteryUsageStats batteryUsageStats = DataProcessor.getBatteryUsageStats(context); 62 final List<BatteryEntry> batteryEntryList = 63 sFakeBatteryEntryListSupplier != null 64 ? sFakeBatteryEntryListSupplier.get() 65 : DataProcessor.generateBatteryEntryListFromBatteryUsageStats( 66 context, batteryUsageStats); 67 if (batteryEntryList == null || batteryEntryList.isEmpty()) { 68 Log.w(TAG, "getBatteryEntryList() returns null or empty content"); 69 } 70 final long elapsedTime = System.currentTimeMillis() - currentTime; 71 Log.d(TAG, String.format("getBatteryUsageStats() in %d/ms", elapsedTime)); 72 if (isFullChargeStart) { 73 DatabaseUtils.recordDateTime(context, DatabaseUtils.KEY_LAST_LOAD_FULL_CHARGE_TIME); 74 DatabaseUtils.sendBatteryEventData( 75 context, 76 ConvertUtils.convertToBatteryEvent( 77 currentTime, BatteryEventType.FULL_CHARGED, 100)); 78 DatabaseUtils.removeDismissedPowerAnomalyKeys(context); 79 } 80 81 // Uploads the BatteryEntry data into database. 82 DatabaseUtils.sendBatteryEntryData( 83 context, currentTime, batteryEntryList, batteryUsageStats, isFullChargeStart); 84 DataProcessor.closeBatteryUsageStats(batteryUsageStats); 85 } 86 87 @VisibleForTesting loadAppUsageData(final Context context, final UserIdsSeries userIdsSeries)88 static void loadAppUsageData(final Context context, final UserIdsSeries userIdsSeries) { 89 final long start = System.currentTimeMillis(); 90 final Map<Long, UsageEvents> appUsageEvents = 91 sFakeAppUsageEventsSupplier != null 92 ? sFakeAppUsageEventsSupplier.get() 93 : DataProcessor.getAppUsageEvents(context, userIdsSeries); 94 if (appUsageEvents == null) { 95 Log.w(TAG, "loadAppUsageData() returns null"); 96 return; 97 } 98 final List<AppUsageEvent> appUsageEventList = 99 sFakeUsageEventsListSupplier != null 100 ? sFakeUsageEventsListSupplier.get() 101 : DataProcessor.generateAppUsageEventListFromUsageEvents( 102 context, appUsageEvents); 103 if (appUsageEventList == null || appUsageEventList.isEmpty()) { 104 Log.w(TAG, "loadAppUsageData() returns null or empty content"); 105 return; 106 } 107 final long elapsedTime = System.currentTimeMillis() - start; 108 Log.d( 109 TAG, 110 String.format( 111 "loadAppUsageData() size=%d in %d/ms", 112 appUsageEventList.size(), elapsedTime)); 113 // Uploads the AppUsageEvent data into database. 114 DatabaseUtils.sendAppUsageEventData(context, appUsageEventList); 115 } 116 preprocessBatteryUsageSlots( final Context context, final UserIdsSeries userIdsSeries)117 private static void preprocessBatteryUsageSlots( 118 final Context context, final UserIdsSeries userIdsSeries) { 119 final long start = System.currentTimeMillis(); 120 final Handler handler = new Handler(Looper.getMainLooper()); 121 final BatteryLevelData batteryLevelData = 122 DataProcessManager.getBatteryLevelData( 123 context, 124 handler, 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