• 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.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.os.Build;
23 import android.os.SystemClock;
24 import android.util.Log;
25 
26 import androidx.annotation.VisibleForTesting;
27 
28 import com.android.settings.fuelgauge.BatteryUtils;
29 import com.android.settings.overlay.FeatureFactory;
30 import com.android.settingslib.fuelgauge.BatteryStatus;
31 
32 import java.time.Duration;
33 import java.util.concurrent.ExecutorService;
34 import java.util.concurrent.Executors;
35 
36 /** A {@link BatteryUsageBroadcastReceiver} for battery usage data requesting. */
37 public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
38     private static final String TAG = "BatteryUsageBroadcastReceiver";
39 
40     /** An intent action to request Settings to clear cache data. */
41     public static final String ACTION_CLEAR_BATTERY_CACHE_DATA =
42             "com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA";
43 
44     /** An intent action for power is plugging. */
45     public static final String ACTION_BATTERY_PLUGGING =
46             "com.android.settings.battery.action.ACTION_BATTERY_PLUGGING";
47 
48     /** An intent action for power is unplugging. */
49     public static final String ACTION_BATTERY_UNPLUGGING =
50             "com.android.settings.battery.action.ACTION_BATTERY_UNPLUGGING";
51 
52     @VisibleForTesting static long sBroadcastDelayFromBoot = Duration.ofMinutes(40).toMillis();
53     @VisibleForTesting static boolean sIsDebugMode = Build.TYPE.equals("userdebug");
54 
55     @VisibleForTesting boolean mFetchBatteryUsageData = false;
56 
57     private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
58 
59     @Override
onReceive(Context context, Intent intent)60     public void onReceive(Context context, Intent intent) {
61         if (intent == null || intent.getAction() == null) {
62             return;
63         }
64         final String action = intent.getAction();
65         Log.d(TAG, "onReceive:" + action);
66         if (com.android.settingslib.fuelgauge.BatteryUtils.isAdditionalProfile(context)) {
67             Log.w(TAG, "do nothing for an additional profile action=" + action);
68             return;
69         }
70         DatabaseUtils.recordDateTime(context, action);
71         final String fullChargeIntentAction =
72                 FeatureFactory.getFeatureFactory()
73                         .getPowerUsageFeatureProvider()
74                         .getFullChargeIntentAction();
75         switch (action) {
76             case Intent.ACTION_BATTERY_LEVEL_CHANGED:
77                 // Only when fullChargeIntentAction is ACTION_BATTERY_LEVEL_CHANGED,
78                 // ACTION_BATTERY_LEVEL_CHANGED will be considered as the full charge event and then
79                 // start usage events fetching.
80                 if (Intent.ACTION_BATTERY_LEVEL_CHANGED.equals(fullChargeIntentAction)) {
81                     Log.d(TAG, "fetch data because of event: ACTION_BATTERY_LEVEL_CHANGED");
82                     tryToFetchUsageData(context);
83                 }
84                 break;
85             case ACTION_BATTERY_PLUGGING:
86                 sendBatteryEventData(context, BatteryEventType.POWER_CONNECTED);
87                 break;
88             case ACTION_BATTERY_UNPLUGGING:
89                 sendBatteryEventData(context, BatteryEventType.POWER_DISCONNECTED);
90                 // Only when fullChargeIntentAction is ACTION_POWER_DISCONNECTED,
91                 // ACTION_BATTERY_UNPLUGGING will be considered as the full charge event and then
92                 // start usage events fetching.
93                 if (Intent.ACTION_POWER_DISCONNECTED.equals(fullChargeIntentAction)) {
94                     Log.d(TAG, "fetch data because of event: ACTION_POWER_DISCONNECTED");
95                     tryToFetchUsageData(context);
96                 }
97                 break;
98             case ACTION_CLEAR_BATTERY_CACHE_DATA:
99                 if (sIsDebugMode) {
100                     BatteryDiffEntry.clearCache();
101                     BatteryEntry.clearUidCache();
102                 }
103                 break;
104         }
105     }
106 
tryToFetchUsageData(Context context)107     private void tryToFetchUsageData(Context context) {
108         final Intent batteryIntent = BatteryUtils.getBatteryIntent(context);
109         if (batteryIntent == null) {
110             Log.w(TAG, "tryToFetchUsageData: ignore from null battery intent");
111             return;
112         }
113         // Returns when battery is not fully charged.
114         if (!BatteryStatus.isCharged(batteryIntent)) {
115             return;
116         }
117 
118         final boolean delayHourlyJobWhenBooting =
119                 FeatureFactory.getFeatureFactory()
120                         .getPowerUsageFeatureProvider()
121                         .delayHourlyJobWhenBooting();
122         final long broadcastDelay = sBroadcastDelayFromBoot - SystemClock.elapsedRealtime();
123         // If current boot time is smaller than expected delay, cancel sending the broadcast.
124         if (delayHourlyJobWhenBooting && broadcastDelay > 0) {
125             Log.d(
126                     TAG,
127                     "cancel sendBroadcastToFetchUsageData when broadcastDelay is "
128                             + broadcastDelay
129                             + "ms.");
130             return;
131         }
132 
133         mFetchBatteryUsageData = true;
134         BatteryUsageDataLoader.enqueueWork(context, /* isFullChargeStart= */ true);
135         BootBroadcastReceiver.invokeJobRecheck(context);
136     }
137 
sendBatteryEventData(Context context, BatteryEventType batteryEventType)138     private void sendBatteryEventData(Context context, BatteryEventType batteryEventType) {
139         final long timestamp = System.currentTimeMillis();
140         final Intent intent = BatteryUtils.getBatteryIntent(context);
141         final int batteryLevel = BatteryStatus.getBatteryLevel(intent);
142         mExecutor.execute(
143                 () ->
144                         DatabaseUtils.sendBatteryEventData(
145                                 context,
146                                 ConvertUtils.convertToBatteryEvent(
147                                         timestamp, batteryEventType, batteryLevel)));
148     }
149 }
150