Merge change 20998

* changes:
  Add norm() and map() to MathUtils.
diff --git a/api/current.xml b/api/current.xml
index 69d4d5c..b9fb85a 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -27892,6 +27892,23 @@
 <parameter name="cursor" type="android.database.Cursor">
 </parameter>
 </method>
+<method name="onQueryEntitiesComplete"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="token" type="int">
+</parameter>
+<parameter name="cookie" type="java.lang.Object">
+</parameter>
+<parameter name="iterator" type="android.content.EntityIterator">
+</parameter>
+</method>
 <method name="onUpdateComplete"
  return="void"
  abstract="false"
@@ -27974,6 +27991,29 @@
 <parameter name="orderBy" type="java.lang.String">
 </parameter>
 </method>
+<method name="startQueryEntities"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="token" type="int">
+</parameter>
+<parameter name="cookie" type="java.lang.Object">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<parameter name="orderBy" type="java.lang.String">
+</parameter>
+</method>
 <method name="startUpdate"
  return="void"
  abstract="false"
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 32a2891..e045105 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1701,6 +1701,14 @@
             printRow(pw, TWO_COUNT_COLUMNS, "numPagers:", stats.numPagers, "inactivePageKB:",
                     (stats.totalBytes - stats.referencedBytes) / 1024);
             printRow(pw, ONE_COUNT_COLUMN, "activePageKB:", stats.referencedBytes / 1024);
