Start collecting mobile radio activity from the radio.

Hook in to the new radio API to find out when the radio
is active and use that to track its state in batter stats.
We also still have the data being tracked from the kernel's
emulation, and continue to use that if we don't get data from
the radio.

Currently this monitoring is turned off until some issues
in the radio can be fixed that are providing bad data.

Also add a new API to get estimated drain and charge times.

Change-Id: Ifc4900fabb8f848f9cda361dce698664ea75f175
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index e640649..f05ddde 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -130,8 +130,8 @@
     // NOTE: Update this list if you add/change any stats above.
     // These characters are supposed to represent "total", "last", "current", 
     // and "unplugged". They were shortened for efficiency sake.
-    private static final String[] STAT_NAMES = { "t", "l", "c", "u" };
-    
+    private static final String[] STAT_NAMES = { "l", "c", "u" };
+
     /**
      * Bump the version on this if the checkin format changes.
      */
@@ -186,7 +186,7 @@
          * Returns the count associated with this Counter for the
          * selected type of statistics.
          *
-         * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT
+         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
          */
         public abstract int getCountLocked(int which);
 
@@ -205,7 +205,7 @@
          * Returns the count associated with this Counter for the
          * selected type of statistics.
          *
-         * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT
+         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
          */
         public abstract long getCountLocked(int which);
 
@@ -224,7 +224,7 @@
          * Returns the count associated with this Timer for the
          * selected type of statistics.
          *
-         * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT
+         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
          */
         public abstract int getCountLocked(int which);
 
@@ -233,7 +233,7 @@
          * selected type of statistics.
          *
          * @param elapsedRealtimeUs current elapsed realtime of system in microseconds
-         * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT
+         * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
          * @return a time in microseconds
          */
         public abstract long getTotalTimeLocked(long elapsedRealtimeUs, int which);
@@ -385,27 +385,27 @@
             /**
              * Returns the total time (in 1/100 sec) spent executing in user code.
              *
-             * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+             * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
              */
             public abstract long getUserTime(int which);
 
             /**
              * Returns the total time (in 1/100 sec) spent executing in system code.
              *
-             * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+             * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
              */
             public abstract long getSystemTime(int which);
 
             /**
              * Returns the number of times the process has been started.
              *
-             * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+             * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
              */
             public abstract int getStarts(int which);
 
             /**
              * Returns the cpu time spent in microseconds while the process was in the foreground.
-             * @param which one of STATS_TOTAL, STATS_LAST, STATS_CURRENT or STATS_UNPLUGGED
+             * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
              * @return foreground cpu time in microseconds
              */
             public abstract long getForegroundTime(int which);
@@ -414,7 +414,7 @@
              * Returns the approximate cpu time spent in microseconds, at a certain CPU speed.
              * @param speedStep the index of the CPU speed. This is not the actual speed of the
              * CPU.
-             * @param which one of STATS_TOTAL, STATS_LAST, STATS_CURRENT or STATS_UNPLUGGED
+             * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
              * @see BatteryStats#getCpuSpeedSteps()
              */
             public abstract long getTimeAtCpuSpeedStep(int speedStep, int which);
@@ -433,7 +433,7 @@
              * Returns the number of times this package has done something that could wake up the
              * device from sleep.
              *
-             * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+             * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
              */
             public abstract int getWakeups(int which);
 
@@ -451,7 +451,7 @@
                  * Returns the amount of time spent started.
                  *
                  * @param batteryUptime elapsed uptime on battery in microseconds.
-                 * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+                 * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
                  * @return
                  */
                 public abstract long getStartTime(long batteryUptime, int which);
@@ -459,14 +459,14 @@
                 /**
                  * Returns the total number of times startService() has been called.
                  *
-                 * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+                 * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
                  */
                 public abstract int getStarts(int which);
 
                 /**
                  * Returns the total number times the service has been launched.
                  *
-                 * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+                 * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
                  */
                 public abstract int getLaunches(int which);
             }
@@ -1285,7 +1285,7 @@
      * Returns the total, last, or current battery uptime in microseconds.
      *
      * @param curTime the elapsed realtime in microseconds.
