summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/BatteryService.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/BatteryService.java')
-rw-r--r--services/java/com/android/server/BatteryService.java116
1 files changed, 115 insertions, 1 deletions
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 9c9a580..3a9a59f 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -20,20 +20,31 @@ import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;
import android.app.ActivityManagerNative;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.BatteryManager;
import android.os.Binder;
+import android.os.Debug;
+import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UEventObserver;
+import android.provider.Checkin;
+import android.provider.Settings;
import android.util.EventLog;
import android.util.Log;
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.String;
+
+
/**
* <p>BatteryService monitors the charging status, and charge level of the device
@@ -60,12 +71,24 @@ import java.lang.String;
class BatteryService extends Binder {
private static final String TAG = BatteryService.class.getSimpleName();
+ private static final boolean LOCAL_LOGV = false;
+
static final int LOG_BATTERY_LEVEL = 2722;
static final int LOG_BATTERY_STATUS = 2723;
static final int LOG_BATTERY_DISCHARGE_STATUS = 2730;
static final int BATTERY_SCALE = 100; // battery capacity is a percentage
+ // Used locally for determining when to make a last ditch effort to log
+ // discharge stats before the device dies.
+ private static final int CRITICAL_BATTERY_LEVEL = 4;
+
+ private static final int DUMP_MAX_LENGTH = 24 * 1024;
+ private static final String[] DUMPSYS_ARGS = new String[] { "-c", "-u" };
+ private static final String BATTERY_STATS_SERVICE_NAME = "batteryinfo";
+
+ private static final String DUMPSYS_DATA_PATH = "/data/system/";
+
// This should probably be exposed in the API, though it's not critical
private static final int BATTERY_PLUGGED_NONE = 0;
@@ -81,6 +104,7 @@ class BatteryService extends Binder {
private int mBatteryVoltage;
private int mBatteryTemperature;
private String mBatteryTechnology;
+ private boolean mBatteryLevelCritical;
private int mLastBatteryStatus;
private int mLastBatteryHealth;
@@ -88,6 +112,7 @@ class BatteryService extends Binder {
private int mLastBatteryLevel;
private int mLastBatteryVoltage;
private int mLastBatteryTemperature;
+ private boolean mLastBatteryLevelCritical;
private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run
@@ -95,6 +120,7 @@ class BatteryService extends Binder {
private long mDischargeStartTime;
private int mDischargeStartLevel;
+
public BatteryService(Context context) {
mContext = context;
mBatteryStats = BatteryStatsService.getService();
@@ -149,6 +175,8 @@ class BatteryService extends Binder {
private synchronized final void update() {
native_update();
+
+ mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
if (mAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
} else if (mUsbOnline) {
@@ -176,6 +204,8 @@ class BatteryService extends Binder {
mDischargeStartLevel, mBatteryLevel);
// make sure we see a discharge event before logging again
mDischargeStartTime = 0;
+
+ logOutlier(duration);
}
} else if (mPlugType == BATTERY_PLUGGED_NONE) {
// charging -> discharging or we just powered up
@@ -197,6 +227,12 @@ class BatteryService extends Binder {
EventLog.writeEvent(LOG_BATTERY_LEVEL,
mBatteryLevel, mBatteryVoltage, mBatteryTemperature);
}
+ if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
+ mPlugType == BATTERY_PLUGGED_NONE) {
+ // We want to make sure we log discharge cycle outliers
+ // if the battery is about to die.
+ logOutlier(SystemClock.elapsedRealtime() - mDischargeStartTime);
+ }
mLastBatteryStatus = mBatteryStatus;
mLastBatteryHealth = mBatteryHealth;
@@ -205,6 +241,7 @@ class BatteryService extends Binder {
mLastPlugType = mPlugType;
mLastBatteryVoltage = mBatteryVoltage;
mLastBatteryTemperature = mBatteryTemperature;
+ mLastBatteryLevelCritical = mBatteryLevelCritical;
sendIntent();
}
@@ -247,6 +284,83 @@ class BatteryService extends Binder {
ActivityManagerNative.broadcastStickyIntent(intent, null);
}
+ private final void logBatteryStats() {
+
+ IBinder batteryInfoService = ServiceManager.getService(BATTERY_STATS_SERVICE_NAME);
+ if (batteryInfoService != null) {
+ byte[] buffer = new byte[DUMP_MAX_LENGTH];
+ File dumpFile = null;
+ FileOutputStream dumpStream = null;
+ try {
+ // dump the service to a file
+ dumpFile = new File(DUMPSYS_DATA_PATH + BATTERY_STATS_SERVICE_NAME + ".dump");
+ dumpStream = new FileOutputStream(dumpFile);
+ batteryInfoService.dump(dumpStream.getFD(), DUMPSYS_ARGS);
+ dumpStream.getFD().sync();
+
+ // read dumped file above into buffer truncated to DUMP_MAX_LENGTH
+ // and insert into events table.
+ int length = (int) Math.min(dumpFile.length(), DUMP_MAX_LENGTH);
+ FileInputStream fileInputStream = new FileInputStream(dumpFile);
+ int nread = fileInputStream.read(buffer, 0, length);
+ if (nread > 0) {
+ Checkin.logEvent(mContext.getContentResolver(),
+ Checkin.Events.Tag.BATTERY_DISCHARGE_INFO,
+ new String(buffer, 0, nread));
+ if (LOCAL_LOGV) Log.v(TAG, "dumped " + nread + "b from " +
+ batteryInfoService + "to log");
+ if (LOCAL_LOGV) Log.v(TAG, "actual dump:" + new String(buffer, 0, nread));
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to dump service '" + BATTERY_STATS_SERVICE_NAME +
+ "':" + e);
+ } catch (IOException e) {
+ Log.e(TAG, "failed to write dumpsys file: " + e);
+ } finally {
+ // make sure we clean up
+ if (dumpStream != null) {
+ try {
+ dumpStream.close();
+ } catch (IOException e) {
+ Log.e(TAG, "failed to close dumpsys output stream");
+ }
+ }
+ if (dumpFile != null && !dumpFile.delete()) {
+ Log.e(TAG, "failed to delete temporary dumpsys file: "
+ + dumpFile.getAbsolutePath());
+ }
+ }
+ }
+ }
+
+ private final void logOutlier(long duration) {
+ ContentResolver cr = mContext.getContentResolver();
+ String dischargeThresholdString = Settings.Gservices.getString(cr,
+ Settings.Gservices.BATTERY_DISCHARGE_THRESHOLD);
+ String durationThresholdString = Settings.Gservices.getString(cr,
+ Settings.Gservices.BATTERY_DISCHARGE_DURATION_THRESHOLD);
+
+ if (dischargeThresholdString != null && durationThresholdString != null) {
+ try {
+ long durationThreshold = Long.parseLong(durationThresholdString);
+ int dischargeThreshold = Integer.parseInt(dischargeThresholdString);
+ if (duration <= durationThreshold &&
+ mDischargeStartLevel - mBatteryLevel >= dischargeThreshold) {
+ // If the discharge cycle is bad enough we want to know about it.
+ logBatteryStats();
+ }
+ if (LOCAL_LOGV) Log.v(TAG, "duration threshold: " + durationThreshold +
+ " discharge threshold: " + dischargeThreshold);
+ if (LOCAL_LOGV) Log.v(TAG, "duration: " + duration + " discharge: " +
+ (mDischargeStartLevel - mBatteryLevel));
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Invalid DischargeThresholds GService string: " +
+ durationThresholdString + " or " + dischargeThresholdString);
+ return;
+ }
+ }
+ }
+
private final int getIcon(int level) {
if (mBatteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
return com.android.internal.R.drawable.stat_sys_battery_charge;