Merge "Add tracking of uid process states in battery stats."
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 21d60c5..d0a6f08 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -115,6 +115,11 @@
     public static final int WIFI_BATCHED_SCAN = 11;
 
     /**
+     * A constant indicating a process state timer
+     */
+    public static final int PROCESS_STATE = 12;
+
+    /**
      * Include all of the data in the stats, including previously saved data.
      */
     public static final int STATS_SINCE_CHARGED = 0;
@@ -150,6 +155,7 @@
     private static final String SENSOR_DATA = "sr";
     private static final String VIBRATOR_DATA = "vib";
     private static final String FOREGROUND_DATA = "fg";
+    private static final String STATE_TIME_DATA = "st";
     private static final String WAKELOCK_DATA = "wl";
     private static final String KERNEL_WAKELOCK_DATA = "kwl";
     private static final String WAKEUP_REASON_DATA = "wr";
@@ -278,7 +284,7 @@
          *
          * @return a Map from Integer sensor ids to Uid.Sensor objects.
          */
-        public abstract Map<Integer, ? extends Sensor> getSensorStats();
+        public abstract SparseArray<? extends Sensor> getSensorStats();
 
         /**
          * Returns a mapping containing active process data.
@@ -328,6 +334,22 @@
         public abstract long getAudioTurnedOnTime(long elapsedRealtimeUs, int which);
         public abstract long getVideoTurnedOnTime(long elapsedRealtimeUs, int which);
         public abstract Timer getForegroundActivityTimer();
+
+        // Time this uid has any processes in foreground state.
+        public static final int PROCESS_STATE_FOREGROUND = 0;
+        // Time this uid has any process in active state (not cached).
+        public static final int PROCESS_STATE_ACTIVE = 1;
+        // Time this uid has any processes running at all.
+        public static final int PROCESS_STATE_RUNNING = 2;
+        // Total number of process states we track.
+        public static final int NUM_PROCESS_STATE = 3;
+
+        static final String[] PROCESS_STATE_NAMES = {
+            "Foreground", "Active", "Running"
+        };
+
+        public abstract long getProcessStateTime(int state, long elapsedRealtimeUs, int which);
+
         public abstract Timer getVibratorOnTimer();
 
         public static final int NUM_WIFI_BATCHED_SCAN_BINS = 5;
@@ -2082,22 +2104,20 @@
                     }
                 }
             }
-                
-            Map<Integer, ? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
-            if (sensors.size() > 0)  {
-                for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> ent
-                        : sensors.entrySet()) {
-                    Uid.Sensor se = ent.getValue();
-                    int sensorNumber = ent.getKey();
-                    Timer timer = se.getSensorTime();
-                    if (timer != null) {
-                        // Convert from microseconds to milliseconds with rounding
-                        long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
-                        int count = timer.getCountLocked(which);
-                        if (totalTime != 0) {
-                            dumpLine(pw, uid, category, SENSOR_DATA, sensorNumber, totalTime, count);
-                        }
-                    } 
+
+            SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+            int NSE = sensors.size();
+            for (int ise=0; ise<NSE; ise++) {
+                Uid.Sensor se = sensors.valueAt(ise);
+                int sensorNumber = sensors.keyAt(ise);
+                Timer timer = se.getSensorTime();
+                if (timer != null) {
+                    // Convert from microseconds to milliseconds with rounding
+                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                    int count = timer.getCountLocked(which);
+                    if (totalTime != 0) {
+                        dumpLine(pw, uid, category, SENSOR_DATA, sensorNumber, totalTime, count);
+                    }
                 }
             }
 
@@ -2121,6 +2141,16 @@
                 }
             }
 
+            Object[] stateTimes = new Object[Uid.NUM_PROCESS_STATE];
+            long totalStateTime = 0;
+            for (int ips=0; ips<Uid.NUM_PROCESS_STATE; ips++) {
+                totalStateTime += u.getProcessStateTime(ips, rawRealtime, which);
+                stateTimes[ips] = (totalStateTime + 500) / 1000;
+            }
+            if (totalStateTime > 0) {
+                dumpLine(pw, uid, category, STATE_TIME_DATA, stateTimes);
+            }
+
             Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
             if (processStats.size() > 0) {
                 for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
@@ -2968,45 +2998,43 @@
                 }
             }
 
-            Map<Integer, ? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
-            if (sensors.size() > 0) {
-                for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> ent
-                    : sensors.entrySet()) {
-                    Uid.Sensor se = ent.getValue();
-                    int sensorNumber = ent.getKey();
-                    sb.setLength(0);
-                    sb.append(prefix);
-                    sb.append("    Sensor ");
-                    int handle = se.getHandle();
-                    if (handle == Uid.Sensor.GPS) {
-                        sb.append("GPS");
-                    } else {
-                        sb.append(handle);
-                    }
-                    sb.append(": ");
+            SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+            int NSE = sensors.size();
+            for (int ise=0; ise<NSE; ise++) {
+                Uid.Sensor se = sensors.valueAt(ise);
+                int sensorNumber = sensors.keyAt(ise);
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Sensor ");
+                int handle = se.getHandle();
+                if (handle == Uid.Sensor.GPS) {
+                    sb.append("GPS");
+                } else {
+                    sb.append(handle);
+                }
+                sb.append(": ");
 
-                    Timer timer = se.getSensorTime();
-                    if (timer != null) {
-                        // Convert from microseconds to milliseconds with rounding
-                        long totalTime = (timer.getTotalTimeLocked(
-                                rawRealtime, which) + 500) / 1000;
-                        int count = timer.getCountLocked(which);
-                        //timer.logState();
-                        if (totalTime != 0) {
-                            formatTimeMs(sb, totalTime);
-                            sb.append("realtime (");
-                            sb.append(count);
-                            sb.append(" times)");
-                        } else {
-                            sb.append("(not used)");
-                        }
+                Timer timer = se.getSensorTime();
+                if (timer != null) {
+                    // Convert from microseconds to milliseconds with rounding
+                    long totalTime = (timer.getTotalTimeLocked(
+                            rawRealtime, which) + 500) / 1000;
+                    int count = timer.getCountLocked(which);
+                    //timer.logState();
+                    if (totalTime != 0) {
+                        formatTimeMs(sb, totalTime);
+                        sb.append("realtime (");
+                        sb.append(count);
+                        sb.append(" times)");
                     } else {
                         sb.append("(not used)");
                     }
-
-                    pw.println(sb.toString());
-                    uidActivity = true;
+                } else {
+                    sb.append("(not used)");
                 }
+
+                pw.println(sb.toString());
+                uidActivity = true;
             }
 
             Timer vibTimer = u.getVibratorOnTimer();
@@ -3047,6 +3075,22 @@
                 }
             }
 
+            long totalStateTime = 0;
+            for (int ips=0; ips<Uid.NUM_PROCESS_STATE; ips++) {
+                long time = u.getProcessStateTime(ips, rawRealtime, which);
+                if (time > 0) {
+                    totalStateTime += time;
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("    ");
+                    sb.append(Uid.PROCESS_STATE_NAMES[ips]);
+                    sb.append(" for: ");
+                    formatTimeMs(sb, (totalStateTime + 500) / 1000);
+                    pw.println(sb.toString());
+                    uidActivity = true;
+                }
+            }
+
             Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
             if (processStats.size() > 0) {
                 for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index ae9b515..98a5843 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -44,6 +44,10 @@
 
     void noteEvent(int code, String name, int uid);
 
+    void noteProcessStart(String name, int uid);
+    void noteProcessState(String name, int uid, int state);
+    void noteProcessFinish(String name, int uid);
+
     void noteStartWakelock(int uid, int pid, String name, String historyName,
             int type, boolean unimportantForLogging);
     void noteStopWakelock(int uid, int pid, String name, String historyName, int type);
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 469aa6f..273de61 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -440,11 +440,11 @@
             }
 
             // Process Sensor usage
-            Map<Integer, ? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
-            for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> sensorEntry
-                    : sensorStats.entrySet()) {
-                Uid.Sensor sensor = sensorEntry.getValue();
-                int sensorHandle = sensor.getHandle();
+            SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
+            int NSE = sensorStats.size();
+            for (int ise=0; ise<NSE; ise++) {
+                Uid.Sensor sensor = sensorStats.valueAt(ise);
+                int sensorHandle = sensorStats.keyAt(ise);
                 BatteryStats.Timer timer = sensor.getSensorTime();
                 long sensorTime = timer.getTotalTimeLocked(mRawRealtime, which) / 1000;
                 double multiplier = 0;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 49d11dc..6ca048e 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -19,6 +19,7 @@
 import static android.net.NetworkStats.UID_ALL;
 import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
 
+import android.app.ActivityManager;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.content.Context;
@@ -43,6 +44,7 @@
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.LogWriter;
 import android.util.PrintWriterPrinter;
@@ -89,7 +91,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 108 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 109 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -2328,6 +2330,38 @@
         addHistoryEventLocked(elapsedRealtime, uptime, code, name, uid);
     }
 
+    public void noteProcessStartLocked(String name, int uid) {
+        uid = mapUid(uid);
+        if (isOnBattery()) {
+            Uid u = getUidStatsLocked(uid);
+            u.getProcessStatsLocked(name).incStartsLocked();
+        }
+        if (!mActiveEvents.updateState(HistoryItem.EVENT_PROC_START, name, uid, 0)) {
+            return;
+        }
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PROC_START, name, uid);
+    }
+
+    public void noteProcessStateLocked(String name, int uid, int state) {
+        uid = mapUid(uid);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        getUidStatsLocked(uid).updateProcessStateLocked(name, state, elapsedRealtime);
+    }
+
+    public void noteProcessFinishLocked(String name, int uid) {
+        uid = mapUid(uid);
+        if (!mActiveEvents.updateState(HistoryItem.EVENT_PROC_FINISH, name, uid, 0)) {
+            return;
+        }
+        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);
+    }
+
     private void requestWakelockCpuUpdate() {
         if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) {
             Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS);
@@ -3765,7 +3799,7 @@
         boolean mWifiScanStarted;
         StopwatchTimer mWifiScanTimer;
 
-        private static final int NO_BATCHED_SCAN_STARTED = -1;
+        static final int NO_BATCHED_SCAN_STARTED = -1;
         int mWifiBatchedScanBinStarted = NO_BATCHED_SCAN_STARTED;
         StopwatchTimer[] mWifiBatchedScanTimer;
 
@@ -3780,6 +3814,10 @@
 
         StopwatchTimer mForegroundActivityTimer;
 
+        static final int PROCESS_STATE_NONE = NUM_PROCESS_STATE;
+        int mProcessState = PROCESS_STATE_NONE;
+        StopwatchTimer[] mProcessStateTimer;
+
         BatchTimer mVibratorOnTimer;
 
         Counter[] mUserActivityCounters;
@@ -3792,22 +3830,22 @@
         /**
          * The statistics we have collected for this uid's wake locks.
          */