-     * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
      */
     public abstract long computeBatteryUptime(long curTime, int which);
 
@@ -1293,7 +1293,7 @@
      * Returns the total, last, or current battery realtime in microseconds.
      *
      * @param curTime the current elapsed realtime in microseconds.
-     * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
      */
     public abstract long computeBatteryRealtime(long curTime, int which);
 
@@ -1301,7 +1301,7 @@
      * Returns the total, last, or current battery screen off uptime in microseconds.
      *
      * @param curTime the elapsed realtime in microseconds.
-     * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
      */
     public abstract long computeBatteryScreenOffUptime(long curTime, int which);
 
@@ -1309,7 +1309,7 @@
      * Returns the total, last, or current battery screen off realtime in microseconds.
      *
      * @param curTime the current elapsed realtime in microseconds.
-     * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
      */
     public abstract long computeBatteryScreenOffRealtime(long curTime, int which);
 
@@ -1317,18 +1317,38 @@
      * Returns the total, last, or current uptime in microseconds.
      *
      * @param curTime the current elapsed realtime in microseconds.
-     * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
      */
     public abstract long computeUptime(long curTime, int which);
 
     /**
      * Returns the total, last, or current realtime in microseconds.
-     * *
+     *
      * @param curTime the current elapsed realtime in microseconds.
-     * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
      */
     public abstract long computeRealtime(long curTime, int which);
-    
+
+    /**
+     * Compute an approximation for how much run time (in microseconds) is remaining on
+     * the battery.  Returns -1 if no time can be computed: either there is not
+     * enough current data to make a decision, or the battery is currently
+     * charging.
+     *
+     * @param curTime The current elepsed realtime in microseconds.
+     */
+    public abstract long computeBatteryTimeRemaining(long curTime);
+
+    /**
+     * Compute an approximation for how much time (in microseconds) remains until the battery
+     * is fully charged.  Returns -1 if no time can be computed: either there is not
+     * enough current data to make a decision, or the battery is currently
+     * discharging.
+     *
+     * @param curTime The current elepsed realtime in microseconds.
+     */
+    public abstract long computeChargeTimeRemaining(long curTime);
+
     public abstract Map<String, ? extends LongCounter> getWakeupReasonStats();
 
     public abstract Map<String, ? extends Timer> getKernelWakelockStats();
@@ -1430,7 +1450,7 @@
      * @param timer a Timer object contining the wakelock times.
      * @param elapsedRealtimeUs the current on-battery time in microseconds.
      * @param name the name of the wakelock.
-     * @param which which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+     * @param which which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
      * @param linePrefix a String to be prepended to each line of output.
      * @return the line prefix
      */
@@ -1464,7 +1484,7 @@
      * @param timer a Timer object contining the wakelock times.
      * @param elapsedRealtimeUs the current time in microseconds.
      * @param name the name of the wakelock.
-     * @param which which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
+     * @param which which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
      * @param linePrefix a String to be prepended to each line of output.
      * @return the line prefix
      */
@@ -1682,7 +1702,7 @@
             }
         }
         
-        BatteryStatsHelper helper = new BatteryStatsHelper(context);
+        BatteryStatsHelper helper = new BatteryStatsHelper(context, false);
         helper.create(this);
         helper.refreshStats(which, UserHandle.USER_ALL);
         List<BatterySipper> sippers = helper.getUsageList();
@@ -1929,6 +1949,8 @@
         final long whichBatteryScreenOffUptime = computeBatteryScreenOffUptime(rawUptime, which);
         final long whichBatteryScreenOffRealtime = computeBatteryScreenOffRealtime(rawRealtime,
                 which);
+        final long batteryTimeRemaining = computeBatteryTimeRemaining(rawRealtime);
+        final long chargeTimeRemaining = computeChargeTimeRemaining(rawRealtime);
 
         StringBuilder sb = new StringBuilder(128);
         
@@ -1963,7 +1985,20 @@
                 sb.append("realtime, ");
                 formatTimeMs(sb, totalUptime / 1000);
                 sb.append("uptime");
