Fix issue @16555033: Battery history overflowing too much
- No longer track process starts/stops normally.
- Increase buffer size to 256KB.
- Buffer size increase requires reworking how battery stats
are retrieved, since it is going to be hitting IPC limits.
- Also, store the last full stats after a reset, to be reported
at the next checkin.
- Also, discharge and charge times are tagged with the screen
and battery save state during that time.
Change-Id: Ie108ac9b626846108a9bb858101ac2b93276ac16
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index 176a81c..cc0d569 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -21,7 +21,6 @@
import android.content.pm.IPackageInstallerSession;
import android.content.pm.InstallSessionInfo;
import android.content.pm.InstallSessionParams;
-import android.os.ParcelFileDescriptor;
/** {@hide} */
interface IPackageInstaller {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 545b19a..ba79f91 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -33,6 +33,7 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
+import android.view.Display;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
@@ -1596,6 +1597,27 @@
*/
public abstract long computeBatteryTimeRemaining(long curTime);
+ // The part of a step duration that is the actual time.
+ public static final long STEP_LEVEL_TIME_MASK = 0x000000ffffffffffL;
+
+ // Bits in a step duration that are the new battery level we are at.
+ public static final long STEP_LEVEL_LEVEL_MASK = 0x0000ff0000000000L;
+ public static final long STEP_LEVEL_LEVEL_SHIFT = 40;
+
+ // Bits in a step duration that are the initial mode we were in at that step.
+ public static final long STEP_LEVEL_INITIAL_MODE_MASK = 0x00ff000000000000L;
+ public static final long STEP_LEVEL_INITIAL_MODE_SHIFT = 48;
+
+ // Bits in a step duration that indicate which modes changed during that step.
+ public static final long STEP_LEVEL_MODIFIED_MODE_MASK = 0xff00000000000000L;
+ public static final long STEP_LEVEL_MODIFIED_MODE_SHIFT = 56;
+
+ // Step duration mode: the screen is on, off, dozed, etc; value is Display.STATE_* - 1.
+ public static final int STEP_LEVEL_MODE_SCREEN_STATE = 0x03;
+
+ // Step duration mode: power save is on.
+ public static final int STEP_LEVEL_MODE_POWER_SAVE = 0x04;
+
/**
* Return the historical number of discharge steps we currently have.
*/
@@ -3620,14 +3642,60 @@
if (!checkin) {
pw.println(header);
}
- String[] lineArgs = new String[1];
+ String[] lineArgs = new String[4];
for (int i=0; i<count; i++) {
+ long duration = steps[i] & STEP_LEVEL_TIME_MASK;
+ int level = (int)((steps[i] & STEP_LEVEL_LEVEL_MASK)
+ >> STEP_LEVEL_LEVEL_SHIFT);
+ long initMode = (steps[i] & STEP_LEVEL_INITIAL_MODE_MASK)
+ >> STEP_LEVEL_INITIAL_MODE_SHIFT;
+ long modMode = (steps[i] & STEP_LEVEL_MODIFIED_MODE_MASK)
+ >> STEP_LEVEL_MODIFIED_MODE_SHIFT;
if (checkin) {
- lineArgs[0] = Long.toString(steps[i]);
+ lineArgs[0] = Long.toString(duration);
+ lineArgs[1] = Integer.toString(level);
+ if ((modMode&STEP_LEVEL_MODE_SCREEN_STATE) == 0) {
+ switch ((int)(initMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
+ case Display.STATE_OFF: lineArgs[2] = "s-"; break;
+ case Display.STATE_ON: lineArgs[2] = "s+"; break;
+ case Display.STATE_DOZE: lineArgs[2] = "sd"; break;
+ case Display.STATE_DOZE_SUSPEND: lineArgs[2] = "sds"; break;
+ default: lineArgs[1] = "?"; break;
+ }
+ } else {
+ lineArgs[2] = "";
+ }
+ if ((modMode&STEP_LEVEL_MODE_POWER_SAVE) == 0) {
+ lineArgs[3] = (initMode&STEP_LEVEL_MODE_POWER_SAVE) != 0 ? "p+" : "p-";
+ } else {
+ lineArgs[3] = "";
+ }
dumpLine(pw, 0 /* uid */, "i" /* category */, header, (Object[])lineArgs);
} else {
pw.print(" #"); pw.print(i); pw.print(": ");
- TimeUtils.formatDuration(steps[i], pw);
+ TimeUtils.formatDuration(duration, pw);
+ pw.print(" to "); pw.print(level);
+ boolean haveModes = false;
+ if ((modMode&STEP_LEVEL_MODE_SCREEN_STATE) == 0) {
+ pw.print(" (");
+ switch ((int)(initMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
+ case Display.STATE_OFF: pw.print("screen-off"); break;
+ case Display.STATE_ON: pw.print("screen-on"); break;
+ case Display.STATE_DOZE: pw.print("screen-doze"); break;
+ case Display.STATE_DOZE_SUSPEND: pw.print("screen-doze-suspend"); break;
+ default: lineArgs[1] = "screen-?"; break;
+ }
+ haveModes = true;
+ }
+ if ((modMode&STEP_LEVEL_MODE_POWER_SAVE) == 0) {
+ pw.print(haveModes ? ", " : " (");
+ pw.print((initMode&STEP_LEVEL_MODE_POWER_SAVE) != 0
+ ? "power-save-on" : "power-save-off");
+ haveModes = true;
+ }
+ if (haveModes) {
+ pw.print(")");
+ }
pw.println();
}
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index c43644a..713fcd8 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -18,7 +18,6 @@
package android.os;
import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
import android.content.pm.UserInfo;
import android.content.RestrictionEntry;
import android.graphics.Bitmap;
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 5655506..40965f0 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -18,6 +18,7 @@
import com.android.internal.os.BatteryStatsImpl;
+import android.os.ParcelFileDescriptor;
import android.os.WorkSource;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.SignalStrength;
@@ -37,6 +38,8 @@
// Remaining methods are only used in Java.
byte[] getStatistics();
+ ParcelFileDescriptor getStatisticsStream();
+
// Return the computed amount of time remaining on battery, in milliseconds.
// Returns -1 if nothing could be computed.
long computeBatteryTimeRemaining();
diff --git a/core/java/com/android/internal/os/AtomicFile.java b/core/java/com/android/internal/os/AtomicFile.java
index 445d10a..5a83f33 100644
--- a/core/java/com/android/internal/os/AtomicFile.java
+++ b/core/java/com/android/internal/os/AtomicFile.java
@@ -40,7 +40,7 @@
* appropriate mutual exclusion invariants whenever it accesses the file.
* </p>
*/
-public class AtomicFile {
+public final class AtomicFile {
private final File mBaseName;
private final File mBackupName;
@@ -129,7 +129,16 @@
} catch (IOException e) {
}
}
-
+
+ public boolean exists() {
+ return mBaseName.exists() || mBackupName.exists();
+ }
+
+ public void delete() {
+ mBaseName.delete();
+ mBackupName.delete();
+ }
+
public FileInputStream openRead() throws FileNotFoundException {
if (mBackupName.exists()) {
mBaseName.delete();
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 6c9b4b8..63be2d4 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -30,19 +30,26 @@
import android.os.BatteryStats;
import android.os.BatteryStats.Uid;
import android.os.Bundle;
+import android.os.MemoryFile;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.telephony.SignalStrength;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BatterySipper.DrainType;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -55,7 +62,7 @@
* The caller must initialize this class as soon as activity object is ready to use (for example, in
* onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
*/
-public class BatteryStatsHelper {
+public final class BatteryStatsHelper {
private static final boolean DEBUG = false;
@@ -63,6 +70,7 @@
private static BatteryStats sStatsXfer;
private static Intent sBatteryBroadcastXfer;
+ private static ArrayMap<File, BatteryStats> sFileXfer = new ArrayMap<>();
final private Context mContext;
final private boolean mCollectBatteryBroadcast;
@@ -117,6 +125,68 @@
mCollectBatteryBroadcast = collectBatteryBroadcast;
}
+ public void storeStatsHistoryInFile(String fname) {
+ synchronized (sFileXfer) {
+ File path = makeFilePath(mContext, fname);
+ sFileXfer.put(path, this.getStats());
+ FileOutputStream fout = null;
+ try {
+ fout = new FileOutputStream(path);
+ Parcel hist = Parcel.obtain();
+ getStats().writeToParcelWithoutUids(hist, 0);
+ byte[] histData = hist.marshall();
+ fout.write(histData);
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to write history to file", e);
+ } finally {
+ if (fout != null) {
+ try {
+ fout.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+ }
+
+ public static BatteryStats statsFromFile(Context context, String fname) {
+ synchronized (sFileXfer) {
+ File path = makeFilePath(context, fname);
+ BatteryStats stats = sFileXfer.get(path);
+ if (stats != null) {
+ return stats;
+ }
+ FileInputStream fin = null;
+ try {
+ fin = new FileInputStream(path);
+ byte[] data = readFully(fin);
+ Parcel parcel = Parcel.obtain();
+ parcel.unmarshall(data, 0, data.length);
+ parcel.setDataPosition(0);
+ return com.android.internal.os.BatteryStatsImpl.CREATOR.createFromParcel(parcel);
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to read history to file", e);
+ } finally {
+ if (fin != null) {
+ try {
+ fin.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+ return getStats(IBatteryStats.Stub.asInterface(
+ ServiceManager.getService(BatteryStats.SERVICE_NAME)));
+ }
+
+ public static void dropFile(Context context, String fname) {
+ makeFilePath(context, fname).delete();
+ }
+
+ private static File makeFilePath(Context context, String fname) {
+ return new File(context.getFilesDir(), fname);
+ }
+
/** Clears the current stats and forces recreating for future use. */
public void clearStats() {
mStats = null;
@@ -853,25 +923,64 @@
public long getChargeTimeRemaining() { return mChargeTimeRemaining; }
+ public static byte[] readFully(FileInputStream stream) throws java.io.IOException {
+ return readFully(stream, stream.available());
+ }
+
+ public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException {
+ int pos = 0;
+ byte[] data = new byte[avail];
+ while (true) {
+ int amt = stream.read(data, pos, data.length-pos);
+ //Log.i("foo", "Read " + amt + " bytes at " + pos
+ // + " of avail " + data.length);
+ if (amt <= 0) {
+ //Log.i("foo", "**** FINISHED READING: pos=" + pos
+ // + " len=" + data.length);
+ return data;
+ }
+ pos += amt;
+ avail = stream.available();
+ if (avail > data.length-pos) {
+ byte[] newData = new byte[pos+avail];
+ System.arraycopy(data, 0, newData, 0, pos);
+ data = newData;
+ }
+ }
+ }
+
private void load() {
if (mBatteryInfo == null) {
return;
}
- try {
- byte[] data = mBatteryInfo.getStatistics();
- Parcel parcel = Parcel.obtain();
- parcel.unmarshall(data, 0, data.length);
- parcel.setDataPosition(0);
- BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
- .createFromParcel(parcel);
- stats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED);
- mStats = stats;
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException:", e);
- }
+ mStats = getStats(mBatteryInfo);
if (mCollectBatteryBroadcast) {
mBatteryBroadcast = mContext.registerReceiver(null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
}
+
+ private static BatteryStatsImpl getStats(IBatteryStats service) {
+ try {
+ ParcelFileDescriptor pfd = service.getStatisticsStream();
+ if (pfd != null) {
+ FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ try {
+ byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor()));
+ Parcel parcel = Parcel.obtain();
+ parcel.unmarshall(data, 0, data.length);
+ parcel.setDataPosition(0);
+ BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR
+ .createFromParcel(parcel);
+ stats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED);
+ return stats;
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to read statistics stream", e);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException:", e);
+ }
+ return null;
+ }
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index aad1156..50b86d0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -109,6 +109,7 @@
private static int sNumSpeedSteps;
private final JournaledFile mFile;
+ public final AtomicFile mCheckinFile;
static final int MSG_UPDATE_WAKELOCKS = 1;
static final int MSG_REPORT_POWER_CHANGE = 2;
@@ -142,7 +143,7 @@
}
}
- private final MyHandler mHandler;
+ public final MyHandler mHandler;
private BatteryCallback mCallback;
@@ -199,8 +200,8 @@
boolean mRecordingHistory = false;
int mNumHistoryItems;
- static final int MAX_HISTORY_BUFFER = 128*1024; // 128KB
- static final int MAX_MAX_HISTORY_BUFFER = 144*1024; // 144KB
+ static final int MAX_HISTORY_BUFFER = 256*1024; // 256KB
+ static final int MAX_MAX_HISTORY_BUFFER = 320*1024; // 320KB
final Parcel mHistoryBuffer = Parcel.obtain();
final HistoryItem mHistoryLastWritten = new HistoryItem();
final HistoryItem mHistoryLastLastWritten = new HistoryItem();
@@ -240,7 +241,7 @@
int mWakeLockNesting;
boolean mWakeLockImportant;
- boolean mRecordAllWakeLocks;
+ boolean mRecordAllHistory;
boolean mNoAutoReset;
int mScreenState = Display.STATE_UNKNOWN;
@@ -342,6 +343,10 @@
static final int MAX_LEVEL_STEPS = 100;
+ int mInitStepMode = 0;
+ int mCurStepMode = 0;
+ int mModStepMode = 0;
+
int mLastDischargeStepLevel;
long mLastDischargeStepTime;
int mMinDischargeStepLevel;
@@ -429,10 +434,11 @@
@GuardedBy("this")
private String[] mWifiIfaces = new String[0];
- // For debugging
public BatteryStatsImpl() {
mFile = null;
+ mCheckinFile = null;
mHandler = null;
+ clearHistoryLocked();
}
public static interface TimeBaseObs {
@@ -2353,6 +2359,9 @@
if (!mActiveEvents.updateState(HistoryItem.EVENT_PROC_START, name, uid, 0)) {
return;
}
+ if (!mRecordAllHistory) {
+ return;
+ }
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PROC_START, name, uid);
@@ -2371,9 +2380,12 @@
}
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
- addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PROC_FINISH, name, uid);
getUidStatsLocked(uid).updateProcessStateLocked(name, Uid.PROCESS_STATE_NONE,
elapsedRealtime);
+ if (!mRecordAllHistory) {
+ return;
+ }
+ addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PROC_FINISH, name, uid);
}
public void noteSyncStartLocked(String name, int uid) {
@@ -2427,11 +2439,41 @@
}
}
- public void setRecordAllWakeLocksLocked(boolean enabled) {
- mRecordAllWakeLocks = enabled;
+ public void setRecordAllHistoryLocked(boolean enabled) {
+ mRecordAllHistory = enabled;
if (!enabled) {
// Clear out any existing state.
mActiveEvents.removeEvents(HistoryItem.EVENT_WAKE_LOCK);
+ // Record the currently running processes as stopping, now that we are no
+ // longer tracking them.
+ HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(
+ HistoryItem.EVENT_PROC);
+ if (active != null) {
+ long mSecRealtime = SystemClock.elapsedRealtime();
+ final long mSecUptime = SystemClock.uptimeMillis();
+ for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
+ SparseIntArray uids = ent.getValue();
+ for (int j=0; j<uids.size(); j++) {
+ addHistoryEventLocked(mSecRealtime, mSecUptime,
+ HistoryItem.EVENT_PROC_FINISH, ent.getKey(), uids.keyAt(j));
+ }
+ }
+ }
+ } else {
+ // Record the currently running processes as starting, now that we are tracking them.
+ HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(
+ HistoryItem.EVENT_PROC);
+ if (active != null) {
+ long mSecRealtime = SystemClock.elapsedRealtime();
+ final long mSecUptime = SystemClock.uptimeMillis();
+ for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
+ SparseIntArray uids = ent.getValue();
+ for (int j=0; j<uids.size(); j++) {
+ addHistoryEventLocked(mSecRealtime, mSecUptime,
+ HistoryItem.EVENT_PROC_START, ent.getKey(), uids.keyAt(j));
+ }
+ }
+ }
}
}
@@ -2452,7 +2494,7 @@
if (historyName == null) {
historyName = name;
}
- if (mRecordAllWakeLocks) {
+ if (mRecordAllHistory) {
if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName,
uid, 0)) {
addHistoryEventLocked(elapsedRealtime, uptime,
@@ -2496,7 +2538,7 @@
uid = mapUid(uid);
if (type == WAKE_TYPE_PARTIAL) {
mWakeLockNesting--;
- if (mRecordAllWakeLocks) {
+ if (mRecordAllHistory) {
if (historyName == null) {
historyName = name;
}
@@ -2788,6 +2830,18 @@
if (DEBUG) Slog.v(TAG, "Screen state: oldState=" + Display.stateToString(oldState)
+ ", newState=" + Display.stateToString(state));
+ if (state != Display.STATE_UNKNOWN) {
+ int stepState = state-1;
+ if (stepState < 4) {
+ mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_SCREEN_STATE) ^ stepState;
+ mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_SCREEN_STATE) | stepState;
+ } else {
+ Slog.wtf(TAG, "Unexpected screen state: " + state);
+ }
+ }
+
+ mInitStepMode = mCurStepMode;
+ mModStepMode = 0;
if (state == Display.STATE_ON) {
// Screen turning on.
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -2924,6 +2978,9 @@
public void noteLowPowerMode(boolean enabled) {
if (mLowPowerModeEnabled != enabled) {
+ int stepState = enabled ? STEP_LEVEL_MODE_POWER_SAVE : 0;
+ mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
+ mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
mLowPowerModeEnabled = enabled;
@@ -6032,8 +6089,14 @@
}
}
- public BatteryStatsImpl(String filename, Handler handler) {
- mFile = new JournaledFile(new File(filename), new File(filename + ".tmp"));
+ public BatteryStatsImpl(File systemDir, Handler handler) {
+ if (systemDir != null) {
+ mFile = new JournaledFile(new File(systemDir, "batterystats.bin"),
+ new File(systemDir, "batterystats.bin.tmp"));
+ } else {
+ mFile = null;
+ }
+ mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
mHandler = new MyHandler(handler.getLooper());
mStartCount++;
mScreenOnTimer = new StopwatchTimer(null, -1, null, mOnBatteryTimeBase);
@@ -6095,6 +6158,7 @@
public BatteryStatsImpl(Parcel p) {
mFile = null;
+ mCheckinFile = null;
mHandler = null;
clearHistoryLocked();
readFromParcel(p);
@@ -6390,6 +6454,10 @@
private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) {
for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
+ if (!mRecordAllHistory && i == HistoryItem.EVENT_PROC) {
+ // Not recording process starts/stops.
+ continue;
+ }
HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(i);
if (active == null) {
continue;
@@ -6455,7 +6523,37 @@
boolean reset = false;
if (!mNoAutoReset && (oldStatus == BatteryManager.BATTERY_STATUS_FULL
|| level >= 90
- || (mDischargeCurrentLevel < 20 && level >= 80))) {
+ || getLowDischargeAmountSinceCharge() >= 60)
+ || (getHighDischargeAmountSinceCharge() >= 60
+ && mHistoryBuffer.dataSize() >= MAX_HISTORY_BUFFER)) {
+ // Before we write, collect a snapshot of the final aggregated
+ // stats to be reported in the next checkin. Only do this if we have
+ // a sufficient amount of data to make it interesting.
+ if (getLowDischargeAmountSinceCharge() >= 20) {
+ final Parcel parcel = Parcel.obtain();
+ writeSummaryToParcel(parcel, true);
+ BackgroundThread.getHandler().post(new Runnable() {
+ @Override public void run() {
+ synchronized (mCheckinFile) {
+ FileOutputStream stream = null;
+ try {
+ stream = mCheckinFile.startWrite();
+ stream.write(parcel.marshall());
+ stream.flush();
+ FileUtils.sync(stream);
+ stream.close();
+ mCheckinFile.finishWrite(stream);
+ } catch (IOException e) {
+ Slog.w("BatteryStats",
+ "Error writing checkin battery statistics", e);
+ mCheckinFile.failWrite(stream);
+ } finally {
+ parcel.recycle();
+ }
+ }
+ }
+ });
+ }
doWrite = true;
resetAllStatsLocked();
mDischargeStartLevel = level;
@@ -6465,6 +6563,8 @@
mLastDischargeStepLevel = level;
mMinDischargeStepLevel = level;
mLastDischargeStepTime = -1;
+ mInitStepMode = mCurStepMode;
+ mModStepMode = 0;
pullPendingStateUpdatesLocked();
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
@@ -6504,6 +6604,8 @@
mLastChargeStepLevel = level;
mMaxChargeStepLevel = level;
mLastChargeStepTime = -1;
+ mInitStepMode = mCurStepMode;
+ mModStepMode = 0;
}
if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
if (mFile != null) {
@@ -6529,14 +6631,17 @@
private static final int BATTERY_PLUGGED_NONE = 0;
private static int addLevelSteps(long[] steps, int stepCount, long lastStepTime,
- int numStepLevels, long elapsedRealtime) {
+ int numStepLevels, long modeBits, long elapsedRealtime) {
if (lastStepTime >= 0 && numStepLevels > 0) {
long duration = elapsedRealtime - lastStepTime;
for (int i=0; i<numStepLevels; i++) {
System.arraycopy(steps, 0, steps, 1, steps.length-1);
long thisDuration = duration / (numStepLevels-i);
duration -= thisDuration;
- steps[0] = thisDuration;
+ if (thisDuration > STEP_LEVEL_TIME_MASK) {
+ thisDuration = STEP_LEVEL_TIME_MASK;
+ }
+ steps[0] = thisDuration | modeBits;
}
stepCount += numStepLevels;
if (stepCount > steps.length) {
@@ -6623,23 +6728,30 @@
if (changed) {
addHistoryRecordLocked(elapsedRealtime, uptime);
}
+ long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
+ | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)
+ | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);
if (onBattery) {
if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
mNumDischargeStepDurations = addLevelSteps(mDischargeStepDurations,
mNumDischargeStepDurations, mLastDischargeStepTime,
- mLastDischargeStepLevel - level, elapsedRealtime);
+ mLastDischargeStepLevel - level, modeBits, elapsedRealtime);
mLastDischargeStepLevel = level;
mMinDischargeStepLevel = level;
mLastDischargeStepTime = elapsedRealtime;
+ mInitStepMode = mCurStepMode;
+ mModStepMode = 0;
}
} else {
if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
mNumChargeStepDurations = addLevelSteps(mChargeStepDurations,
mNumChargeStepDurations, mLastChargeStepTime,
- level - mLastChargeStepLevel, elapsedRealtime);
+ level - mLastChargeStepLevel, modeBits, elapsedRealtime);
mLastChargeStepLevel = level;
mMaxChargeStepLevel = level;
mLastChargeStepTime = elapsedRealtime;
+ mInitStepMode = mCurStepMode;
+ mModStepMode = 0;
}
}
}
@@ -6863,7 +6975,7 @@
}
long total = 0;
for (int i=0; i<numSteps; i++) {
- total += steps[i];
+ total += steps[i] & STEP_LEVEL_TIME_MASK;
}
return total / numSteps;
/*
@@ -6875,7 +6987,7 @@
long totalTime = 0;
int num = 0;
for (int j=0; j<numToAverage && (i+j)<numSteps; j++) {
- totalTime += steps[i+j];
+ totalTime += steps[i+j] & STEP_LEVEL_TIME_MASK;
num++;
}
buckets[numBuckets] = totalTime / num;
@@ -7213,7 +7325,7 @@
}
Parcel out = Parcel.obtain();
- writeSummaryToParcel(out);
+ writeSummaryToParcel(out, true);
mLastWriteTime = SystemClock.elapsedRealtime();
if (mPendingWrite != null) {
@@ -7224,14 +7336,11 @@
if (sync) {
commitPendingDataToDisk();
} else {
- Thread thr = new Thread("BatteryStats-Write") {
- @Override
- public void run() {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ BackgroundThread.getHandler().post(new Runnable() {
+ @Override public void run() {
commitPendingDataToDisk();
}
- };
- thr.start();
+ });
}
}
@@ -7263,29 +7372,6 @@
}
}
- static byte[] readFully(FileInputStream stream) throws java.io.IOException {
- int pos = 0;
- int avail = stream.available();
- byte[] data = new byte[avail];
- while (true) {
- int amt = stream.read(data, pos, data.length-pos);
- //Log.i("foo", "Read " + amt + " bytes at " + pos
- // + " of avail " + data.length);
- if (amt <= 0) {
- //Log.i("foo", "**** FINISHED READING: pos=" + pos
- // + " len=" + data.length);
- return data;
- }
- pos += amt;
- avail = stream.available();
- if (avail > data.length-pos) {
- byte[] newData = new byte[pos+avail];
- System.arraycopy(data, 0, newData, 0, pos);
- data = newData;
- }
- }
- }
-
public void readLocked() {
if (mFile == null) {
Slog.w("BatteryStats", "readLocked: no file associated with this instance");
@@ -7301,7 +7387,7 @@
}
FileInputStream stream = new FileInputStream(file);
- byte[] raw = readFully(stream);
+ byte[] raw = BatteryStatsHelper.readFully(stream);
Parcel in = Parcel.obtain();
in.unmarshall(raw, 0, raw.length);
in.setDataPosition(0);
@@ -7410,7 +7496,7 @@
}
}
- void writeHistory(Parcel out, boolean andOldHistory) {
+ void writeHistory(Parcel out, boolean inclData, boolean andOldHistory) {
if (DEBUG_HISTORY) {
StringBuilder sb = new StringBuilder(128);
sb.append("****************** WRITING mHistoryBaseTime: ");
@@ -7420,6 +7506,11 @@
Slog.i(TAG, sb.toString());
}
out.writeLong(mHistoryBaseTime + mLastHistoryElapsedRealtime);
+ if (!inclData) {
+ out.writeInt(0);
+ out.writeInt(0);
+ return;
+ }
out.writeInt(mHistoryTagPool.size());
for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
HistoryTag tag = ent.getKey();
@@ -7449,7 +7540,7 @@
out.writeLong(-1);
}
- private void readSummaryFromParcel(Parcel in) {
+ public void readSummaryFromParcel(Parcel in) {
final int version = in.readInt();
if (version != VERSION) {
Slog.w("BatteryStats", "readFromParcel: version got " + version
@@ -7742,7 +7833,7 @@
*
* @param out the Parcel to be written to.
*/
- public void writeSummaryToParcel(Parcel out) {
+ public void writeSummaryToParcel(Parcel out, boolean inclHistory) {
pullPendingStateUpdatesLocked();
final long NOW_SYS = SystemClock.uptimeMillis() * 1000;
@@ -7750,7 +7841,7 @@
out.writeInt(VERSION);
- writeHistory(out, true);
+ writeHistory(out, inclHistory, true);
out.writeInt(mStartCount);
out.writeLong(computeUptime(NOW_SYS, STATS_SINCE_CHARGED));
@@ -8194,7 +8285,7 @@
out.writeInt(MAGIC);
- writeHistory(out, false);
+ writeHistory(out, true, false);
out.writeInt(mStartCount);
out.writeLong(mStartClockTime);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e0d4aad..b6f555d 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2200,8 +2200,7 @@
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
systemDir.mkdirs();
- mBatteryStatsService = new BatteryStatsService(new File(
- systemDir, "batterystats.bin").toString(), mHandler);
+ mBatteryStatsService = new BatteryStatsService(systemDir, mHandler);
mBatteryStatsService.getActiveStatistics().readLocked();
mBatteryStatsService.getActiveStatistics().writeAsyncLocked();
mOnBattery = DEBUG_POWER ? true
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3fdeb54..d8da700 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -27,6 +27,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.ServiceManager;
@@ -42,7 +43,10 @@
import com.android.internal.os.PowerProfile;
import com.android.server.LocalServices;
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
@@ -62,8 +66,8 @@
private BluetoothHeadset mBluetoothHeadset;
PowerManagerInternal mPowerManagerInternal;
- BatteryStatsService(String filename, Handler handler) {
- mStats = new BatteryStatsImpl(filename, handler);
+ BatteryStatsService(File systemDir, Handler handler) {
+ mStats = new BatteryStatsImpl(systemDir, handler);
}
public void publish(Context context) {
@@ -117,7 +121,7 @@
public BatteryStatsImpl getActiveStatistics() {
return mStats;
}
-
+
public byte[] getStatistics() {
mContext.enforceCallingPermission(
android.Manifest.permission.BATTERY_STATS, null);
@@ -129,7 +133,24 @@
out.recycle();
return data;
}
-
+
+ public ParcelFileDescriptor getStatisticsStream() {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.BATTERY_STATS, null);
+ //Slog.i("foo", "SENDING BATTERY INFO:");
+ //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
+ Parcel out = Parcel.obtain();
+ mStats.writeToParcel(out, 0);
+ byte[] data = out.marshall();
+ out.recycle();
+ try {
+ return ParcelFileDescriptor.fromData(data, "battery-stats");
+ } catch (IOException e) {
+ Slog.w(TAG, "Unable to create shared memory", e);
+ return null;
+ }
+ }
+
public long computeBatteryTimeRemaining() {
synchronized (mStats) {
long time = mStats.computeBatteryTimeRemaining(SystemClock.elapsedRealtime());
@@ -726,7 +747,8 @@
pw.println(" enable|disable <option>");
pw.println(" Enable or disable a running option. Option state is not saved across boots.");
pw.println(" Options are:");
- pw.println(" full-wake-history: include wake_lock_in battery history, full wake details.");
+ pw.println(" full-history: include additional detailed events in battery history:");
+ pw.println(" wake_lock_in and proc events");
pw.println(" no-auto-reset: don't automatically reset stats when unplugged");
}
@@ -737,9 +759,9 @@
dumpHelp(pw);
return -1;
}
- if ("full-wake-history".equals(args[i])) {
+ if ("full-wake-history".equals(args[i]) || "full-history".equals(args[i])) {
synchronized (mStats) {
- mStats.setRecordAllWakeLocksLocked(enable);
+ mStats.setRecordAllHistoryLocked(enable);
}
} else if ("no-auto-reset".equals(args[i])) {
synchronized (mStats) {
@@ -764,7 +786,8 @@
}
int flags = 0;
- boolean isCheckin = false;
+ boolean useCheckinFormat = false;
+ boolean isRealCheckin = false;
boolean noOutput = false;
boolean writeData = false;
long historyStart = -1;
@@ -773,7 +796,8 @@
for (int i=0; i<args.length; i++) {
String arg = args[i];
if ("--checkin".equals(arg)) {
- isCheckin = true;
+ useCheckinFormat = true;
+ isRealCheckin = true;
} else if ("--history".equals(arg)) {
flags |= BatteryStats.DUMP_HISTORY_ONLY;
} else if ("--history-start".equals(arg)) {
@@ -787,7 +811,7 @@
historyStart = Long.parseLong(args[i]);
writeData = true;
} else if ("-c".equals(arg)) {
- isCheckin = true;
+ useCheckinFormat = true;
flags |= BatteryStats.DUMP_INCLUDE_HISTORY;
} else if ("--unplugged".equals(arg)) {
flags |= BatteryStats.DUMP_UNPLUGGED_ONLY;
@@ -844,8 +868,35 @@
if (noOutput) {
return;
}
- if (isCheckin) {
+ if (useCheckinFormat) {
List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
+ if (isRealCheckin) {
+ // For a real checkin, first we want to prefer to use the last complete checkin
+ // file if there is one.
+ synchronized (mStats.mCheckinFile) {
+ if (mStats.mCheckinFile.exists()) {
+ try {
+ byte[] raw = mStats.mCheckinFile.readFully();
+ if (raw != null) {
+ Parcel in = Parcel.obtain();
+ in.unmarshall(raw, 0, raw.length);
+ in.setDataPosition(0);
+ BatteryStatsImpl checkinStats = new BatteryStatsImpl(
+ null, mStats.mHandler);
+ checkinStats.readSummaryFromParcel(in);
+ in.recycle();
+ checkinStats.dumpCheckinLocked(mContext, pw, apps, flags,
+ historyStart);
+ mStats.mCheckinFile.delete();
+ return;
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failure reading checkin file "
+ + mStats.mCheckinFile.getBaseFile(), e);
+ }
+ }
+ }
+ }
synchronized (mStats) {
mStats.dumpCheckinLocked(mContext, pw, apps, flags, historyStart);
if (writeData) {