-        final HashMap<String, Wakelock> mWakelockStats = new HashMap<String, Wakelock>();
+        final ArrayMap<String, Wakelock> mWakelockStats = new ArrayMap<String, Wakelock>();
 
         /**
          * The statistics we have collected for this uid's sensor activations.
          */
-        final HashMap<Integer, Sensor> mSensorStats = new HashMap<Integer, Sensor>();
+        final SparseArray<Sensor> mSensorStats = new SparseArray<Sensor>();
 
         /**
          * The statistics we have collected for this uid's processes.
          */
-        final HashMap<String, Proc> mProcessStats = new HashMap<String, Proc>();
+        final ArrayMap<String, Proc> mProcessStats = new ArrayMap<String, Proc>();
 
         /**
          * The statistics we have collected for this uid's processes.
          */
-        final HashMap<String, Pkg> mPackageStats = new HashMap<String, Pkg>();
+        final ArrayMap<String, Pkg> mPackageStats = new ArrayMap<String, Pkg>();
 
         /**
          * The transient wake stats we have collected for this uid's pids.
@@ -3825,6 +3863,7 @@
             mWifiBatchedScanTimer = new StopwatchTimer[NUM_WIFI_BATCHED_SCAN_BINS];
             mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED,
                     mWifiMulticastTimers, mOnBatteryTimeBase);
+            mProcessStateTimer = new StopwatchTimer[NUM_PROCESS_STATE];
         }
 
         @Override
@@ -3833,7 +3872,7 @@
         }
 
         @Override
-        public Map<Integer, ? extends BatteryStats.Uid.Sensor> getSensorStats() {
+        public SparseArray<? extends BatteryStats.Uid.Sensor> getSensorStats() {
             return mSensorStats;
         }
 
@@ -4035,6 +4074,21 @@
             }
         }
 
+        void updateUidProcessStateLocked(int state, long elapsedRealtimeMs) {
+            if (mProcessState == state) return;
+
+            if (mProcessState != PROCESS_STATE_NONE) {
+                mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtimeMs);
+            }
+            mProcessState = state;
+            if (state != PROCESS_STATE_NONE) {
+                if (mProcessStateTimer[state] == null) {
+                    makeProcessState(state, null);
+                }
+                mProcessStateTimer[state].startRunningLocked(elapsedRealtimeMs);
+            }
+        }
+
         public BatchTimer createVibratorOnTimerLocked() {
             if (mVibratorOnTimer == null) {
                 mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON, mOnBatteryTimeBase);
@@ -4114,6 +4168,27 @@
             return mForegroundActivityTimer;
         }
 
+        void makeProcessState(int i, Parcel in) {
+            if (i < 0 || i >= NUM_PROCESS_STATE) return;
+
+            if (in == null) {
+                mProcessStateTimer[i] = new StopwatchTimer(this, PROCESS_STATE, null,
+                        mOnBatteryTimeBase);
+            } else {
+                mProcessStateTimer[i] = new StopwatchTimer(this, PROCESS_STATE, null,
+                        mOnBatteryTimeBase, in);
+            }
+        }
+
+        @Override
+        public long getProcessStateTime(int state, long elapsedRealtimeUs, int which) {
+            if (state < 0 || state >= NUM_PROCESS_STATE) return 0;
+            if (mProcessStateTimer[state] == null) {
+                return 0;
+            }
+            return mProcessStateTimer[state].getTotalTimeLocked(elapsedRealtimeUs, which);
+        }
+
         @Override
         public Timer getVibratorOnTimer() {
             return mVibratorOnTimer;
@@ -4281,6 +4356,14 @@
             if (mForegroundActivityTimer != null) {
                 active |= !mForegroundActivityTimer.reset(false);
             }
+            if (mProcessStateTimer != null) {
+                for (int i = 0; i < NUM_PROCESS_STATE; i++) {
+                    if (mProcessStateTimer[i] != null) {
+                        active |= !mProcessStateTimer[i].reset(false);
+                    }
+                }
+                active |= (mProcessState != PROCESS_STATE_NONE);
+            }
             if (mVibratorOnTimer != null) {
                 if (mVibratorOnTimer.reset(false)) {
                     mVibratorOnTimer.detach();
@@ -4305,37 +4388,30 @@
                 mMobileRadioActiveCount.reset(false);
             }
 
-            if (mWakelockStats.size() > 0) {
-                Iterator<Map.Entry<String, Wakelock>> it = mWakelockStats.entrySet().iterator();
-                while (it.hasNext()) {
-                    Map.Entry<String, Wakelock> wakelockEntry = it.next();
-                    Wakelock wl = wakelockEntry.getValue();
-                    if (wl.reset()) {
-                        it.remove();
-                    } else {
-                        active = true;
-                    }
+            for (int iw=mWakelockStats.size()-1; iw>=0; iw--) {
+                Wakelock wl = mWakelockStats.valueAt(iw);
+                if (wl.reset()) {
+                    mWakelockStats.removeAt(iw);
+                } else {
+                    active = true;
                 }
             }
-            if (mSensorStats.size() > 0) {
-                Iterator<Map.Entry<Integer, Sensor>> it = mSensorStats.entrySet().iterator();
-                while (it.hasNext()) {
-                    Map.Entry<Integer, Sensor> sensorEntry = it.next();
-                    Sensor s = sensorEntry.getValue();
-                    if (s.reset()) {
-                        it.remove();
-                    } else {
-                        active = true;
-                    }
+            for (int ise=mSensorStats.size()-1; ise>=0; ise--) {
+                Sensor s = mSensorStats.valueAt(ise);
+                if (s.reset()) {
+                    mSensorStats.removeAt(ise);
+                } else {
+                    active = true;
                 }
             }
-            if (mProcessStats.size() > 0) {
-                Iterator<Map.Entry<String, Proc>> it = mProcessStats.entrySet().iterator();
-                while (it.hasNext()) {
-                    Map.Entry<String, Proc> procEntry = it.next();
-                    procEntry.getValue().detach();
+            for (int ip=mProcessStats.size()-1; ip>=0; ip--) {
+                Proc proc = mProcessStats.valueAt(ip);
+                if (proc.mProcessState == PROCESS_STATE_NONE) {
+                    proc.detach();
+                    mProcessStats.removeAt(ip);
+                } else {
+                    active = true;
                 }
-                mProcessStats.clear();
             }
             if (mPids.size() > 0) {
                 for (int i=mPids.size()-1; i>=0; i--) {
@@ -4413,24 +4489,27 @@
         }
 
         void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) {
-            out.writeInt(mWakelockStats.size());
-            for (Map.Entry<String, Uid.Wakelock> wakelockEntry : mWakelockStats.entrySet()) {
-                out.writeString(wakelockEntry.getKey());
-                Uid.Wakelock wakelock = wakelockEntry.getValue();
+            int NW = mWakelockStats.size();
+            out.writeInt(NW);
+            for (int iw=0; iw<NW; iw++) {
+                out.writeString(mWakelockStats.keyAt(iw));
+                Uid.Wakelock wakelock = mWakelockStats.valueAt(iw);
                 wakelock.writeToParcelLocked(out, elapsedRealtimeUs);
             }
 
-            out.writeInt(mSensorStats.size());
-            for (Map.Entry<Integer, Uid.Sensor> sensorEntry : mSensorStats.entrySet()) {
-                out.writeInt(sensorEntry.getKey());
-                Uid.Sensor sensor = sensorEntry.getValue();
+            int NSE = mSensorStats.size();
+            out.writeInt(NSE);
+            for (int ise=0; ise<NSE; ise++) {
+                out.writeInt(mSensorStats.keyAt(ise));
+                Uid.Sensor sensor = mSensorStats.valueAt(ise);
                 sensor.writeToParcelLocked(out, elapsedRealtimeUs);
             }
 
-            out.writeInt(mProcessStats.size());
-            for (Map.Entry<String, Uid.Proc> procEntry : mProcessStats.entrySet()) {
-                out.writeString(procEntry.getKey());
-                Uid.Proc proc = procEntry.getValue();
+            int NP = mProcessStats.size();
+            out.writeInt(NP);
+            for (int ip=0; ip<NP; ip++) {
+                out.writeString(mProcessStats.keyAt(ip));
+                Uid.Proc proc = mProcessStats.valueAt(ip);
                 proc.writeToParcelLocked(out);
             }
 
@@ -4491,6 +4570,14 @@
             } else {
                 out.writeInt(0);
             }
+            for (int i = 0; i < NUM_PROCESS_STATE; i++) {
+                if (mProcessStateTimer[i] != null) {
+                    out.writeInt(1);
+                    mProcessStateTimer[i].writeToParcel(out, elapsedRealtimeUs);
+                } else {
+                    out.writeInt(0);
+                }
+            }
             if (mVibratorOnTimer != null) {
                 out.writeInt(1);
                 mVibratorOnTimer.writeToParcel(out, elapsedRealtimeUs);
@@ -4614,6 +4701,14 @@
             } else {
                 mForegroundActivityTimer = null;
             }
+            mProcessState = PROCESS_STATE_NONE;
+            for (int i = 0; i < NUM_PROCESS_STATE; i++) {
+                if (in.readInt() != 0) {
+                    makeProcessState(i, in);
+                } else {
+                    mProcessStateTimer[i] = null;
+                }
+            }
             if (in.readInt() != 0) {
                 mVibratorOnTimer = new BatchTimer(Uid.this, VIBRATOR_ON, mOnBatteryTimeBase, in);
             } else {
@@ -4871,6 +4966,11 @@
              */
             int mUnpluggedStarts;
 