-        pw.println(sb.toString());
+        if (batteryTimeRemaining >= 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Battery time remaining: ");
+                    formatTimeMs(sb, batteryTimeRemaining / 1000);
+            pw.println(sb.toString());
+        }
+        if (chargeTimeRemaining >= 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Charge time remaining: ");
+                    formatTimeMs(sb, chargeTimeRemaining / 1000);
+            pw.println(sb.toString());
+        }
         pw.print("  Start clock time: ");
         pw.println(DateFormat.format("yyyy-MM-dd-HH-mm-ss", getStartClockTime()).toString());
 
@@ -2268,7 +2303,7 @@
             pw.println();
         }
 
-        BatteryStatsHelper helper = new BatteryStatsHelper(context);
+        BatteryStatsHelper helper = new BatteryStatsHelper(context, false);
         helper.create(this);
         helper.refreshStats(which, UserHandle.USER_ALL);
         List<BatterySipper> sippers = helper.getUsageList();
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 32f700d..fc89b31 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -19,6 +19,7 @@
 import com.android.internal.os.BatteryStatsImpl;
 
 import android.os.WorkSource;
+import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.SignalStrength;
 
 interface IBatteryStats {
@@ -55,7 +56,7 @@
     void noteScreenOff();
     void noteInputEvent();
     void noteUserActivity(int uid, int event);
-    void noteDataConnectionActive(int type, boolean active, long timestampNs);
+    void noteMobileRadioPowerState(int powerState, long timestampNs);
     void notePhoneOn();
     void notePhoneOff();
     void notePhoneSignalStrength(in SignalStrength signalStrength);
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 1dd1f5e..7ff949e 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -22,6 +22,8 @@
 import static android.os.BatteryStats.NETWORK_WIFI_TX_DATA;
 
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.net.ConnectivityManager;
@@ -60,11 +62,14 @@
     private static final String TAG = BatteryStatsHelper.class.getSimpleName();
 
     private static BatteryStats sStatsXfer;
+    private static Intent sBatteryBroadcastXfer;
 
     final private Context mContext;
+    final private boolean mCollectBatteryBroadcast;
 
     private IBatteryStats mBatteryInfo;
     private BatteryStats mStats;
+    private Intent mBatteryBroadcast;
     private PowerProfile mPowerProfile;
 
     private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>();
@@ -85,6 +90,8 @@
     long mBatteryUptime;
     long mTypeBatteryRealtime;
     long mTypeBatteryUptime;
+    long mBatteryTimeRemaining;
+    long mChargeTimeRemaining;
 
     private long mStatsPeriod = 0;
     private double mMaxPower = 1;
@@ -102,7 +109,12 @@
     private long mAppWifiRunning;
 
     public BatteryStatsHelper(Context context) {
+        this(context, true);
+    }
+
+    public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
         mContext = context;
+        mCollectBatteryBroadcast = collectBatteryBroadcast;
     }
 
     /** Clears the current stats and forces recreating for future use. */
@@ -117,6 +129,13 @@
         return mStats;
     }
 
+    public Intent getBatteryBroadcast() {
+        if (mBatteryBroadcast == null && mCollectBatteryBroadcast) {
+            load();
+        }
+        return mBatteryBroadcast;
+    }
+
     public PowerProfile getPowerProfile() {
         return mPowerProfile;
     }
@@ -129,6 +148,7 @@
     public void create(Bundle icicle) {
         if (icicle != null) {
             mStats = sStatsXfer;
+            mBatteryBroadcast = sBatteryBroadcastXfer;
         }
         mBatteryInfo = IBatteryStats.Stub.asInterface(
                 ServiceManager.getService(BatteryStats.SERVICE_NAME));
@@ -137,6 +157,7 @@
 
     public void storeState() {
         sStatsXfer = mStats;
+        sBatteryBroadcastXfer = mBatteryBroadcast;
     }
 
     public static String makemAh(double power) {
@@ -190,6 +211,8 @@
         mBatteryRealtime = mStats.getBatteryRealtime(rawRealtimeUs);
         mTypeBatteryUptime = mStats.computeBatteryUptime(rawUptimeUs, mStatsType);
         mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
+        mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs);
+        mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs);
 
         if (DEBUG) {
             Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs/1000) + " uptime="
