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.isWorkProfile(context)) { 67 Log.w(TAG, "do nothing for work 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 // Returns when battery is not fully charged. 110 if (!BatteryStatus.isCharged(batteryIntent)) { 111 return; 112 } 113 114 final boolean delayHourlyJobWhenBooting = 115 FeatureFactory.getFeatureFactory() 116 .getPowerUsageFeatureProvider() 117 .delayHourlyJobWhenBooting(); 118 final long broadcastDelay = sBroadcastDelayFromBoot - SystemClock.elapsedRealtime(); 119 // If current boot time is smaller than expected delay, cancel sending the broadcast. 120 if (delayHourlyJobWhenBooting && broadcastDelay > 0) { 121 Log.d( 122 TAG, 123 "cancel sendBroadcastToFetchUsageData when broadcastDelay is " 124 + broadcastDelay 125 + "ms."); 126 return; 127 } 128 129 mFetchBatteryUsageData = true; 130 BatteryUsageDataLoader.enqueueWork(context, /* isFullChargeStart= */ true); 131 BootBroadcastReceiver.invokeJobRecheck(context); 132 } 133 sendBatteryEventData(Context context, BatteryEventType batteryEventType)134 private void sendBatteryEventData(Context context, BatteryEventType batteryEventType) { 135 final long timestamp = System.currentTimeMillis(); 136 final Intent intent = BatteryUtils.getBatteryIntent(context); 137 final int batteryLevel = BatteryStatus.getBatteryLevel(intent); 138 mExecutor.execute( 139 () -> 140 DatabaseUtils.sendBatteryEventData( 141 context, 142 ConvertUtils.convertToBatteryEvent( 143 timestamp, batteryEventType, batteryLevel))); 144 } 145 } 146