• 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 
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