Merge "Initial checkin for pause and resume control" into kraken
diff --git a/api/current.xml b/api/current.xml
index 8dd57f0..41b0423 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -21763,7 +21763,18 @@
  type="int"
  transient="false"
  volatile="false"
- value="150"
+ value="170"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IMPORTANCE_PERCEPTIBLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="130"
  static="true"
  final="true"
  deprecated="not deprecated"
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 793b9d2..7f95bf5 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -726,16 +726,24 @@
         public static final int IMPORTANCE_FOREGROUND = 100;
         
         /**
-         * Constant for {@link #importance}: this process is running a
-         * heavy-weight application and thus should not be killed.
+         * Constant for {@link #importance}: this process is running something
+         * that is actively visible to the user, though not in the immediate
+         * foreground.
          */
-        public static final int IMPORTANCE_HEAVY_WEIGHT = 150;
+        public static final int IMPORTANCE_VISIBLE = 200;
         
         /**
          * Constant for {@link #importance}: this process is running something
-         * that is considered to be actively visible to the user.
+         * that is considered to be actively perceptible to the user.  An
+         * example would be an application performing background music playback.
          */
-        public static final int IMPORTANCE_VISIBLE = 200;
+        public static final int IMPORTANCE_PERCEPTIBLE = 130;
+        
+        /**
+         * Constant for {@link #importance}: this process is running a
+         * heavy-weight application and thus should not be killed.
+         */
+        public static final int IMPORTANCE_HEAVY_WEIGHT = 170;
         
         /**
          * Constant for {@link #importance}: this process is contains services
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index d114bff..4adeaeb 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -51,50 +51,36 @@
     
     /**
      * A constant indicating a sensor timer.
-     * 
-     * {@hide}
      */
     public static final int SENSOR = 3;
     
     /**
      * A constant indicating a a wifi turn on timer
-     *
-     * {@hide}
      */
     public static final int WIFI_TURNED_ON = 4;
     
     /**
      * A constant indicating a full wifi lock timer
-     *
-     * {@hide}
      */
     public static final int FULL_WIFI_LOCK = 5;
     
     /**
      * A constant indicating a scan wifi lock timer
-     *
-     * {@hide}
      */
     public static final int SCAN_WIFI_LOCK = 6;
 
      /**
       * A constant indicating a wifi multicast timer
-      *
-      * {@hide}
       */
      public static final int WIFI_MULTICAST_ENABLED = 7;
 
     /**
      * A constant indicating an audio turn on timer
-     *
-     * {@hide}
      */
     public static final int AUDIO_TURNED_ON = 7;
 
     /**
      * A constant indicating a video turn on timer
-     *
-     * {@hide}
      */
     public static final int VIDEO_TURNED_ON = 8;
 
@@ -391,6 +377,61 @@
         }
     }
 
+    public final class BatteryHistoryRecord implements Parcelable {
+        public BatteryHistoryRecord next;
+        
+        public long time;
+        public byte batteryLevel;
+        
+        public static final int STATE_SCREEN_MASK = 0x000000f;
+        public static final int STATE_SCREEN_SHIFT = 0;
+        public static final int STATE_SIGNAL_STRENGTH_MASK = 0x00000f0;
+        public static final int STATE_SIGNAL_STRENGTH_SHIFT = 4;
+        public static final int STATE_PHONE_STATE_MASK = 0x0000f00;
+        public static final int STATE_PHONE_STATE_SHIFT = 8;
+        public static final int STATE_DATA_CONNECTION_MASK = 0x000f000;
+        public static final int STATE_DATA_CONNECTION_SHIFT = 12;
+        
+        public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<30;
+        public static final int STATE_SCREEN_ON_FLAG = 1<<29;
+        public static final int STATE_GPS_ON_FLAG = 1<<28;
+        public static final int STATE_PHONE_ON_FLAG = 1<<27;
+        public static final int STATE_WIFI_ON_FLAG = 1<<26;
+        public static final int STATE_WIFI_RUNNING_FLAG = 1<<25;
+        public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<24;
+        public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<23;
+        public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<22;
+        public static final int STATE_BLUETOOTH_ON_FLAG = 1<<21;
+        public static final int STATE_AUDIO_ON_FLAG = 1<<20;
+        public static final int STATE_VIDEO_ON_FLAG = 1<<19;
+        
+        public int states;
+
+        public BatteryHistoryRecord() {
+        }
+        
+        public BatteryHistoryRecord(long time, Parcel src) {
+            this.time = time;
+            batteryLevel = (byte)src.readInt();
+            states = src.readInt();
+        }
+        
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeLong(time);
+            dest.writeInt(batteryLevel);
+            dest.writeInt(states);
+        }
+    }
+    
+    /**
+     * Return the current history of battery state changes.
+     */
+    public abstract BatteryHistoryRecord getHistory();
+    
     /**
      * Returns the number of times the device has been started.
      */
