1 /* 2 * Copyright (C) 2019 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.settingslib.fuelgauge; 18 19 import static android.os.BatteryManager.BATTERY_STATUS_FULL; 20 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; 21 import static android.os.BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE; 22 import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT; 23 import static android.os.BatteryManager.EXTRA_CHARGING_STATUS; 24 import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT; 25 import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE; 26 import static android.os.BatteryManager.EXTRA_PLUGGED; 27 import static android.os.BatteryManager.EXTRA_PRESENT; 28 import static android.os.BatteryManager.EXTRA_STATUS; 29 import static android.os.OsProtoEnums.BATTERY_PLUGGED_NONE; 30 31 import android.content.Context; 32 import android.content.Intent; 33 import android.os.BatteryManager; 34 35 import com.android.settingslib.R; 36 37 import java.util.Optional; 38 39 /** 40 * Stores and computes some battery information. 41 */ 42 public class BatteryStatus { 43 private static final int LOW_BATTERY_THRESHOLD = 20; 44 private static final int SEVERE_LOW_BATTERY_THRESHOLD = 10; 45 private static final int EXTREME_LOW_BATTERY_THRESHOLD = 3; 46 private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000; 47 48 public static final int BATTERY_LEVEL_UNKNOWN = -1; 49 public static final int CHARGING_UNKNOWN = -1; 50 public static final int CHARGING_SLOWLY = 0; 51 public static final int CHARGING_REGULAR = 1; 52 public static final int CHARGING_FAST = 2; 53 54 public final int status; 55 public final int level; 56 public final int plugged; 57 public final int chargingStatus; 58 public final int maxChargingWattage; 59 public final boolean present; 60 public final Optional<Boolean> incompatibleCharger; 61 create(Context context, boolean incompatibleCharger)62 public static BatteryStatus create(Context context, boolean incompatibleCharger) { 63 final Intent batteryChangedIntent = BatteryUtils.getBatteryIntent(context); 64 return batteryChangedIntent == null 65 ? null : new BatteryStatus(batteryChangedIntent, incompatibleCharger); 66 } 67 BatteryStatus(int status, int level, int plugged, int chargingStatus, int maxChargingWattage, boolean present)68 public BatteryStatus(int status, int level, int plugged, int chargingStatus, 69 int maxChargingWattage, boolean present) { 70 this.status = status; 71 this.level = level; 72 this.plugged = plugged; 73 this.chargingStatus = chargingStatus; 74 this.maxChargingWattage = maxChargingWattage; 75 this.present = present; 76 this.incompatibleCharger = Optional.empty(); 77 } 78 79 BatteryStatus(Intent batteryChangedIntent)80 public BatteryStatus(Intent batteryChangedIntent) { 81 this(batteryChangedIntent, Optional.empty()); 82 } 83 BatteryStatus(Intent batteryChangedIntent, boolean incompatibleCharger)84 public BatteryStatus(Intent batteryChangedIntent, boolean incompatibleCharger) { 85 this(batteryChangedIntent, Optional.of(incompatibleCharger)); 86 } 87 BatteryStatus(Intent batteryChangedIntent, Optional<Boolean> incompatibleCharger)88 private BatteryStatus(Intent batteryChangedIntent, Optional<Boolean> incompatibleCharger) { 89 status = batteryChangedIntent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN); 90 plugged = batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0); 91 level = getBatteryLevel(batteryChangedIntent); 92 chargingStatus = batteryChangedIntent.getIntExtra(EXTRA_CHARGING_STATUS, 93 CHARGING_POLICY_DEFAULT); 94 present = batteryChangedIntent.getBooleanExtra(EXTRA_PRESENT, true); 95 this.incompatibleCharger = incompatibleCharger; 96 97 maxChargingWattage = calculateMaxChargingMicroWatt(batteryChangedIntent); 98 } 99 100 /** Determine whether the device is plugged. */ isPluggedIn()101 public boolean isPluggedIn() { 102 return isPluggedIn(plugged); 103 } 104 105 /** Determine whether the device is plugged in (USB, power). */ isPluggedInWired()106 public boolean isPluggedInWired() { 107 return plugged == BatteryManager.BATTERY_PLUGGED_AC 108 || plugged == BatteryManager.BATTERY_PLUGGED_USB; 109 } 110 111 /** 112 * Determine whether the device is plugged in wireless. */ isPluggedInWireless()113 public boolean isPluggedInWireless() { 114 return plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS; 115 } 116 117 /** Determine whether the device is plugged in dock. */ isPluggedInDock()118 public boolean isPluggedInDock() { 119 return isPluggedInDock(plugged); 120 } 121 122 /** 123 * Whether or not the device is charged. Note that some devices never return 100% for 124 * battery level, so this allows either battery level or status to determine if the 125 * battery is charged. 126 */ isCharged()127 public boolean isCharged() { 128 return isCharged(status, level); 129 } 130 131 /** Whether battery is low and needs to be charged. */ isBatteryLow()132 public boolean isBatteryLow() { 133 return isLowBattery(level); 134 } 135 136 /** Whether battery defender is enabled. */ isBatteryDefender()137 public boolean isBatteryDefender() { 138 return isBatteryDefender(chargingStatus); 139 } 140 141 /** Return current charging speed is fast, slow or normal. */ getChargingSpeed(Context context)142 public final int getChargingSpeed(Context context) { 143 final int slowThreshold = context.getResources().getInteger( 144 R.integer.config_chargingSlowlyThreshold); 145 final int fastThreshold = context.getResources().getInteger( 146 R.integer.config_chargingFastThreshold); 147 return maxChargingWattage <= 0 ? CHARGING_UNKNOWN : 148 maxChargingWattage < slowThreshold ? CHARGING_SLOWLY : 149 maxChargingWattage > fastThreshold ? CHARGING_FAST : 150 CHARGING_REGULAR; 151 } 152 153 @Override toString()154 public String toString() { 155 return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged 156 + ",chargingStatus=" + chargingStatus + ",maxChargingWattage=" + maxChargingWattage 157 + "}"; 158 } 159 160 /** 161 * Whether or not the device is charged. Note that some devices never return 100% for 162 * battery level, so this allows either battery level or status to determine if the 163 * battery is charged. 164 * 165 * @param batteryChangedIntent ACTION_BATTERY_CHANGED intent 166 * @return true if the device is charged 167 */ isCharged(Intent batteryChangedIntent)168 public static boolean isCharged(Intent batteryChangedIntent) { 169 int status = batteryChangedIntent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN); 170 int level = getBatteryLevel(batteryChangedIntent); 171 return isCharged(status, level); 172 } 173 174 /** 175 * Whether or not the device is charged. Note that some devices never return 100% for 176 * battery level, so this allows either battery level or status to determine if the 177 * battery is charged. 178 * 179 * @param status values for "status" field in the ACTION_BATTERY_CHANGED Intent 180 * @param level values from 0 to 100 181 * @return true if the device is charged 182 */ isCharged(int status, int level)183 public static boolean isCharged(int status, int level) { 184 return status == BATTERY_STATUS_FULL || level >= 100; 185 } 186 187 /** Gets the battery level from the intent. */ getBatteryLevel(Intent batteryChangedIntent)188 public static int getBatteryLevel(Intent batteryChangedIntent) { 189 if (batteryChangedIntent == null) { 190 return BATTERY_LEVEL_UNKNOWN; 191 } 192 final int level = 193 batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, BATTERY_LEVEL_UNKNOWN); 194 final int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 0); 195 return scale == 0 196 ? BATTERY_LEVEL_UNKNOWN 197 : Math.round((level / (float) scale) * 100f); 198 } 199 200 /** Whether the device is plugged or not. */ isPluggedIn(Intent batteryChangedIntent)201 public static boolean isPluggedIn(Intent batteryChangedIntent) { 202 return isPluggedIn(batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0)); 203 } 204 205 /** Whether the device is plugged or not. */ isPluggedIn(int plugged)206 public static boolean isPluggedIn(int plugged) { 207 return plugged == BatteryManager.BATTERY_PLUGGED_AC 208 || plugged == BatteryManager.BATTERY_PLUGGED_USB 209 || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS 210 || plugged == BatteryManager.BATTERY_PLUGGED_DOCK; 211 } 212 213 /** Determine whether the device is plugged in dock. */ isPluggedInDock(Intent batteryChangedIntent)214 public static boolean isPluggedInDock(Intent batteryChangedIntent) { 215 return isPluggedInDock( 216 batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, BATTERY_PLUGGED_NONE)); 217 } 218 219 /** Determine whether the device is plugged in dock. */ isPluggedInDock(int plugged)220 public static boolean isPluggedInDock(int plugged) { 221 return plugged == BatteryManager.BATTERY_PLUGGED_DOCK; 222 } 223 224 /** 225 * Whether the battery is low or not. 226 * 227 * @param batteryChangedIntent the {@link ACTION_BATTERY_CHANGED} intent 228 * @return {@code true} if the battery level is less or equal to {@link LOW_BATTERY_THRESHOLD} 229 */ isLowBattery(Intent batteryChangedIntent)230 public static boolean isLowBattery(Intent batteryChangedIntent) { 231 int level = getBatteryLevel(batteryChangedIntent); 232 return isLowBattery(level); 233 } 234 235 /** 236 * Whether the battery is low or not. 237 * 238 * @param batteryLevel the battery level 239 * @return {@code true} if the battery level is less or equal to {@link LOW_BATTERY_THRESHOLD} 240 */ isLowBattery(int batteryLevel)241 public static boolean isLowBattery(int batteryLevel) { 242 return batteryLevel <= LOW_BATTERY_THRESHOLD; 243 } 244 245 /** 246 * Whether the battery is severe low or not. 247 * 248 * @param batteryChangedIntent the ACTION_BATTERY_CHANGED intent 249 * @return {@code true} if the battery level is less or equal to {@link 250 * SEVERE_LOW_BATTERY_THRESHOLD} 251 */ isSevereLowBattery(Intent batteryChangedIntent)252 public static boolean isSevereLowBattery(Intent batteryChangedIntent) { 253 int level = getBatteryLevel(batteryChangedIntent); 254 return level <= SEVERE_LOW_BATTERY_THRESHOLD; 255 } 256 257 /** 258 * Whether the battery is extreme low or not. 259 * 260 * @param batteryChangedIntent the ACTION_BATTERY_CHANGED intent 261 * @return {@code true} if the battery level is less or equal to {@link 262 * EXTREME_LOW_BATTERY_THRESHOLD} 263 */ isExtremeLowBattery(Intent batteryChangedIntent)264 public static boolean isExtremeLowBattery(Intent batteryChangedIntent) { 265 int level = getBatteryLevel(batteryChangedIntent); 266 return level <= EXTREME_LOW_BATTERY_THRESHOLD; 267 } 268 269 /** 270 * Whether the battery defender is enabled or not. 271 * 272 * @param batteryChangedIntent the ACTION_BATTERY_CHANGED intent 273 * @return {@code true} if the battery defender is enabled. It could be dock defend, dwell 274 * defend, or temp defend 275 */ isBatteryDefender(Intent batteryChangedIntent)276 public static boolean isBatteryDefender(Intent batteryChangedIntent) { 277 int chargingStatus = 278 batteryChangedIntent.getIntExtra(EXTRA_CHARGING_STATUS, CHARGING_POLICY_DEFAULT); 279 return isBatteryDefender(chargingStatus); 280 } 281 282 /** 283 * Whether the battery defender is enabled or not. 284 * 285 * @param chargingStatus for {@link EXTRA_CHARGING_STATUS} field in the ACTION_BATTERY_CHANGED 286 * intent 287 * @return {@code true} if the battery defender is enabled. It could be dock defend, dwell 288 * defend, or temp defend 289 */ isBatteryDefender(int chargingStatus)290 public static boolean isBatteryDefender(int chargingStatus) { 291 return chargingStatus == CHARGING_POLICY_ADAPTIVE_LONGLIFE; 292 } 293 294 /** 295 * Gets the max charging current and max charging voltage form {@link 296 * Intent.ACTION_BATTERY_CHANGED} and calculates the charging speed based on the {@link 297 * R.integer.config_chargingSlowlyThreshold} and {@link R.integer.config_chargingFastThreshold}. 298 * 299 * @param context the application context 300 * @param batteryChangedIntent the intent from {@link Intent.ACTION_BATTERY_CHANGED} 301 * @return the charging speed. {@link CHARGING_REGULAR}, {@link CHARGING_FAST}, {@link 302 * CHARGING_SLOWLY} or {@link CHARGING_UNKNOWN} 303 */ getChargingSpeed(Context context, Intent batteryChangedIntent)304 public static int getChargingSpeed(Context context, Intent batteryChangedIntent) { 305 final int maxChargingMicroWatt = calculateMaxChargingMicroWatt(batteryChangedIntent); 306 if (maxChargingMicroWatt <= 0) { 307 return CHARGING_UNKNOWN; 308 } else if (maxChargingMicroWatt 309 < context.getResources().getInteger(R.integer.config_chargingSlowlyThreshold)) { 310 return CHARGING_SLOWLY; 311 } else if (maxChargingMicroWatt 312 > context.getResources().getInteger(R.integer.config_chargingFastThreshold)) { 313 return CHARGING_FAST; 314 } else { 315 return CHARGING_REGULAR; 316 } 317 } 318 calculateMaxChargingMicroWatt(Intent batteryChangedIntent)319 private static int calculateMaxChargingMicroWatt(Intent batteryChangedIntent) { 320 final int maxChargingMicroAmp = 321 batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1); 322 int maxChargingMicroVolt = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1); 323 if (maxChargingMicroVolt <= 0) { 324 maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT; 325 } 326 327 if (maxChargingMicroAmp > 0) { 328 // Calculating µW = mA * mV 329 return (int) Math.round(maxChargingMicroAmp * 0.001 * maxChargingMicroVolt * 0.001); 330 } else { 331 return -1; 332 } 333 } 334 } 335