Merge change 22944 into eclair

* changes:
  resolved conflicts for merge of 301b97ac to eclair
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index fe3f47f..fafe00f 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1292,6 +1292,11 @@
             // TODO: we should ask the window manager to do something!
             // for now we just do nothing
             return;
+        } catch (IllegalArgumentException e) {
+            Log.e("ViewRoot", "IllegalArgumentException locking surface", e);
+            // TODO: we should ask the window manager to do something!
+            // for now we just do nothing
+            return;
         }
 
         try {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 98f35f4..95edbeb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -19,7 +19,6 @@
 import com.android.server.am.ActivityManagerService;
 import com.android.server.status.StatusBarService;
 
-import dalvik.system.PathClassLoader;
 import dalvik.system.VMRuntime;
 
 import android.app.ActivityManagerNative;
@@ -42,9 +41,6 @@
 import android.util.Log;
 import android.accounts.AccountManagerService;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-
 class ServerThread extends Thread {
     private static final String TAG = "SystemServer";
     private final static boolean INCLUDE_DEMO = false;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 23eb7c1..0d4a4bf 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -239,6 +239,9 @@
     // How long to wait after going idle before forcing apps to GC.
     static final int GC_TIMEOUT = 5*1000;
 
+    // The minimum amount of time between successive GC requests for a process.
+    static final int GC_MIN_INTERVAL = 60*1000;
+
     // How long we wait until giving up on an activity telling us it has
     // finished destroying itself.
     static final int DESTROY_TIMEOUT = 10*1000;
@@ -253,10 +256,23 @@
     // is no longer considered to be a relaunch of the service.
     static final int SERVICE_RESTART_DURATION = 5*1000;
 
+    // How long a service needs to be running until it will start back at
+    // SERVICE_RESTART_DURATION after being killed.
+    static final int SERVICE_RESET_RUN_DURATION = 60*1000;
+
+    // Multiplying factor to increase restart duration time by, for each time
+    // a service is killed before it has run for SERVICE_RESET_RUN_DURATION.
+    static final int SERVICE_RESTART_DURATION_FACTOR = 4;
+    
+    // The minimum amount of time between restarting services that we allow.
+    // That is, when multiple services are restarting, we won't allow each
+    // to restart less than this amount of time from the last one.
+    static final int SERVICE_MIN_RESTART_TIME_BETWEEN = 10*1000;
+
     // Maximum amount of time for there to be no activity on a service before
     // we consider it non-essential and allow its process to go on the
     // LRU background list.
-    static final int MAX_SERVICE_INACTIVITY = 10*60*1000;
+    static final int MAX_SERVICE_INACTIVITY = 30*60*1000;
     
     // How long we wait until we timeout on key dispatching.
     static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
@@ -1102,8 +1118,7 @@
                         }
                     }
                 }
-                break;
-            }
+            } break;
             case SHOW_UID_ERROR_MSG: {
                 // XXX This is a temporary dialog, no need to localize.
                 AlertDialog d = new BaseErrorDialog(mContext);
@@ -1147,7 +1162,7 @@
                 synchronized (ActivityManagerService.this) {
                     resumeTopActivityLocked(null);
                 }
-            }
+            } break;
             case PROC_START_TIMEOUT_MSG: {
                 if (mDidDexOpt) {
                     mDidDexOpt = false;
@@ -1160,12 +1175,12 @@
                 synchronized (ActivityManagerService.this) {
                     processStartTimedOutLocked(app);
                 }
-            }
+            } break;
             case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
                 synchronized (ActivityManagerService.this) {
                     doPendingActivityLaunchesLocked(true);
                 }