+            /**
+             * Current process state.
+             */
+            int mProcessState = PROCESS_STATE_NONE;
+
             SamplingCounter[] mSpeedBins;
 
             ArrayList<ExcessivePower> mExcessivePower;
@@ -5485,6 +5585,49 @@
             return ps;
         }
 
+        public void updateProcessStateLocked(String procName, int state, long elapsedRealtimeMs) {
+            int procState;
+            if (state <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+                procState = PROCESS_STATE_FOREGROUND;
+            } else if (state <= ActivityManager.PROCESS_STATE_RECEIVER) {
+                procState = PROCESS_STATE_ACTIVE;
+            } else {
+                procState = PROCESS_STATE_RUNNING;
+            }
+            updateRealProcessStateLocked(procName, procState, elapsedRealtimeMs);
+        }
+
+        public void updateRealProcessStateLocked(String procName, int procState,
+                long elapsedRealtimeMs) {
+            Proc proc = getProcessStatsLocked(procName);
+            if (proc.mProcessState != procState) {
+                boolean changed;
+                if (procState < proc.mProcessState) {
+                    // Has this process become more important?  If so,
+                    // we may need to change the uid if the currrent uid proc state
+                    // is not as important as what we are now setting.
+                    changed = mProcessState > procState;
+                } else {
+                    // Has this process become less important?  If so,
+                    // we may need to change the uid if the current uid proc state
+                    // is the same importance as the old setting.
+                    changed = mProcessState == proc.mProcessState;
+                }
+                proc.mProcessState = procState;
+                if (changed) {
+                    // uid's state may have changed; compute what the new state should be.
+                    int uidProcState = PROCESS_STATE_NONE;
+                    for (int ip=mProcessStats.size()-1; ip>=0; ip--) {
+                        proc = mProcessStats.valueAt(ip);
+                        if (proc.mProcessState < uidProcState) {
+                            uidProcState = proc.mProcessState;
+                        }
+                    }
+                    updateUidProcessStateLocked(uidProcState, elapsedRealtimeMs);
+                }
+            }
+        }
+
         public SparseArray<? extends Pid> getPidStats() {
             return mPids;
         }