@@ -787,6 +810,10 @@
         return mMaxDrainedPower;
     }
 
+    public long getBatteryTimeRemaining() { return mBatteryTimeRemaining; }
+
+    public long getChargeTimeRemaining() { return mChargeTimeRemaining; }
+
     private void load() {
         if (mBatteryInfo == null) {
             return;
@@ -803,5 +830,9 @@
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException:", e);
         }
+        if (mCollectBatteryBroadcast) {
+            mBatteryBroadcast = mContext.registerReceiver(null,
+                    new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+        }
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index eaedba5..343c507 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -38,6 +38,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.WorkSource;
+import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
@@ -87,7 +88,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 101 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 103 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -284,7 +285,7 @@
     int mBluetoothState = -1;
     final StopwatchTimer[] mBluetoothStateTimer = new StopwatchTimer[NUM_BLUETOOTH_STATES];
 
-    boolean mMobileRadioActive;
+    int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
     StopwatchTimer mMobileRadioActiveTimer;
     StopwatchTimer mMobileRadioActivePerAppTimer;
     LongSamplingCounter mMobileRadioActiveAdjustedTime;
@@ -306,7 +307,9 @@
      */
     int mDischargeStartLevel;
     int mDischargeUnplugLevel;
+    int mDischargePlugLevel;
     int mDischargeCurrentLevel;
+    int mCurrentBatteryLevel;
     int mLowDischargeAmountSinceCharge;
     int mHighDischargeAmountSinceCharge;
     int mDischargeScreenOnUnplugLevel;
@@ -2736,40 +2739,41 @@
         }
     }
 