-            }
+            } break;
             case KILL_APPLICATION_MSG: {
                 synchronized (ActivityManagerService.this) {
                     int uid = msg.arg1;
@@ -4443,17 +4458,26 @@
                 if (!haveBg) {
                     Log.i(TAG, "Low Memory: No more background processes.");
                     EventLog.writeEvent(LOG_AM_LOW_MEMORY, mLRUProcesses.size());
+                    long now = SystemClock.uptimeMillis();
                     for (i=0; i<count; i++) {
                         ProcessRecord rec = mLRUProcesses.get(i);
-                        if (rec.thread != null) {
-                            rec.lastRequestedGc = SystemClock.uptimeMillis();
-                            try {
-                                rec.thread.scheduleLowMemory();
-                            } catch (RemoteException e) {
-                                // Don't care if the process is gone.
+                        if (rec.thread != null &&
+                                (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) {
+                                rec.lastRequestedGc = 0;
+                            } else {
+                                rec.lastRequestedGc = rec.lastLowMemory;
                             }
+                            rec.reportLowMemory = true;
+                            rec.lastLowMemory = now;
+                            mProcessesToGc.remove(rec);
+                            addProcessToGcListLocked(rec);
                         }
                     }
+                    scheduleAppGcsLocked();
                 }
             }
         } else if (Config.LOGD) {
@@ -5137,7 +5161,7 @@
                     isRestrictedBackupMode || !normalMode,
                     mConfiguration, getCommonServicesLocked());
             updateLRUListLocked(app, false);
-            app.lastRequestedGc = SystemClock.uptimeMillis();
+            app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
         } catch (Exception e) {
             // todo: Yikes!  What should we do?  For now we will try to
             // start another process, but that could easily get us in
@@ -8949,6 +8973,24 @@
                         "OnHold Norm", "OnHold PERS", false);
             }
 
+            if (mProcessesToGc.size() > 0) {
+                if (needSep) pw.println(" ");
+                needSep = true;
+                pw.println("  Processes that are waiting to GC:");
+                long now = SystemClock.uptimeMillis();
+                for (int i=0; i<mProcessesToGc.size(); i++) {
+                    ProcessRecord proc = mProcessesToGc.get(i);
+                    pw.print("    Process "); pw.println(proc);
+                    pw.print("      lowMem="); pw.print(proc.reportLowMemory);
+                            pw.print(", last gced=");
+                            pw.print(now-proc.lastRequestedGc);
+                            pw.print(" ms ago, last lowMwm=");
+                            pw.print(now-proc.lastLowMemory);
+                            pw.println(" ms ago");
+                    
+                }
+            }
+            
             if (mProcessCrashTimes.getMap().size() > 0) {
                 if (needSep) pw.println(" ");
                 needSep = true;
@@ -10112,8 +10154,9 @@
             boolean allowCancel) {
         boolean canceled = false;
         
+        final long now = SystemClock.uptimeMillis();
         long minDuration = SERVICE_RESTART_DURATION;
-        long resetTime = minDuration*2*2*2;
+        long resetTime = SERVICE_RESET_RUN_DURATION;
         
         // Any delivered but not yet finished starts should be put back
         // on the pending list.
@@ -10149,24 +10192,46 @@
             // the beginning, so we don't infinitely increase the duration
             // on a service that just occasionally gets killed (which is
             // a normal case, due to process being killed to reclaim memory).
-            long now = SystemClock.uptimeMillis();
             if (now > (r.restartTime+resetTime)) {
                 r.restartCount = 1;
                 r.restartDelay = minDuration;
             } else {
-                r.restartDelay *= 4;
+                r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
                 if (r.restartDelay < minDuration) {
                     r.restartDelay = minDuration;
                 }
             }
         }
+        
+        r.nextRestartTime = now + r.restartDelay;
+        
+        // Make sure that we don't end up restarting a bunch of services
+        // all at the same time.
+        boolean repeat;
+        do {
+            repeat = false;
+            for (int i=mRestartingServices.size()-1; i>=0; i--) {
+                ServiceRecord r2 = mRestartingServices.get(i);
+                if (r2 != r && r.nextRestartTime
+                        >= (r2.nextRestartTime-SERVICE_MIN_RESTART_TIME_BETWEEN)
+                        && r.nextRestartTime
+                        < (r2.nextRestartTime+SERVICE_MIN_RESTART_TIME_BETWEEN)) {
+                    r.nextRestartTime = r2.nextRestartTime + SERVICE_MIN_RESTART_TIME_BETWEEN;
+                    r.restartDelay = r.nextRestartTime - now;
+                    repeat = true;
+                    break;
+                }
+            }
+        } while (repeat);
+        
         if (!mRestartingServices.contains(r)) {
             mRestartingServices.add(r);
         }