@@ -6782,7 +6925,8 @@
         Uid wifiUid = mUidStats.get(Process.WIFI_UID);
         if (wifiUid != null) {
             long uSecTime = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which);
-            for (Uid.Proc proc : wifiUid.mProcessStats.values()) {
+            for (int ip=wifiUid.mProcessStats.size()-1; ip>=0; ip--) {
+                Uid.Proc proc = wifiUid.mProcessStats.valueAt(ip);
                 long totalRunningTime = getGlobalWifiRunningTime(uSecTime, which);
                 for (int i=0; i<mUidStats.size(); i++) {
                     Uid uid = mUidStats.valueAt(i);
@@ -7239,6 +7383,13 @@
             if (in.readInt() != 0) {
                 u.createForegroundActivityTimerLocked().readSummaryFromParcelLocked(in);
             }
+            u.mProcessState = Uid.PROCESS_STATE_NONE;
+            for (int i = 0; i < Uid.NUM_PROCESS_STATE; i++) {
+                if (in.readInt() != 0) {
+                    u.makeProcessState(i, null);
+                    u.mProcessStateTimer[i].readSummaryFromParcelLocked(in);
+                }
+            }
             if (in.readInt() != 0) {
                 u.createVibratorOnTimerLocked().readSummaryFromParcelLocked(in);
             }
@@ -7505,6 +7656,14 @@
             } else {
                 out.writeInt(0);
             }
+            for (int i = 0; i < Uid.NUM_PROCESS_STATE; i++) {
+                if (u.mProcessStateTimer[i] != null) {
+                    out.writeInt(1);
+                    u.mProcessStateTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+                } else {
+                    out.writeInt(0);
+                }
+            }
             if (u.mVibratorOnTimer != null) {
                 out.writeInt(1);
                 u.mVibratorOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -7535,71 +7694,62 @@
 
             int NW = u.mWakelockStats.size();
             out.writeInt(NW);
-            if (NW > 0) {
-                for (Map.Entry<String, BatteryStatsImpl.Uid.Wakelock> ent
-                        : u.mWakelockStats.entrySet()) {
-                    out.writeString(ent.getKey());
-                    Uid.Wakelock wl = ent.getValue();
-                    if (wl.mTimerFull != null) {
-                        out.writeInt(1);
-                        wl.mTimerFull.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
-                    } else {
-                        out.writeInt(0);
-                    }
-                    if (wl.mTimerPartial != null) {
-                        out.writeInt(1);
-                        wl.mTimerPartial.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
-                    } else {
-                        out.writeInt(0);
-                    }
-                    if (wl.mTimerWindow != null) {
-                        out.writeInt(1);
-                        wl.mTimerWindow.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
-                    } else {
-                        out.writeInt(0);
-                    }
+            for (int iw=0; iw<NW; iw++) {
+                out.writeString(u.mWakelockStats.keyAt(iw));
+                Uid.Wakelock wl = u.mWakelockStats.valueAt(iw);
+                if (wl.mTimerFull != null) {
+                    out.writeInt(1);
+                    wl.mTimerFull.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+                } else {
+                    out.writeInt(0);
+                }
+                if (wl.mTimerPartial != null) {
+                    out.writeInt(1);
+                    wl.mTimerPartial.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+                } else {
+                    out.writeInt(0);
+                }
+                if (wl.mTimerWindow != null) {
+                    out.writeInt(1);
+                    wl.mTimerWindow.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+                } else {
+                    out.writeInt(0);
                 }
             }
 
             int NSE = u.mSensorStats.size();
             out.writeInt(NSE);
-            if (NSE > 0) {
-                for (Map.Entry<Integer, BatteryStatsImpl.Uid.Sensor> ent
-                        : u.mSensorStats.entrySet()) {
-                    out.writeInt(ent.getKey());
-                    Uid.Sensor se = ent.getValue();
-                    if (se.mTimer != null) {
-                        out.writeInt(1);
-                        se.mTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
-                    } else {
-                        out.writeInt(0);
-                    }
+            for (int ise=0; ise<NSE; ise++) {
+                out.writeInt(u.mSensorStats.keyAt(ise));
+                Uid.Sensor se = u.mSensorStats.valueAt(ise);
+                if (se.mTimer != null) {
+                    out.writeInt(1);
+                    se.mTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+                } else {
+                    out.writeInt(0);
                 }
             }
 
             int NP = u.mProcessStats.size();
             out.writeInt(NP);
-            if (NP > 0) {
-                for (Map.Entry<String, BatteryStatsImpl.Uid.Proc> ent
-                    : u.mProcessStats.entrySet()) {
-                    out.writeString(ent.getKey());
-                    Uid.Proc ps = ent.getValue();
-                    out.writeLong(ps.mUserTime);
-                    out.writeLong(ps.mSystemTime);
-                    out.writeLong(ps.mForegroundTime);
-                    out.writeInt(ps.mStarts);
-                    final int N = ps.mSpeedBins.length;
-                    out.writeInt(N);
-                    for (int i=0; i<N; i++) {
-                        if (ps.mSpeedBins[i] != null) {
-                            out.writeInt(1);
-                            ps.mSpeedBins[i].writeSummaryFromParcelLocked(out);
-                        } else {
-                            out.writeInt(0);
-                        }
+            for (int ip=0; ip<NP; ip++) {
+                out.writeString(u.mProcessStats.keyAt(ip));
+                Uid.Proc ps = u.mProcessStats.valueAt(ip);
+                out.writeLong(ps.mUserTime);
+                out.writeLong(ps.mSystemTime);
+                out.writeLong(ps.mForegroundTime);
+                out.writeInt(ps.mStarts);
+                final int N = ps.mSpeedBins.length;
+                out.writeInt(N);
+                for (int i=0; i<N; i++) {
+                    if (ps.mSpeedBins[i] != null) {
+                        out.writeInt(1);
+                        ps.mSpeedBins[i].writeSummaryFromParcelLocked(out);
+                    } else {
+                        out.writeInt(0);
                     }
-                    ps.writeExcessivePowerToParcelLocked(out);
                 }
+                ps.writeExcessivePowerToParcelLocked(out);
             }
 
             NP = u.mPackageStats.size();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e10c48f..053fb5a 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -138,7 +138,6 @@
 import android.content.pm.ServiceInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
-import android.graphics.Bitmap;
 import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.Uri;
@@ -2997,12 +2996,10 @@
                     app.processName, uid, uid, gids, debugFlags, mountExternal,
                     app.info.targetSdkVersion, app.info.seinfo, requiredAbi, null);
 
-            BatteryStatsImpl bs = mBatteryStatsService.getActiveStatistics();
-            synchronized (bs) {
-                if (bs.isOnBattery()) {
-                    bs.getProcessStatsLocked(app.uid, app.processName).incStartsLocked();
-                }
+            if (app.isolated) {
+                mBatteryStatsService.addIsolatedUid(app.uid, app.info.uid);
             }
+            mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
 
             EventLog.writeEvent(EventLogTags.AM_PROC_START,
                     UserHandle.getUserId(uid), startResult.pid, uid,
@@ -3052,14 +3049,13 @@
                 mHandler.sendMessageDelayed(msg, startResult.usingWrapper
                         ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
             }
-            mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_PROC_START,
-                    app.processName, app.info.uid);
-            if (app.isolated) {
-                mBatteryStatsService.addIsolatedUid(app.uid, app.info.uid);
-            }
         } catch (RuntimeException e) {
             // XXX do better error recovery.
             app.setPid(0);
+            mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
+            if (app.isolated) {
+                mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
+            }
             Slog.e(TAG, "Failure starting process " + app.processName, e);
         }
     }
@@ -5121,8 +5117,7 @@
                 mPidsSelfLocked.remove(pid);
                 mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
             }
-            mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_PROC_FINISH,
-                    app.processName, app.info.uid);
+            mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
             if (app.isolated) {
                 mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
             }
@@ -5166,8 +5161,7 @@
                         mHeavyWeightProcess.userId, 0));
                 mHeavyWeightProcess = null;
             }
-            mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_PROC_FINISH,
-                    app.processName, app.info.uid);
+            mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
             if (app.isolated) {
                 mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
             }
@@ -13285,8 +13279,7 @@
                 mPidsSelfLocked.remove(app.pid);
                 mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
             }
-            mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_PROC_FINISH,
-                    app.processName, app.info.uid);
+            mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
             if (app.isolated) {
                 mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
             }
