summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Dianne Hackborn <hackbod@google.com> 2014-04-29 11:23:16 -0700
committer Dianne Hackborn <hackbod@google.com> 2014-04-29 11:23:16 -0700
commit260c5020ae65ddd14668b8ae496c169082aa13f6 (patch)
treee69168f593b191849c2c0ca34499349988b84558
parent21cc57319953e018c29876bab68a283600596ddd (diff)
Implement better computing of battery drain time, actual charge time.
Also move time formatter in to framework for use elsewhere. Change-Id: I42b6a44414b68754af65c522bef5591da5643909
-rw-r--r--core/java/android/text/format/Formatter.java65
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java155
-rw-r--r--core/res/res/values/strings.xml39
-rw-r--r--core/res/res/values/symbols.xml11
4 files changed, 268 insertions, 2 deletions
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 9c98b98dedf4..b0cbcd224c87 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -106,4 +106,69 @@ public final class Formatter {
public static String formatIpAddress(int ipv4Address) {
return NetworkUtils.intToInetAddress(ipv4Address).getHostAddress();
}
+
+ private static final int SECONDS_PER_MINUTE = 60;
+ private static final int SECONDS_PER_HOUR = 60 * 60;
+ private static final int SECONDS_PER_DAY = 24 * 60 * 60;
+
+ /**
+ * Returns elapsed time for the given millis, in the following format:
+ * 1 day 5 hrs; will include at most two units, can go down to seconds precision.
+ * @param context the application context
+ * @param millis the elapsed time in milli seconds
+ * @return the formatted elapsed time
+ * @hide
+ */
+ public static String formatShortElapsedTime(Context context, long millis) {
+ long secondsLong = millis / 1000;
+
+ int days = 0, hours = 0, minutes = 0;
+ if (secondsLong >= SECONDS_PER_DAY) {
+ days = (int)(secondsLong / SECONDS_PER_DAY);
+ secondsLong -= days * SECONDS_PER_DAY;
+ }
+ if (secondsLong >= SECONDS_PER_HOUR) {
+ hours = (int)(secondsLong / SECONDS_PER_HOUR);
+ secondsLong -= hours * SECONDS_PER_HOUR;
+ }
+ if (secondsLong >= SECONDS_PER_MINUTE) {
+ minutes = (int)(secondsLong / SECONDS_PER_MINUTE);
+ secondsLong -= minutes * SECONDS_PER_MINUTE;
+ }
+ int seconds = (int)secondsLong;
+
+ if (days >= 2) {
+ days += (hours+12)/24;
+ return context.getString(com.android.internal.R.string.durationDays, days);
+ } else if (days > 0) {
+ if (hours == 1) {
+ return context.getString(com.android.internal.R.string.durationDayHour, days, hours);
+ }
+ return context.getString(com.android.internal.R.string.durationDayHours, days, hours);
+ } else if (hours >= 2) {
+ hours += (minutes+30)/60;
+ return context.getString(com.android.internal.R.string.durationHours, hours);
+ } else if (hours > 0) {
+ if (minutes == 1) {
+ return context.getString(com.android.internal.R.string.durationHourMinute, hours,
+ minutes);
+ }
+ return context.getString(com.android.internal.R.string.durationHourMinutes, hours,
+ minutes);
+ } else if (minutes >= 2) {
+ minutes += (seconds+30)/60;
+ return context.getString(com.android.internal.R.string.durationMinutes, minutes);
+ } else if (minutes > 0) {
+ if (seconds == 1) {
+ return context.getString(com.android.internal.R.string.durationMinuteSecond, minutes,
+ seconds);
+ }
+ return context.getString(com.android.internal.R.string.durationMinuteSeconds, minutes,
+ seconds);
+ } else if (seconds == 1) {
+ return context.getString(com.android.internal.R.string.durationSecond, seconds);
+ } else {
+ return context.getString(com.android.internal.R.string.durationSeconds, seconds);
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 343c507c03ea..93d2297ce2cb 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -88,7 +88,7 @@ public final class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 103 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 104 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -319,6 +319,18 @@ public final class BatteryStatsImpl extends BatteryStats {
int mDischargeAmountScreenOff;
int mDischargeAmountScreenOffSinceCharge;
+ static final int MAX_LEVEL_STEPS = 100;
+
+ int mLastDischargeStepLevel;
+ long mLastDischargeStepTime;
+ int mNumDischargeStepDurations;
+ final long[] mDischargeStepDurations = new long[MAX_LEVEL_STEPS];
+
+ int mLastChargeStepLevel;
+ long mLastChargeStepTime;
+ int mNumChargeStepDurations;
+ final long[] mChargeStepDurations = new long[MAX_LEVEL_STEPS];
+
long mLastWriteTime = 0; // Milliseconds
private int mBluetoothPingCount;
@@ -5721,6 +5733,10 @@ public final class BatteryStatsImpl extends BatteryStats {
mDischargeAmountScreenOnSinceCharge = 0;
mDischargeAmountScreenOff = 0;
mDischargeAmountScreenOffSinceCharge = 0;
+ mLastDischargeStepTime = -1;
+ mNumDischargeStepDurations = 0;
+ mLastChargeStepTime = -1;
+ mNumChargeStepDurations = 0;
}
public void resetAllStatsCmdLocked() {
@@ -5885,7 +5901,10 @@ public final class BatteryStatsImpl extends BatteryStats {
resetAllStatsLocked();
mDischargeStartLevel = level;
reset = true;
+ mNumDischargeStepDurations = 0;
}
+ mLastDischargeStepLevel = level;
+ mLastDischargeStepTime = -1;
pullPendingStateUpdatesLocked();
mHistoryCur.batteryLevel = (byte)level;
mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
@@ -5921,6 +5940,9 @@ public final class BatteryStatsImpl extends BatteryStats {
}
updateDischargeScreenLevelsLocked(mScreenOn, mScreenOn);
updateTimeBasesLocked(false, !mScreenOn, uptime, realtime);
+ mNumChargeStepDurations = 0;
+ mLastChargeStepLevel = level;
+ mLastChargeStepTime = -1;
}
if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
if (mFile != null) {
@@ -5944,6 +5966,24 @@ public final class BatteryStatsImpl extends BatteryStats {
// This should probably be exposed in the API, though it's not critical
private static final int BATTERY_PLUGGED_NONE = 0;
+ private static int addLevelSteps(long[] steps, int stepCount, long lastStepTime,
+ int numStepLevels, 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;
+ }
+ stepCount += numStepLevels;
+ if (stepCount > steps.length) {
+ stepCount = steps.length;
+ }
+ }
+ return stepCount;
+ }
+
public void setBatteryState(int status, int health, int plugType, int level,
int temp, int volt) {
synchronized(this) {
@@ -6021,6 +6061,23 @@ public final class BatteryStatsImpl extends BatteryStats {
if (changed) {
addHistoryRecordLocked(elapsedRealtime, uptime);
}
+ if (onBattery) {
+ if (mLastDischargeStepLevel != level) {
+ mNumDischargeStepDurations = addLevelSteps(mDischargeStepDurations,
+ mNumDischargeStepDurations, mLastDischargeStepTime,
+ mLastDischargeStepLevel - level, elapsedRealtime);
+ mLastDischargeStepLevel = level;
+ mLastDischargeStepTime = elapsedRealtime;
+ }
+ } else {
+ if (mLastChargeStepLevel != level) {
+ mNumChargeStepDurations = addLevelSteps(mChargeStepDurations,
+ mNumChargeStepDurations, mLastChargeStepTime,
+ level - mLastChargeStepLevel, elapsedRealtime);
+ mLastChargeStepLevel = level;
+ mLastChargeStepTime = elapsedRealtime;
+ }
+ }
}
if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
// We don't record history while we are plugged in and fully charged.
@@ -6235,11 +6292,51 @@ public final class BatteryStatsImpl extends BatteryStats {
return mOnBatteryScreenOffTimeBase.computeRealtime(curTime, which);
}
+ private long computeTimePerLevel(long[] steps, int numSteps) {
+ // For now we'll do a simple average across all steps.
+ if (numSteps <= 0) {
+ return -1;
+ }
+ long total = 0;
+ for (int i=0; i<numSteps; i++) {
+ total += steps[i];
+ }
+ return total / numSteps;
+ /*
+ long[] buckets = new long[numSteps];
+ int numBuckets = 0;
+ int numToAverage = 4;
+ int i = 0;
+ while (i < numSteps) {
+ long totalTime = 0;
+ int num = 0;
+ for (int j=0; j<numToAverage && (i+j)<numSteps; j++) {
+ totalTime += steps[i+j];
+ num++;
+ }
+ buckets[numBuckets] = totalTime / num;
+ numBuckets++;
+ numToAverage *= 2;
+ i += num;
+ }
+ if (numBuckets < 1) {
+ return -1;
+ }
+ long averageTime = buckets[numBuckets-1];
+ for (i=numBuckets-2; i>=0; i--) {
+ averageTime = (averageTime + buckets[i]) / 2;
+ }
+ return averageTime;
+ */
+ }
+
@Override
public long computeBatteryTimeRemaining(long curTime) {
if (!mOnBattery) {
return -1;
}
+ /* Simple implementation just looks at the average discharge per level across the
+ entire sample period.
int discharge = (getLowDischargeAmountSinceCharge()+getHighDischargeAmountSinceCharge())/2;
if (discharge < 2) {
return -1;
@@ -6250,14 +6347,24 @@ public final class BatteryStatsImpl extends BatteryStats {
}
long usPerLevel = duration/discharge;
return usPerLevel * mCurrentBatteryLevel;
+ */
+ if (mNumDischargeStepDurations < 1) {
+ return -1;
+ }
+ long msPerLevel = computeTimePerLevel(mDischargeStepDurations, mNumDischargeStepDurations);
+ if (msPerLevel <= 0) {
+ return -1;
+ }
+ return (msPerLevel * mCurrentBatteryLevel) * 1000;
}
@Override
public long computeChargeTimeRemaining(long curTime) {
- if (true || mOnBattery) {
+ if (mOnBattery) {
// Not yet working.
return -1;
}
+ /* Broken
int curLevel = mCurrentBatteryLevel;
int plugLevel = mDischargePlugLevel;
if (plugLevel < 0 || curLevel < (plugLevel+1)) {
@@ -6269,6 +6376,15 @@ public final class BatteryStatsImpl extends BatteryStats {
}
long usPerLevel = duration/(curLevel-plugLevel);
return usPerLevel * (100-curLevel);
+ */
+ if (mNumChargeStepDurations < 1) {
+ return -1;
+ }
+ long msPerLevel = computeTimePerLevel(mChargeStepDurations, mNumChargeStepDurations);
+ if (msPerLevel <= 0) {
+ return -1;
+ }
+ return (msPerLevel * (100-mCurrentBatteryLevel)) * 1000;
}
long getBatteryUptimeLocked() {
@@ -6776,6 +6892,10 @@ public final class BatteryStatsImpl extends BatteryStats {
mHighDischargeAmountSinceCharge = in.readInt();
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
+ mNumDischargeStepDurations = in.readInt();
+ in.readLongArray(mDischargeStepDurations);
+ mNumChargeStepDurations = in.readInt();
+ in.readLongArray(mChargeStepDurations);
mStartCount++;
@@ -7030,6 +7150,10 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(getHighDischargeAmountSinceCharge());
out.writeInt(getDischargeAmountScreenOnSinceCharge());
out.writeInt(getDischargeAmountScreenOffSinceCharge());
+ out.writeInt(mNumDischargeStepDurations);
+ out.writeLongArray(mDischargeStepDurations);
+ out.writeInt(mNumChargeStepDurations);
+ out.writeLongArray(mChargeStepDurations);
mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
@@ -7344,6 +7468,10 @@ public final class BatteryStatsImpl extends BatteryStats {
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOff = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
+ mNumDischargeStepDurations = in.readInt();
+ in.readLongArray(mDischargeStepDurations);
+ mNumChargeStepDurations = in.readInt();
+ in.readLongArray(mChargeStepDurations);
mLastWriteTime = in.readLong();
mBluetoothPingCount = in.readInt();
@@ -7464,6 +7592,10 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeInt(mDischargeAmountScreenOnSinceCharge);
out.writeInt(mDischargeAmountScreenOff);
out.writeInt(mDischargeAmountScreenOffSinceCharge);
+ out.writeInt(mNumDischargeStepDurations);
+ out.writeLongArray(mDischargeStepDurations);
+ out.writeInt(mNumChargeStepDurations);
+ out.writeLongArray(mChargeStepDurations);
out.writeLong(mLastWriteTime);
out.writeInt(getBluetoothPingCount());
@@ -7573,6 +7705,25 @@ public final class BatteryStatsImpl extends BatteryStats {
pr.println("*** Bluetooth active type #" + i + ":");
mBluetoothStateTimer[i].logState(pr, " ");
}
+ StringBuilder sb = new StringBuilder(128);
+ if (mNumDischargeStepDurations > 0) {
+ pr.println("*** Discharge step durations:");
+ for (int i=0; i<mNumDischargeStepDurations; i++) {
+ sb.setLength(0);
+ sb.append(" #"); sb.append(i); sb.append(": ");
+ formatTimeMs(sb, mDischargeStepDurations[i]);
+ pr.println(sb.toString());
+ }
+ }
+ if (mNumChargeStepDurations > 0) {
+ pr.println("*** Charge step durations:");
+ for (int i=0; i<mNumChargeStepDurations; i++) {
+ sb.setLength(0);
+ sb.append(" #"); sb.append(i); sb.append(": ");
+ formatTimeMs(sb, mChargeStepDurations[i]);
+ pr.println(sb.toString());
+ }
+ }
}
super.dumpLocked(context, pw, flags, reqUid, histStart);
}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cb52db291e84..57b2c01b476a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -36,6 +36,45 @@
the placeholders. -->
<string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g><xliff:g id="unit" example="KB">%2$s</xliff:g></string>
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration in days -->
+ <string name="durationDays"><xliff:g id="days">%1$d</xliff:g> days</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one day with hours -->
+ <string name="durationDayHours"><xliff:g id="days">%1$d</xliff:g> day
+ <xliff:g id="hours">%2$d</xliff:g> hrs</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one day with one hours -->
+ <string name="durationDayHour"><xliff:g id="days">%1$d</xliff:g> day
+ <xliff:g id="hours">%2$d</xliff:g> hr</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration in hours -->
+ <string name="durationHours"><xliff:g id="hours">%1$d</xliff:g> hrs</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one hour with minutes -->
+ <string name="durationHourMinutes"><xliff:g id="hours">%1$d</xliff:g> hr
+ <xliff:g id="minutes">%2$d</xliff:g> mins</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one hour with one minute -->
+ <string name="durationHourMinute"><xliff:g id="hours">%1$d</xliff:g> hr
+ <xliff:g id="minutes">%2$d</xliff:g> min</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration in minutes -->
+ <string name="durationMinutes"><xliff:g id="minutes">%1$d</xliff:g> mins</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one minute with seconds -->
+ <string name="durationMinuteSeconds"><xliff:g id="minutes">%1$d</xliff:g> min
+ <xliff:g id="seconds">%2$d</xliff:g> secs</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one minute with one second -->
+ <string name="durationMinuteSecond"><xliff:g id="minutes">%1$d</xliff:g> min
+ <xliff:g id="seconds">%2$d</xliff:g> sec</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration in seconds -->
+ <string name="durationSeconds"><xliff:g id="seconds">%1$d</xliff:g> secs</string>
+
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one second -->
+ <string name="durationSecond"><xliff:g id="seconds">%1$d</xliff:g> sec</string>
+
<!-- Used in Contacts for a field that has no label and in Note Pad
for a note with no name. -->
<string name="untitled">&lt;Untitled&gt;</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 431dab83c0f3..22708109e678 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -506,6 +506,17 @@
<java-symbol type="string" name="display_manager_overlay_display_secure_suffix" />
<java-symbol type="string" name="display_manager_overlay_display_title" />
<java-symbol type="string" name="double_tap_toast" />
+ <java-symbol type="string" name="durationDays" />
+ <java-symbol type="string" name="durationDayHours" />
+ <java-symbol type="string" name="durationDayHour" />
+ <java-symbol type="string" name="durationHours" />
+ <java-symbol type="string" name="durationHourMinutes" />
+ <java-symbol type="string" name="durationHourMinute" />
+ <java-symbol type="string" name="durationMinutes" />
+ <java-symbol type="string" name="durationMinuteSeconds" />
+ <java-symbol type="string" name="durationMinuteSecond" />
+ <java-symbol type="string" name="durationSeconds" />
+ <java-symbol type="string" name="durationSecond" />
<java-symbol type="string" name="elapsed_time_short_format_h_mm_ss" />
<java-symbol type="string" name="elapsed_time_short_format_mm_ss" />
<java-symbol type="string" name="emailTypeCustom" />