-    public void noteDataConnectionActive(int type, boolean active, long timestampNs) {
-        if (ConnectivityManager.isNetworkTypeMobile(type)) {
-            final long elapsedRealtime = SystemClock.elapsedRealtime();
-            final long uptime = SystemClock.uptimeMillis();
-            if (mMobileRadioActive != active) {
-                long realElapsedRealtimeMs;
-                if (active) {
+    public void noteMobileRadioPowerState(int powerState, long timestampNs) {
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        if (mMobileRadioPowerState != powerState) {
+            long realElapsedRealtimeMs;
+            final boolean active =
+                    powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
+                            || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
+            if (active) {
+                realElapsedRealtimeMs = elapsedRealtime;
+                mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+            } else {
+                realElapsedRealtimeMs = timestampNs / (1000*1000);
+                long lastUpdateTimeMs = mMobileRadioActiveTimer.getLastUpdateTimeMs();
+                if (realElapsedRealtimeMs < lastUpdateTimeMs) {
+                    Slog.wtf(TAG, "Data connection inactive timestamp " + realElapsedRealtimeMs
+                            + " is before start time " + lastUpdateTimeMs);
                     realElapsedRealtimeMs = elapsedRealtime;
-                    mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
-                } else {
-                    realElapsedRealtimeMs = timestampNs / (1000*1000);
-                    long lastUpdateTimeMs = mMobileRadioActiveTimer.getLastUpdateTimeMs();
-                    if (realElapsedRealtimeMs < lastUpdateTimeMs) {
-                        Slog.wtf(TAG, "Data connection inactive timestamp " + realElapsedRealtimeMs
-                                + " is before start time " + lastUpdateTimeMs);
-                        realElapsedRealtimeMs = elapsedRealtime;
-                    } else if (realElapsedRealtimeMs < elapsedRealtime) {
-                        mMobileRadioActiveAdjustedTime.addCountLocked(elapsedRealtime
-                                - realElapsedRealtimeMs);
-                    }
-                    mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+                } else if (realElapsedRealtimeMs < elapsedRealtime) {
+                    mMobileRadioActiveAdjustedTime.addCountLocked(elapsedRealtime
+                            - realElapsedRealtimeMs);
                 }
-                if (DEBUG_HISTORY) Slog.v(TAG, "Mobile network active " + active + " to: "
-                        + Integer.toHexString(mHistoryCur.states));
-                addHistoryRecordLocked(elapsedRealtime, uptime);
-                mMobileRadioActive = active;
-                if (active) {
-                    mMobileRadioActiveTimer.startRunningLocked(elapsedRealtime);
-                    mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtime);
-                } else {
-                    mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
-                    updateNetworkActivityLocked(NET_UPDATE_MOBILE, realElapsedRealtimeMs);
-                    mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
-                }
+                mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+            }
+            if (DEBUG_HISTORY) Slog.v(TAG, "Mobile network active " + active + " to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecordLocked(elapsedRealtime, uptime);
+            mMobileRadioPowerState = powerState;
+            if (active) {
+                mMobileRadioActiveTimer.startRunningLocked(elapsedRealtime);
+                mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtime);
+            } else {
+                mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
+                updateNetworkActivityLocked(NET_UPDATE_MOBILE, realElapsedRealtimeMs);
+                mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
             }
         }
     }
@@ -5520,7 +5524,9 @@
         initTimes(uptime, realtime);
         mDischargeStartLevel = 0;
         mDischargeUnplugLevel = 0;
+        mDischargePlugLevel = -1;
         mDischargeCurrentLevel = 0;
+        mCurrentBatteryLevel = 0;
         initDischarge();
         clearHistoryLocked();
     }
@@ -5726,7 +5732,8 @@
         mDischargeStartLevel = mHistoryCur.batteryLevel;
         pullPendingStateUpdatesLocked();
         addHistoryRecordLocked(mSecRealtime, mSecUptime);
-        mDischargeCurrentLevel = mDischargeUnplugLevel = mHistoryCur.batteryLevel;
+        mDischargeCurrentLevel = mDischargeUnplugLevel = mDischargePlugLevel
+                = mCurrentBatteryLevel = mHistoryCur.batteryLevel;
         mOnBatteryTimeBase.reset(uptime, realtime);
         mOnBatteryScreenOffTimeBase.reset(uptime, realtime);
         if ((mHistoryCur.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) == 0) {
@@ -5907,7 +5914,7 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(mSecRealtime, mSecUptime);
-            mDischargeCurrentLevel = level;
+            mDischargeCurrentLevel = mDischargePlugLevel = level;
             if (level < mDischargeUnplugLevel) {
                 mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
                 mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
@@ -5971,6 +5978,10 @@
                     startRecordingHistory(elapsedRealtime, uptime, true);
                 }
             }
+            mCurrentBatteryLevel = level;
+            if (mDischargePlugLevel < 0) {
+                mDischargePlugLevel = level;
+            }
             if (onBattery != mOnBattery) {
                 mHistoryCur.batteryLevel = (byte)level;
                 mHistoryCur.batteryStatus = (byte)status;
@@ -6224,6 +6235,42 @@
         return mOnBatteryScreenOffTimeBase.computeRealtime(curTime, which);
     }
 
+    @Override
+    public long computeBatteryTimeRemaining(long curTime) {
+        if (!mOnBattery) {
+            return -1;
+        }
+        int discharge = (getLowDischargeAmountSinceCharge()+getHighDischargeAmountSinceCharge())/2;
+        if (discharge < 2) {
+            return -1;
+        }
+        long duration = computeBatteryRealtime(curTime, STATS_SINCE_CHARGED);
+        if (duration < 1000*1000) {
+            return -1;
+        }
+        long usPerLevel = duration/discharge;
+        return usPerLevel * mCurrentBatteryLevel;
+    }
+
+    @Override
+    public long computeChargeTimeRemaining(long curTime) {
+        if (true || mOnBattery) {
+            // Not yet working.
+            return -1;
+        }
+        int curLevel = mCurrentBatteryLevel;
+        int plugLevel = mDischargePlugLevel;
+        if (plugLevel < 0 || curLevel < (plugLevel+1)) {
+            return -1;
+        }
+        long duration = computeBatteryRealtime(curTime, STATS_SINCE_UNPLUGGED);
+        if (duration < 1000*1000) {
+            return -1;
+        }
+        long usPerLevel = duration/(curLevel-plugLevel);
+        return usPerLevel * (100-curLevel);
+    }
+
     long getBatteryUptimeLocked() {
         return mOnBatteryTimeBase.getUptime(SystemClock.uptimeMillis() * 1000);
     }
@@ -6722,7 +6769,9 @@
         mOnBatteryTimeBase.readSummaryFromParcel(in);
         mOnBatteryScreenOffTimeBase.readSummaryFromParcel(in);
         mDischargeUnplugLevel = in.readInt();
+        mDischargePlugLevel = in.readInt();
         mDischargeCurrentLevel = in.readInt();
+        mCurrentBatteryLevel = in.readInt();
         mLowDischargeAmountSinceCharge = in.readInt();
         mHighDischargeAmountSinceCharge = in.readInt();
         mDischargeAmountScreenOnSinceCharge = in.readInt();
@@ -6749,7 +6798,7 @@
             mNetworkByteActivityCounters[i].readSummaryFromParcelLocked(in);
             mNetworkPacketActivityCounters[i].readSummaryFromParcelLocked(in);
         }
-        mMobileRadioActive = false;
+        mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
         mMobileRadioActiveTimer.readSummaryFromParcelLocked(in);
         mMobileRadioActivePerAppTimer.readSummaryFromParcelLocked(in);
         mMobileRadioActiveAdjustedTime.readSummaryFromParcelLocked(in);
@@ -6974,7 +7023,9 @@
         mOnBatteryTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS);
         mOnBatteryScreenOffTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS);
         out.writeInt(mDischargeUnplugLevel);