+            
+            // Asset details.
+            String assetAlloc = AssetManager.getAssetAllocations();
+            if (assetAlloc != null) {
+                pw.println(" ");
+                pw.println(" Asset Allocations");
+                pw.print(assetAlloc);
+            }
         }
 
         private void printRow(PrintWriter pw, String format, Object...objs) {
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 7ad6423..97d46f8 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -369,7 +369,26 @@
  * forget to decode it.  (See {@link android.net.Uri#getPathSegments} and
  * {@link android.net.Uri#getLastPathSegment} for helpful utilities you can use here.)</li>
  * </ul>
- * 
+ *
+ * <p><b>Providing access to Content Providers that require permissions.</b>  If your content
+ * provider declares an android:readPermission in your application's manifest, you must provide
+ * access to the search infrastructure to the search suggestion path by including a path-permission
+ * that grants android:readPermission access to "android.permission.GLOBAL_SEARCH".  Granting access
+ * explicitly to the search infrastructure ensures it will be able to access the search suggestions
+ * without needing to know ahead of time any other details of the permissions protecting your
+ * provider.  Content providers that require no permissions are already available to the search
+ * infrastructure.  Here is an example of a provider that protects access to it with permissions,
+ * and provides read access to the search infrastructure to the path that it expects to receive the
+ * suggestion query on:
+ * <pre class="prettyprint">
+ * &lt;provider android:name="MyProvider" android:authorities="myprovider"
+ *        android:readPermission="android.permission.READ_MY_DATA"
+ *        android:writePermission="android.permission.WRITE_MY_DATA"&gt;
+ *    &lt;path-permission android:path="/search_suggest_query"
+ *            android:readPermission="android.permission.GLOBAL_SEARCH" /&gt;
+ * &lt;/provider&gt;
+ * </pre>
+ *
  * <p><b>Handling empty queries.</b>  Your application should handle the "empty query"
  * (no user text entered) case properly, and generate useful suggestions in this case.  There are a
  * number of ways to do this;  Two are outlined here:
@@ -377,7 +396,7 @@
  * unfiltered.  (example: People)</li>
  * <li>For a query search, you could simply present the most recent queries.  This allows the user
  * to quickly repeat a recent search.</li></ul>
- * 
+ *
  * <p><b>The Format of Individual Suggestions.</b>  Your suggestions are communicated back to the
  * Search Manager by way of a {@link android.database.Cursor Cursor}.  The Search Manager will
  * usually pass a null Projection, which means that your provider can simply return all appropriate
@@ -453,13 +472,42 @@
  *         <td align="center">No</td>
  *     </tr>
  *     
+ *     <tr><th>{@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA}</th>
+ *         <td>If this column exists <i>and</i> this element exists at a given row, this is the
+ *             data that will be used when forming the suggestion's intent.  If not provided,
+ *             the Intent's extra data field will be null.  This column allows suggestions to
+ *             provide additional arbitrary data which will be included as an extra under the
+ *             key {@link #EXTRA_DATA_KEY}.</td>
+ *         <td align="center">No.</td>
+ *     </tr>
+ *
  *     <tr><th>{@link #SUGGEST_COLUMN_QUERY}</th>
  *         <td>If this column exists <i>and</i> this element exists at the given row, this is the 
  *             data that will be used when forming the suggestion's query.</td>
  *         <td align="center">Required if suggestion's action is 
  *             {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</td>
  *     </tr>
- *     
+ *
+ *     <tr><th>{@link #SUGGEST_COLUMN_SHORTCUT_ID}</th>
+ *         <td>This column is used to indicate whether a search suggestion should be stored as a
+ *             shortcut, and whether it should be validated.  Shortcuts are usually formed when the
+ *             user clicks a suggestion from Quick Search Box.  If missing, the result will be
+ *             stored as a shortcut and never refreshed.  If set to
+ *             {@link #SUGGEST_NEVER_MAKE_SHORTCUT}, the result will not be stored as a shortcut.
+ *             Otherwise, the shortcut id will be used to check back for for an up to date
+ *             suggestion using {@link #SUGGEST_URI_PATH_SHORTCUT}. Read more about shortcut
+ *             refreshing in the section about
+ *             <a href="#ExposingSearchSuggestionsToQuickSearchBox">exposing search suggestions to
+ *             Quick Search Box</a>.</td>
+ *         <td align="center">No.  Only applicable to sources included in Quick Search Box.</td>
+ *     </tr>
+ *
+ *     <tr><th>{@link #SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING}</th>
+ *         <td>This column is used to specify that a spinner should be shown in lieu of an icon2
+ *             while the shortcut of this suggestion is being refreshed.</td>
+ *         <td align="center">No.  Only applicable to sources included in Quick Search Box.</td>
+ *     </tr>
+ * 
  *     <tr><th><i>Other Columns</i></th>
  *         <td>Finally, if you have defined any <a href="#ActionKeys">Action Keys</a> and you wish 
  *             for them to have suggestion-specific definitions, you'll need to define one 
@@ -536,7 +584,55 @@
  * <a name="ExposingSearchSuggestionsToQuickSearchBox"></a>
  * <h3>Exposing Search Suggestions to Quick Search Box</h3>
  * 
- * <p>
+ * <p>Once your application is setup to provide search suggestions, making them available to the
+ * globally accessable Quick Search Box is as easy as setting android:includeInGlobalSearch to
+ * "true" in your searchable metadata file.  Beyond that, here are some more details of how
+ * suggestions interact with Quick Search Box, and optional ways that you may customize suggestions
+ * for your application.
+ *
+ * <p><b>Source Ranking:</b>  Once your application's search results are made available to Quick
+ * Search Box, how they surface to the user for a particular query will depend on how many
+ * other apps have results for that query, and how often the user has clicked on your results
+ * compared to the other apps'.  The apps with the best track record within Quick Search
+ * Box will get queried earlier and have a better chance of showing their results in the top few
+ * slots.  If there are more results than can be displayed to the user within a screen or two, the
+ * results may spill into a "more results" section that groups the remaining results by
+ * source.  The newest apps with little usage information are given middle of the road positioning
+ * until enough usage information is available to rank it as usual.  The exact formula for ranking
+ * the results is not set in stone, but suffice it is to say that providing quality results will
+ * increase the likelihood that your app's suggestions are provided in a prominent position, and
+ * apps that provide lower quality suggestions will be more likely to be pushed into the spillover
+ * area.
+ *
+ * <p><b>Search Settings:</b>  Each app that is available to Quick Search Box has an entry in the
+ * system settings where the user can enable or disable the inclusion of its results.  Below the
+ * name of the application, each application may provide a brief description of what kind of
+ * information will be made available via a search settings description string pointed to by the
+ * android:searchSettingsDescription attribute in the searchable metadata.
+ *
+ * <p><b>Shortcuts:</b>  Suggestions that are clicked on by the user are automatically made into
+ * shortcuts, or, copied so they can quickly be displayed to the user before querying any of
+ * the sources. Thereafter, the shortcutted suggestion will be displayed for the query that yielded
+ * the suggestion and for any prefixes of that query.  When multiple shortcuts are made available
+ * for a given query, they are ranked based on recency and the number of clicks they have received.
+ * You can control how your suggestions are made into shortcuts, and whether they are refreshed,
+ * using the {@link #SUGGEST_COLUMN_SHORTCUT_ID} column:
+ * <ul><li>Suggestions that do not include a shortcut id column will be made into shortcuts and
+ * never refreshed.  This makes sense for suggestions that refer to data that will never be changed
+ * or removed.</li>
+ * <li>Suggestions that include a shortcut id will be re-queried for a fresh version of the
+ * suggestion each time the shortcut is displayed.  The shortcut will be quickly displayed with
+ * whatever data was most recently available until the refresh query returns, after which the
+ * suggestion will be dynamically refreshed with the up to date information.  The shortcut refresh
+ * query will be sent to your suggestion provider with a uri of {@link #SUGGEST_URI_PATH_SHORTCUT}.
+ * The result should contain one suggestion using the same columns as the suggestion query, or be
+ * empty, indicating that the shortcut is no longer valid.  Shortcut ids make sense when referring
+ * to data that may change over time, such as a contact's presence status.  If a suggestion refers
+ * to data that could take longer to refresh, such as a network based refresh of a stock quote, you
+ * may include {@link #SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING} to show a progress spinner for the
+ * right hand icon until the refresh is complete.</li>
+ * <li>Finally, to prevent a suggestion from being copied into a shortcut, you may provide a
+ * shortcut id with a value of {@link #SUGGEST_NEVER_MAKE_SHORTCUT}.</li></ul>
  * 
  * <a name="ActionKeys"></a>
  * <h3>Action Keys</h3>
@@ -673,7 +769,7 @@
  *             entered.</td>
  *         <td align="center">No</td>
  *     </tr>
- *     
+ *
  *     <tr><th>android:searchButtonText</th>
  *         <td>If provided, this text will replace the default text in the "Search" button.</td>
  *         <td align="center">No</td>
@@ -853,7 +949,48 @@
  *     
  *     </tbody>
  * </table>
- * 
+ *
+ * <p>Elements of search metadata that configure search suggestions being available to Quick Search
+ * Box:
+ * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
+ *
+ *     <thead>
+ *     <tr><th>Attribute</th> <th>Description</th> <th>Required?</th></tr>
+ *     </thead>
+ *
+ *     <tr><th>android:includeInGlobalSearch</th>
+ *         <td>If true, indicates the search suggestions provided by your application should be
+ *             included in the globally accessible Quick Search Box.  The attributes below are only
+ *             applicable if this is set to true.</td>
+ *         <td align="center">Yes</td>
+ *     </tr>
+ *
+ *     <tr><th>android:searchSettingsDescription</th>
+ *         <td>If provided, provides a brief description of the search suggestions that are provided
+ *             by your application to Quick Search Box, and will be displayed in the search settings
+ *             entry for your application.</td>
+ *         <td align="center">No</td>
+ *     </tr>
+ *
+ *     <tr><th>android:queryAfterZeroResults</th>
+ *         <td>Indicates whether a source should be invoked for supersets of queries it has
+ *             returned zero results for in the past.  For example, if a source returned zero
+ *             results for "bo", it would be ignored for "bob".  If set to false, this source
+ *             will only be ignored for a single session; the next time the search dialog is
+ *             invoked, all sources will be queried.  The default value is false.</td>
+ *         <td align="center">No</td>
+ *     </tr>
+ *
+ *     <tr><th>android:searchSuggestThreshold</th>
+ *         <td>Indicates the minimum number of characters needed to trigger a source from Quick
+ *             Search Box.  Only guarantees that a source will not be queried for anything shorter
+ *             than the threshold.  The default value is 0.</td>
+ *         <td align="center">No</td>
+ *     </tr>
+ *
+ *     </tbody>
+ * </table>
+ *
  * <p><b>Additional metadata for search action keys.</b>  For each action key that you would like to
  * define, you'll need to add an additional element defining that key, and using the attributes
  * discussed in <a href="#ActionKeys">Action Keys</a>.  A simple example is shown here:
@@ -1376,10 +1513,10 @@
 
     /**
      * Column name for suggestions cursor. <i>Optional.</i>  This column is used to indicate whether
-     * a search suggestion should be stored as a shortcut, and whether it should be validated.  If
+     * a search suggestion should be stored as a shortcut, and whether it should be refreshed.  If
      * missing, the result will be stored as a shortcut and never validated.  If set to
      * {@link #SUGGEST_NEVER_MAKE_SHORTCUT}, the result will not be stored as a shortcut.
-     * Otherwise, the shortcut id will be used to check back for validation via
+     * Otherwise, the shortcut id will be used to check back for an up to date suggestion using
      * {@link #SUGGEST_URI_PATH_SHORTCUT}.
      */
     public final static String SUGGEST_COLUMN_SHORTCUT_ID = "suggest_shortcut_id";
diff --git a/core/java/android/content/AsyncQueryHandler.java b/core/java/android/content/AsyncQueryHandler.java
index c5606ac..5e88de0 100644
--- a/core/java/android/content/AsyncQueryHandler.java
+++ b/core/java/android/content/AsyncQueryHandler.java
@@ -38,7 +38,8 @@
     private static final int EVENT_ARG_INSERT = 2;
     private static final int EVENT_ARG_UPDATE = 3;
     private static final int EVENT_ARG_DELETE = 4;
-    
+    private static final int EVENT_ARG_QUERY_ENTITIES = 5;
+
     /* package */ final WeakReference<ContentResolver> mResolver;
 
     private static Looper sLooper = null;
@@ -85,13 +86,25 @@
                             cursor.getCount();
                         }
                     } catch (Exception e) {
-                        Log.d(TAG, e.toString());
+                        Log.w(TAG, e.toString());
                         cursor = null;
                     }
 
                     args.result = cursor;
                     break;
 
+                case EVENT_ARG_QUERY_ENTITIES:
+                    EntityIterator iterator = null;
+                    try {
+                        iterator = resolver.queryEntities(args.uri, args.selection,
+                                args.selectionArgs, args.orderBy);
+                    } catch (Exception e) {
+                        Log.w(TAG, e.toString());
+                    }
+
+                    args.result = iterator;
+                    break;
+
                 case EVENT_ARG_INSERT:
                     args.result = resolver.insert(args.uri, args.values);
                     break;
@@ -104,7 +117,6 @@
                 case EVENT_ARG_DELETE:
                     args.result = resolver.delete(args.uri, args.selection, args.selectionArgs);
                     break;
-
             }
 
             // passing the original token value back to the caller
@@ -129,7 +141,7 @@
             if (sLooper == null) {
                 HandlerThread thread = new HandlerThread("AsyncQueryWorker");
                 thread.start();
-                
+
                 sLooper = thread.getLooper();
             }
         }
@@ -183,6 +195,44 @@
     }
 
     /**
+     * This method begins an asynchronous query for an {@link EntityIterator}.
+     * When the query is done {@link #onQueryEntitiesComplete} is called.
+     *
+     * @param token A token passed into {@link #onQueryComplete} to identify the
+     *            query.
+     * @param cookie An object that gets passed into {@link #onQueryComplete}
+     * @param uri The URI, using the content:// scheme, for the content to
+     *            retrieve.
+     * @param selection A filter declaring which rows to return, formatted as an
+     *            SQL WHERE clause (excluding the WHERE itself). Passing null
+     *            will return all rows for the given URI.
+     * @param selectionArgs You may include ?s in selection, which will be
+     *            replaced by the values from selectionArgs, in the order that
+     *            they appear in the selection. The values will be bound as
+     *            Strings.
+     * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
+     *            (excluding the ORDER BY itself). Passing null will use the
+     *            default sort order, which may be unordered.
+     */
+    public void startQueryEntities(int token, Object cookie, Uri uri, String selection,
+            String[] selectionArgs, String orderBy) {
+        // Use the token as what so cancelOperations works properly
+        Message msg = mWorkerThreadHandler.obtainMessage(token);
+        msg.arg1 = EVENT_ARG_QUERY_ENTITIES;
+
+        WorkerArgs args = new WorkerArgs();
+        args.handler = this;
+        args.uri = uri;
+        args.selection = selection;
+        args.selectionArgs = selectionArgs;
+        args.orderBy = orderBy;
+        args.cookie = cookie;
+        msg.obj = args;
+
+        mWorkerThreadHandler.sendMessage(msg);
+    }
+
+    /**
      * Attempts to cancel operation that has not already started. Note that
      * there is no guarantee that the operation will be canceled. They still may
      * result in a call to on[Query/Insert/Update/Delete]Complete after this
@@ -280,8 +330,8 @@
      * Called when an asynchronous query is completed.
      *
      * @param token the token to identify the query, passed in from
-     *        {@link #startQuery}.
-     * @param cookie the cookie object that's passed in from {@link #startQuery}.
+     *            {@link #startQuery}.
+     * @param cookie the cookie object passed in from {@link #startQuery}.
      * @param cursor The cursor holding the results from the query.
      */
     protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
@@ -289,6 +339,17 @@
     }
 
     /**
+     * Called when an asynchronous query is completed.
+     *
+     * @param token The token to identify the query.
+     * @param cookie The cookie object.
+     * @param iterator The iterator holding the query results.
+     */
+    protected void onQueryEntitiesComplete(int token, Object cookie, EntityIterator iterator) {
+        // Empty
+    }
+
+    /**
      * Called when an asynchronous insert is completed.
      *
      * @param token the token to identify the query, passed in from
@@ -338,13 +399,17 @@
 
         int token = msg.what;
         int event = msg.arg1;
-        
+
         // pass token back to caller on each callback.
         switch (event) {
             case EVENT_ARG_QUERY:
                 onQueryComplete(token, args.cookie, (Cursor) args.result);
                 break;
 
+            case EVENT_ARG_QUERY_ENTITIES:
+                onQueryEntitiesComplete(token, args.cookie, (EntityIterator)args.result);
+                break;
+
             case EVENT_ARG_INSERT:
                 onInsertComplete(token, args.cookie, (Uri) args.result);
                 break;
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 8ebe093..0bc8a9d 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -667,6 +667,11 @@
     /**
      * {@hide}
      */
+    public native static final String getAssetAllocations();
+    
+    /**
+     * {@hide}
+     */
     public native static final int getGlobalAssetManagerCount();
     
     private native final int newTheme();
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index c3ae3c2..4a036ec 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -30,6 +30,9 @@
 
     private static native String native_get(String key);
     private static native String native_get(String key, String def);
+    private static native int native_get_int(String key, int def);
+    private static native long native_get_long(String key, long def);
+    private static native boolean native_get_boolean(String key, boolean def);
     private static native void native_set(String key, String def);
 
     /**
@@ -65,11 +68,10 @@
      * @throws IllegalArgumentException if the key exceeds 32 characters
      */
     public static int getInt(String key, int def) {
-        try {
-            return Integer.parseInt(get(key));
-        } catch (NumberFormatException e) {
-            return def;
+        if (key.length() > PROP_NAME_MAX) {
+            throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
         }
+        return native_get_int(key, def);
     }
 
     /**
@@ -81,11 +83,10 @@
      * @throws IllegalArgumentException if the key exceeds 32 characters
      */
     public static long getLong(String key, long def) {
-        try {
-            return Long.parseLong(get(key));
-        } catch (NumberFormatException e) {
-            return def;
+        if (key.length() > PROP_NAME_MAX) {
+            throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
         }
+        return native_get_long(key, def);
     }
 
     /**
@@ -102,27 +103,10 @@
      * @throws IllegalArgumentException if the key exceeds 32 characters
      */
     public static boolean getBoolean(String key, boolean def) {
-        String value = get(key);
-        // Deal with these quick cases first: not found, 0 and 1
-        if (value.equals("")) {
-            return def;
-        } else if (value.equals("0")) {
-            return false;
-        } else if (value.equals("1")) {
-            return true;
-        // now for slower (and hopefully less common) cases
-        } else if (value.equalsIgnoreCase("n") ||
-                   value.equalsIgnoreCase("no") ||
-                   value.equalsIgnoreCase("false") ||
-                   value.equalsIgnoreCase("off")) {
-            return false;
-        } else if (value.equalsIgnoreCase("y") ||
-                   value.equalsIgnoreCase("yes") ||
-                   value.equalsIgnoreCase("true") ||
-                   value.equalsIgnoreCase("on")) {
-            return true;
+        if (key.length() > PROP_NAME_MAX) {
+            throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
         }
-        return def;
+        return native_get_boolean(key, def);
     }
 
     /**
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 04a0ec8..4405a53 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -1032,7 +1032,7 @@
             }
             try {
                 String[] locStrings =  mITts.getLanguage();
-                if (locStrings.length == 3) {
+                if ((locStrings != null) && (locStrings.length == 3)) {
                     return new Locale(locStrings[0], locStrings[1], locStrings[2]);
                 } else {
                     return null;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 2c9e71e..b179a13 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -190,10 +190,12 @@
 
         mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior);
 
-        final int resID = a.getResourceId(com.android.internal.R.styleable.ProgressBar_interpolator, -1);
+        final int resID = a.getResourceId(
+                com.android.internal.R.styleable.ProgressBar_interpolator, 
+                android.R.anim.linear_interpolator); // default to linear interpolator
         if (resID > 0) {
             setInterpolator(context, resID);
-        }
+        } 
 
         setMax(a.getInt(R.styleable.ProgressBar_max, mMax));
 
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 989286e..a2d3cd8 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -310,6 +310,15 @@
                         } else {
                             missingClasses += " " + line;
                         }
+                    } catch (Throwable t) {
+                        Log.e(TAG, "Error preloading " + line + ".", t);
+                        if (t instanceof Error) {
+                            throw (Error) t;
+                        }
+                        if (t instanceof RuntimeException) {
+                            throw (RuntimeException) t;
+                        }
+                        throw new RuntimeException(t);
                     }
                 }
 
diff --git a/core/java/com/android/internal/widget/ContactHeaderWidget.java b/core/java/com/android/internal/widget/ContactHeaderWidget.java
index 58fa0ba..462143f 100644
--- a/core/java/com/android/internal/widget/ContactHeaderWidget.java
+++ b/core/java/com/android/internal/widget/ContactHeaderWidget.java
@@ -35,6 +35,7 @@
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Intents;
 import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.Presence;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
 import android.provider.SocialContract.Activities;
@@ -61,12 +62,13 @@
     private TextView mPhoneticNameView;
     private CheckBox mStarredView;
     private ImageView mPhotoView;
+    private ImageView mPresenceView;
     private TextView mStatusView;
     private int mNoPhotoResource;
     private QueryHandler mQueryHandler;
 
     protected long mContactId;
-    protected Uri mContactDataUri;
+    protected Uri mContactSummaryUri;
     protected Uri mContactUri;
     protected Uri mStatusUri;
 
@@ -84,12 +86,14 @@
         Contacts.DISPLAY_NAME,
         Contacts.STARRED,
         Contacts.PHOTO_ID,
+        Contacts.PRESENCE_STATUS,
     };
     protected static final int HEADER_DISPLAY_NAME_COLUMN_INDEX = 0;
     //TODO: We need to figure out how we're going to get the phonetic name.
     //static final int HEADER_PHONETIC_NAME_COLUMN_INDEX
     protected static final int HEADER_STARRED_COLUMN_INDEX = 1;
     protected static final int HEADER_PHOTO_ID_COLUMN_INDEX = 2;
+    protected static final int HEADER_PRESENCE_STATUS_COLUMN_INDEX = 3;
 
     //Projection used for finding the most recent social status.
     protected static final String[] SOCIAL_PROJECTION = new String[] {
@@ -144,6 +148,8 @@
         mPhotoView.setOnClickListener(this);
         mPhotoView.setOnLongClickListener(this);
 
+        mPresenceView = (ImageView) findViewById(R.id.presence);
+
         mStatusView = (TextView)findViewById(R.id.status);
 
         // Set the photo with a random "no contact" image
@@ -250,7 +256,7 @@
     public void bindFromContactId(long contactId) {
         mContactId = contactId;
         mContactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, mContactId);
-        mContactDataUri = Uri.withAppendedPath(mContactUri, Contacts.Data.CONTENT_DIRECTORY);
+        mContactSummaryUri = ContentUris.withAppendedId(Contacts.CONTENT_SUMMARY_URI, mContactId);
         mStatusUri = ContentUris.withAppendedId(
                 SocialContract.Activities.CONTENT_CONTACT_STATUS_URI, mContactId);
         redrawHeader();
@@ -317,8 +323,8 @@
     }
 
     protected void redrawHeader() {
-        if (mContactDataUri != null) {
-            mQueryHandler.startQuery(TOKEN_CONTACT_INFO, null, mContactDataUri, HEADER_PROJECTION,
+        if (mContactSummaryUri != null) {
+            mQueryHandler.startQuery(TOKEN_CONTACT_INFO, null, mContactSummaryUri, HEADER_PROJECTION,
                     null, null, null);
         }
 
@@ -352,6 +358,10 @@
                 photoBitmap = loadPlaceholderPhoto(null);
             }
             mPhotoView.setImageBitmap(photoBitmap);
+
+            //Set the presence status
+            int presence = c.getInt(HEADER_PRESENCE_STATUS_COLUMN_INDEX);
+            mPresenceView.setImageResource(Presence.getPresenceIconResourceId(presence));
         }
     }
 
diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index ca4fa11..406884b 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -36,9 +36,9 @@
                                 "key must not be null.");
         goto error;
     }
-    
+
     key = env->GetStringUTFChars(keyJ, NULL);
-    
+
     len = property_get(key, buf, "");
     if ((len <= 0) && (defJ != NULL)) {
         rvJ = defJ;
@@ -47,9 +47,9 @@
     } else {
         rvJ = env->NewStringUTF("");
     }
-    
+
     env->ReleaseStringUTFChars(keyJ, key);
-    
+
 error:
     return rvJ;
 }
@@ -60,6 +60,101 @@
     return SystemProperties_getSS(env, clazz, keyJ, NULL);
 }
 
+static jint SystemProperties_get_int(JNIEnv *env, jobject clazz,
+                                      jstring keyJ, jint defJ)
+{
+    int len;
+    const char* key;
+    char buf[PROPERTY_VALUE_MAX];
+    jint result = defJ;
+
+    if (keyJ == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                                "key must not be null.");
+        goto error;
+    }
+
+    key = env->GetStringUTFChars(keyJ, NULL);
+
+    len = property_get(key, buf, "");
+    if (len > 0) {
+        jint temp;
+        if (sscanf(buf, "%d", &temp) == 1)
+            result = temp;
+    }
+
+    env->ReleaseStringUTFChars(keyJ, key);
+
+error:
+    return result;
+}
+
+static jlong SystemProperties_get_long(JNIEnv *env, jobject clazz,
+                                      jstring keyJ, jlong defJ)
+{
+    int len;
+    const char* key;
+    char buf[PROPERTY_VALUE_MAX];
+    jlong result = defJ;
+
+    if (keyJ == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                                "key must not be null.");
+        goto error;
+    }
+
+    key = env->GetStringUTFChars(keyJ, NULL);
+
+    len = property_get(key, buf, "");
+    if (len > 0) {
+        jlong temp;
+        if (sscanf(buf, "%lld", &temp) == 1)
+            result = temp;
+    }
+
+    env->ReleaseStringUTFChars(keyJ, key);
+
+error:
+    return result;
+}
+
+static jboolean SystemProperties_get_boolean(JNIEnv *env, jobject clazz,
+                                      jstring keyJ, jboolean defJ)
+{
+    int len;
+    const char* key;
+    char buf[PROPERTY_VALUE_MAX];
+    jboolean result = defJ;
+
+    if (keyJ == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                                "key must not be null.");
+        goto error;
+    }
+
+    key = env->GetStringUTFChars(keyJ, NULL);
+
+    len = property_get(key, buf, "");
+    if (len == 1) {
+        char ch = buf[0];
+        if (ch == '0' || ch == 'n')
+            result = false;
+        else if (ch == '1' || ch == 'y')
+            result = true;
+    } else if (len > 1) {
+         if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
+            result = false;
+        } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) {
+            result = true;
+        }
+    }
+
+    env->ReleaseStringUTFChars(keyJ, key);
+
+error:
+    return result;
+}
+
 static void SystemProperties_set(JNIEnv *env, jobject clazz,
                                       jstring keyJ, jstring valJ)
 {
@@ -94,6 +189,12 @@
       (void*) SystemProperties_getS },
     { "native_get", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
       (void*) SystemProperties_getSS },
+    { "native_get_int", "(Ljava/lang/String;I)I",
+      (void*) SystemProperties_get_int },
+    { "native_get_long", "(Ljava/lang/String;J)J",
+      (void*) SystemProperties_get_long },
+    { "native_get_boolean", "(Ljava/lang/String;Z)Z",
+      (void*) SystemProperties_get_boolean },
     { "native_set", "(Ljava/lang/String;Ljava/lang/String;)V",
       (void*) SystemProperties_set },
 };
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 66b2506..562cc8f 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -1536,6 +1536,22 @@
     return Asset::getGlobalCount();
 }
 
+static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz)
+{
+    String8 alloc = Asset::getAssetAllocations();
+    if (alloc.length() <= 0) {
+        return NULL;
+    }
+    
+    jstring str = env->NewStringUTF(alloc.string());
+    if (str == NULL) {
+        doThrow(env, "java/lang/OutOfMemoryError");
+        return NULL;
+    }
+    
+    return str;
+}
+
 static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz)
 {
     return AssetManager::getGlobalCount();
@@ -1646,6 +1662,8 @@
         (void*) android_content_AssetManager_destroy },
     { "getGlobalAssetCount", "()I",
         (void*) android_content_AssetManager_getGlobalAssetCount },
+    { "getAssetAllocations", "()Ljava/lang/String;",
+        (void*) android_content_AssetManager_getAssetAllocations },
     { "getGlobalAssetManagerCount", "()I",
         (void*) android_content_AssetManager_getGlobalAssetCount },
 };
diff --git a/core/res/res/layout-ja/contact_header_name.xml b/core/res/res/layout-ja/contact_header_name.xml
index 03332b1..20df5c6 100644
--- a/core/res/res/layout-ja/contact_header_name.xml
+++ b/core/res/res/layout-ja/contact_header_name.xml
@@ -25,6 +25,8 @@
     <TextView android:id="@+id/name"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="end"
     	android:textAppearance="?android:attr/textAppearanceLargeInverse"
     	android:textColor="@android:color/secondary_text_light"
         />
@@ -32,6 +34,8 @@
     <TextView android:id="@+id/phonetic_name"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="end"
     	android:textAppearance="?android:attr/textAppearanceSmallInverse"
     	android:textColor="@android:color/secondary_text_light"
         />
diff --git a/core/res/res/layout/contact_header.xml b/core/res/res/layout/contact_header.xml
index 06cd38c..73e379b 100644
--- a/core/res/res/layout/contact_header.xml
+++ b/core/res/res/layout/contact_header.xml
@@ -48,6 +48,13 @@
                 
     </LinearLayout>
 
+    <ImageView
+        android:id="@+id/presence"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingLeft="3dip"
+        android:paddingRight="6dip"/>
+        
     <CheckBox
         android:id="@+id/star"
         android:layout_width="wrap_content"
diff --git a/core/res/res/layout/contact_header_name.xml b/core/res/res/layout/contact_header_name.xml
index aec943e..77751c3 100644
--- a/core/res/res/layout/contact_header_name.xml
+++ b/core/res/res/layout/contact_header_name.xml
@@ -21,6 +21,6 @@
     android:layout_height="wrap_content"
     android:textAppearance="?android:attr/textAppearanceLargeInverse"
     android:textColor="@android:color/secondary_text_light"
-    android:maxLines="2"
+    android:singleLine="true"
     android:ellipsize="end"
     />
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 285fdc0..24bbea5 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -396,7 +396,7 @@
     jint len = _env->GetArrayLength(data);
     LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
     jint *ptr = _env->GetIntArrayElements(data, NULL);
-    rsAllocationData((RsAllocation)alloc, ptr);
+    rsAllocationRead((RsAllocation)alloc, ptr);
     _env->ReleaseIntArrayElements(data, ptr, JNI_COMMIT);
 }
 
@@ -405,9 +405,9 @@
 {
     RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
-    LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
+    LOG_API("nAllocationRead_f, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
     jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
-    rsAllocationData((RsAllocation)alloc, ptr);
+    rsAllocationRead((RsAllocation)alloc, ptr);
     _env->ReleaseFloatArrayElements(data, ptr, JNI_COMMIT);
 }
 
diff --git a/include/utils/Asset.h b/include/utils/Asset.h
index 453a204..5908bcc 100644
--- a/include/utils/Asset.h
+++ b/include/utils/Asset.h
@@ -45,6 +45,7 @@
     virtual ~Asset(void);
 
     static int32_t getGlobalCount();
+    static String8 getAssetAllocations();
     
     /* used when opening an asset */
     typedef enum AccessMode {
@@ -110,6 +111,12 @@
     virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const = 0;
     
     /*
+     * Return whether this asset's buffer is allocated in RAM (not mmapped).
+     * Note: not virtual so it is safe to call even when being destroyed.
+     */
+    virtual bool isAllocated(void) const { return false; }
+    
+    /*
      * Get a string identifying the asset's source.  This might be a full
      * path, it might be a colon-separated list of identifiers.
      *
@@ -197,6 +204,9 @@
 
     AccessMode  mAccessMode;        // how the asset was opened
     String8    mAssetSource;       // debug string
+    
+    Asset*		mNext;				// linked list.
+    Asset*		mPrev;
 };
 
 
@@ -239,6 +249,7 @@
     virtual off_t getLength(void) const { return mLength; }
     virtual off_t getRemainingLength(void) const { return mLength-mOffset; }
     virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const;
+    virtual bool isAllocated(void) const { return mBuf != NULL; }
 
 private:
     off_t       mStart;         // absolute file offset of start of chunk
@@ -295,6 +306,7 @@
     virtual off_t getLength(void) const { return mUncompressedLen; }
     virtual off_t getRemainingLength(void) const { return mUncompressedLen-mOffset; }
     virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const { return -1; }
+    virtual bool isAllocated(void) const { return mBuf != NULL; }
 
 private:
     off_t       mStart;         // offset to start of compressed data
diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp
index 23cb72d..4295123 100644
--- a/libs/utils/Asset.cpp
+++ b/libs/utils/Asset.cpp
@@ -27,6 +27,7 @@
 #include <utils/ZipUtils.h>
 #include <utils/ZipFileRO.h>
 #include <utils/Log.h>
+#include <utils/threads.h>
 
 #include <string.h>
 #include <memory.h>
@@ -40,24 +41,71 @@
 # define O_BINARY 0
 #endif
 
-static volatile int32_t gCount = 0;
+static Mutex gAssetLock;
+static int32_t gCount = 0;
+static Asset* gHead = NULL;
+static Asset* gTail = NULL;
 
 int32_t Asset::getGlobalCount()
 {
+    AutoMutex _l(gAssetLock);
     return gCount;
 }
 
+String8 Asset::getAssetAllocations()
+{
+    AutoMutex _l(gAssetLock);
+    String8 res;
+    Asset* cur = gHead;
+    while (cur != NULL) {
+        if (cur->isAllocated()) {
+            res.append("    ");
+            res.append(cur->getAssetSource());
+            off_t size = (cur->getLength()+512)/1024;
+            char buf[64];
+            sprintf(buf, ": %dK\n", (int)size);
+            res.append(buf);
+        }
+        cur = cur->mNext;
+    }
+    
+    return res;
+}
+
 Asset::Asset(void)
     : mAccessMode(ACCESS_UNKNOWN)
 {
-    int count = android_atomic_inc(&gCount)+1;
-    //LOGI("Creating Asset %p #%d\n", this, count);
+    AutoMutex _l(gAssetLock);
+    gCount++;
+    mNext = mPrev = NULL;
+    if (gTail == NULL) {
+        gHead = gTail = this;
+  	} else {
+  	    mPrev = gTail;
+  	    gTail->mNext = this;
+  	    gTail = this;
+  	}
+    //LOGI("Creating Asset %p #%d\n", this, gCount);
 }
 
 Asset::~Asset(void)
 {
-    int count = android_atomic_dec(&gCount);
-    //LOGI("Destroying Asset in %p #%d\n", this, count);
+    AutoMutex _l(gAssetLock);
+	gCount--;
+    if (gHead == this) {
+        gHead = mNext;
+    }
+    if (gTail == this) {
+        gTail = mPrev;
+    }
+    if (mNext != NULL) {
+        mNext->mPrev = mPrev;
+    }
+    if (mPrev != NULL) {
+        mPrev->mNext = mNext;
+    }
+    mNext = mPrev = NULL;
+    //LOGI("Destroying Asset in %p #%d\n", this, gCount);
 }
 
 /*
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index 1b99d32..cd24727 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -172,10 +172,18 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
+
+        // TODO replace the call to stopAll() with a method to clear absolutely all upcoming
+        // uses of the native synth, including synthesis to a file, and delete files for which
+        // synthesis was not complete.
+        stopAll("");
+
         // Don't hog the media player
         cleanUpPlayer();
 
-        sNativeSynth.shutdown();
+        if (sNativeSynth != null) {
+            sNativeSynth.shutdown();
+        }
         sNativeSynth = null;
 
         // Unregister all callbacks.
@@ -243,38 +251,70 @@
 
 
     private int setSpeechRate(String callingApp, int rate) {
-        if (isDefaultEnforced()) {
-            return sNativeSynth.setSpeechRate(getDefaultRate());
-        } else {
-            return sNativeSynth.setSpeechRate(rate);
+        int res = TextToSpeech.ERROR;
+        try {
+            if (isDefaultEnforced()) {
+                res = sNativeSynth.setSpeechRate(getDefaultRate());
+            } else {
+                res = sNativeSynth.setSpeechRate(rate);
+            }
+        } catch (NullPointerException e) {
+            // synth will become null during onDestroy()
+            res = TextToSpeech.ERROR;
         }
+        return res;
     }
 
 
     private int setPitch(String callingApp, int pitch) {
-        return sNativeSynth.setPitch(pitch);
+        int res = TextToSpeech.ERROR;
+        try {
+            res = sNativeSynth.setPitch(pitch);
+        } catch (NullPointerException e) {
+            // synth will become null during onDestroy()
+            res = TextToSpeech.ERROR;
+        }
+        return res;
     }
 
 
     private int isLanguageAvailable(String lang, String country, String variant) {
         //Log.v("TtsService", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")");
-        return sNativeSynth.isLanguageAvailable(lang, country, variant);
+        int res = TextToSpeech.LANG_NOT_SUPPORTED;
+        try {
+            res = sNativeSynth.isLanguageAvailable(lang, country, variant);
+        } catch (NullPointerException e) {
+            // synth will become null during onDestroy()
+            res = TextToSpeech.LANG_NOT_SUPPORTED;
+        }
+        return res;
     }
 
 
     private String[] getLanguage() {
-        return sNativeSynth.getLanguage();
+        try {
+            return sNativeSynth.getLanguage();
+        } catch (Exception e) {
+            return null;
+        }
     }
 
 
     private int setLanguage(String callingApp, String lang, String country, String variant) {
         Log.v("TtsService", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
-        if (isDefaultEnforced()) {
-            return sNativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(),
-                    getDefaultLocVariant());
-        } else {
-            return sNativeSynth.setLanguage(lang, country, variant);
+        int res = TextToSpeech.ERROR;
+        try {
+            if (isDefaultEnforced()) {
+                res = sNativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(),
+                        getDefaultLocVariant());
+            } else {
+                res = sNativeSynth.setLanguage(lang, country, variant);
+            }
+        } catch (NullPointerException e) {
+            // synth will become null during onDestroy()
+            res = TextToSpeech.ERROR;
         }
+        return res;
     }
 
 
@@ -402,7 +442,12 @@
                 }
                 if ((mCurrentSpeechItem != null) &&
                      mCurrentSpeechItem.mCallingApp.equals(callingApp)) {
-                    result = sNativeSynth.stop();
+                    try {
+                        result = sNativeSynth.stop();
+                    } catch (NullPointerException e1) {
+                        // synth will become null during onDestroy()
+                        result = TextToSpeech.ERROR;
+                    }
                     mKillList.put(mCurrentSpeechItem, true);
                     if (mPlayer != null) {
                         try {
@@ -434,7 +479,8 @@
 
 
     /**
-     * Stops all speech output and removes any utterances still in the queue globally.
+     * Stops all speech output and removes any utterances still in the queue globally, except
+     * those intended to be synthesized to file.
      */
     private int stopAll(String callingApp) {
         int result = TextToSpeech.ERROR;
@@ -451,7 +497,12 @@
                 if ((mCurrentSpeechItem != null) &&
                     ((mCurrentSpeechItem.mType != SpeechItem.TEXT_TO_FILE) ||
                       mCurrentSpeechItem.mCallingApp.equals(callingApp))) {
-                    result = sNativeSynth.stop();
+                    try {
+                        result = sNativeSynth.stop();
+                    } catch (NullPointerException e1) {
+                        // synth will become null during onDestroy()
+                        result = TextToSpeech.ERROR;
+                    }
                     mKillList.put(mCurrentSpeechItem, true);
                     if (mPlayer != null) {
                         try {
@@ -591,7 +642,12 @@
                         if (speechRate.length() > 0){
                             setSpeechRate("", Integer.parseInt(speechRate));
                         }
-                        sNativeSynth.speak(speechItem.mText, streamType);
+                        try {
+                            sNativeSynth.speak(speechItem.mText, streamType);
+                        } catch (NullPointerException e) {
+                            // synth will become null during onDestroy()
+                            Log.v("TtsService", " null synth, can't speak");
+                        }
                     }
                 } catch (InterruptedException e) {
                     Log.e("TtsService", "TTS speakInternalOnly(): tryLock interrupted");
@@ -660,7 +716,12 @@
                         if (speechRate.length() > 0){
                             setSpeechRate("", Integer.parseInt(speechRate));
                         }
-                        sNativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename);
+                        try {
+                            sNativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename);
+                        } catch (NullPointerException e) {
+                            // synth will become null during onDestroy()
+                            Log.v("TtsService", " null synth, can't synthesize to file");
+                        }
                     }
                 } catch (InterruptedException e) {
                     Log.e("TtsService", "TTS synthToFileInternalOnly(): tryLock interrupted");
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 78db6f9..5439f8b 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -1009,8 +1009,7 @@
         if (success) {
             // delete any hosts that didn't manage to get connected (should happen)
             // if it matters, they'll be reconnected.
-            final int N = mHosts.size();
-            for (int i=0; i<N; i++) {
+            for (int i=mHosts.size()-1; i>=0; i--) {
                 pruneHostLocked(mHosts.get(i));
             }
         } else {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 6907443..c59c9cc3 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -4455,7 +4455,7 @@
     }
 
     final void appNotRespondingLocked(ProcessRecord app, HistoryRecord activity, 
-            final String annotation) {
+            HistoryRecord reportedActivity, final String annotation) {
         if (app.notResponding || app.crashing) {
             return;
         }
@@ -4483,8 +4483,13 @@
 
         StringBuilder info = mStringBuilder;
         info.setLength(0);
-        info.append("ANR (application not responding) in process: ");
+        info.append("ANR in process: ");
         info.append(app.processName);
+        if (reportedActivity != null && reportedActivity.app != null) {
+            info.append(" (last in ");
+            info.append(reportedActivity.app.processName);
+            info.append(")");
+        }
         if (annotation != null) {
             info.append("\nAnnotation: ");
             info.append(annotation);
@@ -4504,10 +4509,44 @@
         } else {
             // Dumping traces to a file so dump all active processes we know about
             synchronized (this) {
-                for (int i = mLRUProcesses.size() - 1 ; i >= 0 ; i--) {
+                // First, these are the most important processes.
+                final int[] imppids = new int[3];
+                int i=0;
+                imppids[0] = app.pid;
+                i++;
+                if (reportedActivity != null && reportedActivity.app != null
+                        && reportedActivity.app.thread != null
+                        && reportedActivity.app.pid != app.pid) {
+                    imppids[i] = reportedActivity.app.pid;
+                    i++;
+                }
+                imppids[i] = Process.myPid();
+                for (i=0; i<imppids.length && imppids[i] != 0; i++) {
+                    Process.sendSignal(imppids[i], Process.SIGNAL_QUIT);
+                    synchronized (this) {
+                        try {
+                            wait(200);
+                        } catch (InterruptedException e) {
+                        }
+                    }
+                }
+                for (i = mLRUProcesses.size() - 1 ; i >= 0 ; i--) {
                     ProcessRecord r = mLRUProcesses.get(i);
-                    if (r.thread != null) {
+                    boolean done = false;
+                    for (int j=0; j<imppids.length && imppids[j] != 0; j++) {
+                        if (imppids[j] == r.pid) {
+                            done = true;
+                            break;
+                        }
+                    }
+                    if (!done && r.thread != null) {
                         Process.sendSignal(r.pid, Process.SIGNAL_QUIT);
+                        synchronized (this) {
+                            try {
+                                wait(200);
+                            } catch (InterruptedException e) {
+                            }
+                        }
                     }
                 }
             }
@@ -4571,7 +4610,7 @@
                 if (!dir.exists()) {
                     fileReady = dir.mkdirs();
                     FileUtils.setPermissions(dir.getAbsolutePath(),
-                            FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO, -1, -1);
+                            FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, -1, -1);
                 } else if (dir.isDirectory()) {
                     fileReady = true;
                 }
@@ -4581,6 +4620,18 @@
                 Log.i(TAG, "Removing old ANR trace file from " + tracesPath);
                 fileReady = f.delete();
             }
+            
+            if (removeExisting) {
+                try {
+                    f.createNewFile();
+                    FileUtils.setPermissions(f.getAbsolutePath(),
+                            FileUtils.S_IRWXU | FileUtils.S_IRWXG
+                            | FileUtils.S_IWOTH | FileUtils.S_IROTH, -1, -1);
+                    fileReady = true;
+                } catch (IOException e) {
+                    Log.w(TAG, "Unable to make ANR traces file", e);
+                }
+            }
         }
 
         return fileReady;
@@ -10639,7 +10690,7 @@
             }
             if (timeout != null && mLRUProcesses.contains(proc)) {
                 Log.w(TAG, "Timeout executing service: " + timeout);
-                appNotRespondingLocked(proc, null, "Executing service "
+                appNotRespondingLocked(proc, null, null, "Executing service "
                         + timeout.name);
             } else {
                 Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
@@ -11434,7 +11485,8 @@
             }
             
             if (app != null) {
-                appNotRespondingLocked(app, null, "Broadcast of " + r.intent.toString());
+                appNotRespondingLocked(app, null, null,
+                        "Broadcast of " + r.intent.toString());
             }
 
             if (mPendingBroadcast == r) {
diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java
index b3fc313..84ded22 100644
--- a/services/java/com/android/server/am/HistoryRecord.java
+++ b/services/java/com/android/server/am/HistoryRecord.java
@@ -470,7 +470,8 @@
                 }
                 
                 if (r.app.instrumentationClass == null) { 
-                    service.appNotRespondingLocked(r.app, r, "keyDispatchingTimedOut");
+                    service.appNotRespondingLocked(r.app, r, this,
+                            "keyDispatchingTimedOut");
                 } else {
                     Bundle info = new Bundle();
                     info.putString("shortMsg", "keyDispatchingTimedOut");
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index f1207e4..af59126 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -277,10 +277,12 @@
             }
 
             if (header != null) {
-                userData = new byte[header.length + textPart.length];
+                // Need 1 byte for UDHL
+                userData = new byte[header.length + textPart.length + 1];
 
-                System.arraycopy(header, 0, userData, 0, header.length);
-                System.arraycopy(textPart, 0, userData, header.length, textPart.length);
+                userData[0] = (byte)header.length;
+                System.arraycopy(header, 0, userData, 1, header.length);
+                System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
             }
             else {
                 userData = textPart;
diff --git a/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewCallbacks.java b/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewCallbacks.java
index 89421e48..29000dd 100644
--- a/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewCallbacks.java
+++ b/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewCallbacks.java
@@ -19,8 +19,8 @@
 import android.app.Instrumentation;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.FlakyTest;
-import android.test.suitebuilder.annotation.MediumTest;
 
+// TODO: tests fail intermittently. Add back MediumTest annotation when fixed
 public class AutoCompleteTextViewCallbacks
         extends ActivityInstrumentationTestCase2<AutoCompleteTextViewSimple> {
 
@@ -32,7 +32,6 @@
 
     /** Test that the initial popup of the suggestions does not select anything.
      */
-    @MediumTest
     @FlakyTest(tolerance=3)
     public void testPopupNoSelection() throws Exception {
         AutoCompleteTextViewSimple theActivity = getActivity();
@@ -57,7 +56,6 @@
     }
 
     /** Test that arrow-down into the popup calls the onSelected callback. */
-    @MediumTest
     @FlakyTest(tolerance=3)
     public void testPopupEnterSelection() throws Exception {
         AutoCompleteTextViewSimple theActivity = getActivity();
@@ -95,7 +93,6 @@
     }
 
     /** Test that arrow-up out of the popup calls the onNothingSelected callback */
-    @MediumTest
     @FlakyTest(tolerance=3)
     public void testPopupLeaveSelection() {
         AutoCompleteTextViewSimple theActivity = getActivity();
diff --git a/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewPopup.java b/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewPopup.java
index 1e4cd20..c48c056 100644
--- a/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewPopup.java
+++ b/tests/FrameworkTest/tests/src/android/widget/AutoCompleteTextViewPopup.java
@@ -20,10 +20,11 @@
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.FlakyTest;
 import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
 
 /**
  * A collection of tests on aspects of the AutoCompleteTextView's popup
+ *
+ * TODO: tests fail intermittently. Add back MediumTest annotation when fixed
  */
 public class AutoCompleteTextViewPopup
         extends ActivityInstrumentationTestCase2<AutoCompleteTextViewSimple> {
@@ -40,7 +41,6 @@
     }
 
     /** Test that we can move the selection and it responds as expected */
-    @MediumTest
     @FlakyTest(tolerance=3)
     public void testPopupSetListSelection() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
@@ -73,7 +73,6 @@
     }
 
     /** Test that we can look at the selection as we move around */
-    @MediumTest
     @FlakyTest(tolerance=3)
     public void testPopupGetListSelection() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
@@ -101,7 +100,6 @@
     }
 
     /** Test that we can clear the selection */
-    @MediumTest
     @FlakyTest(tolerance=3)
     public void testPopupClearListSelection() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
@@ -135,7 +133,6 @@
     }
 
     /** Make sure we handle an empty adapter properly */
-    @MediumTest
     @FlakyTest(tolerance=3)
     public void testPopupNavigateNoAdapter() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
@@ -170,7 +167,6 @@
     }
 
     /** Test the show/hide behavior of the drop-down. */
-    @MediumTest
     @FlakyTest(tolerance=3)
     public void testPopupShow() throws Throwable {
         AutoCompleteTextViewSimple theActivity = getActivity();
diff --git a/tools/preload/20090811.compiled b/tools/preload/20090811.compiled
new file mode 100644
index 0000000..dd61487
--- /dev/null
+++ b/tools/preload/20090811.compiled
Binary files differ
diff --git a/tools/preload/ClassRank.java b/tools/preload/ClassRank.java
index 3699b89..c562d5c 100644
--- a/tools/preload/ClassRank.java
+++ b/tools/preload/ClassRank.java
@@ -26,7 +26,7 @@
      * Increase this number to add more weight to classes which were loaded
      * earlier.
      */
-    static final int SEQUENCE_WEIGHT = 500; // 5 ms
+    static final int SEQUENCE_WEIGHT = 500; // 0.5ms
 
     static final int BUCKET_SIZE = 5;
 
diff --git a/tools/preload/LoadedClass.java b/tools/preload/LoadedClass.java
index 5782807..9ef17f5 100644
--- a/tools/preload/LoadedClass.java
+++ b/tools/preload/LoadedClass.java
@@ -15,10 +15,7 @@
  */
 
 import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
 /**
  * A loaded class.
@@ -54,7 +51,7 @@
     }
 
     void measureMemoryUsage() {
-        this.memoryUsage = MemoryUsage.forClass(name);        
+//        this.memoryUsage = MemoryUsage.forClass(name);
     }
 
     int mlt = -1;
@@ -102,31 +99,20 @@
         }
     }
 
-    /**
-     * Counts loads by apps.
-     */
-    int appLoads() {
-        return operationsByApps(loads);
+    /** Returns names of apps that loaded this class. */
+    Set<String> applicationNames() {
+        Set<String> appNames = new HashSet<String>();
+        addProcessNames(loads, appNames);
+        addProcessNames(initializations, appNames);
+        return appNames;
     }
 
-    /**
-     * Counts inits by apps.
-     */
-    int appInits() {
-        return operationsByApps(initializations);
-    }
-
-    /**
-     * Counts number of app operations in the given list.
-     */
-    private static int operationsByApps(List<Operation> operations) {
-        int byApps = 0;
-        for (Operation operation : operations) {
+    private void addProcessNames(List<Operation> ops, Set<String> appNames) {
+        for (Operation operation : ops) {
             if (operation.process.isApplication()) {
-                byApps++;
+                appNames.add(operation.process.name);
             }
         }
-        return byApps;
     }
 
     public int compareTo(LoadedClass o) {
@@ -160,4 +146,8 @@
 
         return false;
     }
+
+    public boolean isPreloadable() {
+        return systemClass && Policy.isPreloadableClass(name);
+    }
 }
diff --git a/tools/preload/Policy.java b/tools/preload/Policy.java
index 554966b..ade889e3 100644
--- a/tools/preload/Policy.java
+++ b/tools/preload/Policy.java
@@ -24,47 +24,32 @@
 public class Policy {
     
     /**
+     * No constructor - use static methods only
+     */
+    private Policy() {}
+
+    /**
      * This location (in the build system) of the preloaded-classes file.
      */
-    private static final String PRELOADED_CLASS_FILE = "frameworks/base/preloaded-classes";
-    
+    private static final String PRELOADED_CLASS_FILE
+            = "frameworks/base/preloaded-classes";
+
     /**
-     * The internal process name of the system process.  Note, this also shows up as
-     * "system_process", e.g. in ddms.
-     */
-    private static final String SYSTEM_SERVER_PROCESS_NAME = "system_server";
-
-    /** 
-     * Names of non-application processes - these will not be checked for preloaded classes.
-     * 
-     * TODO: Replace this hardcoded list with a walk up the parent chain looking for zygote.
-     */
-    private static final Set<String> NOT_FROM_ZYGOTE = new HashSet<String>(Arrays.asList(
-            "zygote",
-            "dexopt",
-            "unknown",
-            SYSTEM_SERVER_PROCESS_NAME,
-            "com.android.development",
-            "app_process" // am & other shell commands
-    ));
-
-    /** 
-     * Long running services.  These are restricted in their contribution to the preloader
-     * because their launch time is less critical.
+     * Long running services. These are restricted in their contribution to the 
+     * preloader because their launch time is less critical.
      */
     private static final Set<String> SERVICES = new HashSet<String>(Arrays.asList(
-            SYSTEM_SERVER_PROCESS_NAME,
-            "com.android.acore",
-         // Commented out to make sure DefaultTimeZones gets preloaded.
-         // "com.android.phone",
+            "system_server",
             "com.google.process.content",
-            "android.process.media"
+            "android.process.media",
+            "com.google.process.gapps"
     ));
 
     /**
      * Classes which we shouldn't load from the Zygote.
      */
-    private static final Set<String> EXCLUDED_CLASSES = new HashSet<String>(Arrays.asList(
+    private static final Set<String> EXCLUDED_CLASSES
+            = new HashSet<String>(Arrays.asList(
         // Binders
         "android.app.AlarmManager",
         "android.app.SearchManager",
@@ -75,15 +60,9 @@
         "android.os.AsyncTask",
         "android.pim.ContactsAsyncHelper",
         "java.lang.ProcessManager"
-        
     ));
 
     /**
-     * No constructor - use static methods only
-     */
-    private Policy() {}
-    
-    /**
      * Returns the path/file name of the preloaded classes file that will be written 
      * by WritePreloadedClassFile.
      */
@@ -92,13 +71,6 @@
     }
     
     /**
-     * Reports if a given process name was created from zygote
-     */
-    public static boolean isFromZygote(String processName) {
-        return !NOT_FROM_ZYGOTE.contains(processName);
-    }
-    
-    /**
      * Reports if the given process name is a "long running" process or service
      */
     public static boolean isService(String processName) {
diff --git a/tools/preload/PrintCsv.java b/tools/preload/PrintCsv.java
index 9f2a318..62f4271 100644
--- a/tools/preload/PrintCsv.java
+++ b/tools/preload/PrintCsv.java
@@ -18,6 +18,9 @@
 import java.io.FileInputStream;
 import java.io.ObjectInputStream;
 import java.io.BufferedInputStream;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.TreeSet;
 
 /**
  * Prints raw information in CSV format.
@@ -37,13 +40,14 @@
                 + ",Preloaded"
                 + ",Median Load Time (us)"
                 + ",Median Init Time (us)"
+                + ",Process Names"
                 + ",Load Count"
-                + ",Init Count"
-                + ",Managed Heap (B)"
-                + ",Native Heap (B)"
-                + ",Managed Pages (kB)"
-                + ",Native Pages (kB)"
-                + ",Other Pages (kB)");
+                + ",Init Count");
+//                + ",Managed Heap (B)"
+//                + ",Native Heap (B)"
+//                + ",Managed Pages (kB)"
+//                + ",Native Pages (kB)"
+//                + ",Other Pages (kB)");
 
         MemoryUsage baseline = root.baseline;
 
@@ -60,10 +64,23 @@
             System.out.print(',');
             System.out.print(loadedClass.medianInitTimeMicros());
             System.out.print(',');
+            System.out.print('"');
+
+            Set<String> procNames = new TreeSet<String>();
+            for (Operation op : loadedClass.loads)
+                procNames.add(op.process.name);
+            for (Operation op : loadedClass.initializations)
+                procNames.add(op.process.name);
+            for (String name : procNames) {
+                System.out.print(name + "\n");
+            }
+            
+            System.out.print('"');
+            System.out.print(',');
             System.out.print(loadedClass.loads.size());
             System.out.print(',');
             System.out.print(loadedClass.initializations.size());
-
+/*
             if (loadedClass.memoryUsage.isAvailable()) {
                 MemoryUsage subtracted
                         = loadedClass.memoryUsage.subtract(baseline);
@@ -82,7 +99,7 @@
             } else {
                 System.out.print(",n/a,n/a,n/a,n/a,n/a");
             }
-
+*/
             System.out.println();
         }
     }
diff --git a/tools/preload/Proc.java b/tools/preload/Proc.java
index 22697f8..66e04dc 100644
--- a/tools/preload/Proc.java
+++ b/tools/preload/Proc.java
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-import java.util.Set;
-import java.util.HashSet;
 import java.util.Arrays;
 import java.util.List;
 import java.util.ArrayList;
@@ -23,7 +21,6 @@
 import java.util.Map;
 import java.util.HashMap;
 import java.util.Collections;
-import java.util.TreeSet;
 import java.io.Serializable;
 
 /**
@@ -38,11 +35,6 @@
      */
     static final int PERCENTAGE_TO_PRELOAD = 75;
 
-    /**
-     * Maximum number of classes to preload for a given process.
-     */
-    static final int MAX_TO_PRELOAD = 100;
-
     /** Parent process. */
     final Proc parent;
 
@@ -97,11 +89,9 @@
 
     /**
      * Returns a list of classes which should be preloaded.
-     * 
-     * @param takeAllClasses forces all classes to be taken (irrespective of ranking)
      */
-    List<LoadedClass> highestRankedClasses(boolean takeAllClasses) {
-        if (!isApplication()) {
+    List<LoadedClass> highestRankedClasses() {
+        if (!isApplication() || Policy.isService(this.name)) {
             return Collections.emptyList();
         }
 
@@ -114,25 +104,13 @@
         int timeToSave = totalTimeMicros() * percentageToPreload() / 100;
         int timeSaved = 0;
 
-        boolean service = Policy.isService(this.name);
-
+        int count = 0;
         List<LoadedClass> highest = new ArrayList<LoadedClass>();
         for (Operation operation : ranked) {
-            
-            // These are actual ranking decisions, which can be overridden
-            if (!takeAllClasses) {
-                if (highest.size() >= MAX_TO_PRELOAD) {
-                    System.out.println(name + " got " 
-                            + (timeSaved * 100 / timeToSave) + "% through");
-                    break;
-                }
-    
-                if (timeSaved >= timeToSave) {
-                    break;
-                }
+            if (timeSaved >= timeToSave || count++ > 100) {
+                break;
             }
 
-            // The remaining rules apply even to wired-down processes
             if (!Policy.isPreloadableClass(operation.loadedClass.name)) {
                 continue;
             }
@@ -140,13 +118,8 @@
             if (!operation.loadedClass.systemClass) {
                 continue;
             }
-
-            // Only load java.* class for services.
-            if (!service || operation.loadedClass.name.startsWith("java.")) {
-                highest.add(operation.loadedClass);
-            }
-
-            // For services, still count the time even if it's not in java.* 
+    
+            highest.add(operation.loadedClass);
             timeSaved += operation.medianExclusiveTimeMicros();
         }
 
@@ -166,11 +139,13 @@
 
     /** 
      * Returns true if this process is an app.
-     *      
-     * TODO: Replace the hardcoded list with a walk up the parent chain looking for zygote.
      */
     public boolean isApplication() {
-        return Policy.isFromZygote(name);
+        if (name.equals("com.android.development")) {
+            return false;
+        }
+
+        return parent != null && parent.name.equals("zygote");
     }
 
     /**
diff --git a/tools/preload/WritePreloadedClassFile.java b/tools/preload/WritePreloadedClassFile.java
index d87b1f0..b209af0 100644
--- a/tools/preload/WritePreloadedClassFile.java
+++ b/tools/preload/WritePreloadedClassFile.java
@@ -20,8 +20,6 @@
 import java.io.OutputStreamWriter;
 import java.io.Writer;
 import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
 
@@ -32,71 +30,85 @@
 public class WritePreloadedClassFile {
 
     public static void main(String[] args) throws IOException, ClassNotFoundException {
-        
-        // Process command-line arguments first
-        List<String> wiredProcesses = new ArrayList<String>();
-        String inputFileName = null;
-        int argOffset = 0;
-        try {
-            while ("--preload-all-process".equals(args[argOffset])) {
-                argOffset++;
-                wiredProcesses.add(args[argOffset++]);
-            }
-            
-            inputFileName = args[argOffset++];
-        } catch (RuntimeException e) {
-            System.err.println("Usage: WritePreloadedClassFile " +
-                    "[--preload-all-process process-name] " +
-                    "[compiled log file]");
-            System.exit(0);
+        if (args.length != 1) {
+            System.err.println("Usage: WritePreloadedClassFile [compiled log]");
+            System.exit(-1);
         }
+        String rootFile = args[0];
+        Root root = Root.fromFile(rootFile);
 
-        Root root = Root.fromFile(inputFileName);
-
+        // No classes are preloaded to start.
         for (LoadedClass loadedClass : root.loadedClasses.values()) {
             loadedClass.preloaded = false;
         }
 
+        // Open preloaded-classes file for output.
         Writer out = new BufferedWriter(new OutputStreamWriter(
                 new FileOutputStream(Policy.getPreloadedClassFileName()),
                 Charset.forName("US-ASCII")));
 
         out.write("# Classes which are preloaded by com.android.internal.os.ZygoteInit.\n");
         out.write("# Automatically generated by /frameworks/base/tools/preload.\n");
-        out.write("# percent=" + Proc.PERCENTAGE_TO_PRELOAD + ", weight="
-                + ClassRank.SEQUENCE_WEIGHT
+        out.write("# percent=" + Proc.PERCENTAGE_TO_PRELOAD
+                + ", weight=" + ClassRank.SEQUENCE_WEIGHT
                 + ", bucket_size=" + ClassRank.BUCKET_SIZE
                 + "\n");
-        for (String wiredProcess : wiredProcesses) {
-            out.write("# forcing classes loaded by: " + wiredProcess + "\n");
-        }
 
-        Set<LoadedClass> highestRanked = new TreeSet<LoadedClass>();
-        for (Proc proc : root.processes.values()) {
-            // test to see if this is one of the wired-down ("take all classes") processes
-            boolean isWired = wiredProcesses.contains(proc.name);
-            
-            List<LoadedClass> highestForProc = proc.highestRankedClasses(isWired);
+        Set<LoadedClass> toPreload = new TreeSet<LoadedClass>();
 
-            System.out.println(proc.name + ": " + highestForProc.size());
-
-            for (LoadedClass loadedClass : highestForProc) {
-                loadedClass.preloaded = true;
+        // Preload all classes that were loaded by at least 2 apps, if both
+        // apps run at the same time, they'll share memory.
+        for (LoadedClass loadedClass : root.loadedClasses.values()) {
+            if (!loadedClass.isPreloadable()) {
+                continue;
             }
-            highestRanked.addAll(highestForProc);
+
+            Set<String> appNames = loadedClass.applicationNames();
+
+            if (appNames.size() > 3) {
+                toPreload.add(loadedClass);
+            }
         }
 
-        for (LoadedClass loadedClass : highestRanked) {
+        // Try to make individual apps start faster by preloading slowest
+        // classes.
+        for (Proc proc : root.processes.values()) {
+            toPreload.addAll(proc.highestRankedClasses());
+        }
+
+        System.out.println(toPreload.size() + " classes will be preloaded.");
+
+        // Make classes that were already loaded by the zygote explicit.
+        // This adds minimal overhead but avoid confusion about classes not
+        // appearing in the list.
+        addAllClassesFor("zygote", root, toPreload);
+
+        for (LoadedClass loadedClass : toPreload) {
             out.write(loadedClass.name);
             out.write('\n');
         }
 
         out.close();
 
-        System.out.println(highestRanked.size()
-                + " classes will be preloaded.");
-
         // Update data to reflect LoadedClass.preloaded changes.
-        root.toFile(inputFileName);
+        for (LoadedClass loadedClass : toPreload) {
+            loadedClass.preloaded = true;
+        }
+        root.toFile(rootFile);
+    }
+
+    private static void addAllClassesFor(String packageName, Root root,
+                                         Set<LoadedClass> toPreload) {
+        for (Proc proc : root.processes.values()) {
+            if (proc.name.equals(packageName)) {
+                for (Operation operation : proc.operations) {
+                    // TODO: I'm not sure how the zygote loaded classes that
+                    // aren't supposed to be preloadable...
+                    if (operation.loadedClass.isPreloadable()) {
+                        toPreload.add(operation.loadedClass);
+                    }
+                }
+            }
+        }
     }
 }
diff --git a/tools/preload/preload.iml b/tools/preload/preload.iml
index d1fab57..2d87c55 100644
--- a/tools/preload/preload.iml
+++ b/tools/preload/preload.iml
@@ -1,15 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <module relativePaths="true" type="JAVA_MODULE" version="4">
   <component name="NewModuleRootManager" inherit-compiler-output="false">
-    <output url="file:///tmp/preload/" />
+    <output url="file:///tmp/preload" />
+    <output-test url="file:///tmp/preload" />
     <exclude-output />
-    <output-test url="file:///tmp/preload/" />
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntryProperties />
   </component>
 </module>
 
diff --git a/tools/preload/preload.ipr b/tools/preload/preload.ipr
index c5613ad..f78bf76 100644
--- a/tools/preload/preload.ipr
+++ b/tools/preload/preload.ipr
@@ -114,6 +114,7 @@
     <option name="ADDITIONAL_OPTIONS_STRING" value="" />
     <option name="MAXIMUM_HEAP_SIZE" value="128" />
   </component>
+  <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
   <component name="EntryPointsManager">
     <entry_points version="2.0" />
   </component>
@@ -125,13 +126,13 @@
   <component name="IdProvider" IDEtalkID="D171F99B9178C1675593DC9A76A5CC7E" />
   <component name="InspectionProjectProfileManager">
     <option name="PROJECT_PROFILE" value="Project Default" />
-    <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
-    <scopes />
+    <option name="USE_PROJECT_PROFILE" value="true" />
+    <version value="1.0" />
     <profiles>
       <profile version="1.0" is_locked="false">
         <option name="myName" value="Project Default" />
         <option name="myLocal" value="false" />
-        <inspection_tool class="JavaDoc" level="WARNING" enabled="false">
+        <inspection_tool class="JavaDoc" enabled="false" level="WARNING" enabled_by_default="false">
           <option name="TOP_LEVEL_CLASS_OPTIONS">
             <value>
               <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
@@ -160,14 +161,19 @@
           <option name="IGNORE_JAVADOC_PERIOD" value="true" />
           <option name="myAdditionalJavadocTags" value="" />
         </inspection_tool>
-        <inspection_tool class="OnDemandImport" level="WARNING" enabled="true" />
-        <inspection_tool class="SamePackageImport" level="WARNING" enabled="true" />
-        <inspection_tool class="JavaLangImport" level="WARNING" enabled="true" />
-        <inspection_tool class="RedundantImport" level="WARNING" enabled="true" />
-        <inspection_tool class="UnusedImport" level="WARNING" enabled="true" />
+        <inspection_tool class="JavaLangImport" enabled="true" level="WARNING" enabled_by_default="true" />
+        <inspection_tool class="OnDemandImport" enabled="true" level="WARNING" enabled_by_default="true" />
+        <inspection_tool class="RedundantImport" enabled="true" level="WARNING" enabled_by_default="true" />
+        <inspection_tool class="SamePackageImport" enabled="true" level="WARNING" enabled_by_default="true" />
+        <inspection_tool class="UnusedImport" enabled="true" level="WARNING" enabled_by_default="true" />
       </profile>
     </profiles>
-    <list size="0" />
+    <list size="4">
+      <item index="0" class="java.lang.String" itemvalue="WARNING" />
+      <item index="1" class="java.lang.String" itemvalue="SERVER PROBLEM" />
+      <item index="2" class="java.lang.String" itemvalue="INFO" />
+      <item index="3" class="java.lang.String" itemvalue="ERROR" />
+    </list>
   </component>
   <component name="JavacSettings">
     <option name="DEBUGGING_INFO" value="true" />
@@ -332,13 +338,19 @@
     <option name="USE_CLIENT_FILTER" value="true" />
     <option name="CLIENT" value="" />
   </component>
+  <component name="ProjectDetails">
+    <option name="projectName" value="preload" />
+  </component>
   <component name="ProjectFileVersion" converted="true" />
+  <component name="ProjectKey">
+    <option name="state" value="project:///Volumes/Android/donut/frameworks/base/tools/preload/preload.ipr" />
+  </component>
   <component name="ProjectModuleManager">
     <modules>
       <module fileurl="file://$PROJECT_DIR$/preload.iml" filepath="$PROJECT_DIR$/preload.iml" />
     </modules>
   </component>
-  <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.5" project-jdk-type="JavaSDK">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_5" assert-keyword="true" jdk-15="true" project-jdk-name="1.5" project-jdk-type="JavaSDK">
     <output url="file:///tmp/preload" />
   </component>
   <component name="RmicSettings">
@@ -374,6 +386,9 @@
     <option name="myValidatorValidationEnabled" value="true" />
     <option name="myReportErrorsAsWarnings" value="true" />
   </component>
+  <component name="SvnBranchConfigurationManager">
+    <option name="mySupportsUserInfoFilter" value="true" />
+  </component>
   <component name="SvnChangesBrowserSettings">
     <option name="USE_AUTHOR_FIELD" value="true" />
     <option name="AUTHOR" value="" />
@@ -381,15 +396,6 @@
     <option name="USE_PROJECT_SETTINGS" value="true" />
     <option name="USE_ALTERNATE_LOCATION" value="false" />
   </component>
-  <component name="SvnConfiguration">
-    <option name="USER" value="" />
-    <option name="PASSWORD" value="" />
-    <option name="PROCESS_UNRESOLVED" value="false" />
-    <option name="LAST_MERGED_REVISION" />
-    <option name="UPDATE_RUN_STATUS" value="false" />
-    <option name="UPDATE_RECURSIVELY" value="true" />
-    <option name="MERGE_DRY_RUN" value="false" />
-  </component>
   <component name="VCS.FileViewConfiguration">
     <option name="SELECTED_STATUSES" value="DEFAULT" />
     <option name="SELECTED_COLUMNS" value="DEFAULT" />