@@ -16039,7 +16032,7 @@
                 app.notCachedSinceIdle = false;
             }
             if (!doingAll) {
-                setProcessTrackerState(app, mProcessStats.getMemFactorLocked(), now);
+                setProcessTrackerStateLocked(app, mProcessStats.getMemFactorLocked(), now);
             } else {
                 app.procStateChanged = true;
             }
@@ -16092,9 +16085,15 @@
         return success;
     }
 
-    private final void setProcessTrackerState(ProcessRecord proc, int memFactor, long now) {
-        if (proc.thread != null && proc.baseProcessTracker != null) {
-            proc.baseProcessTracker.setState(proc.repProcState, memFactor, now, proc.pkgList);
+    private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
+        if (proc.thread != null) {
+            if (proc.baseProcessTracker != null) {
+                proc.baseProcessTracker.setState(proc.repProcState, memFactor, now, proc.pkgList);
+            }
+            if (proc.repProcState >= 0) {
+                mBatteryStatsService.noteProcessState(proc.processName, proc.info.uid,
+                        proc.repProcState);
+            }
         }
     }
 
@@ -16149,7 +16148,7 @@
         ActivityRecord act = mStackSupervisor.resumedAppLocked();
         String pkg;
         int uid;
-        if (act != null && !act.sleeping) {
+        if (act != null) {
             pkg = act.packageName;
             uid = act.info.applicationInfo.uid;
         } else {
@@ -16430,7 +16429,7 @@
             for (int i=N-1; i>=0; i--) {
                 ProcessRecord app = mLruProcesses.get(i);
                 if (allChanged || app.procStateChanged) {
-                    setProcessTrackerState(app, trackerMemFactor, now);
+                    setProcessTrackerStateLocked(app, trackerMemFactor, now);
                     app.procStateChanged = false;
                 }
                 if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
@@ -16521,7 +16520,7 @@
             for (int i=N-1; i>=0; i--) {
                 ProcessRecord app = mLruProcesses.get(i);
                 if (allChanged || app.procStateChanged) {
-                    setProcessTrackerState(app, trackerMemFactor, now);
+                    setProcessTrackerStateLocked(app, trackerMemFactor, now);
                     app.procStateChanged = false;
                 }
                 if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 5c5e3e3..da444f9 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -33,7 +33,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
-import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.util.Slog;
@@ -166,6 +165,27 @@
         }
     }
 
+    public void noteProcessStart(String name, int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteProcessStartLocked(name, uid);
+        }
+    }
+
+    public void noteProcessState(String name, int uid, int state) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteProcessStateLocked(name, uid, state);
+        }
+    }
+
+    public void noteProcessFinish(String name, int uid) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteProcessFinishLocked(name, uid);
+        }
+    }
+
     public void noteStartWakelock(int uid, int pid, String name, String historyName, int type,
             boolean unimportantForLogging) {
         enforceCallingPermission();