summaryrefslogtreecommitdiff
path: root/com/android/server/BatteryService.java
diff options
context:
space:
mode:
Diffstat (limited to 'com/android/server/BatteryService.java')
-rw-r--r--com/android/server/BatteryService.java587
1 files changed, 430 insertions, 157 deletions
diff --git a/com/android/server/BatteryService.java b/com/android/server/BatteryService.java
index 5106c8d7..46b671bd 100644
--- a/com/android/server/BatteryService.java
+++ b/com/android/server/BatteryService.java
@@ -24,6 +24,7 @@ import android.os.PowerManager;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
import com.android.server.am.BatteryStatsService;
@@ -35,9 +36,15 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.hardware.health.V2_0.HealthInfo;
+import android.hardware.health.V2_0.IHealthInfoCallback;
+import android.hardware.health.V2_0.IHealth;
+import android.hardware.health.V2_0.Result;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
-import android.os.BatteryProperties;
+import android.os.BatteryProperty;
import android.os.Binder;
import android.os.FileUtils;
import android.os.Handler;
@@ -53,6 +60,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.service.battery.BatteryServiceDumpProto;
import android.util.EventLog;
+import android.util.MutableInt;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -62,6 +70,10 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicReference;
/**
* <p>BatteryService monitors the charging status, and charge level of the device
@@ -100,6 +112,8 @@ public final class BatteryService extends SystemService {
private static final int BATTERY_SCALE = 100; // battery capacity is a percentage
+ private static final long HEALTH_HAL_WAIT_MS = 1000;
+
// Used locally for determining when to make a last ditch effort to log
// discharge stats before the device dies.
private int mCriticalBatteryLevel;
@@ -118,8 +132,8 @@ public final class BatteryService extends SystemService {
private final Object mLock = new Object();
- private BatteryProperties mBatteryProps;
- private final BatteryProperties mLastBatteryProps = new BatteryProperties();
+ private HealthInfo mHealthInfo;
+ private final HealthInfo mLastHealthInfo = new HealthInfo();
private boolean mBatteryLevelCritical;
private int mLastBatteryStatus;
private int mLastBatteryHealth;
@@ -157,6 +171,10 @@ public final class BatteryService extends SystemService {
private ActivityManagerInternal mActivityManagerInternal;
+ private HealthServiceWrapper mHealthServiceWrapper;
+ private HealthHalCallback mHealthHalCallback;
+ private BatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
+
public BatteryService(Context context) {
super(context);
@@ -195,17 +213,12 @@ public final class BatteryService extends SystemService {
@Override
public void onStart() {
- IBinder b = ServiceManager.getService("batteryproperties");
- final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =
- IBatteryPropertiesRegistrar.Stub.asInterface(b);
- try {
- batteryPropertiesRegistrar.registerListener(new BatteryListener());
- } catch (RemoteException e) {
- // Should never happen.
- }
+ registerHealthCallback();
mBinderService = new BinderService();
publishBinderService("battery", mBinderService);
+ mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
+ publishBinderService("batteryproperties", mBatteryPropertiesRegistrar);
publishLocalService(BatteryManagerInternal.class, new LocalService());
}
@@ -231,6 +244,49 @@ public final class BatteryService extends SystemService {
}
}
+ private void registerHealthCallback() {
+ mHealthServiceWrapper = new HealthServiceWrapper();
+ mHealthHalCallback = new HealthHalCallback();
+ // IHealth is lazily retrieved.
+ try {
+ mHealthServiceWrapper.init(mHealthHalCallback,
+ new HealthServiceWrapper.IServiceManagerSupplier() {},
+ new HealthServiceWrapper.IHealthSupplier() {});
+ } catch (RemoteException | NoSuchElementException ex) {
+ Slog.w(TAG, "health: cannot register callback. "
+ + "BatteryService will be started with dummy values. Reason: "
+ + ex.getClass().getSimpleName() + ": " + ex.getMessage());
+ update(new HealthInfo());
+ return;
+ }
+
+ // init register for new service notifications, and IServiceManager should return the
+ // existing service in a near future. Wait for this.update() to instantiate
+ // the initial mHealthInfo.
+ long timeWaited = 0;
+ synchronized (mLock) {
+ long beforeWait = SystemClock.uptimeMillis();
+ while (mHealthInfo == null &&
+ (timeWaited = SystemClock.uptimeMillis() - beforeWait) < HEALTH_HAL_WAIT_MS) {
+ try {
+ mLock.wait(HEALTH_HAL_WAIT_MS - timeWaited);
+ } catch (InterruptedException ex) {
+ break;
+ }
+ }
+ if (mHealthInfo == null) {
+ Slog.w(TAG, "health: Waited " + timeWaited + "ms for callbacks but received "
+ + "nothing. BatteryService will be started with dummy values.");
+ update(new HealthInfo());
+ return;
+ }
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "health: Waited " + timeWaited + "ms and received the update.");
+ }
+ }
+
private void updateBatteryWarningLevelLocked() {
final ContentResolver resolver = mContext.getContentResolver();
int defWarnLevel = mContext.getResources().getInteger(
@@ -251,16 +307,16 @@ public final class BatteryService extends SystemService {
private boolean isPoweredLocked(int plugTypeSet) {
// assume we are powered if battery state is unknown so
// the "stay on while plugged in" option will work.
- if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
+ if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
return true;
}
- if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mBatteryProps.chargerAcOnline) {
+ if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_AC) != 0 && mHealthInfo.legacy.chargerAcOnline) {
return true;
}
- if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mBatteryProps.chargerUsbOnline) {
+ if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_USB) != 0 && mHealthInfo.legacy.chargerUsbOnline) {
return true;
}
- if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mBatteryProps.chargerWirelessOnline) {
+ if ((plugTypeSet & BatteryManager.BATTERY_PLUGGED_WIRELESS) != 0 && mHealthInfo.legacy.chargerWirelessOnline) {
return true;
}
return false;
@@ -277,15 +333,15 @@ public final class BatteryService extends SystemService {
* (becomes <= mLowBatteryWarningLevel).
*/
return !plugged
- && mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
- && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel
+ && mHealthInfo.legacy.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+ && mHealthInfo.legacy.batteryLevel <= mLowBatteryWarningLevel
&& (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
}
private void shutdownIfNoPowerLocked() {
// shut down gracefully if our battery is critically low and we are not powered.
// wait until the system has booted before attempting to display the shutdown dialog.
- if (mBatteryProps.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
+ if (mHealthInfo.legacy.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -306,7 +362,7 @@ public final class BatteryService extends SystemService {
// shut down gracefully if temperature is too high (> 68.0C by default)
// wait until the system has booted before attempting to display the
// shutdown dialog.
- if (mBatteryProps.batteryTemperature > mShutdownBatteryTemperature) {
+ if (mHealthInfo.legacy.batteryTemperature > mShutdownBatteryTemperature) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -323,28 +379,51 @@ public final class BatteryService extends SystemService {
}
}
- private void update(BatteryProperties props) {
+ private void update(HealthInfo info) {
synchronized (mLock) {
if (!mUpdatesStopped) {
- mBatteryProps = props;
+ mHealthInfo = info;
// Process the new values.
processValuesLocked(false);
+ mLock.notifyAll(); // for any waiters on new info
} else {
- mLastBatteryProps.set(props);
+ copy(mLastHealthInfo, info);
}
}
}
+ private static void copy(HealthInfo dst, HealthInfo src) {
+ dst.legacy.chargerAcOnline = src.legacy.chargerAcOnline;
+ dst.legacy.chargerUsbOnline = src.legacy.chargerUsbOnline;
+ dst.legacy.chargerWirelessOnline = src.legacy.chargerWirelessOnline;
+ dst.legacy.maxChargingCurrent = src.legacy.maxChargingCurrent;
+ dst.legacy.maxChargingVoltage = src.legacy.maxChargingVoltage;
+ dst.legacy.batteryStatus = src.legacy.batteryStatus;
+ dst.legacy.batteryHealth = src.legacy.batteryHealth;
+ dst.legacy.batteryPresent = src.legacy.batteryPresent;
+ dst.legacy.batteryLevel = src.legacy.batteryLevel;
+ dst.legacy.batteryVoltage = src.legacy.batteryVoltage;
+ dst.legacy.batteryTemperature = src.legacy.batteryTemperature;
+ dst.legacy.batteryCurrent = src.legacy.batteryCurrent;
+ dst.legacy.batteryCycleCount = src.legacy.batteryCycleCount;
+ dst.legacy.batteryFullCharge = src.legacy.batteryFullCharge;
+ dst.legacy.batteryChargeCounter = src.legacy.batteryChargeCounter;
+ dst.legacy.batteryTechnology = src.legacy.batteryTechnology;
+ dst.batteryCurrentAverage = src.batteryCurrentAverage;
+ dst.batteryCapacity = src.batteryCapacity;
+ dst.energyCounter = src.energyCounter;
+ }
+
private void processValuesLocked(boolean force) {
boolean logOutlier = false;
long dischargeDuration = 0;
- mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);
- if (mBatteryProps.chargerAcOnline) {
+ mBatteryLevelCritical = (mHealthInfo.legacy.batteryLevel <= mCriticalBatteryLevel);
+ if (mHealthInfo.legacy.chargerAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
- } else if (mBatteryProps.chargerUsbOnline) {
+ } else if (mHealthInfo.legacy.chargerUsbOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
- } else if (mBatteryProps.chargerWirelessOnline) {
+ } else if (mHealthInfo.legacy.chargerWirelessOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
} else {
mPlugType = BATTERY_PLUGGED_NONE;
@@ -352,30 +431,17 @@ public final class BatteryService extends SystemService {
if (DEBUG) {
Slog.d(TAG, "Processing new values: "
- + "chargerAcOnline=" + mBatteryProps.chargerAcOnline
- + ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline
- + ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline
- + ", maxChargingCurrent" + mBatteryProps.maxChargingCurrent
- + ", maxChargingVoltage" + mBatteryProps.maxChargingVoltage
- + ", batteryStatus=" + mBatteryProps.batteryStatus
- + ", batteryHealth=" + mBatteryProps.batteryHealth
- + ", batteryPresent=" + mBatteryProps.batteryPresent
- + ", batteryLevel=" + mBatteryProps.batteryLevel
- + ", batteryTechnology=" + mBatteryProps.batteryTechnology
- + ", batteryVoltage=" + mBatteryProps.batteryVoltage
- + ", batteryChargeCounter=" + mBatteryProps.batteryChargeCounter
- + ", batteryFullCharge=" + mBatteryProps.batteryFullCharge
- + ", batteryTemperature=" + mBatteryProps.batteryTemperature
+ + "info=" + mHealthInfo
+ ", mBatteryLevelCritical=" + mBatteryLevelCritical
+ ", mPlugType=" + mPlugType);
}
// Let the battery stats keep track of the current level.
try {
- mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,
- mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,
- mBatteryProps.batteryVoltage, mBatteryProps.batteryChargeCounter,
- mBatteryProps.batteryFullCharge);
+ mBatteryStats.setBatteryState(mHealthInfo.legacy.batteryStatus, mHealthInfo.legacy.batteryHealth,
+ mPlugType, mHealthInfo.legacy.batteryLevel, mHealthInfo.legacy.batteryTemperature,
+ mHealthInfo.legacy.batteryVoltage, mHealthInfo.legacy.batteryChargeCounter,
+ mHealthInfo.legacy.batteryFullCharge);
} catch (RemoteException e) {
// Should never happen.
}
@@ -383,16 +449,16 @@ public final class BatteryService extends SystemService {
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
- if (force || (mBatteryProps.batteryStatus != mLastBatteryStatus ||
- mBatteryProps.batteryHealth != mLastBatteryHealth ||
- mBatteryProps.batteryPresent != mLastBatteryPresent ||
- mBatteryProps.batteryLevel != mLastBatteryLevel ||
+ if (force || (mHealthInfo.legacy.batteryStatus != mLastBatteryStatus ||
+ mHealthInfo.legacy.batteryHealth != mLastBatteryHealth ||
+ mHealthInfo.legacy.batteryPresent != mLastBatteryPresent ||
+ mHealthInfo.legacy.batteryLevel != mLastBatteryLevel ||
mPlugType != mLastPlugType ||
- mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
- mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
- mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent ||
- mBatteryProps.maxChargingVoltage != mLastMaxChargingVoltage ||
- mBatteryProps.batteryChargeCounter != mLastChargeCounter ||
+ mHealthInfo.legacy.batteryVoltage != mLastBatteryVoltage ||
+ mHealthInfo.legacy.batteryTemperature != mLastBatteryTemperature ||
+ mHealthInfo.legacy.maxChargingCurrent != mLastMaxChargingCurrent ||
+ mHealthInfo.legacy.maxChargingVoltage != mLastMaxChargingVoltage ||
+ mHealthInfo.legacy.batteryChargeCounter != mLastChargeCounter ||
mInvalidCharger != mLastInvalidCharger)) {
if (mPlugType != mLastPlugType) {
@@ -401,33 +467,33 @@ public final class BatteryService extends SystemService {
// There's no value in this data unless we've discharged at least once and the
// battery level has changed; so don't log until it does.
- if (mDischargeStartTime != 0 && mDischargeStartLevel != mBatteryProps.batteryLevel) {
+ if (mDischargeStartTime != 0 && mDischargeStartLevel != mHealthInfo.legacy.batteryLevel) {
dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
logOutlier = true;
EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
- mDischargeStartLevel, mBatteryProps.batteryLevel);
+ mDischargeStartLevel, mHealthInfo.legacy.batteryLevel);
// make sure we see a discharge event before logging again
mDischargeStartTime = 0;
}
} else if (mPlugType == BATTERY_PLUGGED_NONE) {
// charging -> discharging or we just powered up
mDischargeStartTime = SystemClock.elapsedRealtime();
- mDischargeStartLevel = mBatteryProps.batteryLevel;
+ mDischargeStartLevel = mHealthInfo.legacy.batteryLevel;
}
}
- if (mBatteryProps.batteryStatus != mLastBatteryStatus ||
- mBatteryProps.batteryHealth != mLastBatteryHealth ||
- mBatteryProps.batteryPresent != mLastBatteryPresent ||
+ if (mHealthInfo.legacy.batteryStatus != mLastBatteryStatus ||
+ mHealthInfo.legacy.batteryHealth != mLastBatteryHealth ||
+ mHealthInfo.legacy.batteryPresent != mLastBatteryPresent ||
mPlugType != mLastPlugType) {
EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
- mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0,
- mPlugType, mBatteryProps.batteryTechnology);
+ mHealthInfo.legacy.batteryStatus, mHealthInfo.legacy.batteryHealth, mHealthInfo.legacy.batteryPresent ? 1 : 0,
+ mPlugType, mHealthInfo.legacy.batteryTechnology);
}
- if (mBatteryProps.batteryLevel != mLastBatteryLevel) {
+ if (mHealthInfo.legacy.batteryLevel != mLastBatteryLevel) {
// Don't do this just from voltage or temperature changes, that is
// too noisy.
EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
- mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.batteryTemperature);
+ mHealthInfo.legacy.batteryLevel, mHealthInfo.legacy.batteryVoltage, mHealthInfo.legacy.batteryTemperature);
}
if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
mPlugType == BATTERY_PLUGGED_NONE) {
@@ -440,16 +506,16 @@ public final class BatteryService extends SystemService {
if (!mBatteryLevelLow) {
// Should we now switch in to low battery mode?
if (mPlugType == BATTERY_PLUGGED_NONE
- && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel) {
+ && mHealthInfo.legacy.batteryLevel <= mLowBatteryWarningLevel) {
mBatteryLevelLow = true;
}
} else {
// Should we now switch out of low battery mode?
if (mPlugType != BATTERY_PLUGGED_NONE) {
mBatteryLevelLow = false;
- } else if (mBatteryProps.batteryLevel >= mLowBatteryCloseWarningLevel) {
+ } else if (mHealthInfo.legacy.batteryLevel >= mLowBatteryCloseWarningLevel) {
mBatteryLevelLow = false;
- } else if (force && mBatteryProps.batteryLevel >= mLowBatteryWarningLevel) {
+ } else if (force && mHealthInfo.legacy.batteryLevel >= mLowBatteryWarningLevel) {
// If being forced, the previous state doesn't matter, we will just
// absolutely check to see if we are now above the warning level.
mBatteryLevelLow = false;
@@ -496,7 +562,7 @@ public final class BatteryService extends SystemService {
}
});
} else if (mSentLowBatteryBroadcast &&
- mBatteryProps.batteryLevel >= mLowBatteryCloseWarningLevel) {
+ mHealthInfo.legacy.batteryLevel >= mLowBatteryCloseWarningLevel) {
mSentLowBatteryBroadcast = false;
final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -522,16 +588,16 @@ public final class BatteryService extends SystemService {
logOutlierLocked(dischargeDuration);
}
- mLastBatteryStatus = mBatteryProps.batteryStatus;
- mLastBatteryHealth = mBatteryProps.batteryHealth;
- mLastBatteryPresent = mBatteryProps.batteryPresent;
- mLastBatteryLevel = mBatteryProps.batteryLevel;
+ mLastBatteryStatus = mHealthInfo.legacy.batteryStatus;
+ mLastBatteryHealth = mHealthInfo.legacy.batteryHealth;
+ mLastBatteryPresent = mHealthInfo.legacy.batteryPresent;
+ mLastBatteryLevel = mHealthInfo.legacy.batteryLevel;
mLastPlugType = mPlugType;
- mLastBatteryVoltage = mBatteryProps.batteryVoltage;
- mLastBatteryTemperature = mBatteryProps.batteryTemperature;
- mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent;
- mLastMaxChargingVoltage = mBatteryProps.maxChargingVoltage;
- mLastChargeCounter = mBatteryProps.batteryChargeCounter;
+ mLastBatteryVoltage = mHealthInfo.legacy.batteryVoltage;
+ mLastBatteryTemperature = mHealthInfo.legacy.batteryTemperature;
+ mLastMaxChargingCurrent = mHealthInfo.legacy.maxChargingCurrent;
+ mLastMaxChargingVoltage = mHealthInfo.legacy.maxChargingVoltage;
+ mLastChargeCounter = mHealthInfo.legacy.batteryChargeCounter;
mLastBatteryLevelCritical = mBatteryLevelCritical;
mLastInvalidCharger = mInvalidCharger;
}
@@ -543,38 +609,26 @@ public final class BatteryService extends SystemService {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- int icon = getIconLocked(mBatteryProps.batteryLevel);
+ int icon = getIconLocked(mHealthInfo.legacy.batteryLevel);
intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
- intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);
- intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);
- intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent);
- intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel);
+ intent.putExtra(BatteryManager.EXTRA_STATUS, mHealthInfo.legacy.batteryStatus);
+ intent.putExtra(BatteryManager.EXTRA_HEALTH, mHealthInfo.legacy.batteryHealth);
+ intent.putExtra(BatteryManager.EXTRA_PRESENT, mHealthInfo.legacy.batteryPresent);
+ intent.putExtra(BatteryManager.EXTRA_LEVEL, mHealthInfo.legacy.batteryLevel);
intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
- intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);
- intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);
- intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);
+ intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.legacy.batteryVoltage);
+ intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.legacy.batteryTemperature);
+ intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.legacy.batteryTechnology);
intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
- intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
- intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mBatteryProps.maxChargingVoltage);
- intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mBatteryProps.batteryChargeCounter);
+ intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.legacy.maxChargingCurrent);
+ intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.legacy.maxChargingVoltage);
+ intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.legacy.batteryChargeCounter);
if (DEBUG) {
- Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. level:" + mBatteryProps.batteryLevel +
- ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +
- ", health:" + mBatteryProps.batteryHealth +
- ", present:" + mBatteryProps.batteryPresent +
- ", voltage: " + mBatteryProps.batteryVoltage +
- ", temperature: " + mBatteryProps.batteryTemperature +
- ", technology: " + mBatteryProps.batteryTechnology +
- ", AC powered:" + mBatteryProps.chargerAcOnline +
- ", USB powered:" + mBatteryProps.chargerUsbOnline +
- ", Wireless powered:" + mBatteryProps.chargerWirelessOnline +
- ", icon:" + icon + ", invalid charger:" + mInvalidCharger +
- ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent +
- ", maxChargingVoltage:" + mBatteryProps.maxChargingVoltage +
- ", chargeCounter:" + mBatteryProps.batteryChargeCounter);
+ Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
+ + ", info:" + mHealthInfo.toString());
}
mHandler.post(new Runnable() {
@@ -635,14 +689,14 @@ public final class BatteryService extends SystemService {
long durationThreshold = Long.parseLong(durationThresholdString);
int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
if (duration <= durationThreshold &&
- mDischargeStartLevel - mBatteryProps.batteryLevel >= dischargeThreshold) {
+ mDischargeStartLevel - mHealthInfo.legacy.batteryLevel >= dischargeThreshold) {
// If the discharge cycle is bad enough we want to know about it.
logBatteryStatsLocked();
}
if (DEBUG) Slog.v(TAG, "duration threshold: " + durationThreshold +
" discharge threshold: " + dischargeThreshold);
if (DEBUG) Slog.v(TAG, "duration: " + duration + " discharge: " +
- (mDischargeStartLevel - mBatteryProps.batteryLevel));
+ (mDischargeStartLevel - mHealthInfo.legacy.batteryLevel));
} catch (NumberFormatException e) {
Slog.e(TAG, "Invalid DischargeThresholds GService string: " +
durationThresholdString + " or " + dischargeThresholdString);
@@ -651,14 +705,14 @@ public final class BatteryService extends SystemService {
}
private int getIconLocked(int level) {
- if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
+ if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
return com.android.internal.R.drawable.stat_sys_battery_charge;
- } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
+ } else if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
return com.android.internal.R.drawable.stat_sys_battery;
- } else if (mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
- || mBatteryProps.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
+ } else if (mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
+ || mHealthInfo.legacy.batteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
- && mBatteryProps.batteryLevel >= 100) {
+ && mHealthInfo.legacy.batteryLevel >= 100) {
return com.android.internal.R.drawable.stat_sys_battery_charge;
} else {
return com.android.internal.R.drawable.stat_sys_battery;
@@ -720,11 +774,11 @@ public final class BatteryService extends SystemService {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
if (!mUpdatesStopped) {
- mLastBatteryProps.set(mBatteryProps);
+ copy(mLastHealthInfo, mHealthInfo);
}
- mBatteryProps.chargerAcOnline = false;
- mBatteryProps.chargerUsbOnline = false;
- mBatteryProps.chargerWirelessOnline = false;
+ mHealthInfo.legacy.chargerAcOnline = false;
+ mHealthInfo.legacy.chargerUsbOnline = false;
+ mHealthInfo.legacy.chargerWirelessOnline = false;
long ident = Binder.clearCallingIdentity();
try {
mUpdatesStopped = true;
@@ -751,30 +805,30 @@ public final class BatteryService extends SystemService {
}
try {
if (!mUpdatesStopped) {
- mLastBatteryProps.set(mBatteryProps);
+ copy(mLastHealthInfo, mHealthInfo);
}
boolean update = true;
switch (key) {
case "present":
- mBatteryProps.batteryPresent = Integer.parseInt(value) != 0;
+ mHealthInfo.legacy.batteryPresent = Integer.parseInt(value) != 0;
break;
case "ac":
- mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
+ mHealthInfo.legacy.chargerAcOnline = Integer.parseInt(value) != 0;
break;
case "usb":
- mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;
+ mHealthInfo.legacy.chargerUsbOnline = Integer.parseInt(value) != 0;
break;
case "wireless":
- mBatteryProps.chargerWirelessOnline = Integer.parseInt(value) != 0;
+ mHealthInfo.legacy.chargerWirelessOnline = Integer.parseInt(value) != 0;
break;
case "status":
- mBatteryProps.batteryStatus = Integer.parseInt(value);
+ mHealthInfo.legacy.batteryStatus = Integer.parseInt(value);
break;
case "level":
- mBatteryProps.batteryLevel = Integer.parseInt(value);
+ mHealthInfo.legacy.batteryLevel = Integer.parseInt(value);
break;
case "temp":
- mBatteryProps.batteryTemperature = Integer.parseInt(value);
+ mHealthInfo.legacy.batteryTemperature = Integer.parseInt(value);
break;
case "invalid":
mInvalidCharger = Integer.parseInt(value);
@@ -806,7 +860,7 @@ public final class BatteryService extends SystemService {
try {
if (mUpdatesStopped) {
mUpdatesStopped = false;
- mBatteryProps.set(mLastBatteryProps);
+ copy(mHealthInfo, mLastHealthInfo);
processValuesFromShellLocked(pw, opts);
}
} finally {
@@ -833,20 +887,20 @@ public final class BatteryService extends SystemService {
if (mUpdatesStopped) {
pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
}
- pw.println(" AC powered: " + mBatteryProps.chargerAcOnline);
- pw.println(" USB powered: " + mBatteryProps.chargerUsbOnline);
- pw.println(" Wireless powered: " + mBatteryProps.chargerWirelessOnline);
- pw.println(" Max charging current: " + mBatteryProps.maxChargingCurrent);
- pw.println(" Max charging voltage: " + mBatteryProps.maxChargingVoltage);
- pw.println(" Charge counter: " + mBatteryProps.batteryChargeCounter);
- pw.println(" status: " + mBatteryProps.batteryStatus);
- pw.println(" health: " + mBatteryProps.batteryHealth);
- pw.println(" present: " + mBatteryProps.batteryPresent);
- pw.println(" level: " + mBatteryProps.batteryLevel);
+ pw.println(" AC powered: " + mHealthInfo.legacy.chargerAcOnline);
+ pw.println(" USB powered: " + mHealthInfo.legacy.chargerUsbOnline);
+ pw.println(" Wireless powered: " + mHealthInfo.legacy.chargerWirelessOnline);
+ pw.println(" Max charging current: " + mHealthInfo.legacy.maxChargingCurrent);
+ pw.println(" Max charging voltage: " + mHealthInfo.legacy.maxChargingVoltage);
+ pw.println(" Charge counter: " + mHealthInfo.legacy.batteryChargeCounter);
+ pw.println(" status: " + mHealthInfo.legacy.batteryStatus);
+ pw.println(" health: " + mHealthInfo.legacy.batteryHealth);
+ pw.println(" present: " + mHealthInfo.legacy.batteryPresent);
+ pw.println(" level: " + mHealthInfo.legacy.batteryLevel);
pw.println(" scale: " + BATTERY_SCALE);
- pw.println(" voltage: " + mBatteryProps.batteryVoltage);
- pw.println(" temperature: " + mBatteryProps.batteryTemperature);
- pw.println(" technology: " + mBatteryProps.batteryTechnology);
+ pw.println(" voltage: " + mHealthInfo.legacy.batteryVoltage);
+ pw.println(" temperature: " + mHealthInfo.legacy.batteryTemperature);
+ pw.println(" technology: " + mHealthInfo.legacy.batteryTechnology);
} else {
Shell shell = new Shell();
shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
@@ -860,25 +914,25 @@ public final class BatteryService extends SystemService {
synchronized (mLock) {
proto.write(BatteryServiceDumpProto.ARE_UPDATES_STOPPED, mUpdatesStopped);
int batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_NONE;
- if (mBatteryProps.chargerAcOnline) {
+ if (mHealthInfo.legacy.chargerAcOnline) {
batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_AC;
- } else if (mBatteryProps.chargerUsbOnline) {
+ } else if (mHealthInfo.legacy.chargerUsbOnline) {
batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_USB;
- } else if (mBatteryProps.chargerWirelessOnline) {
+ } else if (mHealthInfo.legacy.chargerWirelessOnline) {
batteryPluggedValue = BatteryServiceDumpProto.BATTERY_PLUGGED_WIRELESS;
}
proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
- proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
- proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mBatteryProps.maxChargingVoltage);
- proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mBatteryProps.batteryChargeCounter);
- proto.write(BatteryServiceDumpProto.STATUS, mBatteryProps.batteryStatus);
- proto.write(BatteryServiceDumpProto.HEALTH, mBatteryProps.batteryHealth);
- proto.write(BatteryServiceDumpProto.IS_PRESENT, mBatteryProps.batteryPresent);
- proto.write(BatteryServiceDumpProto.LEVEL, mBatteryProps.batteryLevel);
+ proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.legacy.maxChargingCurrent);
+ proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mHealthInfo.legacy.maxChargingVoltage);
+ proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.legacy.batteryChargeCounter);
+ proto.write(BatteryServiceDumpProto.STATUS, mHealthInfo.legacy.batteryStatus);
+ proto.write(BatteryServiceDumpProto.HEALTH, mHealthInfo.legacy.batteryHealth);
+ proto.write(BatteryServiceDumpProto.IS_PRESENT, mHealthInfo.legacy.batteryPresent);
+ proto.write(BatteryServiceDumpProto.LEVEL, mHealthInfo.legacy.batteryLevel);
proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE);
- proto.write(BatteryServiceDumpProto.VOLTAGE, mBatteryProps.batteryVoltage);
- proto.write(BatteryServiceDumpProto.TEMPERATURE, mBatteryProps.batteryTemperature);
- proto.write(BatteryServiceDumpProto.TECHNOLOGY, mBatteryProps.batteryTechnology);
+ proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.legacy.batteryVoltage);
+ proto.write(BatteryServiceDumpProto.TEMPERATURE, mHealthInfo.legacy.batteryTemperature);
+ proto.write(BatteryServiceDumpProto.TECHNOLOGY, mHealthInfo.legacy.batteryTechnology);
}
proto.flush();
}
@@ -911,8 +965,8 @@ public final class BatteryService extends SystemService {
* Synchronize on BatteryService.
*/
public void updateLightsLocked() {
- final int level = mBatteryProps.batteryLevel;
- final int status = mBatteryProps.batteryStatus;
+ final int level = mHealthInfo.legacy.batteryLevel;
+ final int status = mHealthInfo.legacy.batteryStatus;
if (level < mLowBatteryWarningLevel) {
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
// Solid red when battery is charging
@@ -938,15 +992,43 @@ public final class BatteryService extends SystemService {
}
}
- private final class BatteryListener extends IBatteryPropertiesListener.Stub {
- @Override public void batteryPropertiesChanged(BatteryProperties props) {
- final long identity = Binder.clearCallingIdentity();
+ private final class HealthHalCallback extends IHealthInfoCallback.Stub
+ implements HealthServiceWrapper.Callback {
+ @Override public void healthInfoChanged(HealthInfo props) {
+ BatteryService.this.update(props);
+ }
+ // on new service registered
+ @Override public void onRegistration(IHealth oldService, IHealth newService,
+ String instance) {
+ if (newService == null) return;
+
+ try {
+ if (oldService != null) {
+ int r = oldService.unregisterCallback(this);
+ if (r != Result.SUCCESS) {
+ Slog.w(TAG, "health: cannot unregister previous callback: " +
+ Result.toString(r));
+ }
+ }
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "health: cannot unregister previous callback (transaction error): "
+ + ex.getMessage());
+ }
+
try {
- BatteryService.this.update(props);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ int r = newService.registerCallback(this);
+ if (r != Result.SUCCESS) {
+ Slog.w(TAG, "health: cannot register callback: " + Result.toString(r));
+ return;
+ }
+ // registerCallback does NOT guarantee that update is called
+ // immediately, so request a manual update here.
+ newService.update();
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "health: cannot register callback (transaction error): "
+ + ex.getMessage());
}
- }
+ }
}
private final class BinderService extends Binder {
@@ -967,6 +1049,63 @@ public final class BatteryService extends SystemService {
}
}
+ // Reduced IBatteryPropertiesRegistrar that only implements getProperty for usage
+ // in BatteryManager.
+ private final class BatteryPropertiesRegistrar extends IBatteryPropertiesRegistrar.Stub {
+ public void registerListener(IBatteryPropertiesListener listener) {
+ Slog.e(TAG, "health: must not call registerListener on battery properties");
+ }
+ public void unregisterListener(IBatteryPropertiesListener listener) {
+ Slog.e(TAG, "health: must not call unregisterListener on battery properties");
+ }
+ public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
+ IHealth service = mHealthServiceWrapper.getLastService();
+ final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
+ switch(id) {
+ case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
+ service.getChargeCounter((int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
+ service.getCurrentNow((int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
+ service.getCurrentAverage((int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CAPACITY:
+ service.getCapacity((int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_STATUS:
+ service.getChargeStatus((int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
+ service.getEnergyCounter((int result, long value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ }
+ return outResult.value;
+ }
+ public void scheduleUpdate() {
+ Slog.e(TAG, "health: must not call scheduleUpdate on battery properties");
+ }
+ }
+
private final class LocalService extends BatteryManagerInternal {
@Override
public boolean isPowered(int plugTypeSet) {
@@ -985,7 +1124,7 @@ public final class BatteryService extends SystemService {
@Override
public int getBatteryLevel() {
synchronized (mLock) {
- return mBatteryProps.batteryLevel;
+ return mHealthInfo.legacy.batteryLevel;
}
}
@@ -1003,4 +1142,138 @@ public final class BatteryService extends SystemService {
}
}
}
+
+ /**
+ * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when
+ * necessary.
+ *
+ * On new registration of IHealth service, {@link #onRegistration onRegistration} is called and
+ * the internal service is refreshed.
+ * On death of an existing IHealth service, the internal service is NOT cleared to avoid
+ * race condition between death notification and new service notification. Hence,
+ * a caller must check for transaction errors when calling into the service.
+ *
+ * @hide Should only be used internally.
+ */
+ @VisibleForTesting
+ static final class HealthServiceWrapper {
+ private static final String TAG = "HealthServiceWrapper";
+ public static final String INSTANCE_HEALTHD = "backup";
+ public static final String INSTANCE_VENDOR = "default";
+ // All interesting instances, sorted by priority high -> low.
+ private static final List<String> sAllInstances =
+ Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
+
+ private final IServiceNotification mNotification = new Notification();
+ private Callback mCallback;
+ private IHealthSupplier mHealthSupplier;
+
+ private final Object mLastServiceSetLock = new Object();
+ // Last IHealth service received.
+ // set must be also be guarded with mLastServiceSetLock to ensure ordering.
+ private final AtomicReference<IHealth> mLastService = new AtomicReference<>();
+
+ /**
+ * init should be called after constructor. For testing purposes, init is not called by
+ * constructor.
+ */
+ HealthServiceWrapper() {
+ }
+
+ IHealth getLastService() {
+ return mLastService.get();
+ }
+
+ /**
+ * Start monitoring registration of new IHealth services. Only instances that are in
+ * {@code sAllInstances} and in device / framework manifest are used. This function should
+ * only be called once.
+ * @throws RemoteException transaction error when talking to IServiceManager
+ * @throws NoSuchElementException if one of the following cases:
+ * - No service manager;
+ * - none of {@code sAllInstances} are in manifests (i.e. not
+ * available on this device), or none of these instances are available to current
+ * process.
+ * @throws NullPointerException when callback is null or supplier is null
+ */
+ void init(Callback callback,
+ IServiceManagerSupplier managerSupplier,
+ IHealthSupplier healthSupplier)
+ throws RemoteException, NoSuchElementException, NullPointerException {
+ if (callback == null || managerSupplier == null || healthSupplier == null)
+ throw new NullPointerException();
+
+ mCallback = callback;
+ mHealthSupplier = healthSupplier;
+
+ IServiceManager manager = managerSupplier.get();
+ for (String name : sAllInstances) {
+ if (manager.getTransport(IHealth.kInterfaceName, name) ==
+ IServiceManager.Transport.EMPTY) {
+ continue;
+ }
+
+ manager.registerForNotifications(IHealth.kInterfaceName, name, mNotification);
+ Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + name);
+ return;
+ }
+
+ throw new NoSuchElementException(String.format(
+ "No IHealth service instance among %s is available. Perhaps no permission?",
+ sAllInstances.toString()));
+ }
+
+ interface Callback {
+ /**
+ * This function is invoked asynchronously when a new and related IServiceNotification
+ * is received.
+ * @param service the recently retrieved service from IServiceManager.
+ * Can be a dead service before service notification of a new service is delivered.
+ * Implementation must handle cases for {@link RemoteException}s when calling
+ * into service.
+ * @param instance instance name.
+ */
+ void onRegistration(IHealth oldService, IHealth newService, String instance);
+ }
+
+ /**
+ * Supplier of services.
+ * Must not return null; throw {@link NoSuchElementException} if a service is not available.
+ */
+ interface IServiceManagerSupplier {
+ default IServiceManager get() throws NoSuchElementException, RemoteException {
+ return IServiceManager.getService();
+ }
+ }
+ /**
+ * Supplier of services.
+ * Must not return null; throw {@link NoSuchElementException} if a service is not available.
+ */
+ interface IHealthSupplier {
+ default IHealth get(String name) throws NoSuchElementException, RemoteException {
+ return IHealth.getService(name);
+ }
+ }
+
+ private class Notification extends IServiceNotification.Stub {
+ @Override
+ public final void onRegistration(String interfaceName, String instanceName,
+ boolean preexisting) {
+ if (!IHealth.kInterfaceName.equals(interfaceName)) return;
+ if (!sAllInstances.contains(instanceName)) return;
+ try {
+ // ensures the order of multiple onRegistration on different threads.
+ synchronized (mLastServiceSetLock) {
+ IHealth newService = mHealthSupplier.get(instanceName);
+ IHealth oldService = mLastService.getAndSet(newService);
+ Slog.i(TAG, "health: new instance registered " + instanceName);
+ mCallback.onRegistration(oldService, newService, instanceName);
+ }
+ } catch (NoSuchElementException | RemoteException ex) {
+ Slog.e(TAG, "health: Cannot get instance '" + instanceName + "': " +
+ ex.getMessage() + ". Perhaps no permission?");
+ }
+ }
+ }
+ }
}