@@ -1443,6 +1484,20 @@
      */
     @SuppressWarnings("unused")
     public void dumpLocked(PrintWriter pw) {
+        BatteryHistoryRecord rec = getHistory();
+        if (rec != null) {
+            pw.println("Battery History:");
+            while (rec != null) {
+                pw.print("  ");
+                pw.print(rec.time);
+                pw.print(" ");
+                pw.print(rec.batteryLevel);
+                pw.print(" ");
+                pw.println(Integer.toHexString(rec.states));
+                rec = rec.next;
+            }
+        }
+        
         pw.println("Total Statistics (Current and Historic):");
         pw.println("  System starts: " + getStartCount()
                 + ", currently on battery: " + getIsOnBattery());
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index aadb576..3833725 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -56,12 +56,13 @@
 public final class BatteryStatsImpl extends BatteryStats {
     private static final String TAG = "BatteryStatsImpl";
     private static final boolean DEBUG = false;
-
+    private static final boolean DEBUG_HISTORY = false;
+    
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
     private static final int MAGIC = 0xBA757475; // 'BATSTATS' 
 
     // Current on-disk Parcel version
-    private static final int VERSION = 43;
+    private static final int VERSION = 44;
 
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
@@ -94,6 +95,11 @@
     // is unplugged from power.
     final ArrayList<Unpluggable> mUnpluggables = new ArrayList<Unpluggable>();
     
+    BatteryHistoryRecord mHistory;
+    BatteryHistoryRecord mHistoryEnd;
+    BatteryHistoryRecord mHistoryCache;
+    final BatteryHistoryRecord mHistoryCur = new BatteryHistoryRecord();
+    
     int mStartCount;
 
     long mBatteryUptime;
@@ -1042,6 +1048,37 @@
         mBtHeadset = headset;
     }
 
+    void addHistoryRecord(long curTime) {
+        BatteryHistoryRecord rec = mHistoryCache;
+        if (rec != null) {
+            mHistoryCache = rec.next;
+        } else {
+            rec = new BatteryHistoryRecord();
+        }
+        rec.time = curTime;
+        rec.batteryLevel = mHistoryCur.batteryLevel;
+        rec.states = mHistoryCur.states;
+        addHistoryRecord(rec);
+    }
+    
+    void addHistoryRecord(BatteryHistoryRecord rec) {
+        rec.next = null;
+        if (mHistoryEnd != null) {
+            mHistoryEnd.next = rec;
+            mHistoryEnd = rec;
+        } else {
+            mHistory = mHistoryEnd = rec;
+        }
+    }
+    
+    void clearHistory() {
+        if (mHistory != null) {
+            mHistoryEnd.next = mHistoryCache;
+            mHistoryCache = mHistory;
+            mHistory = mHistoryEnd = null;
+        }
+    }
+    
     public void doUnplug(long batteryUptime, long batteryRealtime) {
         for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
             Uid u = mUidStats.valueAt(iu);
@@ -1094,16 +1131,37 @@
         mBluetoothPingStart = -1;
     }
 
+    int mGpsNesting;
+    
     public void noteStartGps(int uid) {
+        if (mGpsNesting == 0) {
+            mHistoryCur.states |= BatteryHistoryRecord.STATE_GPS_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
+        }
+        mGpsNesting++;
         getUidStatsLocked(uid).noteStartGps();
     }
     
     public void noteStopGps(int uid) {
+        mGpsNesting--;
+        if (mGpsNesting == 0) {
+            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_GPS_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
+        }
         getUidStatsLocked(uid).noteStopGps();
     }
 
     public void noteScreenOnLocked() {
         if (!mScreenOn) {
+            mHistoryCur.states |= BatteryHistoryRecord.STATE_SCREEN_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
+            mGpsNesting++;
             mScreenOn = true;
             mScreenOnTimer.startRunningLocked(this);
             if (mScreenBrightnessBin >= 0) {
@@ -1114,6 +1172,10 @@
     
     public void noteScreenOffLocked() {
         if (mScreenOn) {
+            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_SCREEN_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             mScreenOn = false;
             mScreenOnTimer.stopRunningLocked(this);
             if (mScreenBrightnessBin >= 0) {
@@ -1128,6 +1190,11 @@
         if (bin < 0) bin = 0;
         else if (bin >= NUM_SCREEN_BRIGHTNESS_BINS) bin = NUM_SCREEN_BRIGHTNESS_BINS-1;
         if (mScreenBrightnessBin != bin) {
+            mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_SCREEN_MASK)
+                    | (bin << BatteryHistoryRecord.STATE_SCREEN_SHIFT);
+            if (DEBUG_HISTORY) Slog.v(TAG, "Screen brightness " + bin + " to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             if (mScreenOn) {
                 if (mScreenBrightnessBin >= 0) {
                     mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this);
@@ -1148,6 +1215,10 @@
     
     public void notePhoneOnLocked() {
         if (!mPhoneOn) {
+            mHistoryCur.states |= BatteryHistoryRecord.STATE_PHONE_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             mPhoneOn = true;
             mPhoneOnTimer.startRunningLocked(this);
         }
@@ -1155,6 +1226,10 @@
     
     public void notePhoneOffLocked() {
         if (mPhoneOn) {
+            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_PHONE_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             mPhoneOn = false;
             mPhoneOnTimer.stopRunningLocked(this);
         }
@@ -1195,7 +1270,15 @@
                 mPhoneSignalScanningTimer.startRunningLocked(this);
             }
         }
-        mPhoneServiceState = state;
+        
+        if (mPhoneServiceState != state) {
+            mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_PHONE_STATE_MASK)
+                    | (state << BatteryHistoryRecord.STATE_PHONE_STATE_SHIFT);
+            if (DEBUG_HISTORY) Slog.v(TAG, "Phone state " + bin + " to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
+            mPhoneServiceState = state;
+        }
     }
 
     public void notePhoneSignalStrengthLocked(SignalStrength signalStrength) {
@@ -1222,6 +1305,11 @@
             else bin = SIGNAL_STRENGTH_POOR;
         }
         if (mPhoneSignalStrengthBin != bin) {
+            mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_SIGNAL_STRENGTH_MASK)
+                    | (bin << BatteryHistoryRecord.STATE_SIGNAL_STRENGTH_SHIFT);
+            if (DEBUG_HISTORY) Slog.v(TAG, "Signal strength " + bin + " to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             if (mPhoneSignalStrengthBin >= 0) {
                 mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].stopRunningLocked(this);
             }
@@ -1250,6 +1338,11 @@
         }
         if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
         if (mPhoneDataConnectionType != bin) {
+            mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_DATA_CONNECTION_MASK)
+                    | (bin << BatteryHistoryRecord.STATE_DATA_CONNECTION_SHIFT);
+            if (DEBUG_HISTORY) Slog.v(TAG, "Data connection " + bin + " to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             if (mPhoneDataConnectionType >= 0) {
                 mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(this);
             }
@@ -1260,6 +1353,10 @@
     
     public void noteWifiOnLocked(int uid) {
         if (!mWifiOn) {
+            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "WIFI on to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             mWifiOn = true;
             mWifiOnTimer.startRunningLocked(this);
         }
@@ -1274,6 +1371,10 @@
     
     public void noteWifiOffLocked(int uid) {
         if (mWifiOn) {
+            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "WIFI off to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             mWifiOn = false;
             mWifiOnTimer.stopRunningLocked(this);
         }
@@ -1285,6 +1386,10 @@
 
     public void noteAudioOnLocked(int uid) {
         if (!mAudioOn) {
+            mHistoryCur.states |= BatteryHistoryRecord.STATE_AUDIO_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             mAudioOn = true;
             mAudioOnTimer.startRunningLocked(this);
         }
@@ -1293,6 +1398,10 @@
     
     public void noteAudioOffLocked(int uid) {
         if (mAudioOn) {
+            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_AUDIO_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             mAudioOn = false;
             mAudioOnTimer.stopRunningLocked(this);
         }
@@ -1301,6 +1410,10 @@
 
     public void noteVideoOnLocked(int uid) {
         if (!mVideoOn) {
+            mHistoryCur.states |= BatteryHistoryRecord.STATE_VIDEO_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             mVideoOn = true;
             mVideoOnTimer.startRunningLocked(this);
         }
@@ -1309,6 +1422,10 @@
     
     public void noteVideoOffLocked(int uid) {
         if (mVideoOn) {
+            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_VIDEO_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             mVideoOn = false;
             mVideoOnTimer.stopRunningLocked(this);
         }
@@ -1317,6 +1434,10 @@
 
     public void noteWifiRunningLocked() {
         if (!mWifiRunning) {
+            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_RUNNING_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "WIFI running to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             mWifiRunning = true;
             mWifiRunningTimer.startRunningLocked(this);
         }
@@ -1324,6 +1445,10 @@
 
     public void noteWifiStoppedLocked() {
         if (mWifiRunning) {
+            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_RUNNING_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "WIFI stopped to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             mWifiRunning = false;
             mWifiRunningTimer.stopRunningLocked(this);
         }
@@ -1331,6 +1456,10 @@
 
     public void noteBluetoothOnLocked() {
         if (!mBluetoothOn) {
+            mHistoryCur.states |= BatteryHistoryRecord.STATE_BLUETOOTH_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth on to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             mBluetoothOn = true;
             mBluetoothOnTimer.startRunningLocked(this);
         }
@@ -1338,32 +1467,84 @@
     
     public void noteBluetoothOffLocked() {
         if (mBluetoothOn) {
+            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_BLUETOOTH_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth off to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
             mBluetoothOn = false;
             mBluetoothOnTimer.stopRunningLocked(this);
         }
     }
     
+    int mWifiFullLockNesting = 0;
+    
     public void noteFullWifiLockAcquiredLocked(int uid) {
+        if (mWifiFullLockNesting == 0) {
+            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_FULL_LOCK_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
+        }
+        mWifiFullLockNesting++;
         getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked();
     }
 
     public void noteFullWifiLockReleasedLocked(int uid) {
+        mWifiFullLockNesting--;
+        if (mWifiFullLockNesting == 0) {
+            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_FULL_LOCK_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
+        }
         getUidStatsLocked(uid).noteFullWifiLockReleasedLocked();
     }
 
+    int mWifiScanLockNesting = 0;
+    
     public void noteScanWifiLockAcquiredLocked(int uid) {
+        if (mWifiScanLockNesting == 0) {
+            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_SCAN_LOCK_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan lock on to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
+        }
+        mWifiScanLockNesting++;
         getUidStatsLocked(uid).noteScanWifiLockAcquiredLocked();
     }
 
     public void noteScanWifiLockReleasedLocked(int uid) {
+        mWifiScanLockNesting--;
+        if (mWifiScanLockNesting == 0) {
+            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_SCAN_LOCK_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan lock off to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
+        }
         getUidStatsLocked(uid).noteScanWifiLockReleasedLocked();
     }
 
+    int mWifiMulticastNesting = 0;
+    
     public void noteWifiMulticastEnabledLocked(int uid) {
+        if (mWifiMulticastNesting == 0) {
+            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_MULTICAST_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
+        }
+        mWifiMulticastNesting++;
         getUidStatsLocked(uid).noteWifiMulticastEnabledLocked();
     }
 
     public void noteWifiMulticastDisabledLocked(int uid) {
+        mWifiMulticastNesting--;
+        if (mWifiMulticastNesting == 0) {
+            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_MULTICAST_ON_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecord(SystemClock.elapsedRealtime());
+        }
         getUidStatsLocked(uid).noteWifiMulticastDisabledLocked();
     }
 
@@ -2766,6 +2947,11 @@
     }
 
     @Override
+    public BatteryHistoryRecord getHistory() {
+        return mHistory;
+    }
+    
+    @Override
     public int getStartCount() {
         return mStartCount;
     }
@@ -2784,6 +2970,12 @@
                 long mSecRealtime = SystemClock.elapsedRealtime();
                 long realtime = mSecRealtime * 1000;
                 if (onBattery) {
+                    clearHistory();
+                    mHistoryCur.batteryLevel = (byte)level;
+                    mHistoryCur.states &= ~BatteryHistoryRecord.STATE_BATTERY_PLUGGED_FLAG;
+                    if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
+                            + Integer.toHexString(mHistoryCur.states));
+                    addHistoryRecord(mSecRealtime);
                     mTrackBatteryUptimeStart = uptime;
                     mTrackBatteryRealtimeStart = realtime;
                     mUnpluggedBatteryUptime = getBatteryUptimeLocked(uptime);
@@ -2791,6 +2983,11 @@
                     mDischargeCurrentLevel = mDischargeStartLevel = level;
                     doUnplug(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
                 } else {
+                    mHistoryCur.batteryLevel = (byte)level;
+                    mHistoryCur.states |= BatteryHistoryRecord.STATE_BATTERY_PLUGGED_FLAG;
+                    if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
+                            + Integer.toHexString(mHistoryCur.states));
+                    addHistoryRecord(mSecRealtime);
                     mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart;
                     mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart;
                     mDischargeCurrentLevel = level;
@@ -2806,7 +3003,11 @@
     }
     
     public void recordCurrentLevel(int level) {
-        mDischargeCurrentLevel = level;
+        if (mDischargeCurrentLevel != level) {
+            mDischargeCurrentLevel = level;
+            mHistoryCur.batteryLevel = (byte)level;
+            addHistoryRecord(SystemClock.elapsedRealtime());
+        }
     }
     
     public void updateKernelWakelocksLocked() {
@@ -3146,6 +3347,24 @@
         return 0;
     }
 
+    void readHistory(Parcel in) {
+        mHistory = mHistoryEnd = mHistoryCache = null;
+        long time;
+        while ((time=in.readLong()) >= 0) {
+            BatteryHistoryRecord rec = new BatteryHistoryRecord(time, in);
+            addHistoryRecord(rec);
+        }
+    }
+    
+    void writeHistory(Parcel out) {
+        BatteryHistoryRecord rec = mHistory;
+        while (rec != null) {
+            if (rec.time >= 0) rec.writeToParcel(out, 0);
+            rec = rec.next;
+        }
+        out.writeLong(-1);
+    }
+    
     private void readSummaryFromParcel(Parcel in) {
         final int version = in.readInt();
         if (version != VERSION) {
@@ -3154,6 +3373,8 @@
             return;
         }
 
+        readHistory(in);
+        
         mStartCount = in.readInt();
         mBatteryUptime = in.readLong();
         mBatteryLastUptime = in.readLong();
@@ -3325,6 +3546,8 @@
 
         out.writeInt(VERSION);
 
+        writeHistory(out);
+        
         out.writeInt(mStartCount);
         out.writeLong(computeBatteryUptime(NOW_SYS, STATS_TOTAL));
         out.writeLong(computeBatteryUptime(NOW_SYS, STATS_CURRENT));
@@ -3493,6 +3716,8 @@
             throw new ParcelFormatException("Bad magic number");
         }
 
+        readHistory(in);
+        
         mStartCount = in.readInt();
         mBatteryUptime = in.readLong();
         mBatteryLastUptime = in.readLong();
@@ -3591,6 +3816,9 @@
         final long batteryRealtime = getBatteryRealtimeLocked(uSecRealtime);
         
         out.writeInt(MAGIC);
+        
+        writeHistory(out);
+        
         out.writeInt(mStartCount);
         out.writeLong(mBatteryUptime);
         out.writeLong(mBatteryLastUptime);
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 2a8c035..4a1580f 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -1152,7 +1152,7 @@
         mConnectingDataSource = new NuHTTPDataSource;
 
         mLock.unlock();
-        status_t err = mConnectingDataSource->connect(mUri/*, mUriHeaders */);
+        status_t err = mConnectingDataSource->connect(mUri, &mUriHeaders);
         mLock.lock();
 
         if (err != OK) {
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 6e4f9df..90a596c 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -109,7 +109,7 @@
         source = new FileSource(uri + 7);
     } else if (!strncasecmp("http://", uri, 7)) {
         sp<NuHTTPDataSource> httpSource = new NuHTTPDataSource;
-        if (httpSource->connect(uri /* , headers */) != OK) {
+        if (httpSource->connect(uri, headers) != OK) {
             return NULL;
         }
         source = new NuCachedSource2(httpSource);
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 8587c1b..ab9285d 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -4,6 +4,7 @@
 
 #include "include/NuHTTPDataSource.h"
 
+#include <cutils/properties.h>
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaErrors.h>
 
@@ -72,18 +73,33 @@
 NuHTTPDataSource::~NuHTTPDataSource() {
 }
 
-status_t NuHTTPDataSource::connect(const char *uri, off_t offset) {
+status_t NuHTTPDataSource::connect(
+        const char *uri,
+        const KeyedVector<String8, String8> *overrides,
+        off_t offset) {
+    String8 headers;
+    MakeFullHeaders(overrides, &headers);
+
+    return connect(uri, headers, offset);
+}
+
+status_t NuHTTPDataSource::connect(
+        const char *uri,
+        const String8 &headers,
+        off_t offset) {
     String8 host, path;
     unsigned port;
     if (!ParseURL(uri, &host, &port, &path)) {
         return ERROR_MALFORMED;
     }
 
-    return connect(host, port, path, offset);
+    return connect(host, port, path, headers, offset);
 }
 
 status_t NuHTTPDataSource::connect(
-        const char *host, unsigned port, const char *path, off_t offset) {
+        const char *host, unsigned port, const char *path,
+        const String8 &headers,
+        off_t offset) {
     LOGI("connect to %s:%u%s @%ld", host, port, path, offset);
 
     bool needsToReconnect = true;
@@ -99,6 +115,7 @@
     mHost = host;
     mPort = port;
     mPath = path;
+    mHeaders = headers;
 
     status_t err = OK;
 
@@ -133,6 +150,7 @@
             request.append(rangeHeader);
         }
 
+        request.append(mHeaders);
         request.append("\r\n");
 
         int httpStatus;
@@ -151,10 +169,17 @@
 
             mHTTP.disconnect();
 
-            return connect(value.c_str());
+            return connect(value.c_str(), headers, offset);
         }
 
-        CHECK(httpStatus >= 200 && httpStatus < 300);
+        if (httpStatus < 200 || httpStatus >= 300) {
+            mState = DISCONNECTED;
+            mHTTP.disconnect();
+
+            return ERROR_IO;
+        }
+
+        applyTimeoutResponse();
 
         if (offset == 0) {
             string value;
@@ -200,7 +225,8 @@
     if (offset != mOffset) {
         String8 host = mHost;
         String8 path = mPath;
-        status_t err = connect(host, mPort, path, offset);
+        String8 headers = mHeaders;
+        status_t err = connect(host, mPort, path, headers, offset);
 
         if (err != OK) {
             return err;
@@ -262,4 +288,51 @@
     return kWantsPrefetching;
 }
 
+// static
+void NuHTTPDataSource::MakeFullHeaders(
+        const KeyedVector<String8, String8> *overrides, String8 *headers) {
+    headers->setTo("");
+
+    headers->append("User-Agent: stagefright/1.1 (Linux;Android ");
+
+#if (PROPERTY_VALUE_MAX < 8)
+#error "PROPERTY_VALUE_MAX must be at least 8"
+#endif
+
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.build.version.release", value, "Unknown");
+    headers->append(value);
+    headers->append(")\r\n");
+
+    if (overrides == NULL) {
+        return;
+    }
+
+    for (size_t i = 0; i < overrides->size(); ++i) {
+        String8 line;
+        line.append(overrides->keyAt(i));
+        line.append(": ");
+        line.append(overrides->valueAt(i));
+        line.append("\r\n");
+
+        headers->append(line);
+    }
+}
+
+void NuHTTPDataSource::applyTimeoutResponse() {
+    string timeout;
+    if (mHTTP.find_header_value("X-SocketTimeout", &timeout)) {
+        const char *s = timeout.c_str();
+        char *end;
+        long tmp = strtol(s, &end, 10);
+        if (end == s || *end != '\0') {
+            LOGW("Illegal X-SocketTimeout value given.");
+            return;
+        }
+
+        LOGI("overriding default timeout, new timeout is %ld seconds", tmp);
+        mHTTP.setReceiveTimeout(tmp);
+    }
+}
+
 }  // namespace android
diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h
index 33339e2..8593a91 100644
--- a/media/libstagefright/include/NuHTTPDataSource.h
+++ b/media/libstagefright/include/NuHTTPDataSource.h
@@ -13,10 +13,9 @@
 struct NuHTTPDataSource : public DataSource {
     NuHTTPDataSource();
 
-    status_t connect(const char *uri, off_t offset = 0);
-
     status_t connect(
-            const char *host, unsigned port, const char *path,
+            const char *uri,
+            const KeyedVector<String8, String8> *headers = NULL,
             off_t offset = 0);
 
     void disconnect();
@@ -44,12 +43,27 @@
     String8 mHost;
     unsigned mPort;
     String8 mPath;
+    String8 mHeaders;
 
     HTTPStream mHTTP;
     off_t mOffset;
     off_t mContentLength;
     bool mContentLengthValid;
 
+    status_t connect(
+            const char *uri, const String8 &headers, off_t offset);
+
+    status_t connect(
+            const char *host, unsigned port, const char *path,
+            const String8 &headers,
+            off_t offset);
+
+    void applyTimeoutResponse();
+
+    static void MakeFullHeaders(
+            const KeyedVector<String8, String8> *overrides,
+            String8 *headers);
+
     NuHTTPDataSource(const NuHTTPDataSource &);
     NuHTTPDataSource &operator=(const NuHTTPDataSource &);
 };
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index cfcac86..0946ba2 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -288,6 +288,17 @@
     // system/rootdir/init.rc on startup.
     static final int SECONDARY_SERVER_ADJ;
 
+    // This is a process with a heavy-weight application.  It is in the
+    // background, but we want to try to avoid killing it.  Value set in
+    // system/rootdir/init.rc on startup.
+    static final int HEAVY_WEIGHT_APP_ADJ;
+
+    // This is a process only hosting components that are perceptible to the
+    // user, and we really want to avoid killing them, but they are not
+    // immediately visible. An example is background music playback.  Value set in
+    // system/rootdir/init.rc on startup.
+    static final int PERCEPTIBLE_APP_ADJ;
+
     // This is a process only hosting activities that are visible to the
     // user, so we'd prefer they don't disappear. Value set in
     // system/rootdir/init.rc on startup.
@@ -313,6 +324,8 @@
     static final int HOME_APP_MEM;
     static final int BACKUP_APP_MEM;
     static final int SECONDARY_SERVER_MEM;
+    static final int HEAVY_WEIGHT_APP_MEM;
+    static final int PERCEPTIBLE_APP_MEM;
     static final int VISIBLE_APP_MEM;
     static final int FOREGROUND_APP_MEM;
 
@@ -333,37 +346,40 @@
     // been idle for less than 120 seconds.
     static final long EMPTY_APP_IDLE_OFFSET = 120*1000;
     
+    static int getIntProp(String name, boolean allowZero) {
+        String str = SystemProperties.get(name);
+        if (str == null) {
+            throw new IllegalArgumentException("Property not defined: " + name);
+        }
+        int val = Integer.valueOf(str);
+        if (val == 0 && !allowZero) {
+            throw new IllegalArgumentException("Property must not be zero: " + name);
+        }
+        return val;
+    }
+    
     static {
         // These values are set in system/rootdir/init.rc on startup.
-        FOREGROUND_APP_ADJ =
-            Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_ADJ"));
-        VISIBLE_APP_ADJ =
-            Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_ADJ"));
-        SECONDARY_SERVER_ADJ =
-            Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_ADJ"));
-        BACKUP_APP_ADJ =
-            Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_ADJ"));
-        HOME_APP_ADJ =
-            Integer.valueOf(SystemProperties.get("ro.HOME_APP_ADJ"));
-        HIDDEN_APP_MIN_ADJ =
-            Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MIN_ADJ"));
-        EMPTY_APP_ADJ =
-            Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_ADJ"));
-        HIDDEN_APP_MAX_ADJ = EMPTY_APP_ADJ-1;
-        FOREGROUND_APP_MEM =
-            Integer.valueOf(SystemProperties.get("ro.FOREGROUND_APP_MEM"))*PAGE_SIZE;
-        VISIBLE_APP_MEM =
-            Integer.valueOf(SystemProperties.get("ro.VISIBLE_APP_MEM"))*PAGE_SIZE;
-        SECONDARY_SERVER_MEM =
-            Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
-        BACKUP_APP_MEM =
-            Integer.valueOf(SystemProperties.get("ro.BACKUP_APP_MEM"))*PAGE_SIZE;
-        HOME_APP_MEM =
-            Integer.valueOf(SystemProperties.get("ro.HOME_APP_MEM"))*PAGE_SIZE;
-        HIDDEN_APP_MEM =
-            Integer.valueOf(SystemProperties.get("ro.HIDDEN_APP_MEM"))*PAGE_SIZE;
-        EMPTY_APP_MEM =
-            Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_MEM"))*PAGE_SIZE;
+        FOREGROUND_APP_ADJ = getIntProp("ro.FOREGROUND_APP_ADJ", true);
+        VISIBLE_APP_ADJ = getIntProp("ro.VISIBLE_APP_ADJ", true);
+        PERCEPTIBLE_APP_ADJ = getIntProp("ro.PERCEPTIBLE_APP_ADJ", true);
+        HEAVY_WEIGHT_APP_ADJ = getIntProp("ro.HEAVY_WEIGHT_APP_ADJ", true);
+        SECONDARY_SERVER_ADJ = getIntProp("ro.SECONDARY_SERVER_ADJ", true);
+        BACKUP_APP_ADJ = getIntProp("ro.BACKUP_APP_ADJ", true);
+        HOME_APP_ADJ = getIntProp("ro.HOME_APP_ADJ", true);
+        HIDDEN_APP_MIN_ADJ = getIntProp("ro.HIDDEN_APP_MIN_ADJ", true);
+        EMPTY_APP_ADJ = getIntProp("ro.EMPTY_APP_ADJ", true);
+        // These days we use the last empty slot for hidden apps as well.
+        HIDDEN_APP_MAX_ADJ = EMPTY_APP_ADJ;
+        FOREGROUND_APP_MEM = getIntProp("ro.FOREGROUND_APP_MEM", false)*PAGE_SIZE;
+        VISIBLE_APP_MEM = getIntProp("ro.VISIBLE_APP_MEM", false)*PAGE_SIZE;
+        PERCEPTIBLE_APP_MEM = getIntProp("ro.PERCEPTIBLE_APP_MEM", false)*PAGE_SIZE;
+        HEAVY_WEIGHT_APP_MEM = getIntProp("ro.HEAVY_WEIGHT_APP_MEM", false)*PAGE_SIZE;
+        SECONDARY_SERVER_MEM = getIntProp("ro.SECONDARY_SERVER_MEM", false)*PAGE_SIZE;
+        BACKUP_APP_MEM = getIntProp("ro.BACKUP_APP_MEM", false)*PAGE_SIZE;
+        HOME_APP_MEM = getIntProp("ro.HOME_APP_MEM", false)*PAGE_SIZE;
+        HIDDEN_APP_MEM = getIntProp("ro.HIDDEN_APP_MEM", false)*PAGE_SIZE;
+        EMPTY_APP_MEM = getIntProp("ro.EMPTY_APP_MEM", false)*PAGE_SIZE;
     }
     
     static final int MY_PID = Process.myPid();
@@ -4974,8 +4990,8 @@
                                 (rec.lastLowMemory+GC_MIN_INTERVAL) <= now) {
                             // The low memory report is overriding any current
                             // state for a GC request.  Make sure to do
-                            // visible/foreground processes first.
-                            if (rec.setAdj <= VISIBLE_APP_ADJ) {
+                            // heavy/important/visible/foreground processes first.
+                            if (rec.setAdj <= HEAVY_WEIGHT_APP_ADJ) {
                                 rec.lastRequestedGc = 0;
                             } else {
                                 rec.lastRequestedGc = rec.lastLowMemory;
@@ -8102,8 +8118,8 @@
                         r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
                     }
                     cpr.clients.add(r);
-                    if (cpr.app != null && r.setAdj >= VISIBLE_APP_ADJ) {
-                        // If this is a visible app accessing the provider,
+                    if (cpr.app != null && r.setAdj <= PERCEPTIBLE_APP_ADJ) {
+                        // If this is a perceptible app accessing the provider,
                         // make sure to count it as being accessed and thus
                         // back up on the LRU list.  This is good because
                         // content providers are often expensive to start.
@@ -9741,10 +9757,12 @@
                         currApp.lru = 0;
                     } else if (adj >= SECONDARY_SERVER_ADJ) {
                         currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
+                    } else if (adj >= HEAVY_WEIGHT_APP_ADJ) {
+                        currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_HEAVY_WEIGHT;
+                    } else if (adj >= PERCEPTIBLE_APP_ADJ) {
+                        currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
                     } else if (adj >= VISIBLE_APP_ADJ) {
                         currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
-                    } else if (app == mHeavyWeightProcess) {
-                        currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_HEAVY_WEIGHT;
                     } else {
                         currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
                     }
@@ -10006,7 +10024,7 @@
             needSep = true;
             pw.println("  Running processes (most recent first):");
             dumpProcessList(pw, this, mLruProcesses, "    ",
-                    "App ", "PERS", true);
+                    "Proc", "PERS", true);
             needSep = true;
         }
 
@@ -10502,7 +10520,8 @@
             String prefix, String normalLabel, String persistentLabel,
             boolean inclOomAdj) {
         int numPers = 0;
-        for (int i=list.size()-1; i>=0; i--) {
+        final int N = list.size()-1;
+        for (int i=N; i>=0; i--) {
             ProcessRecord r = (ProcessRecord)list.get(i);
             if (false) {
                 pw.println(prefix + (r.persistent ? persistentLabel : normalLabel)
@@ -10520,6 +10539,10 @@
                     oomAdj = buildOomTag("svc", "  ", r.setAdj, SECONDARY_SERVER_ADJ);
                 } else if (r.setAdj >= BACKUP_APP_ADJ) {
                     oomAdj = buildOomTag("bckup", null, r.setAdj, BACKUP_APP_ADJ);
+                } else if (r.setAdj >= HEAVY_WEIGHT_APP_ADJ) {
+                    oomAdj = buildOomTag("hvy  ", null, r.setAdj, HEAVY_WEIGHT_APP_ADJ);
+                } else if (r.setAdj >= PERCEPTIBLE_APP_ADJ) {
+                    oomAdj = buildOomTag("prcp ", null, r.setAdj, PERCEPTIBLE_APP_ADJ);
                 } else if (r.setAdj >= VISIBLE_APP_ADJ) {
                     oomAdj = buildOomTag("vis  ", null, r.setAdj, VISIBLE_APP_ADJ);
                 } else if (r.setAdj >= FOREGROUND_APP_ADJ) {
@@ -10545,10 +10568,27 @@
                 }
                 pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)",
                         prefix, (r.persistent ? persistentLabel : normalLabel),
-                        i, oomAdj, schedGroup, r.toShortString(), r.adjType));
+                        N-i, oomAdj, schedGroup, r.toShortString(), r.adjType));
                 if (r.adjSource != null || r.adjTarget != null) {
-                    pw.println(prefix + "          " + r.adjTarget
-                            + "<=" + r.adjSource);
+                    pw.print(prefix);
+                    pw.print("          ");
+                    if (r.adjTarget instanceof ComponentName) {
+                        pw.print(((ComponentName)r.adjTarget).flattenToShortString());
+                    } else if (r.adjTarget != null) {
+                        pw.print(r.adjTarget.toString());
+                    } else {
+                        pw.print("{null}");
+                    }
+                    pw.print("<=");
+                    if (r.adjSource instanceof ProcessRecord) {
+                        pw.print("Proc{");
+                        pw.print(((ProcessRecord)r.adjSource).toShortString());
+                        pw.println("}");
+                    } else if (r.adjSource != null) {
+                        pw.println(r.adjSource.toString());
+                    } else {
+                        pw.println("{null}");
+                    }
                 }
             } else {
                 pw.println(String.format("%s%s #%2d: %s",
@@ -14092,11 +14132,6 @@
             adj = FOREGROUND_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "instrumentation";
-        } else if (app == mHeavyWeightProcess) {
-            // We don't want to kill the current heavy-weight process.
-            adj = FOREGROUND_APP_ADJ;
-            schedGroup = Process.THREAD_GROUP_DEFAULT;
-            app.adjType = "heavy";
         } else if (app.persistentActivities > 0) {
             // Special persistent activities...  shouldn't be used these days.
             adj = FOREGROUND_APP_ADJ;
@@ -14117,15 +14152,20 @@
             app.adjType = "exec-service";
         } else if (app.foregroundServices) {
             // The user is aware of this app, so make it visible.
-            adj = VISIBLE_APP_ADJ;
+            adj = PERCEPTIBLE_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "foreground-service";
         } else if (app.forcingToForeground != null) {
             // The user is aware of this app, so make it visible.
-            adj = VISIBLE_APP_ADJ;
+            adj = PERCEPTIBLE_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "force-foreground";
             app.adjSource = app.forcingToForeground;
+        } else if (app == mHeavyWeightProcess) {
+            // We don't want to kill the current heavy-weight process.
+            adj = HEAVY_WEIGHT_APP_ADJ;
+            schedGroup = Process.THREAD_GROUP_DEFAULT;
+            app.adjType = "heavy";
         } else if (app == mHomeProcess) {
             // This process is hosting what we currently consider to be the
             // home app, so we don't want to let it go into the background.
@@ -14220,7 +14260,7 @@
                             ProcessRecord client = cr.binding.client;
                             int myHiddenAdj = hiddenAdj;
                             if (myHiddenAdj > client.hiddenAdj) {
-                                if (client.hiddenAdj > VISIBLE_APP_ADJ) {
+                                if (client.hiddenAdj >= VISIBLE_APP_ADJ) {
                                     myHiddenAdj = client.hiddenAdj;
                                 } else {
                                     myHiddenAdj = VISIBLE_APP_ADJ;
@@ -14229,7 +14269,7 @@
                             int clientAdj = computeOomAdjLocked(
                                 client, myHiddenAdj, TOP_APP, true);
                             if (adj > clientAdj) {
-                                adj = clientAdj > VISIBLE_APP_ADJ
+                                adj = clientAdj >= VISIBLE_APP_ADJ
                                         ? clientAdj : VISIBLE_APP_ADJ;
                                 if (!client.hidden) {
                                     app.hidden = false;
@@ -14340,7 +14380,7 @@
         //      " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj);
         if (adj > app.maxAdj) {
             adj = app.maxAdj;
-            if (app.maxAdj <= VISIBLE_APP_ADJ) {
+            if (app.maxAdj <= PERCEPTIBLE_APP_ADJ) {
                 schedGroup = Process.THREAD_GROUP_DEFAULT;
             }
         }
@@ -14392,7 +14432,7 @@
         if (canGcNowLocked()) {
             while (mProcessesToGc.size() > 0) {
                 ProcessRecord proc = mProcessesToGc.remove(0);
-                if (proc.curRawAdj > VISIBLE_APP_ADJ || proc.reportLowMemory) {
+                if (proc.curRawAdj > PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) {
                     if ((proc.lastRequestedGc+GC_MIN_INTERVAL)
                             <= SystemClock.uptimeMillis()) {
                         // To avoid spamming the system, we will GC processes one
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 33bbc13..ba13c51 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -96,6 +96,7 @@
     public void noteStartWakelock(int uid, String name, int type) {
         enforceCallingPermission();
         synchronized (mStats) {
+            Slog.i("battery", "Start wake lock: " + uid + " " + name);
             mStats.getUidStatsLocked(uid).noteStartWakeLocked(name, type);
         }
     }
@@ -103,6 +104,7 @@
     public void noteStopWakelock(int uid, String name, int type) {
         enforceCallingPermission();
         synchronized (mStats) {
+            Slog.i("battery", "Stop wake lock: " + uid + " " + name);
             mStats.getUidStatsLocked(uid).noteStopWakeLocked(name, type);
         }
     }