+        out.writeInt(mDischargePlugLevel);
         out.writeInt(mDischargeCurrentLevel);
+        out.writeInt(mCurrentBatteryLevel);
         out.writeInt(getLowDischargeAmountSinceCharge());
         out.writeInt(getHighDischargeAmountSinceCharge());
         out.writeInt(getDischargeAmountScreenOnSinceCharge());
@@ -7262,7 +7313,7 @@
             mNetworkByteActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
             mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
         }
-        mMobileRadioActive = false;
+        mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
         mMobileRadioActiveTimer = new StopwatchTimer(null, -400, null, mOnBatteryTimeBase, in);
         mMobileRadioActivePerAppTimer = new StopwatchTimer(null, -401, null, mOnBatteryTimeBase,
                 in);
@@ -7284,7 +7335,9 @@
                     null, mOnBatteryTimeBase, in);
         }
         mDischargeUnplugLevel = in.readInt();
+        mDischargePlugLevel = in.readInt();
         mDischargeCurrentLevel = in.readInt();
+        mCurrentBatteryLevel = in.readInt();
         mLowDischargeAmountSinceCharge = in.readInt();
         mHighDischargeAmountSinceCharge = in.readInt();
         mDischargeAmountScreenOn = in.readInt();
@@ -7402,7 +7455,9 @@
             mBluetoothStateTimer[i].writeToParcel(out, uSecRealtime);
         }
         out.writeInt(mDischargeUnplugLevel);
+        out.writeInt(mDischargePlugLevel);
         out.writeInt(mDischargeCurrentLevel);
+        out.writeInt(mCurrentBatteryLevel);
         out.writeInt(mLowDischargeAmountSinceCharge);
         out.writeInt(mHighDischargeAmountSinceCharge);
         out.writeInt(mDischargeAmountScreenOn);
@@ -7499,6 +7554,7 @@
                 pr.println("*** Data connection type #" + i + ":");
                 mPhoneDataConnectionsTimer[i].logState(pr, "  ");
             }
+            pr.println("*** mMobileRadioPowerState=" + mMobileRadioPowerState);
             pr.println("*** Mobile network active timer:");
             mMobileRadioActiveTimer.logState(pr, "  ");
             pr.println("*** Mobile network active adjusted timer:");