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 44 private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000; 45 46 public static final int BATTERY_LEVEL_UNKNOWN = -1; 47 public static final int CHARGING_UNKNOWN = -1; 48 public static final int CHARGING_SLOWLY = 0; 49 public static final int CHARGING_REGULAR = 1; 50 public static final int CHARGING_FAST = 2; 51 public static final int LOW_BATTERY_THRESHOLD = 20; 52 public static final int SEVERE_LOW_BATTERY_THRESHOLD = 10; 53 public static final int EXTREME_LOW_BATTERY_THRESHOLD = 3; 54 55 public final int status; 56 public final int level; 57 public final int plugged; 58 public final int chargingStatus; 59 public final int maxChargingWattage; 60 public final boolean present; 61 public final Optional<Boolean> incompatibleCharger; 62 create(Context context, boolean incompatibleCharger)63 public static BatteryStatus create(Context context, boolean incompatibleCharger) { 64 final Intent batteryChangedIntent = BatteryUtils.getBatteryIntent(context); 65 return batteryChangedIntent == null 66 ? null : new BatteryStatus(batteryChangedIntent, incompatibleCharger); 67 } 68 BatteryStatus(int status, int level, int plugged, int chargingStatus, int maxChargingWattage, boolean present)69 public BatteryStatus(int status, int level, int plugged, int chargingStatus, 70 int maxChargingWattage, boolean present) { 71 this.status = status; 72 this.level = level; 73 this.plugged = plugged; 74 this.chargingStatus = chargingStatus; 75 this.maxChargingWattage = maxChargingWattage; 76 this.present = present; 77 this.incompatibleCharger = Optional.empty(); 78 } 79 80 BatteryStatus(Intent batteryChangedIntent)81 public BatteryStatus(Intent batteryChangedIntent) { 82 this(batteryChangedIntent, Optional.empty()); 83 } 84 BatteryStatus(Intent batteryChangedIntent, boolean incompatibleCharger)85 public BatteryStatus(Intent batteryChangedIntent, boolean incompatibleCharger) { 86 this(batteryChangedIntent, Optional.of(incompatibleCharger)); 87 } 88 BatteryStatus(Intent batteryChangedIntent, Optional<Boolean> incompatibleCharger)89 private BatteryStatus(Intent batteryChangedIntent, Optional<Boolean> incompatibleCharger) { 90 status = batteryChangedIntent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN); 91 plugged = batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0); 92 level = getBatteryLevel(batteryChangedIntent); 93 chargingStatus = batteryChangedIntent.getIntExtra(EXTRA_CHARGING_STATUS, 94 CHARGING_POLICY_DEFAULT); 95 present = batteryChangedIntent.getBooleanExtra(EXTRA_PRESENT, true); 96 this.incompatibleCharger = incompatibleCharger; 97 98 maxChargingWattage = calculateMaxChargingMicroWatt(batteryChangedIntent); 99 } 100 101 /** Determine whether the device is plugged. */ isPluggedIn()102 public boolean isPluggedIn() { 103 return isPluggedIn(plugged); 104 } 105 106 /** Determine whether the device is plugged in (USB, power). */ isPluggedInWired()107 public boolean isPluggedInWired() { 108 return plugged == BatteryManager.BATTERY_PLUGGED_AC 109 || plugged == BatteryManager.BATTERY_PLUGGED_USB; 110 } 111 112 /** 113 * Determine whether the device is plugged in wireless. 114 */ isPluggedInWireless()115 public boolean isPluggedInWireless() { 116 return plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS; 117 } 118 119 /** Determine whether the device is plugged in dock. */ isPluggedInDock()120 public boolean isPluggedInDock() { 121 return isPluggedInDock(plugged); 122 } 123 124 /** 125 * Whether or not the device is charged. Note that some devices never return 100% for 126 * battery level, so this allows either battery level or status to determine if the 127 * battery is charged. 128 */ isCharged()129 public boolean isCharged() { 130 return isCharged(status, level); 131 } 132 133 /** Whether battery is low and needs to be charged. */ isBatteryLow()134 public boolean isBatteryLow() { 135 return isLowBattery(level); 136 } 137 138 /** Whether battery defender is enabled. */ isBatteryDefender()139 public boolean isBatteryDefender() { 140 return isBatteryDefender(chargingStatus); 141 } 142 143 /** Return current charging speed is fast, slow or normal. */ getChargingSpeed(Context context)144 public final int getChargingSpeed(Context context) { 145 final int slowThreshold = context.getResources().getInteger( 146 R.integer.config_chargingSlowlyThreshold); 147 final int fastThreshold = context.getResources().getInteger( 148 getFastChargingThresholdResId()); 149 150 return maxChargingWattage <= 0 ? CHARGING_UNKNOWN : 151 maxChargingWattage < slowThreshold ? CHARGING_SLOWLY : 152 maxChargingWattage > fastThreshold ? CHARGING_FAST : 153 CHARGING_REGULAR; 154 } 155 156 @Override toString()157 public String toString() { 158 return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged 159 + ",chargingStatus=" + chargingStatus + ",maxChargingWattage=" + maxChargingWattage 160 + "}"; 161 } 162 163 /** 164 * Whether or not the device is charged. Note that some devices never return 100% for 165 * battery level, so this allows either battery level or status to determine if the 166 * battery is charged. 167 * 168 * @param batteryChangedIntent ACTION_BATTERY_CHANGED intent 169 * @return true if the device is charged 170 */ isCharged(Intent batteryChangedIntent)171 public static boolean isCharged(Intent batteryChangedIntent) { 172 int status = batteryChangedIntent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN); 173 int level = getBatteryLevel(batteryChangedIntent); 174 return isCharged(status, level); 175 } 176 177 /** 178 * Whether or not the device is charged. Note that some devices never return 100% for 179 * battery level, so this allows either battery level or status to determine if the 180 * battery is charged. 181 * 182 * @param status values for "status" field in the ACTION_BATTERY_CHANGED Intent 183 * @param level values from 0 to 100 184 * @return true if the device is charged 185 */ isCharged(int status, int level)186 public static boolean isCharged(int status, int level) { 187 return status == BATTERY_STATUS_FULL || level >= 100; 188 } 189 190 /** 191 * Whether or not the device is charged. Note that some devices never return 100% for battery 192 * level, so this allows either battery level or status to determine if the battery is charged. 193 * 194 * @param status the value from extra {@link BatteryManager.EXTRA_STATUS} of 195 * {@link Intent.ACTION_BATTERY_CHANGED} intent 196 * @param level the value from extra {@link BatteryManager.EXTRA_LEVEL} of 197 * {@link Intent.ACTION_BATTERY_CHANGED} intent 198 * @param scale the value from extra {@link BatteryManager.EXTRA_SCALE} of 199 * {@link Intent.ACTION_BATTERY_CHANGED} intent 200 */ isCharged(int status, int level, int scale)201 public static boolean isCharged(int status, int level, int scale) { 202 var batteryLevel = getBatteryLevel(level, scale); 203 return isCharged(status, batteryLevel); 204 } 205 206 /** Gets the battery level from the intent. */ getBatteryLevel(Intent batteryChangedIntent)207 public static int getBatteryLevel(Intent batteryChangedIntent) { 208 if (batteryChangedIntent == null) { 209 return BATTERY_LEVEL_UNKNOWN; 210 } 211 final int level = 212 batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, BATTERY_LEVEL_UNKNOWN); 213 final int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 0); 214 return getBatteryLevel(level, scale); 215 } 216 217 /** 218 * Gets the battery level from the value of {@link Intent.BATTERY_CHANGED_INTENT}'s EXTRA_LEVEL 219 * and EXTRA_SCALE. 220 */ getBatteryLevel(int level, int scale)221 public static int getBatteryLevel(int level, int scale) { 222 return scale == 0 223 ? BATTERY_LEVEL_UNKNOWN 224 : Math.round((level / (float) scale) * 100f); 225 } 226 227 /** Returns the plugged type from {@code batteryChangedIntent}. */ getPluggedType(Intent batteryChangedIntent)228 public static int getPluggedType(Intent batteryChangedIntent) { 229 return batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0); 230 } 231 232 /** Whether the device is plugged or not. */ isPluggedIn(Intent batteryChangedIntent)233 public static boolean isPluggedIn(Intent batteryChangedIntent) { 234 return isPluggedIn(getPluggedType(batteryChangedIntent)); 235 } 236 237 /** Whether the device is plugged or not. */ isPluggedIn(int plugged)238 public static boolean isPluggedIn(int plugged) { 239 return plugged == BatteryManager.BATTERY_PLUGGED_AC 240 || plugged == BatteryManager.BATTERY_PLUGGED_USB 241 || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS 242 || plugged == BatteryManager.BATTERY_PLUGGED_DOCK; 243 } 244 245 /** Determine whether the device is plugged in dock. */ isPluggedInDock(Intent batteryChangedIntent)246 public static boolean isPluggedInDock(Intent batteryChangedIntent) { 247 return isPluggedInDock( 248 batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, BATTERY_PLUGGED_NONE)); 249 } 250 251 /** Determine whether the device is plugged in dock. */ isPluggedInDock(int plugged)252 public static boolean isPluggedInDock(int plugged) { 253 return plugged == BatteryManager.BATTERY_PLUGGED_DOCK; 254 } 255 256 /** 257 * Whether the battery is low or not. 258 * 259 * @param batteryChangedIntent the {@link ACTION_BATTERY_CHANGED} intent 260 * @return {@code true} if the battery level is less or equal to {@link LOW_BATTERY_THRESHOLD} 261 */ isLowBattery(Intent batteryChangedIntent)262 public static boolean isLowBattery(Intent batteryChangedIntent) { 263 int level = getBatteryLevel(batteryChangedIntent); 264 return isLowBattery(level); 265 } 266 267 /** 268 * Whether the battery is low or not. 269 * 270 * @param batteryLevel the battery level 271 * @return {@code true} if the battery level is less or equal to {@link LOW_BATTERY_THRESHOLD} 272 */ isLowBattery(int batteryLevel)273 public static boolean isLowBattery(int batteryLevel) { 274 return batteryLevel <= LOW_BATTERY_THRESHOLD; 275 } 276 277 /** 278 * Whether the battery is severe low or not. 279 * 280 * @param batteryChangedIntent the ACTION_BATTERY_CHANGED intent 281 * @return {@code true} if the battery level is less or equal to {@link 282 * SEVERE_LOW_BATTERY_THRESHOLD} 283 */ isSevereLowBattery(Intent batteryChangedIntent)284 public static boolean isSevereLowBattery(Intent batteryChangedIntent) { 285 int batteryLevel = getBatteryLevel(batteryChangedIntent); 286 return isSevereLowBattery(batteryLevel); 287 } 288 289 /** 290 * Whether the battery is severe low or not. 291 * 292 * @param batteryLevel the value of battery level 293 * @return {@code true} if the battery level is less or equal to {@link 294 * SEVERE_LOW_BATTERY_THRESHOLD} 295 */ isSevereLowBattery(int batteryLevel)296 public static boolean isSevereLowBattery(int batteryLevel) { 297 return batteryLevel <= SEVERE_LOW_BATTERY_THRESHOLD; 298 } 299 300 /** 301 * Whether the battery is extreme low or not. 302 * 303 * @param batteryChangedIntent the ACTION_BATTERY_CHANGED intent 304 * @return {@code true} if the battery level is less or equal to {@link 305 * EXTREME_LOW_BATTERY_THRESHOLD} 306 */ isExtremeLowBattery(Intent batteryChangedIntent)307 public static boolean isExtremeLowBattery(Intent batteryChangedIntent) { 308 int level = getBatteryLevel(batteryChangedIntent); 309 return isExtremeLowBattery(level); 310 } 311 312 /** 313 * Whether the battery is extreme low or not. 314 * 315 * @return {@code true} if the {@code batteryLevel} is less or equal to 316 * {@link EXTREME_LOW_BATTERY_THRESHOLD} 317 */ isExtremeLowBattery(int batteryLevel)318 public static boolean isExtremeLowBattery(int batteryLevel) { 319 return batteryLevel <= EXTREME_LOW_BATTERY_THRESHOLD; 320 } 321 322 /** 323 * Whether the battery defender is enabled or not. 324 * 325 * @param batteryChangedIntent the ACTION_BATTERY_CHANGED intent 326 * @return {@code true} if the battery defender is enabled. It could be dock defend, dwell 327 * defend, or temp defend 328 */ isBatteryDefender(Intent batteryChangedIntent)329 public static boolean isBatteryDefender(Intent batteryChangedIntent) { 330 int chargingStatus = 331 batteryChangedIntent.getIntExtra(EXTRA_CHARGING_STATUS, CHARGING_POLICY_DEFAULT); 332 return isBatteryDefender(chargingStatus); 333 } 334 335 /** 336 * Whether the battery defender is enabled or not. 337 * 338 * @param chargingStatus for {@link EXTRA_CHARGING_STATUS} field in the ACTION_BATTERY_CHANGED 339 * intent 340 * @return {@code true} if the battery defender is enabled. It could be dock defend, dwell 341 * defend, or temp defend 342 */ isBatteryDefender(int chargingStatus)343 public static boolean isBatteryDefender(int chargingStatus) { 344 return chargingStatus == CHARGING_POLICY_ADAPTIVE_LONGLIFE; 345 } 346 347 /** 348 * Calculates the charging speed based on the {@link R.integer.config_chargingSlowlyThreshold} 349 * and {@link R.integer.config_chargingFastThreshold}. 350 * 351 * @param context the application context 352 * @param batteryChangedIntent the intent from {@link Intent.ACTION_BATTERY_CHANGED} 353 * @return the charging speed. {@link CHARGING_REGULAR}, {@link CHARGING_FAST}, {@link 354 * CHARGING_SLOWLY} or {@link CHARGING_UNKNOWN} 355 */ getChargingSpeed(Context context, Intent batteryChangedIntent)356 public static int getChargingSpeed(Context context, Intent batteryChangedIntent) { 357 final int maxChargingMicroCurrent = 358 batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1); 359 int maxChargingMicroVolt = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1); 360 361 return calculateChargingSpeed(context, maxChargingMicroCurrent, maxChargingMicroVolt); 362 } 363 364 /** 365 * Calculates the charging speed based on the {@link R.integer.config_chargingSlowlyThreshold} 366 * and {@link R.integer.config_chargingFastThreshold}. 367 * 368 * @param maxChargingMicroCurrent the max charging micro current that is retrieved form the 369 * extra of {@link Intent.Action_BATTERY_CHANGED} 370 * @param maxChargingMicroVolt the max charging micro voltage that is retrieved form the extra 371 * of {@link Intent.Action_BATTERY_CHANGED} 372 * @return the charging speed. {@link CHARGING_REGULAR}, {@link CHARGING_FAST}, {@link 373 * CHARGING_SLOWLY} or {@link CHARGING_UNKNOWN} 374 */ calculateChargingSpeed( Context context, int maxChargingMicroCurrent, int maxChargingMicroVolt)375 public static int calculateChargingSpeed( 376 Context context, int maxChargingMicroCurrent, int maxChargingMicroVolt) { 377 final int maxChargingMicroWatt = 378 calculateMaxChargingMicroWatt(maxChargingMicroCurrent, maxChargingMicroVolt); 379 380 if (maxChargingMicroWatt <= 0) { 381 return CHARGING_UNKNOWN; 382 } else if (maxChargingMicroWatt 383 < context.getResources().getInteger(R.integer.config_chargingSlowlyThreshold)) { 384 return CHARGING_SLOWLY; 385 } else if (maxChargingMicroWatt 386 > context.getResources().getInteger(getFastChargingThresholdResId())) { 387 return CHARGING_FAST; 388 } else { 389 return CHARGING_REGULAR; 390 } 391 } 392 calculateMaxChargingMicroWatt(Intent batteryChangedIntent)393 private static int calculateMaxChargingMicroWatt(Intent batteryChangedIntent) { 394 final int maxChargingMicroAmp = 395 batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1); 396 int maxChargingMicroVolt = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1); 397 398 return calculateMaxChargingMicroWatt(maxChargingMicroAmp, maxChargingMicroVolt); 399 } 400 calculateMaxChargingMicroWatt(int maxChargingMicroAmp, int maxChargingMicroVolt)401 private static int calculateMaxChargingMicroWatt(int maxChargingMicroAmp, 402 int maxChargingMicroVolt) { 403 if (maxChargingMicroVolt <= 0) { 404 maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT; 405 } 406 407 if (maxChargingMicroAmp > 0) { 408 // Calculating µW = mA * mV 409 return (int) Math.round(maxChargingMicroAmp * 0.001 * maxChargingMicroVolt * 0.001); 410 } else { 411 return -1; 412 } 413 } 414 getFastChargingThresholdResId()415 private static int getFastChargingThresholdResId() { 416 return BatteryUtils.isChargingStringV2Enabled() 417 ? R.integer.config_chargingFastThreshold_v2 418 : R.integer.config_chargingFastThreshold; 419 } 420 } 421