diff options
Diffstat (limited to 'services/java/com/android/server/BatteryService.java')
-rw-r--r-- | services/java/com/android/server/BatteryService.java | 116 |
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; |