+        
         r.cancelNotification();
         
         mHandler.removeCallbacks(r.restarter);
-        mHandler.postDelayed(r.restarter, r.restartDelay);
+        mHandler.postAtTime(r.restarter, r.nextRestartTime);
         r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
         Log.w(TAG, "Scheduling restart of crashed service "
                 + r.shortName + " in " + r.restartDelay + "ms");
@@ -12656,15 +12721,15 @@
         if (app == TOP_APP) {
             // The last app on the list is the foreground app.
             adj = FOREGROUND_APP_ADJ;
-            app.adjType = "top";
+            app.adjType = "top-activity";
         } else if (app.instrumentationClass != null) {
             // Don't want to kill running instrumentation.
             adj = FOREGROUND_APP_ADJ;
-            app.adjType = "instr";
+            app.adjType = "instrumentation";
         } else if (app.persistentActivities > 0) {
             // Special persistent activities...  shouldn't be used these days.
             adj = FOREGROUND_APP_ADJ;
-            app.adjType = "pers";
+            app.adjType = "persistent";
         } else if (app.curReceiver != null ||
                 (mPendingBroadcast != null && mPendingBroadcast.curApp == app)) {
             // An app that is currently receiving a broadcast also
@@ -12693,6 +12758,7 @@
         } else if ((N=app.activities.size()) != 0) {
             // This app is in the background with paused activities.
             adj = hiddenAdj;
+            app.adjType = "bg-activities";
             for (int j=0; j<N; j++) {
                 if (((HistoryRecord)app.activities.get(j)).visible) {
                     // This app has a visible activity!
@@ -12732,7 +12798,7 @@
             // its services we may bump it up from there.
             if (adj > hiddenAdj) {
                 adj = hiddenAdj;
-                app.adjType = "services";
+                app.adjType = "bg-services";
             }
             final long now = SystemClock.uptimeMillis();
             // This process is more important if the top activity is
@@ -12874,7 +12940,12 @@
         try {
             app.lastRequestedGc = SystemClock.uptimeMillis();
             if (app.thread != null) {
-                app.thread.processInBackground();
+                if (app.reportLowMemory) {
+                    app.reportLowMemory = false;
+                    app.thread.scheduleLowMemory();
+                } else {
+                    app.thread.processInBackground();
+                }
             }
         } catch (Exception e) {
             // whatever.
@@ -12903,14 +12974,24 @@
         if (canGcNow()) {
             while (mProcessesToGc.size() > 0) {
                 ProcessRecord proc = mProcessesToGc.remove(0);
-                if (proc.curRawAdj > VISIBLE_APP_ADJ) {
-                    // To avoid spamming the system, we will GC processes one
-                    // at a time, waiting a few seconds between each.
-                    performAppGcLocked(proc);
-                    scheduleAppGcsLocked();
-                    return;
+                if (proc.curRawAdj > VISIBLE_APP_ADJ || proc.reportLowMemory) {
+                    if ((proc.lastRequestedGc+GC_MIN_INTERVAL)
+                            <= SystemClock.uptimeMillis()) {
+                        // To avoid spamming the system, we will GC processes one
+                        // at a time, waiting a few seconds between each.
+                        performAppGcLocked(proc);
+                        scheduleAppGcsLocked();
+                        return;
+                    } else {
+                        // It hasn't been long enough since we last GCed this
+                        // process...  put it in the list to wait for its time.
+                        addProcessToGcListLocked(proc);
+                        break;
+                    }
                 }
             }
+            
+            scheduleAppGcsLocked();
         }
     }
     
@@ -12931,8 +13012,39 @@
      */
     final void scheduleAppGcsLocked() {
         mHandler.removeMessages(GC_BACKGROUND_PROCESSES_MSG);
-        Message msg = mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG);
-        mHandler.sendMessageDelayed(msg, GC_TIMEOUT);
+        
+        if (mProcessesToGc.size() > 0) {
+            // Schedule a GC for the time to the next process.
+            ProcessRecord proc = mProcessesToGc.get(0);
+            Message msg = mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG);
+            
+            long when = mProcessesToGc.get(0).lastRequestedGc + GC_MIN_INTERVAL;
+            long now = SystemClock.uptimeMillis();
+            if (when < (now+GC_TIMEOUT)) {
+                when = now + GC_TIMEOUT;
+            }
+            mHandler.sendMessageAtTime(msg, when);
+        }
+    }
+    
+    /**
+     * Add a process to the array of processes waiting to be GCed.  Keeps the
+     * list in sorted order by the last GC time.  The process can't already be
+     * on the list.
+     */
+    final void addProcessToGcListLocked(ProcessRecord proc) {
+        boolean added = false;
+        for (int i=mProcessesToGc.size()-1; i>=0; i--) {
+            if (mProcessesToGc.get(i).lastRequestedGc <
+                    proc.lastRequestedGc) {
+                added = true;
+                mProcessesToGc.add(i+1, proc);
+                break;
+            }
+        }
+        if (!added) {
+            mProcessesToGc.add(0, proc);
+        }
     }
     
     /**
@@ -12942,11 +13054,11 @@
      */
     final void scheduleAppGcLocked(ProcessRecord app) {
         long now = SystemClock.uptimeMillis();
-        if ((app.lastRequestedGc+5000) > now) {
+        if ((app.lastRequestedGc+GC_MIN_INTERVAL) > now) {
             return;
         }
         if (!mProcessesToGc.contains(app)) {
-            mProcessesToGc.add(app);
+            addProcessToGcListLocked(app);
             scheduleAppGcsLocked();
         }
     }
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 544d034..76fdf09 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -71,6 +71,8 @@
     ComponentName instrumentationResultClass;// copy of instrumentationClass
     BroadcastRecord curReceiver;// receiver currently running in the app
     long lastRequestedGc;       // When we last asked the app to do a gc
+    long lastLowMemory;         // When we last told the app that memory is low
+    boolean reportLowMemory;    // Set to true when waiting to report low mem
     int lastPss;                // Last pss size reported by app.
     String adjType;             // Debugging: primary thing impacting oom_adj.
     Object adjSource;           // Debugging: option dependent object.
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index afbf9c7..2534410 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -136,6 +136,7 @@
         if (permission != null) {
             pw.print(prefix); pw.print("permission="); pw.println(permission);
         }
+        long now = SystemClock.uptimeMillis();
         pw.print(prefix); pw.print("baseDir="); pw.print(baseDir);
                 if (!resDir.equals(baseDir)) pw.print(" resDir="); pw.print(resDir);
                 pw.print(" dataDir="); pw.println(dataDir);
@@ -145,8 +146,8 @@
                     pw.print(" foregroundId="); pw.print(foregroundId);
                     pw.print(" foregroundNoti="); pw.println(foregroundNoti);
         }
-        pw.print(prefix); pw.print("lastActivity="); pw.print(lastActivity);
-                pw.print(" executingStart="); pw.print(executingStart);
+        pw.print(prefix); pw.print("lastActivity="); pw.print(lastActivity-now);
+                pw.print(" executingStart="); pw.print(executingStart-now);
                 pw.print(" restartTime="); pw.println(restartTime);
         if (startRequested || lastStartId != 0) {
             pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
@@ -158,8 +159,8 @@
                 || restartDelay != 0 || nextRestartTime != 0) {
             pw.print(prefix); pw.print("executeNesting="); pw.print(executeNesting);
                     pw.print(" restartCount="); pw.print(restartCount);
-                    pw.print(" restartDelay="); pw.print(restartDelay);
-                    pw.print(" nextRestartTime="); pw.print(nextRestartTime);
+                    pw.print(" restartDelay="); pw.print(restartDelay-now);
+                    pw.print(" nextRestartTime="); pw.print(nextRestartTime-now);
                     pw.print(" crashCount="); pw.println(crashCount);
         }
         if (deliveredStarts.size() > 0) {