Merge "New update of preloaded classes for Froyo."
diff --git a/api/current.xml b/api/current.xml
index 33a8020..031506b 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -6785,6 +6785,17 @@
  visibility="public"
 >
 </field>
+<field name="restoreAnyVersion"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843451"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="restoreNeedsApplication"
  type="int"
  transient="false"
@@ -14630,7 +14641,7 @@
 </parameter>
 <parameter name="features" type="java.lang.String[]">
 </parameter>
-<parameter name="activityForPrompting" type="android.app.Activity">
+<parameter name="activity" type="android.app.Activity">
 </parameter>
 <parameter name="addAccountOptions" type="android.os.Bundle">
 </parameter>
@@ -26277,6 +26288,83 @@
 </parameter>
 </method>
 </interface>
+<class name="UiModeManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="disableCarMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getNightMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setNightMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="int">
+</parameter>
+</method>
+<field name="MODE_AUTO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODE_NIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODE_NOTNIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="WallpaperInfo"
  extends="java.lang.Object"
  abstract="false"
@@ -34549,6 +34637,17 @@
  visibility="public"
 >
 </field>
+<field name="UI_MODE_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;uimode&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="VIBRATOR_SERVICE"
  type="java.lang.String"
  transient="false"
@@ -113095,6 +113194,28 @@
  visibility="public"
 >
 </method>
+<method name="getGlobalClassInitCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGlobalClassInitTime"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getGlobalExternalAllocCount"
  return="int"
  abstract="false"
@@ -113341,6 +113462,28 @@
  visibility="public"
 >
 </method>
+<method name="resetGlobalClassInitCount"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="resetGlobalClassInitTime"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="resetGlobalExternalAllocCount"
  return="void"
  abstract="false"
@@ -133377,6 +133520,17 @@
  visibility="public"
 >
 </field>
+<field name="ACTION_ADD_ACCOUNT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.settings.ADD_ACCOUNT_SETTINGS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTION_AIRPLANE_MODE_SETTINGS"
  type="java.lang.String"
  transient="false"
@@ -133685,6 +133839,17 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_AUTHORITIES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;authorities&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="Settings.NameValueTable"
  extends="java.lang.Object"
@@ -211651,6 +211816,17 @@
 <parameter name="measureSpec" type="int">
 </parameter>
 </method>
+<method name="resume"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="seekTo"
  return="void"
  abstract="false"
@@ -211764,6 +211940,17 @@
  visibility="public"
 >
 </method>
+<method name="suspend"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 </class>
 <class name="ViewAnimator"
  extends="android.widget.FrameLayout"
@@ -215715,7 +215902,7 @@
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -215798,7 +215985,7 @@
  value="&quot;/sdcard/dmtrace.trace&quot;"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -215835,6 +216022,28 @@
  visibility="public"
 >
 </field>
+<field name="KIND_GLOBAL_CLASS_INIT_COUNT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KIND_GLOBAL_CLASS_INIT_TIME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="64"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KIND_GLOBAL_EXT_ALLOCATED_BYTES"
  type="int"
  transient="false"
@@ -215934,6 +216143,28 @@
  visibility="public"
 >
 </field>
+<field name="KIND_THREAD_CLASS_INIT_COUNT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2097152"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KIND_THREAD_CLASS_INIT_TIME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4194304"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KIND_THREAD_EXT_ALLOCATED_BYTES"
  type="int"
  transient="false"
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index d640de1..b6c9de4 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -88,6 +88,8 @@
 
         if (op.equals("start")) {
             runStart();
+        } else if (op.equals("startservice")) {
+            runStartService();
         } else if (op.equals("instrument")) {
             runInstrument();
         } else if (op.equals("broadcast")) {
@@ -183,6 +185,15 @@
         return intent;
     }
 
+    private void runStartService() throws Exception {
+        Intent intent = makeIntent();
+        System.out.println("Starting service: " + intent);
+        ComponentName cn = mAm.startService(null, intent, intent.getType());
+        if (cn == null) {
+            System.err.println("Error: Not found; no service started.");
+        }
+    }
+
     private void runStart() throws Exception {
         Intent intent = makeIntent();
         System.out.println("Starting: " + intent);
@@ -496,6 +507,8 @@
                 "    start an Activity: am start [-D] <INTENT>\n" +
                 "        -D: enable debugging\n" +
                 "\n" +
+                "    start a Service: am startservice <INTENT>\n" +
+                "\n" +
                 "    send a broadcast Intent: am broadcast <INTENT>\n" +
                 "\n" +
                 "    start an Instrumentation: am instrument [flags] <COMPONENT>\n" +
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 78f90a1..adec5a4 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -96,6 +96,7 @@
 
     run_command("NETWORK INTERFACES", 10, "netcfg", NULL);
     dump_file("NETWORK ROUTES", "/proc/net/route");
+    dump_file("ARP CACHE", "/proc/net/arp");
 
 #ifdef FWDUMP_bcm4329
     run_command("DUMP WIFI FIRMWARE LOG", 60,
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 92ae310..66f3676 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -68,7 +68,7 @@
 /* other handy constants */
 
 #define PROTECTED_DIR_PREFIX  "/data/app-private/"
-#define SDCARD_DIR_PREFIX  "/asec/"
+#define SDCARD_DIR_PREFIX  "/mnt/asec/"
 
 #define DALVIK_CACHE_PREFIX   "/data/dalvik-cache/"
 #define DALVIK_CACHE_POSTFIX  "/classes.dex"
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 68d8bb0..52f767e 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -17,6 +17,8 @@
 
 LOCAL_CFLAGS += -Wno-multichar
 
+LOCAL_MODULE_TAGS := debug
+
 LOCAL_MODULE:= stagefright
 
 include $(BUILD_EXECUTABLE)
@@ -39,6 +41,8 @@
 
 LOCAL_CFLAGS += -Wno-multichar
 
+LOCAL_MODULE_TAGS := debug
+
 LOCAL_MODULE:= record
 
 include $(BUILD_EXECUTABLE)
@@ -61,6 +65,8 @@
 
 LOCAL_CFLAGS += -Wno-multichar
 
+LOCAL_MODULE_TAGS := debug
+
 LOCAL_MODULE:= audioloop
 
 include $(BUILD_EXECUTABLE)
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 43a0f30..e2263fc 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -43,36 +43,92 @@
 import com.google.android.collect.Maps;
 
 /**
- * A class that helps with interactions with the AccountManager Service. It provides
- * methods to allow for account, password, and authtoken management for all accounts on the
- * device. One accesses the {@link AccountManager} by calling:
- * <pre>
- *    AccountManager accountManager = AccountManager.get(context);
- * </pre>
+ * This class provides access to a centralized registry of the user's
+ * online accounts.  With this service, users only need to enter their
+ * credentials (username and password) once for any account, granting
+ * applications access to online resources with "one-click" approval.
  *
- * <p>
- * The AccountManager Service provides storage for the accounts known to the system,
- * provides methods to manage them, and allows the registration of authenticators to
- * which operations such as addAccount and getAuthToken are delegated.
- * <p>
- * Many of the calls take an {@link AccountManagerCallback} and {@link Handler} as parameters.
- * These calls return immediately but run asynchronously. If a callback is provided then
- * {@link AccountManagerCallback#run} will be invoked wen the request completes, successfully
- * or not. An {@link AccountManagerFuture} is returned by these requests and also passed into the
- * callback. The result if retrieved by calling {@link AccountManagerFuture#getResult()} which
- * either returns the result or throws an exception as appropriate.
- * <p>
- * The asynchronous request can be made blocking by not providing a callback and instead
- * calling {@link AccountManagerFuture#getResult()} on the future that is returned. This will
- * cause the running thread to block until the result is returned. Keep in mind that one
- * should not block the main thread in this way. Instead one should either use a callback,
- * thus making the call asynchronous, or make the blocking call on a separate thread.
- * getResult() will throw an {@link IllegalStateException} if you call it from the main thread
- * before the request has completed, i.e. before the callback has been invoked.
- * <p>
- * If one wants to ensure that the callback is invoked from a specific handler then they should
- * pass the handler to the request. This makes it easier to ensure thread-safety by running
- * all of one's logic from a single handler.
+ * <p>Different online services have different ways of handling accounts and
+ * authentication, so the account manager uses pluggable <em>authenticator</em>
+ * modules for different <em>account types</em>.  The authenticators (which
+ * may be written by third parties) handle the actual details of validating
+ * account credentials and storing account information.  For example, Google,
+ * Facebook, and Microsoft Exchange each have their own authenticator.
+ *
+ * <p>Many servers support some notion of an <em>authentication token</em>,
+ * which can be used to authenticate a request to the server without sending
+ * the user's actual password.  (Auth tokens are normally created with a
+ * separate request which does include the user's credentials.)  AccountManager
+ * can generate these auth tokens for applications, so the application doesn't
+ * need to handle passwords directly.  Auth tokens are normally reusable, and
+ * cached by AccountManager, but must be refreshed periodically.  It's the
+ * responsibility of applications to <em>invalidate</em> auth tokens when they
+ * stop working so the AccountManager knows it needs to regenerate them.
+ *
+ * <p>Applications accessing a server normally go through these steps:
+ *
+ * <ul>
+ * <li>Get an instance of AccountManager using {@link #get(Context)}.
+ *
+ * <li>List the available accounts using {@link #getAccountsByType} or
+ * {@link #getAccountsByTypeAndFeatures}.  Normally applications will only
+ * be interested in accounts with one particular <em>type</em>, which
+ * identifies the authenticator.  Account <em>features</em> are used to
+ * identify particular account subtypes and capabilities.  Both the account
+ * type and features are authenticator-specific strings, and must be known by
+ * the application in coordination with its preferred authenticators.
+ *
+ * <li>Select one or more of the available accounts, possibly by asking the
+ * user for their preference.  If no suitable accounts are available,
+ * {@link #addAccount} may be called to prompt the user to create an
+ * account of the appropriate type.
+ *
+ * <li>Request an auth token for the selected account(s) using one of the
+ * {@link #getAuthToken} methods or related helpers.  Refer to the description
+ * of each method for exact usage and error handling details.
+ *
+ * <li>Make the request using the auth token.  The form of the auth token,
+ * the format of the request, and the protocol used are all specific to the
+ * service you are accessing.  The application makes the request itself, using
+ * whatever network and protocol libraries are useful.
+ *
+ * <li><b>Important:</b> If the request fails with an authentication error,
+ * it could be that a cached auth token is stale and no longer honored by
+ * the server.  The application must call {@link #invalidateAuthToken} to remove
+ * the token from the cache, otherwise requests will continue failing!  After
+ * invalidating the auth token, immediately go back to the "Request an auth
+ * token" step above.  If the process fails the second time, then it can be
+ * treated as a "genuine" authentication failure and the user notified or other
+ * appropriate actions taken.
+ * </ul>
+ *
+ * <p>Some AccountManager methods may require interaction with the user to
+ * prompt for credentials, present options, or ask the user to add an account.
+ * The caller may choose whether to allow AccountManager to directly launch the
+ * necessary user interface and wait for the user, or to return an Intent which
+ * the caller may use to launch the interface, or (in some cases) to install a
+ * notification which the user can select at any time to launch the interface.
+ * To have AccountManager launch the interface directly, the caller must supply
+ * the current foreground {@link Activity} context.
+ *
+ * <p>Many AccountManager methods take {@link AccountManagerCallback} and
+ * {@link Handler} as parameters.  These methods return immediately but
+ * run asynchronously. If a callback is provided then
+ * {@link AccountManagerCallback#run} will be invoked on the Handler's
+ * thread when the request completes, successfully or not.
+ * An {@link AccountManagerFuture} is returned by these requests and also
+ * supplied to the callback (if any).  The result is retrieved by calling
+ * {@link AccountManagerFuture#getResult()} which waits for the operation
+ * to complete (if necessary) and either returns the result or throws an
+ * exception if an error occurred during the operation.
+ * To make the request synchronously, call
+ * {@link AccountManagerFuture#getResult()} immediately on receiving the
+ * future from the method.  No callback need be supplied.
+ *
+ * <p>Requests which may block, including
+ * {@link AccountManagerFuture#getResult()}, must never be called on
+ * the application's main event thread.  These operations throw
+ * {@link IllegalStateException} if they are used on the main thread.
  */
 public class AccountManager {
     private static final String TAG = "AccountManager";
@@ -85,34 +141,65 @@
     public static final int ERROR_CODE_BAD_ARGUMENTS = 7;
     public static final int ERROR_CODE_BAD_REQUEST = 8;
 
-    public static final String KEY_ACCOUNTS = "accounts";
-    public static final String KEY_AUTHENTICATOR_TYPES = "authenticator_types";
-    public static final String KEY_USERDATA = "userdata";
-    public static final String KEY_AUTHTOKEN = "authtoken";
-    public static final String KEY_PASSWORD = "password";
+    /**
+     * The Bundle key used for the {@link String} account name in results
+     * from methods which return information about a particular account.
+     */
     public static final String KEY_ACCOUNT_NAME = "authAccount";
+
+    /**
+     * The Bundle key used for the {@link String} account type in results
+     * from methods which return information about a particular account.
+     */
     public static final String KEY_ACCOUNT_TYPE = "accountType";
-    public static final String KEY_ERROR_CODE = "errorCode";
-    public static final String KEY_ERROR_MESSAGE = "errorMessage";
+
+    /**
+     * The Bundle key used for the auth token value in results
+     * from {@link #getAuthToken} and friends.
+     */
+    public static final String KEY_AUTHTOKEN = "authtoken";
+
+    /**
+     * The Bundle key used for an {@link Intent} in results from methods that
+     * may require the caller to interact with the user.  The Intent can
+     * be used to start the corresponding user interface activity.
+     */
     public static final String KEY_INTENT = "intent";
-    public static final String KEY_BOOLEAN_RESULT = "booleanResult";
+
+    /**
+     * The Bundle key used to supply the password directly in options to
+     * {@link #confirmCredentials}, rather than prompting the user with
+     * the standard password prompt.
+     */
+    public static final String KEY_PASSWORD = "password";
+
+    public static final String KEY_ACCOUNTS = "accounts";
     public static final String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse";
     public static final String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse";
+    public static final String KEY_AUTHENTICATOR_TYPES = "authenticator_types";
     public static final String KEY_AUTH_FAILED_MESSAGE = "authFailedMessage";
     public static final String KEY_AUTH_TOKEN_LABEL = "authTokenLabelKey";
+    public static final String KEY_BOOLEAN_RESULT = "booleanResult";
+    public static final String KEY_ERROR_CODE = "errorCode";
+    public static final String KEY_ERROR_MESSAGE = "errorMessage";
+    public static final String KEY_USERDATA = "userdata";
+
     public static final String ACTION_AUTHENTICATOR_INTENT =
             "android.accounts.AccountAuthenticator";
     public static final String AUTHENTICATOR_META_DATA_NAME =
-                    "android.accounts.AccountAuthenticator";
+            "android.accounts.AccountAuthenticator";
     public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
 
     private final Context mContext;
     private final IAccountManager mService;
     private final Handler mMainHandler;
+
     /**
      * Action sent as a broadcast Intent by the AccountsService
-     * when accounts are added to and/or removed from the device's
-     * database.
+     * when accounts are added, accounts are removed, or an
+     * account's credentials (saved password, etc) are changed.
+     *
+     * @see #addOnAccountsUpdatedListener
      */
     public static final String LOGIN_ACCOUNTS_CHANGED_ACTION =
         "android.accounts.LOGIN_ACCOUNTS_CHANGED";
@@ -136,26 +223,36 @@
     }
 
     /**
-     * Retrieve an AccountManager instance that is associated with the context that is passed in.
-     * Certain calls such as {@link #addOnAccountsUpdatedListener} use this context internally,
-     * so the caller must take care to use a {@link Context} whose lifetime is associated with
-     * the listener registration.
+     * Gets an AccountManager instance associated with a Context.
+     * The {@link Context} will be used as long as the AccountManager is
+     * active, so make sure to use a {@link Context} whose lifetime is
+     * commensurate with any listeners registered to
+     * {@link #addOnAccountsUpdatedListener} or similar methods.
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>No permission is required to call this method.
+     *
      * @param context The {@link Context} to use when necessary
-     * @return an {@link AccountManager} instance that is associated with context
+     * @return An {@link AccountManager} instance
      */
     public static AccountManager get(Context context) {
         return (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
     }
 
     /**
-     * Get the password that is associated with the account. Returns null if the account does
-     * not exist.
-     * <p>
-     * It is safe to call this method from the main thread.
-     * <p>
-     * Requires that the caller has permission
-     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
-     * with the same UID as the Authenticator for the account.
+     * Gets the saved password associated with the account.
+     * This is intended for authenticators and related code; applications
+     * should get an auth token instead.
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
+     * and to have the same UID as the account's authenticator.
+     *
+     * @param account The account to query for a password
+     * @return The account's password, null if none or if the account doesn't exist
      */
     public String getPassword(final Account account) {
         try {
@@ -167,14 +264,19 @@
     }
 
     /**
-     * Get the user data named by "key" that is associated with the account.
-     * Returns null if the account does not exist or if it does not have a value for key.
-     * <p>
-     * It is safe to call this method from the main thread.
-     * <p>
-     * Requires that the caller has permission
-     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
-     * with the same UID as the Authenticator for the account.
+     * Gets the user data named by "key" associated with the account.
+     * This is intended for authenticators and related code to store
+     * arbitrary metadata along with accounts.  The meaning of the keys
+     * and values is up to the authenticator for the account.
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
+     * and to have the same UID as the account's authenticator.
+     *
+     * @param account The account to query for user data
+     * @return The user data, null if the account or key doesn't exist
      */
     public String getUserData(final Account account, final String key) {
         try {
@@ -186,14 +288,15 @@
     }
 
     /**
-     * Query the AccountManager Service for an array that contains a
-     * {@link AuthenticatorDescription} for each registered authenticator.
-     * @return an array that contains all the authenticators known to the AccountManager service.
-     * This array will be empty if there are no authenticators and will never return null.
-     * <p>
-     * It is safe to call this method from the main thread.
-     * <p>
-     * No permission is required to make this call.
+     * Lists the currently registered authenticators.
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>No permission is required to call this method.
+     *
+     * @return An array of {@link AuthenticatorDescription} for every
+     *     authenticator known to the AccountManager service.  Empty (never
+     *     null) if no authenticators are known.
      */
     public AuthenticatorDescription[] getAuthenticatorTypes() {
         try {
@@ -205,13 +308,16 @@
     }
 
     /**
-     * Query the AccountManager Service for all accounts.
-     * @return an array that contains all the accounts known to the AccountManager service.
-     * This array will be empty if there are no accounts and will never return null.
-     * <p>
-     * It is safe to call this method from the main thread.
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS}
+     * Lists all accounts of any type registered on the device.
+     * Equivalent to getAccountsByType(null).
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#GET_ACCOUNTS}.
+     *
+     * @return An array of {@link Account}, one for each account.  Empty
+     *     (never null) if no accounts have been added.
      */
     public Account[] getAccounts() {
         try {
@@ -223,15 +329,20 @@
     }
 
     /**
-     * Query the AccountManager for the set of accounts that have a given type. If null
-     * is passed as the type than all accounts are returned.
-     * @param type the account type by which to filter, or null to get all accounts
-     * @return an array that contains the accounts that match the specified type. This array
-     * will be empty if no accounts match. It will never return null.
-     * <p>
-     * It is safe to call this method from the main thread.
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS}
+     * Lists all accounts of a particular type.  The account type is a
+     * string token corresponding to the authenticator and useful domain
+     * of the account.  For example, there are types corresponding to Google
+     * and Facebook.  The exact string token to use will be published somewhere
+     * associated with the authenticator in question.
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#GET_ACCOUNTS}.
+     *
+     * @param type The type of accounts to return, null to retrieve all accounts
+     * @return An array of {@link Account}, one per matching account.  Empty
+     *     (never null) if no accounts of the specified type have been added.
      */
     public Account[] getAccountsByType(String type) {
         try {
@@ -243,45 +354,27 @@
     }
 
     /**
-     * Tests that the given account has the specified features. If this account does not exist
-     * then this call returns false.
-     * <p>
-     * This call returns immediately but runs asynchronously and the result is accessed via the
-     * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
-     * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
-     * method asynchronously then they will generally pass in a callback object that will get
-     * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
-     * they will generally pass null for the callback and instead call
-     * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
-     * which will then block until the request completes.
-     * <p>
-     * Do not block the main thread waiting this method's result.
-     * <p>
-     * Not allowed from main thread (but allowed from other threads):
-     * <pre>
-     * Boolean result = hasFeatures(account, features, callback, handler).getResult();
-     * </pre>
-     * Allowed from main thread:
-     * <pre>
-     * hasFeatures(account, features, new AccountManagerCallback<Boolean>() {
-     *    public void run(AccountManagerFuture<Boolean> future) {
-     *        Boolean result = future.getResult();
-     *        // use result
-     *    }
-     * }, handler);
-     * </pre>
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS}.
+     * Finds out whether a particular account has all the specified features.
+     * Account features are authenticator-specific string tokens identifying
+     * boolean account properties.  For example, features are used to tell
+     * whether Google accounts have a particular service (such as Google
+     * Calendar or Google Talk) enabled.  The feature names and their meanings
+     * are published somewhere associated with the authenticator in question.
+     *
+     * <p>This method may be called from any thread, but the returned
+     * {@link AccountManagerFuture} must not be used on the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#GET_ACCOUNTS}.
      *
      * @param account The {@link Account} to test
-     * @param features the features for which to test
-     * @param callback A callback to invoke when the request completes. If null then
-     * no callback is invoked.
-     * @param handler The {@link Handler} to use to invoke the callback. If null then the
-     * main thread's {@link Handler} is used.
-     * @return an {@link AccountManagerFuture} that represents the future result of the call.
-     * The future result is a {@link Boolean} that is true if the account exists and has the
-     * specified features.
+     * @param features An array of the account features to check
+     * @param callback Callback to invoke when the request completes,
+     *     null for no callback
+     * @param handler {@link Handler} identifying the callback thread,
+     *     null for the main thread
+     * @return An {@link AccountManagerFuture} which resolves to a Boolean,
+     * true if the account exists and has all of the specified features.
      */
     public AccountManagerFuture<Boolean> hasFeatures(final Account account,
             final String[] features,
@@ -300,527 +393,31 @@
     }
 
     /**
-     * Add an account to the AccountManager's set of known accounts.
-     * <p>
-     * It is safe to call this method from the main thread.
-     * <p>
-     * Requires that the caller has permission
-     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
-     * with the same UID as the Authenticator for the account.
-     * @param account The account to add
-     * @param password The password to associate with the account. May be null.
-     * @param userdata A bundle of key/value pairs to set as the account's userdata. May be null.
-     * @return true if the account was sucessfully added, false otherwise, for example,
-     * if the account already exists or if the account is null
-     */
-    public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
-        try {
-            return mService.addAccount(account, password, userdata);
-        } catch (RemoteException e) {
-            // won't ever happen
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * Removes the given account. If this account does not exist then this call has no effect.
-     * <p>
-     * This call returns immediately but runs asynchronously and the result is accessed via the
-     * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
-     * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
-     * method asynchronously then they will generally pass in a callback object that will get
-     * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
-     * they will generally pass null for the callback and instead call
-     * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
-     * which will then block until the request completes.
-     * <p>
-     * Do not block the main thread waiting this method's result.
-     * <p>
-     * Not allowed from main thread (but allowed from other threads):
-     * <pre>
-     * Boolean result = removeAccount(account, callback, handler).getResult();
-     * </pre>
-     * Allowed from main thread:
-     * <pre>
-     * removeAccount(account, new AccountManagerCallback<Boolean>() {
-     *    public void run(AccountManagerFuture<Boolean> future) {
-     *        Boolean result = future.getResult();
-     *        // use result
-     *    }
-     * }, handler);
-     * </pre>
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
+     * Lists all accounts of a type which have certain features.  The account
+     * type identifies the authenticator (see {@link #getAccountsByType}).
+     * Account features are authenticator-specific string tokens identifying
+     * boolean account properties (see {@link #hasFeatures}).
      *
-     * @param account The {@link Account} to remove
-     * @param callback A callback to invoke when the request completes. If null then
-     * no callback is invoked.
-     * @param handler The {@link Handler} to use to invoke the callback. If null then the
-     * main thread's {@link Handler} is used.
-     * @return an {@link AccountManagerFuture} that represents the future result of the call.
-     * The future result is a {@link Boolean} that is true if the account is successfully removed
-     * or false if the authenticator refuses to remove the account.
-     */
-    public AccountManagerFuture<Boolean> removeAccount(final Account account,
-            AccountManagerCallback<Boolean> callback, Handler handler) {
-        return new Future2Task<Boolean>(handler, callback) {
-            public void doWork() throws RemoteException {
-                mService.removeAccount(mResponse, account);
-            }
-            public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
-                if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
-                    throw new AuthenticatorException("no result in response");
-                }
-                return bundle.getBoolean(KEY_BOOLEAN_RESULT);
-            }
-        }.start();
-    }
-
-    /**
-     * Removes the given authtoken. If this authtoken does not exist for the given account type
-     * then this call has no effect.
-     * <p>
-     * It is safe to call this method from the main thread.
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
-     * @param accountType the account type of the authtoken to invalidate
-     * @param authToken the authtoken to invalidate
-     */
-    public void invalidateAuthToken(final String accountType, final String authToken) {
-        try {
-            mService.invalidateAuthToken(accountType, authToken);
-        } catch (RemoteException e) {
-            // won't ever happen
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * Gets the authtoken named by "authTokenType" for the specified account if it is cached
-     * by the AccountManager. If no authtoken is cached then null is returned rather than
-     * asking the authenticaticor to generate one. If the account or the
-     * authtoken do not exist then null is returned.
-     * <p>
-     * It is safe to call this method from the main thread.
-     * <p>
-     * Requires that the caller has permission
-     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
-     * with the same UID as the Authenticator for the account.
-     * @param account the account whose authtoken is to be retrieved, must not be null
-     * @param authTokenType the type of authtoken to retrieve
-     * @return an authtoken for the given account and authTokenType, if one is cached by the
-     * AccountManager, null otherwise.
-     */
-    public String peekAuthToken(final Account account, final String authTokenType) {
-        if (account == null) {
-            Log.e(TAG, "peekAuthToken: the account must not be null");
-            return null;
-        }
-        if (authTokenType == null) {
-            return null;
-        }
-        try {
-            return mService.peekAuthToken(account, authTokenType);
-        } catch (RemoteException e) {
-            // won't ever happen
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * Sets the password for the account. The password may be null. If the account does not exist
-     * then this call has no affect.
-     * <p>
-     * It is safe to call this method from the main thread.
-     * <p>
-     * Requires that the caller has permission
-     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
-     * with the same UID as the Authenticator for the account.
-     * @param account the account whose password is to be set. Must not be null.
-     * @param password the password to set for the account. May be null.
-     */
-    public void setPassword(final Account account, final String password) {
-        if (account == null) {
-            Log.e(TAG, "the account must not be null");
-            return;
-        }
-        try {
-            mService.setPassword(account, password);
-        } catch (RemoteException e) {
-            // won't ever happen
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * Sets the password for account to null. If the account does not exist then this call
-     * has no effect.
-     * <p>
-     * It is safe to call this method from the main thread.
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
-     * @param account the account whose password is to be cleared. Must not be null.
-     */
-    public void clearPassword(final Account account) {
-        if (account == null) {
-            Log.e(TAG, "the account must not be null");
-            return;
-        }
-        try {
-            mService.clearPassword(account);
-        } catch (RemoteException e) {
-            // won't ever happen
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * Sets account's userdata named "key" to the specified value. If the account does not
-     * exist then this call has no effect.
-     * <p>
-     * It is safe to call this method from the main thread.
-     * <p>
-     * Requires that the caller has permission
-     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
-     * with the same UID as the Authenticator for the account.
-     * @param account the account whose userdata is to be set. Must not be null.
-     * @param key the key of the userdata to set. Must not be null.
-     * @param value the value to set. May be null.
-     */
-    public void setUserData(final Account account, final String key, final String value) {
-        if (account == null) {
-            Log.e(TAG, "the account must not be null");
-            return;
-        }
-        if (key == null) {
-            Log.e(TAG, "the key must not be null");
-            return;
-        }
-        try {
-            mService.setUserData(account, key, value);
-        } catch (RemoteException e) {
-            // won't ever happen
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * Sets the authtoken named by "authTokenType" to the value specified by authToken.
-     * If the account does not exist then this call has no effect.
-     * <p>
-     * It is safe to call this method from the main thread.
-     * <p>
-     * Requires that the caller has permission
-     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
-     * with the same UID as the Authenticator for the account.
-     * @param account the account whose authtoken is to be set. Must not be null.
-     * @param authTokenType the type of the authtoken to set. Must not be null.
-     * @param authToken the authToken to set. May be null.
-     */
-    public void setAuthToken(Account account, final String authTokenType, final String authToken) {
-        try {
-            mService.setAuthToken(account, authTokenType, authToken);
-        } catch (RemoteException e) {
-            // won't ever happen
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * Convenience method that makes a blocking call to
-     * {@link #getAuthToken(Account, String, boolean, AccountManagerCallback, Handler)}
-     * then extracts and returns the value of {@link #KEY_AUTHTOKEN} from its result.
-     * <p>
-     * It is not safe to call this method from the main thread. See {@link #getAuthToken}.
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#USE_CREDENTIALS}.
-     * @param account the account whose authtoken is to be retrieved, must not be null
-     * @param authTokenType the type of authtoken to retrieve
-     * @param notifyAuthFailure if true, cause the AccountManager to put up a "sign-on" notification
-     * for the account if no authtoken is cached by the AccountManager and the the authenticator
-     * does not have valid credentials to get an authtoken.
-     * @return an authtoken for the given account and authTokenType, if one is cached by the
-     * AccountManager, null otherwise.
-     * @throws AuthenticatorException if the authenticator is not present, unreachable or returns
-     * an invalid response.
-     * @throws OperationCanceledException if the request is canceled for any reason
-     * @throws java.io.IOException if the authenticator experiences an IOException while attempting
-     * to communicate with its backend server.
-     */
-    public String blockingGetAuthToken(Account account, String authTokenType,
-            boolean notifyAuthFailure)
-            throws OperationCanceledException, IOException, AuthenticatorException {
-        Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */,
-                null /* handler */).getResult();
-        return bundle.getString(KEY_AUTHTOKEN);
-    }
-
-    /**
-     * Request that an authtoken of the specified type be returned for an account.
-     * If the Account Manager has a cached authtoken of the requested type then it will
-     * service the request itself. Otherwise it will pass the request on to the authenticator.
-     * The authenticator can try to service this request with information it already has stored
-     * in the AccountManager but may need to launch an activity to prompt the
-     * user to enter credentials. If it is able to retrieve the authtoken it will be returned
-     * in the result.
-     * <p>
-     * If the authenticator needs to prompt the user for credentials it will return an intent to
-     * an activity that will do the prompting. The supplied activity will be used to launch the
-     * intent and the result will come from the launched activity.
-     * <p>
-     * This call returns immediately but runs asynchronously and the result is accessed via the
-     * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
-     * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
-     * method asynchronously then they will generally pass in a callback object that will get
-     * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
-     * they will generally pass null for the callback and instead call
-     * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
-     * which will then block until the request completes.
-     * <p>
-     * Do not block the main thread waiting this method's result.
-     * <p>
-     * Not allowed from main thread (but allowed from other threads):
-     * <pre>
-     * Bundle result = getAuthToken(
-     *   account, authTokenType, options, activity, callback, handler).getResult();
-     * </pre>
-     * Allowed from main thread:
-     * <pre>
-     * getAuthToken(account, authTokenType, options, activity, new AccountManagerCallback<Bundle>() {
-     *    public void run(AccountManagerFuture<Bundle> future) {
-     *        Bundle result = future.getResult();
-     *        // use result
-     *    }
-     * }, handler);
-     * </pre>
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#USE_CREDENTIALS}.
+     * <p>Unlike {@link #getAccountsByType}, this method calls the authenticator,
+     * which may contact the server or do other work to check account features,
+     * so the method returns an {@link AccountManagerFuture}.
      *
-     * @param account The account whose credentials are to be updated.
-     * @param authTokenType the auth token to retrieve as part of updating the credentials.
-     * May be null.
-     * @param options authenticator specific options for the request
-     * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
-     * the intent will be started with this activity. If you do not with to have the intent
-     * started automatically then use the other form,
-     * {@link #getAuthToken(Account, String, boolean, AccountManagerCallback, android.os.Handler)}
-     * @param callback A callback to invoke when the request completes. If null then
-     * no callback is invoked.
-     * @param handler The {@link Handler} to use to invoke the callback. If null then the
-     * main thread's {@link Handler} is used.
-     * @return an {@link AccountManagerFuture} that represents the future result of the call.
-     * The future result is a {@link Bundle} that contains:
-     * <ul>
-     * <li> {@link #KEY_ACCOUNT_NAME}, {@link #KEY_ACCOUNT_TYPE} and {@link #KEY_AUTHTOKEN}
-     * </ul>
-     * If the user presses "back" then the request will be canceled.
-     */
-    public AccountManagerFuture<Bundle> getAuthToken(
-            final Account account, final String authTokenType, final Bundle options,
-            final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
-        if (activity == null) throw new IllegalArgumentException("activity is null");
-        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
-        return new AmsTask(activity, handler, callback) {
-            public void doWork() throws RemoteException {
-                mService.getAuthToken(mResponse, account, authTokenType,
-                        false /* notifyOnAuthFailure */, true /* expectActivityLaunch */,
-                        options);
-            }
-        }.start();
-    }
-
-    /**
-     * Request that an authtoken of the specified type be returned for an account.
-     * If the Account Manager has a cached authtoken of the requested type then it will
-     * service the request itself. Otherwise it will pass the request on to the authenticator.
-     * The authenticator can try to service this request with information it already has stored
-     * in the AccountManager but may need to launch an activity to prompt the
-     * user to enter credentials. If it is able to retrieve the authtoken it will be returned
-     * in the result.
-     * <p>
-     * If the authenticator needs to prompt the user for credentials, rather than returning the
-     * authtoken it will instead return an intent for
-     * an activity that will do the prompting. If an intent is returned and notifyAuthFailure
-     * is true then a notification will be created that launches this intent. This intent can be
-     * invoked by the caller directly to start the activity that prompts the user for the
-     * updated credentials. Otherwise this activity will not be run until the user activates
-     * the notification.
-     * <p>
-     * This call returns immediately but runs asynchronously and the result is accessed via the
-     * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
-     * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
-     * method asynchronously then they will generally pass in a callback object that will get
-     * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
-     * they will generally pass null for the callback and instead call
-     * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
-     * which will then block until the request completes.
-     * <p>
-     * Do not block the main thread waiting this method's result.
-     * <p>
-     * Not allowed from main thread (but allowed from other threads):
-     * <pre>
-     * Bundle result = getAuthToken(
-     *   account, authTokenType, notifyAuthFailure, callback, handler).getResult();
-     * </pre>
-     * Allowed from main thread:
-     * <pre>
-     * getAuthToken(account, authTokenType, notifyAuthFailure, new AccountManagerCallback<Bundle>() {
-     *    public void run(AccountManagerFuture<Bundle> future) {
-     *        Bundle result = future.getResult();
-     *        // use result
-     *    }
-     * }, handler);
-     * </pre>
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#USE_CREDENTIALS}.
+     * <p>This method may be called from any thread, but the returned
+     * {@link AccountManagerFuture} must not be used on the main thread.
      *
-     * @param account The account whose credentials are to be updated.
-     * @param authTokenType the auth token to retrieve as part of updating the credentials.
-     * May be null.
-     * @param notifyAuthFailure if true and the authenticator returns a {@link #KEY_INTENT} in the
-     * result then a "sign-on needed" notification will be created that will launch this intent.
-     * @param callback A callback to invoke when the request completes. If null then
-     * no callback is invoked.
-     * @param handler The {@link Handler} to use to invoke the callback. If null then the
-     * main thread's {@link Handler} is used.
-     * @return an {@link AccountManagerFuture} that represents the future result of the call.
-     * The future result is a {@link Bundle} that contains either:
-     * <ul>
-     * <li> {@link #KEY_INTENT}, which is to be used to prompt the user for the credentials
-     * <li> {@link #KEY_ACCOUNT_NAME}, {@link #KEY_ACCOUNT_TYPE} and {@link #KEY_AUTHTOKEN}
-     * if the authenticator is able to retrieve the auth token
-     * </ul>
-     * If the user presses "back" then the request will be canceled.
-     */
-    public AccountManagerFuture<Bundle> getAuthToken(
-            final Account account, final String authTokenType, final boolean notifyAuthFailure,
-            AccountManagerCallback<Bundle> callback, Handler handler) {
-        if (account == null) throw new IllegalArgumentException("account is null");
-        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
-        return new AmsTask(null, handler, callback) {
-            public void doWork() throws RemoteException {
-                mService.getAuthToken(mResponse, account, authTokenType,
-                        notifyAuthFailure, false /* expectActivityLaunch */, null /* options */);
-            }
-        }.start();
-    }
-
-    /**
-     * Request that an account be added with the given accountType. This request
-     * is processed by the authenticator for the account type. If no authenticator is registered
-     * in the system then {@link AuthenticatorException} is thrown.
-     * <p>
-     * This call returns immediately but runs asynchronously and the result is accessed via the
-     * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
-     * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
-     * method asynchronously then they will generally pass in a callback object that will get
-     * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
-     * they will generally pass null for the callback and instead call
-     * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
-     * which will then block until the request completes.
-     * <p>
-     * Do not block the main thread waiting this method's result.
-     * <p>
-     * Not allowed from main thread (but allowed from other threads):
-     * <pre>
-     * Bundle result = addAccount(
-     *   account, authTokenType, features, options, activity, callback, handler).getResult();
-     * </pre>
-     * Allowed from main thread:
-     * <pre>
-     * addAccount(account, authTokenType, features, options, activity, new AccountManagerCallback<Bundle>() {
-     *    public void run(AccountManagerFuture<Bundle> future) {
-     *        Bundle result = future.getResult();
-     *        // use result
-     *    }
-     * }, handler);
-     * </pre>
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#GET_ACCOUNTS}.
      *
-     * @param accountType The type of account to add. This must not be null.
-     * @param authTokenType The account that is added should be able to service this auth token
-     * type. This may be null.
-     * @param requiredFeatures The account that is added should support these features.
-     * This array may be null or empty.
-     * @param addAccountOptions A bundle of authenticator-specific options that is passed on
-     * to the authenticator. This may be null.
-     * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
-     * the intent will be started with this activity. If activity is null then the result will
-     * be returned as-is.
-     * @param callback A callback to invoke when the request completes. If null then
-     * no callback is invoked.
-     * @param handler The {@link Handler} to use to invoke the callback. If null then the
-     * main thread's {@link Handler} is used.
-     * @return an {@link AccountManagerFuture} that represents the future result of the call.
-     * The future result is a {@link Bundle} that contains either:
-     * <ul>
-     * <li> {@link #KEY_INTENT}, or
-     * <li> {@link #KEY_ACCOUNT_NAME}, {@link #KEY_ACCOUNT_TYPE}
-     * </ul>
-     */
-    public AccountManagerFuture<Bundle> addAccount(final String accountType,
-            final String authTokenType, final String[] requiredFeatures,
-            final Bundle addAccountOptions,
-            final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
-        return new AmsTask(activity, handler, callback) {
-            public void doWork() throws RemoteException {
-                if (accountType == null) {
-                    Log.e(TAG, "the account must not be null");
-                    // to unblock caller waiting on Future.get()
-                    set(new Bundle());
-                    return;
-                }
-                mService.addAcount(mResponse, accountType, authTokenType,
-                        requiredFeatures, activity != null, addAccountOptions);
-            }
-        }.start();
-    }
-
-    /**
-     * Queries for accounts that match the given account type and feature set.
-     * <p>
-     * This call returns immediately but runs asynchronously and the result is accessed via the
-     * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
-     * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
-     * method asynchronously then they will generally pass in a callback object that will get
-     * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
-     * they will generally pass null for the callback and instead call
-     * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
-     * which will then block until the request completes.
-     * <p>
-     * Do not block the main thread waiting this method's result.
-     * <p>
-     * Not allowed from main thread (but allowed from other threads):
-     * <pre>
-     * Account[] result =
-     *   getAccountsByTypeAndFeatures(accountType, features, callback, handler).getResult();
-     * </pre>
-     * Allowed from main thread:
-     * <pre>
-     * getAccountsByTypeAndFeatures(accountType, features, new AccountManagerCallback<Account[]>() {
-     *    public void run(AccountManagerFuture<Account[]> future) {
-     *         Account[] result = future.getResult();
-     *        // use result
-     *    }
-     * }, handler);
-     * </pre>
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS}.
-     *
-     * @param type The type of {@link Account} to return. If null is passed in then an empty
-     * array will be returned.
-     * @param features the features with which to filter the accounts list. Each returned account
-     * will have all specified features. This may be null, which will mean the account list will
-     * not be filtered by features, making this functionally identical to
-     * {@link #getAccountsByType(String)}.
-     * @param callback A callback to invoke when the request completes. If null then
-     * no callback is invoked.
-     * @param handler The {@link Handler} to use to invoke the callback. If null then the
-     * main thread's {@link Handler} is used.
-     * @return an {@link AccountManagerFuture} that represents the future result of the call.
-     * The future result is a an {@link Account} array that contains accounts of the specified
-     * type that match all the requested features.
+     * @param type The type of accounts to return, must not be null
+     * @param features An array of the account features to require,
+     *     may be null or empty
+     * @param callback Callback to invoke when the request completes,
+     *     null for no callback
+     * @param handler {@link Handler} identifying the callback thread,
+     *     null for the main thread
+     * @return An {@link AccountManagerFuture} which resolves to an array of
+     *     {@link Account}, one per account of the specified type which
+     *     matches the requested features.
      */
     public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
             final String type, final String[] features,
@@ -849,57 +446,535 @@
     }
 
     /**
-     * Requests that the authenticator checks that the user knows the credentials for the account.
-     * This is typically done by returning an intent to an activity that prompts the user to
-     * enter the credentials. This request
-     * is processed by the authenticator for the account. If no matching authenticator is
-     * registered in the system then {@link AuthenticatorException} is thrown.
-     * <p>
-     * This call returns immediately but runs asynchronously and the result is accessed via the
-     * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
-     * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
-     * method asynchronously then they will generally pass in a callback object that will get
-     * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
-     * they will generally pass null for the callback and instead call
-     * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
-     * which will then block until the request completes.
-     * <p>
-     * Do not block the main thread waiting this method's result.
-     * <p>
-     * Not allowed from main thread (but allowed from other threads):
-     * <pre>
-     * Bundle result = confirmCredentials(
-     *   account, options, activity, callback, handler).getResult();
-     * </pre>
-     * Allowed from main thread:
-     * <pre>
-     * confirmCredentials(account, options, activity, new AccountManagerCallback<Bundle>() {
-     *    public void run(AccountManagerFuture<Bundle> future) {
-     *        Bundle result = future.getResult();
-     *        // use result
-     *    }
-     * }, handler);
-     * </pre>
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
+     * Adds an account directly to the AccountManager.  Normally used by sign-up
+     * wizards associated with authenticators, not directly by applications.
      *
-     * @param account The account whose credentials are to be checked
-     * @param options authenticator specific options for the request
-     * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
-     * the intent will be started with this activity. If activity is null then the result will
-     * be returned as-is.
-     * @param callback A callback to invoke when the request completes. If null then
-     * no callback is invoked.
-     * @param handler The {@link Handler} to use to invoke the callback. If null then the
-     * main thread's {@link Handler} is used.
-     * @return an {@link AccountManagerFuture} that represents the future result of the call.
-     * The future result is a {@link Bundle} that contains either:
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
+     * and to have the same UID as the added account's authenticator.
+     *
+     * @param account The {@link Account} to add
+     * @param password The password to associate with the account, null for none
+     * @param userdata String values to use for the account's userdata, null for none
+     * @return Whether the account was successfully added.  False if the account
+     *     already exists, the account is null, or another error occurs.
+     */
+    public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
+        try {
+            return mService.addAccount(account, password, userdata);
+        } catch (RemoteException e) {
+            // won't ever happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Removes an account from the AccountManager.  Does nothing if the account
+     * does not exist.  Does not delete the account from the server.
+     * The authenticator may have its own policies preventing account
+     * deletion, in which case the account will not be deleted.
+     *
+     * <p>This method may be called from any thread, but the returned
+     * {@link AccountManagerFuture} must not be used on the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
+     *
+     * @param account The {@link Account} to remove
+     * @param callback Callback to invoke when the request completes,
+     *     null for no callback
+     * @param handler {@link Handler} identifying the callback thread,
+     *     null for the main thread
+     * @return An {@link AccountManagerFuture} which resolves to a Boolean,
+     *     true if the account has been successfully removed,
+     *     false if the authenticator forbids deleting this account.
+     */
+    public AccountManagerFuture<Boolean> removeAccount(final Account account,
+            AccountManagerCallback<Boolean> callback, Handler handler) {
+        return new Future2Task<Boolean>(handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.removeAccount(mResponse, account);
+            }
+            public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
+                if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
+                    throw new AuthenticatorException("no result in response");
+                }
+                return bundle.getBoolean(KEY_BOOLEAN_RESULT);
+            }
+        }.start();
+    }
+
+    /**
+     * Removes an auth token from the AccountManager's cache.  Does nothing if
+     * the auth token is not currently in the cache.  Applications must call this
+     * method when the auth token is found to have expired or otherwise become
+     * invalid for authenticating requests.  The AccountManager does not validate
+     * or expire cached auth tokens otherwise.
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#MANAGE_ACCOUNTS} or
+     * {@link android.Manifest.permission#USE_CREDENTIALS}
+     *
+     * @param accountType The account type of the auth token to invalidate
+     * @param authToken The auth token to invalidate
+     */
+    public void invalidateAuthToken(final String accountType, final String authToken) {
+        try {
+            mService.invalidateAuthToken(accountType, authToken);
+        } catch (RemoteException e) {
+            // won't ever happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Gets an auth token from the AccountManager's cache.  If no auth
+     * token is cached for this account, null will be returned -- a new
+     * auth token will not be generated, and the server will not be contacted.
+     * Intended for use by the authenticator, not directly by applications.
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
+     * and to have the same UID as the account's authenticator.
+     *
+     * @param account The account to fetch an auth token for
+     * @param authTokenType The type of auth token to fetch, see {#getAuthToken}
+     * @return The cached auth token for this account and type, or null if
+     *     no auth token is cached or the account does not exist.
+     */
+    public String peekAuthToken(final Account account, final String authTokenType) {
+        if (account == null) {
+            Log.e(TAG, "peekAuthToken: the account must not be null");
+            return null;
+        }
+        if (authTokenType == null) {
+            return null;
+        }
+        try {
+            return mService.peekAuthToken(account, authTokenType);
+        } catch (RemoteException e) {
+            // won't ever happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Sets or forgets a saved password.  This modifies the local copy of the
+     * password used to automatically authenticate the user; it does
+     * not change the user's account password on the server.  Intended for use
+     * by the authenticator, not directly by applications.
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
+     * and have the same UID as the account's authenticator.
+     *
+     * @param account The account to set a password for
+     * @param password The password to set, null to clear the password
+     */
+    public void setPassword(final Account account, final String password) {
+        if (account == null) {
+            Log.e(TAG, "the account must not be null");
+            return;
+        }
+        try {
+            mService.setPassword(account, password);
+        } catch (RemoteException e) {
+            // won't ever happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Forgets a saved password.  This erases the local copy of the password;
+     * it does not change the user's account password on the server.
+     * Has the same effect as setPassword(account, null) but requires fewer
+     * permissions, and may be used by applications or management interfaces
+     * to "sign out" from an account.
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#MANAGE_ACCOUNTS}
+     *
+     * @param account The account whose password to clear
+     */
+    public void clearPassword(final Account account) {
+        if (account == null) {
+            Log.e(TAG, "the account must not be null");
+            return;
+        }
+        try {
+            mService.clearPassword(account);
+        } catch (RemoteException e) {
+            // won't ever happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Sets one userdata key for an account.  Intended by use for the
+     * authenticator to stash state for itself, not directly by applications.
+     * The meaning of the keys and values is up to the authenticator.
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
+     * and to have the same UID as the account's authenticator.
+     *
+     * @param account The account to set the userdata for
+     * @param key The userdata key to set.  Must not be null
+     * @param value The value to set, null to clear this userdata key
+     */
+    public void setUserData(final Account account, final String key, final String value) {
+        if (account == null) {
+            Log.e(TAG, "the account must not be null");
+            return;
+        }
+        if (key == null) {
+            Log.e(TAG, "the key must not be null");
+            return;
+        }
+        try {
+            mService.setUserData(account, key, value);
+        } catch (RemoteException e) {
+            // won't ever happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Adds an auth token to the AccountManager cache for an account.
+     * If the account does not exist then this call has no effect.
+     * Replaces any previous auth token for this account and auth token type.
+     * Intended for use by the authenticator, not directly by applications.
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
+     * and to have the same UID as the account's authenticator.
+     *
+     * @param account The account to set an auth token for
+     * @param authTokenType The type of the auth token, see {#getAuthToken}
+     * @param authToken The auth token to add to the cache
+     */
+    public void setAuthToken(Account account, final String authTokenType, final String authToken) {
+        try {
+            mService.setAuthToken(account, authTokenType, authToken);
+        } catch (RemoteException e) {
+            // won't ever happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * This convenience helper synchronously gets an auth token with
+     * {@link #getAuthToken(Account, String, boolean, AccountManagerCallback, Handler)}.
+     *
+     * <p>This method may block while a network request completes, and must
+     * never be made from the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#USE_CREDENTIALS}.
+     *
+     * @param account The account to fetch an auth token for
+     * @param authTokenType The auth token type, see {#link getAuthToken}
+     * @param notifyAuthFailure If true, display a notification and return null
+     *     if authentication fails; if false, prompt and wait for the user to
+     *     re-enter correct credentials before returning
+     * @return An auth token of the specified type for this account, or null
+     *     if authentication fails or none can be fetched.
+     * @throws AuthenticatorException if the authenticator failed to respond
+     * @throws OperationCanceledException if the request was canceled for any
+     *     reason, including the user canceling a credential request
+     * @throws java.io.IOException if the authenticator experienced an I/O problem
+     *     creating a new auth token, usually because of network trouble
+     */
+    public String blockingGetAuthToken(Account account, String authTokenType,
+            boolean notifyAuthFailure)
+            throws OperationCanceledException, IOException, AuthenticatorException {
+        Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */,
+                null /* handler */).getResult();
+        return bundle.getString(KEY_AUTHTOKEN);
+    }
+
+    /**
+     * Gets an auth token of the specified type for a particular account,
+     * prompting the user for credentials if necessary.  This method is
+     * intended for applications running in the foreground where it makes
+     * sense to ask the user directly for a password.
+     *
+     * <p>If a previously generated auth token is cached for this account and
+     * type, then it will be returned.  Otherwise, if we have a saved password
+     * the server accepts, it will be used to generate a new auth token.
+     * Otherwise, the user will be asked for a password, which will be sent to
+     * the server to generate a new auth token.
+     *
+     * <p>The value of the auth token type depends on the authenticator.
+     * Some services use different tokens to access different functionality --
+     * for example, Google uses different auth tokens to access Gmail and
+     * Google Calendar for the same account.
+     *
+     * <p>This method may be called from any thread, but the returned
+     * {@link AccountManagerFuture} must not be used on the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#USE_CREDENTIALS}.
+     *
+     * @param account The account to fetch an auth token for
+     * @param authTokenType The auth token type, an authenticator-dependent
+     *     string token, must not be null
+     * @param options Authenticator-specific options for the request,
+     *     may be null or empty
+     * @param activity The {@link Activity} context to use for launching a new
+     *     authenticator-defined sub-Activity to prompt the user for a password
+     *     if necessary; used only to call startActivity(); must not be null.
+     * @param callback Callback to invoke when the request completes,
+     *     null for no callback
+     * @param handler {@link Handler} identifying the callback thread,
+     *     null for the main thread
+     * @return An {@link AccountManagerFuture} which resolves to a Bundle with
+     *     at least the following fields:
      * <ul>
-     * <li> {@link #KEY_INTENT}, which is to be used to prompt the user for the credentials
-     * <li> {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE} if the user enters the correct
-     * credentials
+     * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account you supplied
+     * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
+     * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted
      * </ul>
-     * If the user presses "back" then the request will be canceled.
+     *
+     * (Other authenticator-specific values may be returned.)  If an auth token
+     * could not be fetched, {@link AccountManagerFuture#getResult()} throws:
+     * <ul>
+     * <li> {@link AuthenticatorException} if the authenticator failed to respond
+     * <li> {@link OperationCanceledException} if the operation is canceled for
+     *      any reason, incluidng the user canceling a credential request
+     * <li> {@link IOException} if the authenticator experienced an I/O problem
+     *      creating a new auth token, usually because of network trouble
+     * </ul>
+     */
+    public AccountManagerFuture<Bundle> getAuthToken(
+            final Account account, final String authTokenType, final Bundle options,
+            final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
+        if (activity == null) throw new IllegalArgumentException("activity is null");
+        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
+        return new AmsTask(activity, handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.getAuthToken(mResponse, account, authTokenType,
+                        false /* notifyOnAuthFailure */, true /* expectActivityLaunch */,
+                        options);
+            }
+        }.start();
+    }
+
+    /**
+     * Gets an auth token of the specified type for a particular account,
+     * optionally raising a notification if the user must enter credentials.
+     * This method is intended for background tasks and services where the
+     * user should not be immediately interrupted with a password prompt.
+     *
+     * <p>If a previously generated auth token is cached for this account and
+     * type, then it will be returned.  Otherwise, if we have saved credentials
+     * the server accepts, it will be used to generate a new auth token.
+     * Otherwise, an Intent will be returned which, when started, will prompt
+     * the user for a password.  If the notifyAuthFailure parameter is set,
+     * the same Intent will be associated with a status bar notification,
+     * alerting the user that they need to enter a password at some point.
+     *
+     * <p>If the intent is left in a notification, you will need to wait until
+     * the user gets around to entering a password before trying again,
+     * which could be hours or days or never.  When it does happen, the
+     * account manager will broadcast the {@link #LOGIN_ACCOUNTS_CHANGED_ACTION}
+     * {@link Intent}, which applications can use to trigger another attempt
+     * to fetch an auth token.
+     *
+     * <p>If notifications are not enabled, it is the application's
+     * responsibility to launch the returned intent at some point to let
+     * the user enter credentials.  In either case, the result from this
+     * call will not wait for user action.
+     *
+     * <p>The value of the auth token type depends on the authenticator.
+     * Some services use different tokens to access different functionality --
+     * for example, Google uses different auth tokens to access Gmail and
+     * Google Calendar for the same account.
+     *
+     * <p>This method may be called from any thread, but the returned
+     * {@link AccountManagerFuture} must not be used on the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#USE_CREDENTIALS}.
+     *
+     * @param account The account to fetch an auth token for
+     * @param authTokenType The auth token type, an authenticator-dependent
+     *     string token, must not be null
+     * @param notifyAuthFailure True to add a notification to prompt the
+     *     user for a password if necessary, false to leave that to the caller
+     * @param callback Callback to invoke when the request completes,
+     *     null for no callback
+     * @param handler {@link Handler} identifying the callback thread,
+     *     null for the main thread
+     * @return An {@link AccountManagerFuture} which resolves to a Bundle with
+     *     at least the following fields on success:
+     * <ul>
+     * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account you supplied
+     * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
+     * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted
+     * </ul>
+     *
+     * (Other authenticator-specific values may be returned.)  If the user
+     * must enter credentials, the returned Bundle contains only
+     * {@link #KEY_INTENT} with the {@link Intent} needed to launch a prompt.
+     *
+     * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws:
+     * <ul>
+     * <li> {@link AuthenticatorException} if the authenticator failed to respond
+     * <li> {@link OperationCanceledException} if the operation is canceled for
+     *      any reason, incluidng the user canceling a credential request
+     * <li> {@link IOException} if the authenticator experienced an I/O problem
+     *      creating a new auth token, usually because of network trouble
+     * </ul>
+     */
+    public AccountManagerFuture<Bundle> getAuthToken(
+            final Account account, final String authTokenType, final boolean notifyAuthFailure,
+            AccountManagerCallback<Bundle> callback, Handler handler) {
+        if (account == null) throw new IllegalArgumentException("account is null");
+        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
+        return new AmsTask(null, handler, callback) {
+            public void doWork() throws RemoteException {
+                mService.getAuthToken(mResponse, account, authTokenType,
+                        notifyAuthFailure, false /* expectActivityLaunch */, null /* options */);
+            }
+        }.start();
+    }
+
+    /**
+     * Asks the user to add an account of a specified type.  The authenticator
+     * for this account type processes this request with the appropriate user
+     * interface.  If the user does elect to create a new account, the account
+     * name is returned.
+     *
+     * <p>This method may be called from any thread, but the returned
+     * {@link AccountManagerFuture} must not be used on the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
+     *
+     * @param accountType The type of account to add; must not be null
+     * @param authTokenType The type of auth token (see {@link #getAuthToken})
+     *     this account will need to be able to generate, null for none
+     * @param requiredFeatures The features (see {@link #hasFeatures}) this
+     *     account must have, null for none
+     * @param addAccountOptions Authenticator-specific options for the request,
+     *     may be null or empty
+     * @param activity The {@link Activity} context to use for launching a new
+     *     authenticator-defined sub-Activity to prompt the user to create an
+     *     account; used only to call startActivity(); if null, the prompt
+     *     will not be launched directly, but the necessary {@link Intent}
+     *     will be returned to the caller instead
+     * @param callback Callback to invoke when the request completes,
+     *     null for no callback
+     * @param handler {@link Handler} identifying the callback thread,
+     *     null for the main thread
+     * @return An {@link AccountManagerFuture} which resolves to a Bundle with
+     *     these fields if activity was specified and an account was created:
+     * <ul>
+     * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created
+     * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
+     * </ul>
+     *
+     * If no activity was specified, the returned Bundle contains only
+     * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
+     * actual account creation process.
+     *
+     * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws:
+     * <ul>
+     * <li> {@link AuthenticatorException} if no authenticator was registered for
+     *      this account type or the authenticator failed to respond
+     * <li> {@link OperationCanceledException} if the operation was canceled for
+     *      any reason, including the user canceling the creation process
+     * <li> {@link IOException} if the authenticator experienced an I/O problem
+     *      creating a new account, usually because of network trouble
+     * </ul>
+     */
+    public AccountManagerFuture<Bundle> addAccount(final String accountType,
+            final String authTokenType, final String[] requiredFeatures,
+            final Bundle addAccountOptions,
+            final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
+        return new AmsTask(activity, handler, callback) {
+            public void doWork() throws RemoteException {
+                if (accountType == null) {
+                    Log.e(TAG, "the account must not be null");
+                    // to unblock caller waiting on Future.get()
+                    set(new Bundle());
+                    return;
+                }
+                mService.addAcount(mResponse, accountType, authTokenType,
+                        requiredFeatures, activity != null, addAccountOptions);
+            }
+        }.start();
+    }
+
+    /**
+     * Confirms that the user knows the password for an account to make extra
+     * sure they are the owner of the account.  The user-entered password can
+     * be supplied directly, otherwise the authenticator for this account type
+     * prompts the user with the appropriate interface.  This method is
+     * intended for applications which want extra assurance; for example, the
+     * phone lock screen uses this to let the user unlock the phone with an
+     * account password if they forget the lock pattern.
+     *
+     * <p>If the user-entered password matches a saved password for this
+     * account, the request is considered valid; otherwise the authenticator
+     * verifies the password (usually by contacting the server).
+     *
+     * <p>This method may be called from any thread, but the returned
+     * {@link AccountManagerFuture} must not be used on the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
+     *
+     * @param account The account to confirm password knowledge for
+     * @param options Authenticator-specific options for the request;
+     *     if the {@link #KEY_PASSWORD} string field is present, the
+     *     authenticator may use it directly rather than prompting the user;
+     *     may be null or empty
+     * @param activity The {@link Activity} context to use for launching a new
+     *     authenticator-defined sub-Activity to prompt the user to enter a
+     *     password; used only to call startActivity(); if null, the prompt
+     *     will not be launched directly, but the necessary {@link Intent}
+     *     will be returned to the caller instead
+     * @param callback Callback to invoke when the request completes,
+     *     null for no callback
+     * @param handler {@link Handler} identifying the callback thread,
+     *     null for the main thread
+     * @return An {@link AccountManagerFuture} which resolves to a Bundle
+     *     with these fields if activity or password was supplied and
+     *     the account was successfully verified:
+     * <ul>
+     * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created
+     * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
+     * <li> {@link #KEY_BOOLEAN_RESULT} - true to indicate success
+     * </ul>
+     *
+     * If no activity or password was specified, the returned Bundle contains
+     * only {@link #KEY_INTENT} with the {@link Intent} needed to launch the
+     * password prompt.
+     *
+     * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws:
+     * <ul>
+     * <li> {@link AuthenticatorException} if the authenticator failed to respond
+     * <li> {@link OperationCanceledException} if the operation was canceled for
+     *      any reason, including the user canceling the password prompt
+     * <li> {@link IOException} if the authenticator experienced an I/O problem
+     *      verifying the password, usually because of network trouble
+     * </ul>
      */
     public AccountManagerFuture<Bundle> confirmCredentials(final Account account,
             final Bundle options,
@@ -914,59 +989,52 @@
     }
 
     /**
-     * Requests that the authenticator update the the credentials for a user. This is typically
-     * done by returning an intent to an activity that will prompt the user to update the stored
-     * credentials for the account. This request
-     * is processed by the authenticator for the account. If no matching authenticator is
-     * registered in the system then {@link AuthenticatorException} is thrown.
-     * <p>
-     * This call returns immediately but runs asynchronously and the result is accessed via the
-     * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
-     * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
-     * method asynchronously then they will generally pass in a callback object that will get
-     * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
-     * they will generally pass null for the callback and instead call
-     * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
-     * which will then block until the request completes.
-     * <p>
-     * Do not block the main thread waiting this method's result.
-     * <p>
-     * Not allowed from main thread (but allowed from other threads):
-     * <pre>
-     * Bundle result = updateCredentials(
-     *   account, authTokenType, options, activity, callback, handler).getResult();
-     * </pre>
-     * Allowed from main thread:
-     * <pre>
-     * updateCredentials(account, authTokenType, options, activity, new AccountManagerCallback<Bundle>() {
-     *    public void run(AccountManagerFuture<Bundle> future) {
-     *        Bundle result = future.getResult();
-     *        // use result
-     *    }
-     * }, handler);
-     * </pre>
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
+     * Asks the user to enter a new password for an account, updating the
+     * saved credentials for the account.  Normally this happens automatically
+     * when the server rejects credentials during an auth token fetch, but this
+     * can be invoked directly to ensure we have the correct credentials stored.
      *
-     * @param account The account whose credentials are to be updated.
-     * @param authTokenType the auth token to retrieve as part of updating the credentials.
-     * May be null.
-     * @param options authenticator specific options for the request
-     * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
-     * the intent will be started with this activity. If activity is null then the result will
-     * be returned as-is.
-     * @param callback A callback to invoke when the request completes. If null then
-     * no callback is invoked.
-     * @param handler The {@link Handler} to use to invoke the callback. If null then the
-     * main thread's {@link Handler} is used.
-     * @return an {@link AccountManagerFuture} that represents the future result of the call.
-     * The future result is a {@link Bundle} that contains either:
+     * <p>This method may be called from any thread, but the returned
+     * {@link AccountManagerFuture} must not be used on the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
+     *
+     * @param account The account to update credentials for
+     * @param authTokenType The credentials entered must allow an auth token
+     *     of this type to be created (but no actual auth token is returned);
+     *     may be null
+     * @param options Authenticator-specific options for the request;
+     *     may be null or empty
+     * @param activity The {@link Activity} context to use for launching a new
+     *     authenticator-defined sub-Activity to prompt the user to enter a
+     *     password; used only to call startActivity(); if null, the prompt
+     *     will not be launched directly, but the necessary {@link Intent}
+     *     will be returned to the caller instead
+     * @param callback Callback to invoke when the request completes,
+     *     null for no callback
+     * @param handler {@link Handler} identifying the callback thread,
+     *     null for the main thread
+     * @return An {@link AccountManagerFuture} which resolves to a Bundle
+     *     with these fields if an activity was supplied and the account
+     *     credentials were successfully updated:
      * <ul>
-     * <li> {@link #KEY_INTENT}, which is to be used to prompt the user for the credentials
-     * <li> {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE} if the user enters the correct
-     * credentials.
+     * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created
+     * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
      * </ul>
-     * If the user presses "back" then the request will be canceled.
+     *
+     * If no activity was specified, the returned Bundle contains only
+     * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
+     * password prompt.
+     *
+     * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws:
+     * <ul>
+     * <li> {@link AuthenticatorException} if the authenticator failed to respond
+     * <li> {@link OperationCanceledException} if the operation was canceled for
+     *      any reason, including the user canceling the password prompt
+     * <li> {@link IOException} if the authenticator experienced an I/O problem
+     *      verifying the password, usually because of network trouble
+     * </ul>
      */
     public AccountManagerFuture<Bundle> updateCredentials(final Account account,
             final String authTokenType,
@@ -982,53 +1050,41 @@
     }
 
     /**
-     * Request that the properties for an authenticator be updated. This is typically done by
-     * returning an intent to an activity that will allow the user to make changes. This request
-     * is processed by the authenticator for the account. If no matching authenticator is
-     * registered in the system then {@link AuthenticatorException} is thrown.
-     * <p>
-     * This call returns immediately but runs asynchronously and the result is accessed via the
-     * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
-     * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
-     * method asynchronously then they will generally pass in a callback object that will get
-     * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
-     * they will generally pass null for the callback and instead call
-     * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
-     * which will then block until the request completes.
-     * <p>
-     * Do not block the main thread waiting this method's result.
-     * <p>
-     * Not allowed from main thread (but allowed from other threads):
-     * <pre>
-     * Bundle result = editProperties(accountType, activity, callback, handler).getResult();
-     * </pre>
-     * Allowed from main thread:
-     * <pre>
-     * editProperties(accountType, activity, new AccountManagerCallback<Bundle>() {
-     *    public void run(AccountManagerFuture<Bundle> future) {
-     *        Bundle result = future.getResult();
-     *        // use result
-     *    }
-     * }, handler);
-     * </pre>
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
+     * Offers the user an opportunity to change an authenticator's settings.
+     * These properties are for the authenticator in general, not a particular
+     * account.  Not all authenticators support this method.
      *
-     * @param accountType The account type of the authenticator whose properties are to be edited.
-     * @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
-     * the intent will be started with this activity. If activity is null then the result will
-     * be returned as-is.
-     * @param callback A callback to invoke when the request completes. If null then
-     * no callback is invoked.
-     * @param handler The {@link Handler} to use to invoke the callback. If null then the
-     * main thread's {@link Handler} is used.
-     * @return an {@link AccountManagerFuture} that represents the future result of the call.
-     * The future result is a {@link Bundle} that contains either:
+     * <p>This method may be called from any thread, but the returned
+     * {@link AccountManagerFuture} must not be used on the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
+     *
+     * @param accountType The account type associated with the authenticator
+     *     to adjust
+     * @param activity The {@link Activity} context to use for launching a new
+     *     authenticator-defined sub-Activity to adjust authenticator settings;
+     *     used only to call startActivity(); if null, the settings dialog will
+     *     not be launched directly, but the necessary {@link Intent} will be
+     *     returned to the caller instead
+     * @param callback Callback to invoke when the request completes,
+     *     null for no callback
+     * @param handler {@link Handler} identifying the callback thread,
+     *     null for the main thread
+     * @return An {@link AccountManagerFuture} which resolves to a Bundle
+     *     which is empty if properties were edited successfully, or
+     *     if no activity was specified, contains only {@link #KEY_INTENT}
+     *     needed to launch the authenticator's settings dialog.
+     *
+     * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws:
      * <ul>
-     * <li> {@link #KEY_INTENT}, which is to be used to prompt the user for the credentials
-     * <li> nothing, returned if the edit completes successfully
+     * <li> {@link AuthenticatorException} if no authenticator was registered for
+     *      this account type or the authenticator failed to respond
+     * <li> {@link OperationCanceledException} if the operation was canceled for
+     *      any reason, including the user canceling the settings dialog
+     * <li> {@link IOException} if the authenticator experienced an I/O problem
+     *      updating settings, usually because of network trouble
      * </ul>
-     * If the user presses "back" then the request will be canceled.
      */
     public AccountManagerFuture<Bundle> editProperties(final String accountType,
             final Activity activity, final AccountManagerCallback<Bundle> callback,
@@ -1475,57 +1531,68 @@
     }
 
     /**
-     * Convenience method that combines the functionality of {@link #getAccountsByTypeAndFeatures},
-     * {@link #getAuthToken(Account, String, Bundle, Activity, AccountManagerCallback, Handler)},
-     * and {@link #addAccount}. It first gets the list of accounts that match accountType and the
-     * feature set. If there are none then {@link #addAccount} is invoked with the authTokenType
-     * feature set, and addAccountOptions. If there is exactly one then
-     * {@link #getAuthToken(Account, String, Bundle, Activity, AccountManagerCallback, Handler)} is
-     * called with that account. If there are more than one then a chooser activity is launched
-     * to prompt the user to select one of them and then the authtoken is retrieved for it,
-     * <p>
-     * This call returns immediately but runs asynchronously and the result is accessed via the
-     * {@link AccountManagerFuture} that is returned. This future is also passed as the sole
-     * parameter to the {@link AccountManagerCallback}. If the caller wished to use this
-     * method asynchronously then they will generally pass in a callback object that will get
-     * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
-     * they will generally pass null for the callback and instead call
-     * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
-     * which will then block until the request completes.
-     * <p>
-     * Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
+     * This convenience helper combines the functionality of
+     * {@link #getAccountsByTypeAndFeatures}, {@link #getAuthToken}, and
+     * {@link #addAccount}.
      *
-     * @param accountType the accountType to query; this must be non-null
-     * @param authTokenType the type of authtoken to retrieve; this must be non-null
-     * @param features a filter for the accounts. See {@link #getAccountsByTypeAndFeatures}.
-     * @param activityForPrompting The activity used to start any account management
-     * activities that are required to fulfill this request. This may be null.
-     * @param addAccountOptions authenticator-specific options used if an account needs to be added
-     * @param getAuthTokenOptions authenticator-specific options passed to getAuthToken
-     * @param callback A callback to invoke when the request completes. If null then
-     * no callback is invoked.
-     * @param handler The {@link Handler} to use to invoke the callback. If null then the
-     * main thread's {@link Handler} is used.
-     * @return an {@link AccountManagerFuture} that represents the future result of the call.
-     * The future result is a {@link Bundle} that contains either:
+     * <p>This method gets a list of the accounts matching the
+     * specified type and feature set; if there is exactly one, it is
+     * used; if there are more than one, the user is prompted to pick one;
+     * if there are none, the user is prompted to add one.  Finally,
+     * an auth token is acquired for the chosen account.
+     *
+     * <p>This method may be called from any thread, but the returned
+     * {@link AccountManagerFuture} must not be used on the main thread.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
+     *
+     * @param accountType The account type required
+     *     (see {@link #getAccountsByType}), must not be null
+     * @param authTokenType The desired auth token type
+     *     (see {@link #getAuthToken}), must not be null
+     * @param features Required features for the account
+     *     (see {@link #getAccountsByTypeAndFeatures}), may be null or empty
+     * @param activity The {@link Activity} context to use for launching new
+     *     sub-Activities to prompt to add an account, select an account,
+     *     and/or enter a password, as necessary; used only to call
+     *     startActivity(); should not be null
+     * @param addAccountOptions Authenticator-specific options to use for
+     *     adding new accounts; may be null or empty
+     * @param getAuthTokenOptions Authenticator-specific options to use for
+     *     getting auth tokens; may be null or empty
+     * @param callback Callback to invoke when the request completes,
+     *     null for no callback
+     * @param handler {@link Handler} identifying the callback thread,
+     *     null for the main thread
+     * @return An {@link AccountManagerFuture} which resolves to a Bundle with
+     *     at least the following fields:
      * <ul>
-     * <li> {@link #KEY_INTENT}, if no activity is supplied yet an activity needs to launched to
-     * fulfill the request.
-     * <li> {@link #KEY_ACCOUNT_NAME}, {@link #KEY_ACCOUNT_TYPE} and {@link #KEY_AUTHTOKEN} if the
-     * request completes successfully.
+     * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account
+     * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
+     * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted
      * </ul>
-     * If the user presses "back" then the request will be canceled.
+     *
+     * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws:
+     * <ul>
+     * <li> {@link AuthenticatorException} if no authenticator was registered for
+     *      this account type or the authenticator failed to respond
+     * <li> {@link OperationCanceledException} if the operation was canceled for
+     *      any reason, including the user canceling any operation
+     * <li> {@link IOException} if the authenticator experienced an I/O problem
+     *      updating settings, usually because of network trouble
+     * </ul>
      */
     public AccountManagerFuture<Bundle> getAuthTokenByFeatures(
             final String accountType, final String authTokenType, final String[] features,
-            final Activity activityForPrompting, final Bundle addAccountOptions,
+            final Activity activity, final Bundle addAccountOptions,
             final Bundle getAuthTokenOptions,
             final AccountManagerCallback<Bundle> callback, final Handler handler) {
         if (accountType == null) throw new IllegalArgumentException("account type is null");
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
         final GetAuthTokenByTypeAndFeaturesTask task =
                 new GetAuthTokenByTypeAndFeaturesTask(accountType, authTokenType, features,
-                activityForPrompting, addAccountOptions, getAuthTokenOptions, callback, handler);
+                activity, addAccountOptions, getAuthTokenOptions, callback, handler);
         task.start();
         return task;
     }
@@ -1552,18 +1619,26 @@
     };
 
     /**
-     * Add a {@link OnAccountsUpdateListener} to this instance of the {@link AccountManager}.
-     * The listener is guaranteed to be invoked on the thread of the Handler that is passed
-     * in or the main thread's Handler if handler is null.
-     * <p>
-     * You must remove this listener before the context that was used to retrieve this
-     * {@link AccountManager} instance goes away. This generally means when the Activity
-     * or Service you are running is stopped.
-     * @param listener the listener to add
-     * @param handler the Handler whose thread will be used to invoke the listener. If null
-     * the AccountManager context's main thread will be used.
-     * @param updateImmediately if true then the listener will be invoked as a result of this
-     * call.
+     * Adds an {@link OnAccountsUpdateListener} to this instance of the
+     * {@link AccountManager}.  This listener will be notified whenever the
+     * list of accounts on the device changes.
+     *
+     * <p>As long as this listener is present, the AccountManager instance
+     * will not be garbage-collected, and neither will the {@link Context}
+     * used to retrieve it, which may be a large Activity instance.  To avoid
+     * memory leaks, you must remove this listener before then.  Normally
+     * listeners are added in an Activity or Service's {@link Activity#onCreate}
+     * and removed in {@link Activity#onDestroy}.
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>No permission is required to call this method.
+     *
+     * @param listener The listener to send notifications to
+     * @param handler {@link Handler} identifying the thread to use
+     *     for notifications, null for the main thread
+     * @param updateImmediately If true, the listener will be invoked
+     *     (on the handler thread) right away with the current account list
      * @throws IllegalArgumentException if listener is null
      * @throws IllegalStateException if listener was already added
      */
@@ -1596,9 +1671,15 @@
     }
 
     /**
-     * Remove an {@link OnAccountsUpdateListener} that was previously registered with
-     * {@link #addOnAccountsUpdatedListener}.
-     * @param listener the listener to remove
+     * Removes an {@link OnAccountsUpdateListener} previously registered with
+     * {@link #addOnAccountsUpdatedListener}.  The listener will no longer
+     * receive notifications of account changes.
+     *
+     * <p>It is safe to call this method from the main thread.
+     *
+     * <p>No permission is required to call this method.
+     *
+     * @param listener The previously added listener to remove
      * @throws IllegalArgumentException if listener is null
      * @throws IllegalStateException if listener was not already added
      */
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 770554e..2aaf5b0 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -565,7 +565,7 @@
     }
 
     public void invalidateAuthToken(String accountType, String authToken) {
-        checkManageAccountsPermission();
+        checkManageAccountsOrUseCredentialsPermissions();
         long identityToken = clearCallingIdentity();
         try {
             SQLiteDatabase db = mOpenHelper.getWritableDatabase();
@@ -691,11 +691,21 @@
         if (account == null) {
             return;
         }
-        ContentValues values = new ContentValues();
-        values.put(ACCOUNTS_PASSWORD, password);
-        mOpenHelper.getWritableDatabase().update(TABLE_ACCOUNTS, values,
-                ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
-                new String[]{account.name, account.type});
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        db.beginTransaction();
+        try {
+            final ContentValues values = new ContentValues();
+            values.put(ACCOUNTS_PASSWORD, password);
+            final long accountId = getAccountId(db, account);
+            if (accountId >= 0) {
+                final String[] argsAccountId = {String.valueOf(accountId)};
+                db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
+                db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
+                db.setTransactionSuccessful();
+            }
+        } finally {
+            db.endTransaction();
+        }
         sendAccountsChangedBroadcast();
     }
 
@@ -1134,7 +1144,10 @@
         long identityToken = clearCallingIdentity();
         try {
             if (features == null || features.length == 0) {
-                getAccountsByType(type);
+                Account[] accounts = getAccountsByType(type);
+                Bundle result = new Bundle();
+                result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
+                onResult(response, result);
                 return;
             }
             new GetAccountsByTypeAndFeatureSession(response, type, features).bind();
@@ -1734,17 +1747,22 @@
         }
     }
 
-    private void checkBinderPermission(String permission) {
+    /** Succeeds if any of the specified permissions are granted. */
+    private void checkBinderPermission(String... permissions) {
         final int uid = Binder.getCallingUid();
-        if (mContext.checkCallingOrSelfPermission(permission) !=
-                PackageManager.PERMISSION_GRANTED) {
-            String msg = "caller uid " + uid + " lacks " + permission;
-            Log.w(TAG, msg);
-            throw new SecurityException(msg);
+
+        for (String perm : permissions) {
+            if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "caller uid " + uid + " has " + perm);
+                }
+                return;
+            }
         }
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "caller uid " + uid + " has " + permission);
-        }
+
+        String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions);
+        Log.w(TAG, msg);
+        throw new SecurityException(msg);
     }
 
     private boolean inSystemImage(int callerUid) {
@@ -1835,6 +1853,11 @@
         checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS);
     }
 
+    private void checkManageAccountsOrUseCredentialsPermissions() {
+        checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS,
+                Manifest.permission.USE_CREDENTIALS);
+    }
+
     /**
      * Allow callers with the given uid permission to get credentials for account/authTokenType.
      * <p>
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 05bbf3b..b38aeda 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1308,14 +1308,16 @@
         }
 
         // close any cursors we are managing.
-        int numCursors = mManagedCursors.size();
-        for (int i = 0; i < numCursors; i++) {
-            ManagedCursor c = mManagedCursors.get(i);
-            if (c != null) {
-                c.mCursor.close();
+        synchronized (mManagedCursors) {
+            int numCursors = mManagedCursors.size();
+            for (int i = 0; i < numCursors; i++) {
+                ManagedCursor c = mManagedCursors.get(i);
+                if (c != null) {
+                    c.mCursor.close();
+                }
             }
+            mManagedCursors.clear();
         }
-        mManagedCursors.clear();
     }
 
     /**
@@ -3778,13 +3780,15 @@
     }
     
     final void performRestart() {
-        final int N = mManagedCursors.size();
-        for (int i=0; i<N; i++) {
-            ManagedCursor mc = mManagedCursors.get(i);
-            if (mc.mReleased || mc.mUpdated) {
-                mc.mCursor.requery();
-                mc.mReleased = false;
-                mc.mUpdated = false;
+        synchronized (mManagedCursors) {
+            final int N = mManagedCursors.size();
+            for (int i=0; i<N; i++) {
+                ManagedCursor mc = mManagedCursors.get(i);
+                if (mc.mReleased || mc.mUpdated) {
+                    mc.mCursor.requery();
+                    mc.mReleased = false;
+                    mc.mUpdated = false;
+                }
             }
         }
 
@@ -3850,12 +3854,14 @@
                     " did not call through to super.onStop()");
             }
     
-            final int N = mManagedCursors.size();
-            for (int i=0; i<N; i++) {
-                ManagedCursor mc = mManagedCursors.get(i);
-                if (!mc.mReleased) {
-                    mc.mCursor.deactivate();
-                    mc.mReleased = true;
+            synchronized (mManagedCursors) {
+                final int N = mManagedCursors.size();
+                for (int i=0; i<N; i++) {
+                    ManagedCursor mc = mManagedCursors.get(i);
+                    if (!mc.mReleased) {
+                        mc.mCursor.deactivate();
+                        mc.mReleased = true;
+                    }
                 }
             }
     
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 13cc3ba..0756c71 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -77,9 +77,12 @@
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
+import java.net.URL;
 import java.util.ArrayList;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -460,6 +463,7 @@
                     mClassLoader =
                         ApplicationLoaders.getDefault().getClassLoader(
                             zip, mDataDir, mBaseClassLoader);
+                    initializeJavaContextClassLoader();
                 } else {
                     if (mBaseClassLoader == null) {
                         mClassLoader = ClassLoader.getSystemClassLoader();
@@ -471,6 +475,120 @@
             }
         }
 
+        /**
+         * Setup value for Thread.getContextClassLoader(). If the
+         * package will not run in in a VM with other packages, we set
+         * the Java context ClassLoader to the
+         * PackageInfo.getClassLoader value. However, if this VM can
+         * contain multiple packages, we intead set the Java context
+         * ClassLoader to a proxy that will warn about the use of Java
+         * context ClassLoaders and then fall through to use the
+         * system ClassLoader.
+         *
+         * <p> Note that this is similar to but not the same as the
+         * android.content.Context.getClassLoader(). While both
+         * context class loaders are typically set to the
+         * PathClassLoader used to load the package archive in the
+         * single application per VM case, a single Android process
+         * may contain several Contexts executing on one thread with
+         * their own logical ClassLoaders while the Java context
+         * ClassLoader is a thread local. This is why in the case when
+         * we have multiple packages per VM we do not set the Java
+         * context ClassLoader to an arbitrary but instead warn the
+         * user to set their own if we detect that they are using a
+         * Java library that expects it to be set.
+         */
+        private void initializeJavaContextClassLoader() {
+            IPackageManager pm = getPackageManager();
+            android.content.pm.PackageInfo pi;
+            try {
+                pi = pm.getPackageInfo(mPackageName, 0);
+            } catch (RemoteException e) {
+                throw new AssertionError(e);
+            }
+            /*
+             * Two possible indications that this package could be
+             * sharing its virtual machine with other packages:
+             *
+             * 1.) the sharedUserId attribute is set in the manifest,
+             *     indicating a request to share a VM with other
+             *     packages with the same sharedUserId.
+             *
+             * 2.) the application element of the manifest has an
+             *     attribute specifying a non-default process name,
+             *     indicating the desire to run in another packages VM.
+             */
+            boolean sharedUserIdSet = (pi.sharedUserId != null);
+            boolean processNameNotDefault =
+                (pi.applicationInfo != null &&
+                 !mPackageName.equals(pi.applicationInfo.processName));
+            boolean sharable = (sharedUserIdSet || processNameNotDefault);
+            ClassLoader contextClassLoader =
+                (sharable)
+                ? new WarningContextClassLoader()
+                : mClassLoader;
+            Thread.currentThread().setContextClassLoader(contextClassLoader);
+        }
+
+        private static class WarningContextClassLoader extends ClassLoader {
+
+            private static boolean warned = false;
+
+            private void warn(String methodName) {
+                if (warned) {
+                    return;
+                }
+                warned = true;
+                Thread.currentThread().setContextClassLoader(getParent());
+                Log.w(TAG, "ClassLoader." + methodName + ": " +
+                      "The class loader returned by " +
+                      "Thread.getContextClassLoader() may fail for processes " +
+                      "that host multiple applications. You should explicitly " +
+                      "specify a context class loader. For example: " +
+                      "Thread.setContextClassLoader(getClass().getClassLoader());");
+            }
+
+            @Override public URL getResource(String resName) {
+                warn("getResource");
+                return getParent().getResource(resName);
+            }
+
+            @Override public Enumeration<URL> getResources(String resName) throws IOException {
+                warn("getResources");
+                return getParent().getResources(resName);
+            }
+
+            @Override public InputStream getResourceAsStream(String resName) {
+                warn("getResourceAsStream");
+                return getParent().getResourceAsStream(resName);
+            }
+
+            @Override public Class<?> loadClass(String className) throws ClassNotFoundException {
+                warn("loadClass");
+                return getParent().loadClass(className);
+            }
+
+            @Override public void setClassAssertionStatus(String cname, boolean enable) {
+                warn("setClassAssertionStatus");
+                getParent().setClassAssertionStatus(cname, enable);
+            }
+
+            @Override public void setPackageAssertionStatus(String pname, boolean enable) {
+                warn("setPackageAssertionStatus");
+                getParent().setPackageAssertionStatus(pname, enable);
+            }
+
+            @Override public void setDefaultAssertionStatus(boolean enable) {
+                warn("setDefaultAssertionStatus");
+                getParent().setDefaultAssertionStatus(enable);
+            }
+
+            @Override public void clearAssertionStatus() {
+                warn("clearAssertionStatus");
+                getParent().clearAssertionStatus();
+            }
+        }
+
         public String getAppDir() {
             return mAppDir;
         }
@@ -2495,7 +2613,6 @@
                             " did not call through to super.onPostCreate()");
                     }
                 }
-                r.state = null;
             }
             r.paused = true;
 
@@ -2526,6 +2643,7 @@
 
         if (a != null) {
             r.createdConfig = new Configuration(mConfiguration);
+            Bundle oldState = r.state;
             handleResumeActivity(r.token, false, r.isForward);
 
             if (!r.activity.mFinished && r.startsNotResumed) {
@@ -2541,6 +2659,9 @@
                 try {
                     r.activity.mCalled = false;
                     mInstrumentation.callActivityOnPause(r.activity);
+                    // We need to keep around the original state, in case
+                    // we need to be created again.
+                    r.state = oldState;
                     if (!r.activity.mCalled) {
                         throw new SuperNotCalledException(
                             "Activity " + r.intent.getComponent().toShortString() +
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 832f599..fe81056 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -16,8 +16,16 @@
 
 package android.app;
 
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemProperties;
+import android.provider.Settings;
 import android.util.Printer;
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -38,6 +46,13 @@
  */
 
 public class ApplicationErrorReport implements Parcelable {
+    // System property defining error report receiver for system apps
+    static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps";
+
+    // System property defining default error report receiver
+    static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default";
+
+    
     /**
      * Uninitialized error report.
      */
@@ -54,8 +69,13 @@
     public static final int TYPE_ANR = 2;
 
     /**
+     * An error report about an application that's consuming too much battery.
+     */
+    public static final int TYPE_BATTERY = 3;
+
+    /**
      * Type of this report. Can be one of {@link #TYPE_NONE},
-     * {@link #TYPE_CRASH} or {@link #TYPE_ANR}.
+     * {@link #TYPE_CRASH}, {@link #TYPE_ANR}, or {@link #TYPE_BATTERY}.
      */
     public int type;
 
@@ -99,6 +119,11 @@
     public AnrInfo anrInfo;
 
     /**
+     * Text containing battery usage data.
+     */
+    public String batteryText;
+    
+    /**
      * Create an uninitialized instance of {@link ApplicationErrorReport}.
      */
     public ApplicationErrorReport() {
@@ -112,6 +137,68 @@
         readFromParcel(in);
     }
 
+    public static ComponentName getErrorReportReceiver(Context context,
+            String packageName, int appFlags) {
+        // check if error reporting is enabled in secure settings
+        int enabled = Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.SEND_ACTION_APP_ERROR, 0);
+        if (enabled == 0) {
+            return null;
+        }
+
+        PackageManager pm = context.getPackageManager();
+
+        // look for receiver in the installer package
+        String candidate = pm.getInstallerPackageName(packageName);
+        ComponentName result = getErrorReportReceiver(pm, packageName, candidate);
+        if (result != null) {
+            return result;
+        }
+
+        // if the error app is on the system image, look for system apps
+        // error receiver
+        if ((appFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+            candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
+            result = getErrorReportReceiver(pm, packageName, candidate);
+            if (result != null) {
+                return result;
+            }
+        }
+
+        // if there is a default receiver, try that
+        candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
+        return getErrorReportReceiver(pm, packageName, candidate);
+    }
+    
+    /**
+     * Return activity in receiverPackage that handles ACTION_APP_ERROR.
+     *
+     * @param pm PackageManager isntance
+     * @param errorPackage package which caused the error
+     * @param receiverPackage candidate package to receive the error
+     * @return activity component within receiverPackage which handles
+     * ACTION_APP_ERROR, or null if not found
+     */
+    static ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage,
+            String receiverPackage) {
+        if (receiverPackage == null || receiverPackage.length() == 0) {
+            return null;
+        }
+
+        // break the loop if it's the error report receiver package that crashed
+        if (receiverPackage.equals(errorPackage)) {
+            return null;
+        }
+
+        Intent intent = new Intent(Intent.ACTION_APP_ERROR);
+        intent.setPackage(receiverPackage);
+        ResolveInfo info = pm.resolveActivity(intent, 0);
+        if (info == null || info.activityInfo == null) {
+            return null;
+        }
+        return new ComponentName(receiverPackage, info.activityInfo.name);
+    }
+
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(type);
         dest.writeString(packageName);
@@ -127,6 +214,9 @@
             case TYPE_ANR:
                 anrInfo.writeToParcel(dest, flags);
                 break;
+            case TYPE_BATTERY:
+                dest.writeString(batteryText);
+                break;
         }
     }
 
@@ -142,10 +232,17 @@
             case TYPE_CRASH:
                 crashInfo = new CrashInfo(in);
                 anrInfo = null;
+                batteryText = null;
                 break;
             case TYPE_ANR:
                 anrInfo = new AnrInfo(in);
                 crashInfo = null;
+                batteryText = null;
+                break;
+            case TYPE_BATTERY:
+                batteryText = in.readString();
+                anrInfo = null;
+                crashInfo = null;
                 break;
         }
     }
@@ -347,6 +444,9 @@
             case TYPE_ANR:
                 anrInfo.dump(pw, prefix);
                 break;
+            case TYPE_BATTERY:
+                pw.println(batteryText);
+                break;
         }
     }
 }
diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/BackupAgent.java
index a2bfc76..4695c21 100644
--- a/core/java/android/app/BackupAgent.java
+++ b/core/java/android/app/BackupAgent.java
@@ -31,10 +31,18 @@
 import java.io.IOException;
 
 /**
- * This is the central interface between an application and Android's
- * settings backup mechanism.
- *
- * <p>STOPSHIP write more documentation about the backup process here.
+ * This is the central interface between an application and Android's settings
+ * backup mechanism. Any implementation of a backup agent should perform backup
+ * and restore actions in
+ * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
+ * and {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor)}
+ * respectively.
+ * <p>
+ * A backup agent based on convenient helper classes is available in
+ * {@link android.backup.BackupHelperAgent} for less complex implementation
+ * requirements.
+ * <p>
+ * STOPSHIP write more documentation about the backup process here.
  */
 public abstract class BackupAgent extends ContextWrapper {
     private static final String TAG = "BackupAgent";
@@ -51,51 +59,58 @@
     }
 
     /**
-     * The application is being asked to write any data changed since the
-     * last time it performed a backup operation.  The state data recorded
-     * during the last backup pass is provided in the oldState file descriptor.
-     * If oldState is null, no old state is available and the application should perform
-     * a full backup.  In both cases, a representation of the final backup state after
-     * this pass should be written to the file pointed to by the newStateFd file descriptor.
-     *
-     * @param oldState An open, read-only ParcelFileDescriptor pointing to the last backup
-     *                 state provided by the application.  May be null, in which
-     *                 case no prior state is being provided and the application should
-     *                 perform a full backup.
-     * @param data A structured wrapper around an open, read/write ParcelFileDescriptor
-     *             pointing to the backup data destination.  Typically the application will use
-     *             backup helper classes to write to this file.
-     * @param newState An open, read/write ParcelFileDescriptor pointing to an empty
-     *                 file.  The application should record the final backup state
-     *                 here after writing the requested data to dataFd.
+     * The application is being asked to write any data changed since the last
+     * time it performed a backup operation. The state data recorded during the
+     * last backup pass is provided in the <code>oldState</code> file
+     * descriptor. If <code>oldState</code> is <code>null</code>, no old state
+     * is available and the application should perform a full backup. In both
+     * cases, a representation of the final backup state after this pass should
+     * be written to the file pointed to by the file descriptor wrapped in
+     * <code>newState</code>.
+     * 
+     * @param oldState An open, read-only ParcelFileDescriptor pointing to the
+     *            last backup state provided by the application. May be
+     *            <code>null</code>, in which case no prior state is being
+     *            provided and the application should perform a full backup.
+     * @param data A structured wrapper around an open, read/write
+     *            ParcelFileDescriptor pointing to the backup data destination.
+     *            Typically the application will use backup helper classes to
+     *            write to this file.
+     * @param newState An open, read/write ParcelFileDescriptor pointing to an
+     *            empty file. The application should record the final backup
+     *            state here after writing the requested data to dataFd.
      */
     public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
              ParcelFileDescriptor newState) throws IOException;
-    
+
     /**
-     * The application is being restored from backup, and should replace any
-     * existing data with the contents of the backup.  The backup data is
-     * provided in the file pointed to by the dataFd file descriptor.  Once
-     * the restore is finished, the application should write a representation
-     * of the final state to the newStateFd file descriptor,
-     *
-     * <p>The application is responsible for properly erasing its old data and
-     * replacing it with the data supplied to this method.  No "clear user data"
-     * operation will be performed automatically by the operating system.  The
-     * exception to this is in the case of a failed restore attempt:  if onRestore()
-     * throws an exception, the OS will assume that the application's data may now
-     * be in an incoherent state, and will clear it before proceeding.
-     *
-     * @param data A structured wrapper around an open, read-only ParcelFileDescriptor
-     *             pointing to a full snapshot of the application's data.  Typically the
-     *             application will use helper classes to read this data.
-     * @param appVersionCode The android:versionCode value of the application that backed
-     *        up this particular data set.  This makes it easier for an application's
-     *        agent to distinguish among several possible older data versions when
-     *        asked to perform the restore operation.
-     * @param newState An open, read/write ParcelFileDescriptor pointing to an empty
-     *                 file.  The application should record the final backup state
-     *                 here after restoring its data from dataFd.
+     * The application is being restored from backup and should replace any
+     * existing data with the contents of the backup. The backup data is
+     * provided in the file descriptor pointed to by the
+     * {@link android.backup.BackupDataInput} instance <code>data</code>. Once
+     * the restore is finished, the application should write a representation of
+     * the final state to the <code>newState</code> file descriptor.
+     * <p>
+     * The application is responsible for properly erasing its old data and
+     * replacing it with the data supplied to this method. No "clear user data"
+     * operation will be performed automatically by the operating system. The
+     * exception to this is in the case of a failed restore attempt: if
+     * onRestore() throws an exception, the OS will assume that the
+     * application's data may now be in an incoherent state, and will clear it
+     * before proceeding.
+     * 
+     * @param data A structured wrapper around an open, read-only
+     *            ParcelFileDescriptor pointing to a full snapshot of the
+     *            application's data. Typically the application will use helper
+     *            classes to read this data.
+     * @param appVersionCode The android:versionCode value of the application
+     *            that backed up this particular data set. This makes it easier
+     *            for an application's agent to distinguish among several
+     *            possible older data versions when asked to perform the restore
+     *            operation.
+     * @param newState An open, read/write ParcelFileDescriptor pointing to an
+     *            empty file. The application should record the final backup
+     *            state here after restoring its data from dataFd.
      */
     public abstract void onRestore(BackupDataInput data, int appVersionCode,
             ParcelFileDescriptor newState)
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1d004ee..50dcdf9 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -194,6 +194,7 @@
     private AccountManager mAccountManager; // protected by mSync
     private DropBoxManager mDropBoxManager = null;
     private DevicePolicyManager mDevicePolicyManager = null;
+    private UiModeManager mUiModeManager = null;
 
     private final Object mSync = new Object();
 
@@ -960,6 +961,8 @@
             return getDropBoxManager();
         } else if (DEVICE_POLICY_SERVICE.equals(name)) {
             return getDevicePolicyManager();
+        } else if (UI_MODE_SERVICE.equals(name)) {
+            return getUiModeManager();
         }
 
         return null;
@@ -1146,13 +1149,22 @@
     private DevicePolicyManager getDevicePolicyManager() {
         synchronized (mSync) {
             if (mDevicePolicyManager == null) {
-                mDevicePolicyManager = new DevicePolicyManager(this,
+                mDevicePolicyManager = DevicePolicyManager.create(this,
                         mMainThread.getHandler());
             }
         }
         return mDevicePolicyManager;
     }
 
+    private UiModeManager getUiModeManager() {
+        synchronized (mSync) {
+            if (mUiModeManager == null) {
+                mUiModeManager = new UiModeManager();
+            }
+        }
+        return mUiModeManager;
+    }
+
     @Override
     public int checkPermission(String permission, int pid, int uid) {
         if (permission == null) {
diff --git a/core/java/android/app/DeviceAdminInfo.java b/core/java/android/app/DeviceAdminInfo.java
index bedf4b4..61e1bb339 100644
--- a/core/java/android/app/DeviceAdminInfo.java
+++ b/core/java/android/app/DeviceAdminInfo.java
@@ -18,6 +18,7 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -331,6 +332,19 @@
         return res;
     }
     
+    /** @hide */
+    public void writePoliciesToXml(XmlSerializer out)
+            throws IllegalArgumentException, IllegalStateException, IOException {
+        out.attribute(null, "flags", Integer.toString(mUsesPolicies));
+    }
+    
+    /** @hide */
+    public void readPoliciesFromXml(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        mUsesPolicies = Integer.parseInt(
+                parser.getAttributeValue(null, "flags"));
+    }
+    
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "Receiver:");
         mReceiver.dump(pw, prefix + "  ");
diff --git a/core/java/android/app/DevicePolicyManager.java b/core/java/android/app/DevicePolicyManager.java
index d611807..0e8c1ab 100644
--- a/core/java/android/app/DevicePolicyManager.java
+++ b/core/java/android/app/DevicePolicyManager.java
@@ -49,13 +49,18 @@
     
     private final Handler mHandler;
 
-    /*package*/ DevicePolicyManager(Context context, Handler handler) {
+    private DevicePolicyManager(Context context, Handler handler) {
         mContext = context;
         mHandler = handler;
         mService = IDevicePolicyManager.Stub.asInterface(
                 ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
     }
 
+    /*package*/ static DevicePolicyManager create(Context context, Handler handler) {
+        DevicePolicyManager me = new DevicePolicyManager(context, handler);
+        return me.mService != null ? me : null;
+    }
+    
     /**
      * Activity action: ask the user to add a new device administrator to the system.
      * The desired policy is the ComponentName of the policy in the
@@ -133,6 +138,20 @@
     }
     
     /**
+     * @hide
+     */
+    public boolean packageHasActiveAdmins(String packageName) {
+        if (mService != null) {
+            try {
+                return mService.packageHasActiveAdmins(packageName);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return false;
+    }
+    
+    /**
      * Remove a current administration component.  This can only be called
      * by the application that owns the administration component; if you
      * try to remove someone else's component, a security exception will be
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 4598bb5..0ed5eb8 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -822,13 +822,15 @@
         final SearchManager searchManager = (SearchManager) mContext
                 .getSystemService(Context.SEARCH_SERVICE);
 
-        // associate search with owner activity if possible (otherwise it will default to
-        // global search).
+        // associate search with owner activity
         final ComponentName appName = getAssociatedActivity();
-        final boolean globalSearch = (appName == null);
-        searchManager.startSearch(null, false, appName, null, globalSearch);
-        dismiss();
-        return true;
+        if (appName != null) {
+            searchManager.startSearch(null, false, appName, null, false);
+            dismiss();
+            return true;
+        } else {
+            return false;
+        }
     }
 
     /**
diff --git a/core/java/android/app/IDevicePolicyManager.aidl b/core/java/android/app/IDevicePolicyManager.aidl
index ae5c4bf..b138720 100644
--- a/core/java/android/app/IDevicePolicyManager.aidl
+++ b/core/java/android/app/IDevicePolicyManager.aidl
@@ -49,6 +49,7 @@
     void setActiveAdmin(in ComponentName policyReceiver);
     boolean isAdminActive(in ComponentName policyReceiver);
     List<ComponentName> getActiveAdmins();
+    boolean packageHasActiveAdmins(String packageName);
     void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result);
     void removeActiveAdmin(in ComponentName policyReceiver);
     
diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl
index 9ba7863..cb03d2c 100644
--- a/core/java/android/app/ISearchManager.aidl
+++ b/core/java/android/app/ISearchManager.aidl
@@ -24,9 +24,8 @@
 
 /** @hide */
 interface ISearchManager {
-   SearchableInfo getSearchableInfo(in ComponentName launchActivity, boolean globalSearch);
+   SearchableInfo getSearchableInfo(in ComponentName launchActivity);
    List<SearchableInfo> getSearchablesInGlobalSearch();
-   List<SearchableInfo> getSearchablesForWebSearch();
-   SearchableInfo getDefaultSearchableForWebSearch();
-   void setDefaultWebSearch(in ComponentName component);
+   ComponentName getGlobalSearchActivity();
+   ComponentName getWebSearchActivity();
 }
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 581b436..6a02a58 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -274,7 +274,7 @@
         SearchManager searchManager = (SearchManager)
                 mContext.getSystemService(Context.SEARCH_SERVICE);
         // Try to get the searchable info for the provided component.
-        mSearchable = searchManager.getSearchableInfo(componentName, false);
+        mSearchable = searchManager.getSearchableInfo(componentName);
 
         if (mSearchable == null) {
             return false;
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index ce5f1bf..b54e53d 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -137,21 +137,11 @@
  * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);   // search within your activity
  * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_GLOBAL);  // search using platform global search</pre>
  * 
- * <p><b>How to enable global search with Quick Search Box.</b>  In addition to searching within
+ * <p><b>How to start global search.</b>  In addition to searching within
  * your activity or application, you can also use the Search Manager to invoke a platform-global
- * search, which uses Quick Search Box to search across the device and the web. There are two ways
- * to do this:
- * <ul><li>You can simply define "search" within your application or activity to mean global search.
- * This is described in more detail in the 
- * <a href="#SearchabilityMetadata">Searchability Metadata</a> section.  Briefly, you will
- * add a single meta-data entry to your manifest, declaring that the default search
- * for your application is "*".  This indicates to the system that no application-specific
- * search activity is provided, and that it should launch web-based search instead.</li>
- * <li>Simply do nothing and the default implementation of
- * {@link android.app.Activity#onSearchRequested} will cause global search to be triggered.
- * (You can also always trigger search via a direct call to {@link android.app.Activity#startSearch}.
- * This is most useful if you wish to provide local searchability <i>and</i> access to global
- * search.)</li></ul> 
+ * search, which uses Quick Search Box to search across the device and the web.
+ * Override {@link android.app.Activity#onSearchRequested} and call
+ * {@link android.app.Activity#startSearch} with {@code globalSearch} set to {@code true}.
  * 
  * <p><b>How to disable search from your activity.</b> Search is a system-wide feature and users
  * will expect it to be available in all contexts.  If your UI design absolutely precludes
@@ -871,12 +861,8 @@
  * 
  * <p>The simplest way to specify this is to add a <i>search reference</i> element to the
  * application entry in the <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> file.  
- * The value of this reference can be either of:
- * <ul><li>The name of your searchable activity.  
- * It is typically prefixed by '.' to indicate that it's in the same package.</li>
- * <li>A "*" indicates that the system may select a default searchable activity, in which
- * case it will typically select web-based search.</li>
- * </ul>
+ * The value of this reference should be the name of your searchable activity.
+ * It is typically prefixed by '.' to indicate that it's in the same package.
  *
  * <p>Here is a snippet showing the necessary addition to the manifest entry for your 
  * non-searchable activities.
@@ -1639,6 +1625,7 @@
             return;
         }
         Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.setComponent(globalSearchActivity);
         // TODO: Always pass name of calling package as an extra?
         if (appSearchData != null) {
@@ -1661,32 +1648,15 @@
     /**
      * Gets the name of the global search activity.
      *
-     * This is currently implemented by returning the first activity that handles
-     * the GLOBAL_SEARCH intent and has the GLOBAL_SEARCH permission. If we allow
-     * more than one global search acitivity to be installed, this code must be changed.
-     *
-     * TODO: Doing this every time we start global search is inefficient. Will fix that once
-     * we have settled on the right mechanism for finding the global search activity.
-     *
      * @hide
      */
     public ComponentName getGlobalSearchActivity() {
-        Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH);
-        PackageManager pm = mContext.getPackageManager();
-        List<ResolveInfo> activities =
-                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
-        int count = activities.size();
-        for (int i = 0; i < count; i++) {
-            ActivityInfo ai = activities.get(i).activityInfo;
-            if (pm.checkPermission(Manifest.permission.GLOBAL_SEARCH,
-                    ai.packageName) == PackageManager.PERMISSION_GRANTED) {
-                return new ComponentName(ai.packageName, ai.name);
-            } else {
-                Log.w(TAG, "Package " + ai.packageName + " wants to handle GLOBAL_SEARCH, "
-                        + "but does not have the GLOBAL_SEARCH permission.");
-            }
+        try {
+            return mService.getGlobalSearchActivity();
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getGlobalSearchActivity() failed: " + ex);
+            return null;
         }
-        return null;
     }
 
     /**
@@ -1699,13 +1669,12 @@
      * @hide
      */
     public ComponentName getWebSearchActivity() {
-        ComponentName globalSearch = getGlobalSearchActivity();
-        if (globalSearch == null) {
+        try {
+            return mService.getWebSearchActivity();
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getWebSearchActivity() failed: " + ex);
             return null;
         }
-        Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
-        intent.setPackage(globalSearch.getPackageName());
-        return intent.resolveActivity(mContext.getPackageManager());
     }
 
     /**
@@ -1839,27 +1808,7 @@
      */
     public SearchableInfo getSearchableInfo(ComponentName componentName) {
         try {
-            return mService.getSearchableInfo(componentName, false);
-        } catch (RemoteException ex) {
-            Log.e(TAG, "getSearchableInfo() failed: " + ex);
-            return null;
-        }
-    }
-
-    /**
-     * Gets information about a searchable activity.
-     *
-     * @param componentName The activity to get searchable information for.
-     * @param globalSearch If <code>false</code>, return information about the given activity.
-     *        If <code>true</code>, return information about the global search activity. 
-     * @return Searchable information, or <code>null</code> if the activity is not searchable.
-     * 
-     * @hide because SearchableInfo is not part of the API.
-     */
-    public SearchableInfo getSearchableInfo(ComponentName componentName,
-            boolean globalSearch) {
-        try {
-            return mService.getSearchableInfo(componentName, globalSearch);
+            return mService.getSearchableInfo(componentName);
         } catch (RemoteException ex) {
             Log.e(TAG, "getSearchableInfo() failed: " + ex);
             return null;
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
new file mode 100644
index 0000000..aca8ab4
--- /dev/null
+++ b/core/java/android/app/UiModeManager.java
@@ -0,0 +1,83 @@
+package android.app;
+
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * This class provides access to the system uimode services.  These services
+ * allow applications to control UI modes of the device.
+ * It provides functionality to disable the car mode and it gives access to the
+ * night mode settings.
+ *
+ * <p>You do not instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.UI_MODE_SERVICE)}.
+ */
+public class UiModeManager {
+    private static final String TAG = "UiModeManager";
+
+    public static final int MODE_NOTNIGHT = 1;
+    public static final int MODE_NIGHT = 2;
+    public static final int MODE_AUTO = 3;
+
+    private IUiModeManager mService;
+
+    /*package*/ UiModeManager() {
+        mService = IUiModeManager.Stub.asInterface(
+                ServiceManager.getService("uimode"));
+    }
+
+    /**
+     * Disables the car mode.
+     */
+    public void disableCarMode() {
+        if (mService != null) {
+            try {
+                mService.disableCarMode();
+            } catch (RemoteException e) {
+                Log.e(TAG, "disableCarMode: RemoteException", e);
+            }
+        }
+    }
+
+    /**
+     * Sets the night mode.  Changes to the night mode are only effective when
+     * the car mode is enabled on a device.
+     *
+     * <p>The mode can be one of:
+     * <ul>
+     *   <li><em>{@link #MODE_NOTNIGHT}<em> - sets the device into notnight
+     *       mode.</li>
+     *   <li><em>{@link #MODE_NIGHT}</em> - sets the device into night mode.
+     *   </li>
+     *   <li><em>{@link #MODE_AUTO}</em> - automatic night/notnight switching
+     *       depending on the location and certain other sensors.</li>
+     */
+    public void setNightMode(int mode) {
+        if (mService != null) {
+            try {
+                mService.setNightMode(mode);
+            } catch (RemoteException e) {
+                Log.e(TAG, "setNightMode: RemoteException", e);
+            }
+        }
+    }
+
+    /**
+     * Returns the currently configured night mode.
+     *
+     * @return {@link #MODE_NOTNIGHT}, {@link #MODE_NIGHT} or {@link #MODE_AUTO}
+     *         When an error occurred -1 is returned.
+     */
+    public int getNightMode() {
+        if (mService != null) {
+            try {
+                return mService.getNightMode();
+            } catch (RemoteException e) {
+                Log.e(TAG, "getNightMode: RemoteException", e);
+            }
+        }
+        return -1;
+    }
+}
diff --git a/core/java/android/backup/AbsoluteFileBackupHelper.java b/core/java/android/backup/AbsoluteFileBackupHelper.java
index 6bf848f..5a8034b 100644
--- a/core/java/android/backup/AbsoluteFileBackupHelper.java
+++ b/core/java/android/backup/AbsoluteFileBackupHelper.java
@@ -21,7 +21,6 @@
 import android.util.Log;
 
 import java.io.File;
-import java.io.FileDescriptor;
 
 /**
  * Like FileBackupHelper, but takes absolute paths for the files instead of
diff --git a/core/java/android/backup/BackupDataInput.java b/core/java/android/backup/BackupDataInput.java
index 295dc66..a08ee75 100644
--- a/core/java/android/backup/BackupDataInput.java
+++ b/core/java/android/backup/BackupDataInput.java
@@ -16,8 +16,6 @@
 
 package android.backup;
 
-import android.content.Context;
-
 import java.io.FileDescriptor;
 import java.io.IOException;
 
diff --git a/core/java/android/backup/BackupDataOutput.java b/core/java/android/backup/BackupDataOutput.java
index 672d01f..34879d8 100644
--- a/core/java/android/backup/BackupDataOutput.java
+++ b/core/java/android/backup/BackupDataOutput.java
@@ -16,8 +16,6 @@
 
 package android.backup;
 
-import android.content.Context;
-
 import java.io.FileDescriptor;
 import java.io.IOException;
 
diff --git a/core/java/android/backup/BackupHelper.java b/core/java/android/backup/BackupHelper.java
index fc48cf2..7eedd01 100644
--- a/core/java/android/backup/BackupHelper.java
+++ b/core/java/android/backup/BackupHelper.java
@@ -18,16 +18,19 @@
 
 import android.os.ParcelFileDescriptor;
 
-import java.io.InputStream;
-
 /**
- * STOPSHIP: document!
+ * A convenient interface to be used with the
+ * {@link android.backup.BackupHelperAgent} to implement backup and restore of
+ * arbitrary data types.
+ * <p>
+ * STOPSHOP: document!
  */
 public interface BackupHelper {
     /**
-     * Based on oldState, determine which of the files from the application's data directory
-     * need to be backed up, write them to the data stream, and fill in newState with the
-     * state as it exists now.
+     * Based on <code>oldState</code>, determine which of the files from the
+     * application's data directory need to be backed up, write them to
+     * <code>data</code>, and fill in <code>newState</code> with the state as it
+     * exists now.
      */
     public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
             ParcelFileDescriptor newState);
diff --git a/core/java/android/backup/BackupHelperAgent.java b/core/java/android/backup/BackupHelperAgent.java
index dc17154f..7fb44f4 100644
--- a/core/java/android/backup/BackupHelperAgent.java
+++ b/core/java/android/backup/BackupHelperAgent.java
@@ -17,24 +17,19 @@
 package android.backup;
 
 import android.app.BackupAgent;
-import android.backup.BackupHelper;
-import android.backup.BackupHelperDispatcher;
-import android.backup.BackupDataInput;
-import android.backup.BackupDataOutput;
 import android.os.ParcelFileDescriptor;
-import android.util.Log;
 
 import java.io.IOException;
 
 /**
- * A convenient BackupAgent wrapper class that automatically manages heterogeneous
- * data sets within the backup data, each identified by a unique key prefix.  An
- * application will typically extend this class in their own backup agent.  Then,
- * within the agent's onBackup() and onRestore() methods, it will call
- * {@link #addHelper(String, BackupHelper)} one or more times to specify the data
- * sets, then invoke super.onBackup() or super.onRestore() to have the BackupHelperAgent
- * implementation process the data.
- *
+ * A convenient BackupAgent wrapper class that automatically manages
+ * heterogeneous data sets within the backup data, each identified by a unique
+ * key prefix. An application will typically extend this class in their own
+ * backup agent. Then, within the agent's onBackup() and onRestore() methods, it
+ * will call {@link #addHelper(String, BackupHelper)} one or more times to
+ * specify the data sets, then invoke super.onBackup() or super.onRestore() to
+ * have the BackupHelperAgent implementation process the data.
+ * <p>
  * STOPSHIP: document!
  */
 public class BackupHelperAgent extends BackupAgent {
diff --git a/core/java/android/backup/BackupHelperDispatcher.java b/core/java/android/backup/BackupHelperDispatcher.java
index bf2c44d..68076db 100644
--- a/core/java/android/backup/BackupHelperDispatcher.java
+++ b/core/java/android/backup/BackupHelperDispatcher.java
@@ -19,12 +19,10 @@
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
 import java.io.FileDescriptor;
-import java.util.TreeMap;
+import java.io.IOException;
 import java.util.Map;
+import java.util.TreeMap;
 
 /** @hide */
 public class BackupHelperDispatcher {
diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java
index 4bf59eb..2dff975 100644
--- a/core/java/android/backup/BackupManager.java
+++ b/core/java/android/backup/BackupManager.java
@@ -23,33 +23,40 @@
 import android.util.Log;
 
 /**
- * BackupManager is the interface to the system's backup service.
- * Applications simply instantiate one, and then use that instance
- * to communicate with the backup infrastructure.
- *
- * <p>When your application has made changes to data it wishes to have
- * backed up, call {@link #dataChanged()} to notify the backup service.
- * The system will then schedule a backup operation to occur in the near
- * future.  Repeated calls to {@link #dataChanged()} have no further effect
- * until the backup operation actually occurs.
- *
- * <p>The backup operation itself begins with the system launching the
- * {@link android.app.BackupAgent} subclass declared in your manifest.  See the
+ * BackupManager is the interface to the system's backup service. Applications
+ * simply instantiate one, and then use that instance to communicate with the
+ * backup infrastructure.
+ * <p>
+ * When an application has made changes to data which should be backed up, a
+ * call to {@link #dataChanged()} will notify the backup service. The system
+ * will then schedule a backup operation to occur in the near future. Repeated
+ * calls to {@link #dataChanged()} have no further effect until the backup
+ * operation actually occurs.
+ * <p>
+ * The backup operation itself begins with the system launching the
+ * {@link android.app.BackupAgent} subclass declared in your manifest. See the
  * documentation for {@link android.app.BackupAgent} for a detailed description
  * of how the backup then proceeds.
- *
- * <p>STOPSHIP more documentation here!  Include the attributes:
- *    android:backupAgent
- *    android:allowBackup
- *    android:restoreNeedsApplication
- *    android:killAfterRestore
+ * <p>
+ * A simple implementation of a BackupAgent useful for backing up Preferences
+ * and files is available by using {@link android.backup.BackupHelperAgent}.
+ * <p>
+ * STOPSHIP: more documentation!
+ * <p>
+ * <b>XML attributes</b>
+ * <p>
+ * See {@link android.R.styleable#AndroidManifestApplication 
+ * AndroidManifest.xml's application attributes}
+ * 
+ * @attr ref android.R.styleable#AndroidManifestApplication_allowBackup
+ * @attr ref android.R.styleable#AndroidManifestApplication_backupAgent
+ * @attr ref
+ *       android.R.styleable#AndroidManifestApplication_restoreNeedsApplication
+ * @attr ref android.R.styleable#AndroidManifestApplication_killAfterRestore
  */
 public class BackupManager {
     private static final String TAG = "BackupManager";
 
-    /** @hide TODO: REMOVE THIS */
-    public static final boolean EVEN_THINK_ABOUT_DOING_RESTORE = true;
-
     private Context mContext;
     private static IBackupManager sService;
 
@@ -78,9 +85,6 @@
      * {@link android.app.BackupAgent} subclass will be scheduled when you call this method.
      */
     public void dataChanged() {
-        if (!EVEN_THINK_ABOUT_DOING_RESTORE) {
-            return;
-        }
         checkServiceBinder();
         if (sService != null) {
             try {
@@ -100,9 +104,6 @@
      * permission if the package named in the argument is not the caller's own.
      */
     public static void dataChanged(String packageName) {
-        if (!EVEN_THINK_ABOUT_DOING_RESTORE) {
-            return;
-        }
         checkServiceBinder();
         if (sService != null) {
             try {
@@ -118,9 +119,6 @@
      * {@link android.backup.RestoreSession} class for documentation on that process.
      */
     public RestoreSession beginRestoreSession() {
-        if (!EVEN_THINK_ABOUT_DOING_RESTORE) {
-            return null;
-        }
         RestoreSession session = null;
         checkServiceBinder();
         if (sService != null) {
diff --git a/core/java/android/backup/FileBackupHelper.java b/core/java/android/backup/FileBackupHelper.java
index 68b4d42..cc859e2 100644
--- a/core/java/android/backup/FileBackupHelper.java
+++ b/core/java/android/backup/FileBackupHelper.java
@@ -21,10 +21,23 @@
 import android.util.Log;
 
 import java.io.File;
-import java.io.FileDescriptor;
 
 /**
- * STOPSHIP: document!  [manages backup of a set of files; restore is totally opaque]
+ * A helper class which can be used in conjunction with
+ * {@link android.backup.BackupHelperAgent} to manage the backup of a set of
+ * files. Whenever backup is performed, all files changed since the last backup
+ * will be saved in their entirety. During the first time the backup happens,
+ * all the files in the list will be backed up. Note that this should only be
+ * used with small configuration files and not with large binary files.
+ * <p>
+ * Any files not present in the list of files during the restore procedure will
+ * be ignored. If files present in a previous version of an application are
+ * removed in subsequent versions, it is the responsibility of the developer to
+ * design a mechanism to remove those files. Otherwise files no longer needed
+ * will linger and consume space on the device.
+ * <p>
+ * STOPSHIP: document! [manages backup of a set of files; restore is totally
+ * opaque]
  */
 public class FileBackupHelper extends FileBackupHelperBase implements BackupHelper {
     private static final String TAG = "FileBackupHelper";
@@ -50,9 +63,16 @@
     }
 
     /**
-     * Based on oldState, determine which of the files from the application's data directory
-     * need to be backed up, write them to the data stream, and fill in newState with the
-     * state as it exists now.
+     * Based on <code>oldState</code>, determine which of the files from the
+     * application's data directory need to be backed up, write them to the data
+     * stream, and fill in <code>newState</code> with the state as it exists
+     * now. When <code>oldState</code> is <code>null</code>, all the files will
+     * be backed up.
+     * <p>
+     * This should be called from {@link android.backup.BackupHelperAgent}
+     * directly. See
+     * {@link android.app.BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
+     * for a description of parameter meanings.
      */
     public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
             ParcelFileDescriptor newState) {
diff --git a/core/java/android/backup/FileBackupHelperBase.java b/core/java/android/backup/FileBackupHelperBase.java
index a0ff38b..7cb1ccc 100644
--- a/core/java/android/backup/FileBackupHelperBase.java
+++ b/core/java/android/backup/FileBackupHelperBase.java
@@ -22,10 +22,12 @@
 
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileOutputStream;
 
+/**
+ * Base class for the {@link android.backup.FileBackupHelper} implementation.
+ */
 class FileBackupHelperBase {
-    private static final String TAG = "RestoreHelperBase";
+    private static final String TAG = "FileBackupHelperBase";
 
     int mPtr;
     Context mContext;
@@ -45,8 +47,8 @@
     }
 
     /**
-     * Check the parameters so the native code doens't have to throw all the exceptions
-     * since it's easier to do that from java.
+     * Check the parameters so the native code doesn't have to throw all the exceptions
+     * since it's easier to do that from Java.
      */
     static void performBackup_checked(ParcelFileDescriptor oldState, BackupDataOutput data,
             ParcelFileDescriptor newState, String[] files, String[] keys) {
diff --git a/core/java/android/backup/SharedPreferencesBackupHelper.java b/core/java/android/backup/SharedPreferencesBackupHelper.java
index f9c97a3..7ba80db 100644
--- a/core/java/android/backup/SharedPreferencesBackupHelper.java
+++ b/core/java/android/backup/SharedPreferencesBackupHelper.java
@@ -17,13 +17,19 @@
 package android.backup;
 
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
 import java.io.File;
-import java.io.FileDescriptor;
 
 /**
+ * A helper class which can be used in conjunction with
+ * {@link android.backup.BackupHelperAgent} to manage the backup of
+ * {@link android.content.SharedPreferences}. Whenever backup is performed it
+ * will back up all named shared preferences which have changed since the last
+ * backup.
+ * <p>
  * STOPSHIP: document!
  */
 public class SharedPreferencesBackupHelper extends FileBackupHelperBase implements BackupHelper {
diff --git a/core/java/android/backup/package.html b/core/java/android/backup/package.html
new file mode 100644
index 0000000..13c0eb8
--- /dev/null
+++ b/core/java/android/backup/package.html
@@ -0,0 +1,22 @@
+<HTML>
+<BODY>
+<p>Package containing the backup and restore functionality available to
+applications. All backup management is controlled through
+{@link android.backup.BackupManager}.  Individual backup functionality is
+implemented through a subclass {@link android.app.BackupAgent} and a
+simple and easy-to-use implementation is provided in
+{@link android.backup.BackupHelperAgent}.</p>
+
+<p>STOPSHIP: add more documenation and remove Dev Guide link if not written!</p>
+
+<p>The backup APIs let applications:</p>
+<ul>
+  <li>Perform backup of arbitrary data</li>
+  <li>Easily perform backup of Preferences and files</li>
+  <li>Handle restore of data</li>
+</ul>
+
+<p>For a detailed guide to using the backup APIs, see the <a
+href="{@docRoot}guide/topics/backup.html">Backup Dev Guide topic</a>.</p>
+</BODY>
+</HTML>
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index cf9c58f..e77e76f 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -664,6 +664,10 @@
      * determine which channel to connect to.
      * <p>The remote device will be authenticated and communication on this
      * socket will be encrypted.
+     * <p>Hint: If you are connecting to a Bluetooth serial board then try
+     * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB.
+     * However if you are connecting to an Android peer then please generate
+     * your own unique UUID.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
      *
      * @param uuid service record uuid to lookup RFCOMM channel
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index c4ba05d..7ca0f01 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -126,7 +126,7 @@
     }
     
     /**
-     * The samee as {@link #flattenToString()}, but abbreviates the class
+     * The same as {@link #flattenToString()}, but abbreviates the class
      * name if it is a suffix of the package.  The result can still be used
      * with {@link #unflattenFromString(String)}.
      * 
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 45f361e..29f388a 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -167,9 +167,9 @@
     /** @hide */
     public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
 
-    // Always log queries which take 100ms+; shorter queries are
+    // Always log queries which take 500ms+; shorter queries are
     // sampled accordingly.
-    private static final int SLOW_THRESHOLD_MILLIS = 100;
+    private static final int SLOW_THRESHOLD_MILLIS = 500;
     private final Random mRandom = new Random();  // guarded by itself
 
     public ContentResolver(Context context) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 672e5f7..897d702 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1204,6 +1204,8 @@
      * <dt> {@link #INPUT_METHOD_SERVICE} ("input_method")
      * <dd> An {@link android.view.inputmethod.InputMethodManager InputMethodManager}
      * for management of input methods.
+     * <dt> {@link #UI_MODE_SERVICE} ("uimode")
+     * <dd> An {@link android.app.UiModeManager} for controlling UI modes.
      * </dl>
      * 
      * <p>Note:  System services obtained via this API may be closely associated with
@@ -1249,6 +1251,8 @@
      * @see android.telephony.TelephonyManager
      * @see #INPUT_METHOD_SERVICE
      * @see android.view.inputmethod.InputMethodManager
+     * @see #UI_MODE_SERVICE
+     * @see android.app.UiModeManager
      */
     public abstract Object getSystemService(String name);
 
@@ -1511,6 +1515,14 @@
     public static final String DEVICE_POLICY_SERVICE = "device_policy";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.app.UiModeManager} for controlling UI modes.
+     *
+     * @see #getSystemService
+     */
+    public static final String UI_MODE_SERVICE = "uimode";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 1b0437c..607605d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1316,6 +1316,21 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
     /**
+     * @hide
+     * Broadcast Action: Ask system services if there is any reason to
+     * restart the given package.  The data contains the name of the
+     * package.
+     * <ul>
+     * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
+     * <li> {@link #EXTRA_PACKAGES} String array of all packages to check.
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
+    /**
      * Broadcast Action: The user has restarted a package, and all of its
      * processes have been killed.  All runtime state
      * associated with it (processes, alarms, notifications, etc) should
@@ -2098,6 +2113,7 @@
      * number to call in a {@link android.content.Intent#ACTION_CALL}.
      */
     public static final String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
+    
     /**
      * Used as an int extra field in {@link android.content.Intent#ACTION_UID_REMOVED}
      * intents to supply the uid the package had been assigned.  Also an optional
@@ -2108,6 +2124,11 @@
     public static final String EXTRA_UID = "android.intent.extra.UID";
 
     /**
+     * @hide String array of package names.
+     */
+    public static final String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
+
+    /**
      * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED}
      * intents to indicate whether this represents a full uninstall (removing
      * both the code and its data) or a partial uninstall (leaving its data,
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 2e405c1..8773f59 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -248,6 +248,19 @@
     public static final int FLAG_NATIVE_DEBUGGABLE = 1<<21;
 
     /**
+     * Value for {@link #flags}: Set to true if the application's backup
+     * agent claims to be able to handle restore data even "from the future,"
+     * i.e. from versions of the application with a versionCode greater than
+     * the one currently installed on the device.
+     *
+     * <p>If android:allowBackup is set to false or no android:backupAgent
+     * is specified, this flag will be ignored.
+     *
+     * {@hide}
+     */
+    public static final int FLAG_RESTORE_ANY_VERSION = 1<<22;
+
+    /**
      * Flags associated with the application.  Any combination of
      * {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE},
      * {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index f793a00..399a87d 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -309,7 +309,7 @@
      * MountService uses this to call into the package manager to update
      * status of sdcard.
      */
-    void updateExternalMediaStatus(boolean mounted);
+    boolean updateExternalMediaStatus(boolean mounted);
 
     String nextPackageToClean(String lastPackage);
 
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 5823560..98aacaa 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1369,8 +1369,8 @@
         if (allowBackup) {
             ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
 
-            // backupAgent, killAfterRestore, and restoreNeedsApplication are only relevant
-            // if backup is possible for the given application.
+            // backupAgent, killAfterRestore, restoreNeedsApplication, and restoreAnyVersion
+            // are only relevant if backup is possible for the given application.
             String backupAgent = sa.getNonResourceString(
                     com.android.internal.R.styleable.AndroidManifestApplication_backupAgent);
             if (backupAgent != null) {
@@ -1390,6 +1390,11 @@
                         false)) {
                     ai.flags |= ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION;
                 }
+                if (sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestApplication_restoreAnyVersion,
+                        false)) {
+                    ai.flags |= ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
+                }
             }
         }
         
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 7f9a5c6..1070f08 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -24,6 +24,7 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.HashMap;
 
 /**
  * Provides access to an application's raw asset files; see {@link Resources}
@@ -59,6 +60,8 @@
     private static final String TAG = "AssetManager";
     private static final boolean localLOGV = Config.LOGV || false;
     
+    private static final boolean DEBUG_REFS = false;
+    
     private static final Object sSync = new Object();
     private static AssetManager sSystem = null;
 
@@ -72,6 +75,7 @@
     
     private int mNumRefs = 1;
     private boolean mOpen = true;
+    private HashMap<Integer, RuntimeException> mRefStacks; 
  
     /**
      * Create a new AssetManager containing only the basic system assets.
@@ -82,6 +86,10 @@
      */
     public AssetManager() {
         synchronized (this) {
+            if (DEBUG_REFS) {
+                mNumRefs = 0;
+                incRefsLocked(this.hashCode());
+            }
             init();
             if (localLOGV) Log.v(TAG, "New asset manager: " + this);
             ensureSystemAssets();
@@ -99,6 +107,12 @@
     }
     
     private AssetManager(boolean isSystem) {
+        if (DEBUG_REFS) {
+            synchronized (this) {
+                mNumRefs = 0;
+                incRefsLocked(this.hashCode());
+            }
+        }
         init();
         if (localLOGV) Log.v(TAG, "New asset manager: " + this);
     }
@@ -122,7 +136,7 @@
             //                   + ", released=" + mReleased);
             if (mOpen) {
                 mOpen = false;
-                decRefsLocked();
+                decRefsLocked(this.hashCode());
             }
         }
     }
@@ -298,8 +312,9 @@
             }
             int asset = openAsset(fileName, accessMode);
             if (asset != 0) {
-                mNumRefs++;
-                return new AssetInputStream(asset);
+                AssetInputStream res = new AssetInputStream(asset);
+                incRefsLocked(res.hashCode());
+                return res;
             }
         }
         throw new FileNotFoundException("Asset file: " + fileName);
@@ -389,8 +404,9 @@
             }
             int asset = openNonAssetNative(cookie, fileName, accessMode);
             if (asset != 0) {
-                mNumRefs++;
-                return new AssetInputStream(asset);
+                AssetInputStream res = new AssetInputStream(asset);
+                incRefsLocked(res.hashCode());
+                return res;
             }
         }
         throw new FileNotFoundException("Asset absolute file: " + fileName);
@@ -468,16 +484,17 @@
             }
             int xmlBlock = openXmlAssetNative(cookie, fileName);
             if (xmlBlock != 0) {
-                mNumRefs++;
-                return new XmlBlock(this, xmlBlock);
+                XmlBlock res = new XmlBlock(this, xmlBlock);
+                incRefsLocked(res.hashCode());
+                return res;
             }
         }
         throw new FileNotFoundException("Asset XML file: " + fileName);
     }
 
-    /*package*/ void xmlBlockGone() {
+    /*package*/ void xmlBlockGone(int id) {
         synchronized (this) {
-            decRefsLocked();
+            decRefsLocked(id);
         }
     }
 
@@ -486,20 +503,34 @@
             if (!mOpen) {
                 throw new RuntimeException("Assetmanager has been closed");
             }
-            mNumRefs++;
-            return newTheme();
+            int res = newTheme();
+            incRefsLocked(res);
+            return res;
         }
     }
 
     /*package*/ final void releaseTheme(int theme) {
         synchronized (this) {
             deleteTheme(theme);
-            decRefsLocked();
+            decRefsLocked(theme);
         }
     }
 
     protected void finalize() throws Throwable {
-        destroy();
+        try {
+            if (DEBUG_REFS && mNumRefs != 0) {
+                Log.w(TAG, "AssetManager " + this
+                        + " finalized with non-zero refs: " + mNumRefs);
+                if (mRefStacks != null) {
+                    for (RuntimeException e : mRefStacks.values()) {
+                        Log.w(TAG, "Reference from here", e);
+                    }
+                }
+            }
+            destroy();
+        } finally {
+            super.finalize();
+        }
     }
     
     public final class AssetInputStream extends InputStream {
@@ -526,7 +557,7 @@
                 if (mAsset != 0) {
                     destroyAsset(mAsset);
                     mAsset = 0;
-                    decRefsLocked();
+                    decRefsLocked(hashCode());
                 }
             }
         }
@@ -710,7 +741,22 @@
     private native final void init();
     private native final void destroy();
 
-    private final void decRefsLocked() {
+    private final void incRefsLocked(int id) {
+        if (DEBUG_REFS) {
+            if (mRefStacks == null) {
+                mRefStacks = new HashMap<Integer, RuntimeException>();
+                RuntimeException ex = new RuntimeException();
+                ex.fillInStackTrace();
+                mRefStacks.put(this.hashCode(), ex);
+            }
+        }
+        mNumRefs++;
+    }
+    
+    private final void decRefsLocked(int id) {
+        if (DEBUG_REFS && mRefStacks != null) {
+            mRefStacks.remove(id);
+        }
         mNumRefs--;
         //System.out.println("Dec streams: mNumRefs=" + mNumRefs
         //                   + " mReleased=" + mReleased);
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
index f800232..3c2c30a 100644
--- a/core/java/android/content/res/XmlBlock.java
+++ b/core/java/android/content/res/XmlBlock.java
@@ -59,7 +59,7 @@
         if (mOpenCount == 0) {
             nativeDestroy(mNative);
             if (mAssets != null) {
-                mAssets.xmlBlockGone();
+                mAssets.xmlBlockGone(hashCode());
             }
         }
     }
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index eb85822..a7a1d9a 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -95,12 +95,16 @@
         }
     }
 
-    /* package */ synchronized boolean isInUse() {
-        return mInUse;
-    }
-
-    /* package */ synchronized void acquire() {
+    /**
+     * returns true if acquire() succeeds. false otherwise.
+     */
+    /* package */ synchronized boolean acquire() {
+        if (mInUse) {
+            // someone already has acquired it.
+            return false;
+        }
         mInUse = true;
+        return true;
     }
 
     /* package */ synchronized void release() {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 8fd8e28..c13dd23 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -208,11 +208,11 @@
 
     // Things related to query logging/sampling for debugging
     // slow/frequent queries during development.  Always log queries
-    // which take 100ms+; shorter queries are sampled accordingly.
+    // which take 500ms+; shorter queries are sampled accordingly.
     // Commit statements, which are typically slow, are logged
     // together with the most recently executed SQL statement, for
     // disambiguation.
-    private static final int QUERY_LOG_TIME_IN_MILLIS = 100;
+    private static final int QUERY_LOG_TIME_IN_MILLIS = 500;
     private static final int QUERY_LOG_SQL_LENGTH = 64;
     private static final String COMMIT_SQL = "COMMIT;";
     private final Random mRandom = new Random();
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 2bb2f5d..7a29cb4 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -57,20 +57,19 @@
             mCompiledSql = new SQLiteCompiledSql(db, sql);
 
             // add it to the cache of compiled-sqls
-            db.addToCompiledQueries(sql, mCompiledSql);
+            // but before adding it and thus making it available for anyone else to use it,
+            // make sure it is acquired by me.
             mCompiledSql.acquire();
+            db.addToCompiledQueries(sql, mCompiledSql);
         } else {
             // it is already in compiled-sql cache.
-            if (mCompiledSql.isInUse()) {
-                // but the CompiledSql in cache is in use by some other SQLiteProgram object.
+            // try to acquire the object.
+            if (!mCompiledSql.acquire()) {
+                // the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object.
                 // we can't have two different SQLiteProgam objects can't share the same
                 // CompiledSql object. create a new one.
                 // finalize it when I am done with it in "this" object.
                 mCompiledSql = new SQLiteCompiledSql(db, sql);
-            } else {
-                // the CompiledSql in cache is NOT in use by any other SQLiteProgram object.
-                // it is safe to give it to this SQLIteProgram Object.
-                mCompiledSql.acquire();
             }
         }
         nStatement = mCompiledSql.nStatement;
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
index 9aa23fe..915c5d7 100644
--- a/core/java/android/net/InterfaceConfiguration.java
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -45,10 +45,10 @@
     }
 
     private static void putAddress(StringBuffer buf, int addr) {
-        buf.append(addr  & 0xff).append('.').
-            append((addr >>>= 8) & 0xff).append('.').
-            append((addr >>>= 8) & 0xff).append('.').
-            append((addr >>>= 8) & 0xff);
+        buf.append((addr >> 24) & 0xff).append('.').
+            append((addr >> 16) & 0xff).append('.').
+            append((addr >> 8) & 0xff).append('.').
+            append(addr & 0xff);
     }
 
     /** Implement the Parcelable interface {@hide} */
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b706c5c..56a05ee 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -750,11 +750,8 @@
      * Checkin server version of dump to produce more compact, computer-readable log.
      * 
      * NOTE: all times are expressed in 'ms'.
-     * @param fd
-     * @param pw
-     * @param which
      */
-    private final void dumpCheckinLocked(PrintWriter pw, int which) {
+    public final void dumpCheckinLocked(PrintWriter pw, int which, int reqUid) {
         final long rawUptime = SystemClock.uptimeMillis() * 1000;
         final long rawRealtime = SystemClock.elapsedRealtime() * 1000;
         final long batteryUptime = getBatteryUptime(rawUptime);
@@ -856,19 +853,24 @@
                     getDischargeCurrentLevel());
         }
         
-        Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
-        if (kernelWakelocks.size() > 0) {
-            for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
-                sb.setLength(0);
-                printWakeLockCheckin(sb, ent.getValue(), batteryRealtime, null, which, "");
-
-                dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(), 
-                        sb.toString());
+        if (reqUid < 0) {
+            Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
+            if (kernelWakelocks.size() > 0) {
+                for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
+                    sb.setLength(0);
+                    printWakeLockCheckin(sb, ent.getValue(), batteryRealtime, null, which, "");
+    
+                    dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(), 
+                            sb.toString());
+                }
             }
         }
         
         for (int iu = 0; iu < NU; iu++) {
             final int uid = uidStats.keyAt(iu);
+            if (reqUid >= 0 && uid != reqUid) {
+                continue;
+            }
             Uid u = uidStats.valueAt(iu);
             // Dump Network stats per uid, if any
             long rx = u.getTcpBytesReceived(which);
@@ -987,7 +989,7 @@
     }
 
     @SuppressWarnings("unused")
-    private final void dumpLocked(PrintWriter pw, String prefix, int which) {
+    public final void dumpLocked(PrintWriter pw, String prefix, int which, int reqUid) {
         final long rawUptime = SystemClock.uptimeMillis() * 1000;
         final long rawRealtime = SystemClock.elapsedRealtime() * 1000;
         final long batteryUptime = getBatteryUptime(rawUptime);
@@ -1063,23 +1065,25 @@
         long fullWakeLockTimeTotalMicros = 0;
         long partialWakeLockTimeTotalMicros = 0;
         
-        Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
-        if (kernelWakelocks.size() > 0) {
-            for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
-                
-                String linePrefix = ": ";
-                sb.setLength(0);
-                sb.append(prefix);
-                sb.append("  Kernel Wake lock ");
-                sb.append(ent.getKey());
-                linePrefix = printWakeLock(sb, ent.getValue(), batteryRealtime, null, which, 
-                        linePrefix);
-                if (!linePrefix.equals(": ")) {
-                    sb.append(" realtime");
-                } else {
-                    sb.append(": (nothing executed)");
+        if (reqUid < 0) {
+            Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
+            if (kernelWakelocks.size() > 0) {
+                for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
+                    
+                    String linePrefix = ": ";
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("  Kernel Wake lock ");
+                    sb.append(ent.getKey());
+                    linePrefix = printWakeLock(sb, ent.getValue(), batteryRealtime, null, which, 
+                            linePrefix);
+                    if (!linePrefix.equals(": ")) {
+                        sb.append(" realtime");
+                    } else {
+                        sb.append(": (nothing executed)");
+                    }
+                    pw.println(sb.toString());
                 }
-                pw.println(sb.toString());
             }
         }
     
@@ -1212,7 +1216,12 @@
 
         for (int iu=0; iu<NU; iu++) {
             final int uid = uidStats.keyAt(iu);
+            if (reqUid >= 0 && uid != reqUid) {
+                continue;
+            }
+            
             Uid u = uidStats.valueAt(iu);
+            
             pw.println(prefix + "  #" + uid + ":");
             boolean uidActivity = false;
             
@@ -1421,16 +1430,16 @@
         pw.println("Total Statistics (Current and Historic):");
         pw.println("  System starts: " + getStartCount()
                 + ", currently on battery: " + getIsOnBattery());
-        dumpLocked(pw, "", STATS_TOTAL);
+        dumpLocked(pw, "", STATS_TOTAL, -1);
         pw.println("");
         pw.println("Last Run Statistics (Previous run of system):");
-        dumpLocked(pw, "", STATS_LAST);
+        dumpLocked(pw, "", STATS_LAST, -1);
         pw.println("");
         pw.println("Current Battery Statistics (Currently running system):");
-        dumpLocked(pw, "", STATS_CURRENT);
+        dumpLocked(pw, "", STATS_CURRENT, -1);
         pw.println("");
         pw.println("Unplugged Statistics (Since last unplugged from power):");
-        dumpLocked(pw, "", STATS_UNPLUGGED);
+        dumpLocked(pw, "", STATS_UNPLUGGED, -1);
     }
     
     @SuppressWarnings("unused")
@@ -1445,13 +1454,13 @@
         }
         
         if (isUnpluggedOnly) {
-            dumpCheckinLocked(pw, STATS_UNPLUGGED);
+            dumpCheckinLocked(pw, STATS_UNPLUGGED, -1);
         }
         else {
-            dumpCheckinLocked(pw, STATS_TOTAL);
-            dumpCheckinLocked(pw, STATS_LAST);
-            dumpCheckinLocked(pw, STATS_UNPLUGGED);
-            dumpCheckinLocked(pw, STATS_CURRENT);
+            dumpCheckinLocked(pw, STATS_TOTAL, -1);
+            dumpCheckinLocked(pw, STATS_LAST, -1);
+            dumpCheckinLocked(pw, STATS_UNPLUGGED, -1);
+            dumpCheckinLocked(pw, STATS_CURRENT, -1);
         }
     }
     
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 9ee251e..a4c595d 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -541,6 +541,14 @@
     public static int getGlobalFreedSize() {
         return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_FREED_BYTES);
     }
+    public static int getGlobalClassInitCount() {
+        /* number of classes that have been successfully initialized */
+        return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_COUNT);
+    }
+    public static int getGlobalClassInitTime() {
+        /* cumulative elapsed time for class initialization, in usec */
+        return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_TIME);
+    }
     public static int getGlobalExternalAllocCount() {
         return VMDebug.getAllocCount(VMDebug.KIND_GLOBAL_EXT_ALLOCATED_OBJECTS);
     }
@@ -584,6 +592,12 @@
     public static void resetGlobalFreedSize() {
         VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_FREED_BYTES);
     }
+    public static void resetGlobalClassInitCount() {
+        VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_COUNT);
+    }
+    public static void resetGlobalClassInitTime() {
+        VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_CLASS_INIT_TIME);
+    }
     public static void resetGlobalExternalAllocCount() {
         VMDebug.resetAllocCount(VMDebug.KIND_GLOBAL_EXT_ALLOCATED_OBJECTS);
     }
diff --git a/core/java/android/pim/RecurrenceSet.java b/core/java/android/pim/RecurrenceSet.java
index 5d09fb5..635323e 100644
--- a/core/java/android/pim/RecurrenceSet.java
+++ b/core/java/android/pim/RecurrenceSet.java
@@ -18,7 +18,6 @@
 
 import android.content.ContentValues;
 import android.database.Cursor;
-import android.os.Bundle;
 import android.provider.Calendar;
 import android.text.TextUtils;
 import android.text.format.Time;
@@ -26,6 +25,7 @@
 import android.util.Log;
 
 import java.util.List;
+import java.util.regex.Pattern;
 
 /**
  * Basic information about a recurrence, following RFC 2445 Section 4.8.5.
@@ -36,6 +36,7 @@
     private final static String TAG = "CalendarProvider";
 
     private final static String RULE_SEPARATOR = "\n";
+    private final static String FOLDING_SEPARATOR = "\n ";
 
     // TODO: make these final?
     public EventRecurrence[] rrules = null;
@@ -309,7 +310,8 @@
         String rdateStr = values.getAsString(Calendar.Events.RDATE);
         String exruleStr = values.getAsString(Calendar.Events.EXRULE);
         String exdateStr = values.getAsString(Calendar.Events.EXDATE);
-        boolean allDay = values.getAsInteger(Calendar.Events.ALL_DAY) == 1;
+        Integer allDayInteger = values.getAsInteger(Calendar.Events.ALL_DAY);
+        boolean allDay = (null != allDayInteger) ? (allDayInteger == 1) : false;
 
         if ((dtstart == -1) ||
             (TextUtils.isEmpty(duration))||
@@ -361,7 +363,7 @@
         if (TextUtils.isEmpty(ruleStr)) {
             return;
         }
-        String[] rrules = ruleStr.split(RULE_SEPARATOR);
+        String[] rrules = getRuleStrings(ruleStr);
         for (String rrule : rrules) {
             ICalendar.Property prop = new ICalendar.Property(propertyName);
             prop.setValue(rrule);
@@ -369,6 +371,52 @@
         }
     }
 
+    private static String[] getRuleStrings(String ruleStr) {
+        if (null == ruleStr) {
+            return new String[0];
+        }
+        String unfoldedRuleStr = unfold(ruleStr);
+        String[] split = unfoldedRuleStr.split(RULE_SEPARATOR);
+        int count = split.length;
+        for (int n = 0; n < count; n++) {
+            split[n] = fold(split[n]);
+        }
+        return split;
+    }
+
+
+    private static final Pattern IGNORABLE_ICAL_WHITESPACE_RE =
+            Pattern.compile("(?:\\r\\n?|\\n)[ \t]");
+
+    private static final Pattern FOLD_RE = Pattern.compile(".{75}");
+
+    /**
+    * fold and unfolds ical content lines as per RFC 2445 section 4.1.
+    *
+    * <h3>4.1 Content Lines</h3>
+    *
+    * <p>The iCalendar object is organized into individual lines of text, called
+    * content lines. Content lines are delimited by a line break, which is a CRLF
+    * sequence (US-ASCII decimal 13, followed by US-ASCII decimal 10).
+    *
+    * <p>Lines of text SHOULD NOT be longer than 75 octets, excluding the line
+    * break. Long content lines SHOULD be split into a multiple line
+    * representations using a line "folding" technique. That is, a long line can
+    * be split between any two characters by inserting a CRLF immediately
+    * followed by a single linear white space character (i.e., SPACE, US-ASCII
+    * decimal 32 or HTAB, US-ASCII decimal 9). Any sequence of CRLF followed
+    * immediately by a single linear white space character is ignored (i.e.,
+    * removed) when processing the content type.
+    */
+    public static String fold(String unfoldedIcalContent) {
+        return FOLD_RE.matcher(unfoldedIcalContent).replaceAll("$0\r\n ");
+    }
+
+    public static String unfold(String foldedIcalContent) {
+        return IGNORABLE_ICAL_WHITESPACE_RE.matcher(
+            foldedIcalContent).replaceAll("");
+    }
+
     private static void addPropertyForDateStr(ICalendar.Component component,
                                               String propertyName,
                                               String dateStr) {
diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java
index 2eb25954..194fe33 100644
--- a/core/java/android/pim/vcard/VCardComposer.java
+++ b/core/java/android/pim/vcard/VCardComposer.java
@@ -26,8 +26,6 @@
 import android.net.Uri;
 import android.os.RemoteException;
 import android.pim.vcard.exception.VCardException;
-import android.provider.CallLog;
-import android.provider.CallLog.Calls;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
@@ -44,8 +42,6 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.text.TextUtils;
-import android.text.format.Time;
 import android.util.CharsetUtils;
 import android.util.Log;
 
@@ -60,7 +56,6 @@
 import java.lang.reflect.Method;
 import java.nio.charset.UnsupportedCharsetException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -125,12 +120,6 @@
 
     public static final String VCARD_TYPE_STRING_DOCOMO = "docomo";
 
-    // Property for call log entry
-    private static final String VCARD_PROPERTY_X_TIMESTAMP = "X-IRMC-CALL-DATETIME";
-    private static final String VCARD_PROPERTY_CALLTYPE_INCOMING = "INCOMING";
-    private static final String VCARD_PROPERTY_CALLTYPE_OUTGOING = "OUTGOING";
-    private static final String VCARD_PROPERTY_CALLTYPE_MISSED = "MISSED";
-
     private static final String SHIFT_JIS = "SHIFT_JIS";
     private static final String UTF_8 = "UTF-8";
 
@@ -275,30 +264,14 @@
 
     private final String mCharsetString;
     private boolean mTerminateIsCalled;
-    final private List<OneEntryHandler> mHandlerList;
+    private final List<OneEntryHandler> mHandlerList;
 
     private String mErrorReason = NO_ERROR;
 
-    private boolean mIsCallLogComposer;
-
     private static final String[] sContactsProjection = new String[] {
         Contacts._ID,
     };
 
-    /** The projection to use when querying the call log table */
-    private static final String[] sCallLogProjection = new String[] {
-            Calls.NUMBER, Calls.DATE, Calls.TYPE, Calls.CACHED_NAME, Calls.CACHED_NUMBER_TYPE,
-            Calls.CACHED_NUMBER_LABEL
-    };
-    private static final int NUMBER_COLUMN_INDEX = 0;
-    private static final int DATE_COLUMN_INDEX = 1;
-    private static final int CALL_TYPE_COLUMN_INDEX = 2;
-    private static final int CALLER_NAME_COLUMN_INDEX = 3;
-    private static final int CALLER_NUMBERTYPE_COLUMN_INDEX = 4;
-    private static final int CALLER_NUMBERLABEL_COLUMN_INDEX = 5;
-
-    private static final String FLAG_TIMEZONE_UTC = "Z";
-
     public VCardComposer(Context context) {
         this(context, VCardConfig.VCARD_TYPE_DEFAULT, true);
     }
@@ -377,6 +350,7 @@
         if (contentUri == null) {
             return false;
         }
+
         if (mCareHandlerErrors) {
             List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
                     mHandlerList.size());
@@ -396,10 +370,7 @@
         }
 
         final String[] projection;
-        if (CallLog.Calls.CONTENT_URI.equals(contentUri)) {
-            projection = sCallLogProjection;
-            mIsCallLogComposer = true;
-        } else if (Contacts.CONTENT_URI.equals(contentUri) ||
+        if (Contacts.CONTENT_URI.equals(contentUri) ||
                 CONTACTS_TEST_CONTENT_URI.equals(contentUri)) {
             projection = sContactsProjection;
         } else {
@@ -426,11 +397,7 @@
             return false;
         }
 
-        if (mIsCallLogComposer) {
-            mIdColumn = -1;
-        } else {
-            mIdColumn = mCursor.getColumnIndex(Contacts._ID);
-        }
+        mIdColumn = mCursor.getColumnIndex(Contacts._ID);
 
         return true;
     }
@@ -448,19 +415,14 @@
             mErrorReason = FAILURE_REASON_NOT_INITIALIZED;
             return false;
         }
-        String name = null;
         String vcard;
         try {
-            if (mIsCallLogComposer) {
-                vcard = createOneCallLogEntryInternal();
+            if (mIdColumn >= 0) {
+                vcard = createOneEntryInternal(mCursor.getString(mIdColumn),
+                        getEntityIteratorMethod);
             } else {
-                if (mIdColumn >= 0) {
-                    vcard = createOneEntryInternal(mCursor.getString(mIdColumn),
-                            getEntityIteratorMethod);
-                } else {
-                    Log.e(LOG_TAG, "Incorrect mIdColumn: " + mIdColumn);
-                    return true;
-                }
+                Log.e(LOG_TAG, "Incorrect mIdColumn: " + mIdColumn);
+                return true;
             }
         } catch (VCardException e) {
             Log.e(LOG_TAG, "VCardException has been thrown: " + e.getMessage());
@@ -468,7 +430,7 @@
         } catch (OutOfMemoryError error) {
             // Maybe some data (e.g. photo) is too big to have in memory. But it
             // should be rare.
-            Log.e(LOG_TAG, "OutOfMemoryError occured. Ignore the entry: " + name);
+            Log.e(LOG_TAG, "OutOfMemoryError occured. Ignore the entry.");
             System.gc();
             // TODO: should tell users what happened?
             return true;
@@ -630,99 +592,4 @@
     public String getErrorReason() {
         return mErrorReason;
     }
-
-    /**
-     * This static function is to compose vCard for phone own number
-     */
-    public String composeVCardForPhoneOwnNumber(int phonetype, String phoneName,
-            String phoneNumber, boolean vcardVer21) {
-        final int vcardType = (vcardVer21 ?
-                VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8 :
-                    VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8);
-        final VCardBuilder builder = new VCardBuilder(vcardType);
-        boolean needCharset = false;
-        if (!(VCardUtils.containsOnlyPrintableAscii(phoneName))) {
-            needCharset = true;
-        }
-        builder.appendLine(VCardConstants.PROPERTY_FN, phoneName, needCharset, false);
-        builder.appendLine(VCardConstants.PROPERTY_N, phoneName, needCharset, false);
-
-        if (!TextUtils.isEmpty(phoneNumber)) {
-            String label = Integer.toString(phonetype);
-            builder.appendTelLine(phonetype, label, phoneNumber, false);
-        }
-
-        return builder.toString();
-    }
-
-    /**
-     * Format according to RFC 2445 DATETIME type.
-     * The format is: ("%Y%m%dT%H%M%SZ").
-     */
-    private final String toRfc2455Format(final long millSecs) {
-        Time startDate = new Time();
-        startDate.set(millSecs);
-        String date = startDate.format2445();
-        return date + FLAG_TIMEZONE_UTC;
-    }
-
-    /**
-     * Try to append the property line for a call history time stamp field if possible.
-     * Do nothing if the call log type gotton from the database is invalid.
-     */
-    private void tryAppendCallHistoryTimeStampField(final VCardBuilder builder) {
-        // Extension for call history as defined in
-        // in the Specification for Ic Mobile Communcation - ver 1.1,
-        // Oct 2000. This is used to send the details of the call
-        // history - missed, incoming, outgoing along with date and time
-        // to the requesting device (For example, transferring phone book
-        // when connected over bluetooth)
-        //
-        // e.g. "X-IRMC-CALL-DATETIME;MISSED:20050320T100000Z"
-        final int callLogType = mCursor.getInt(CALL_TYPE_COLUMN_INDEX);
-        final String callLogTypeStr;
-        switch (callLogType) {
-            case Calls.INCOMING_TYPE: {
-                callLogTypeStr = VCARD_PROPERTY_CALLTYPE_INCOMING;
-                break;
-            }
-            case Calls.OUTGOING_TYPE: {
-                callLogTypeStr = VCARD_PROPERTY_CALLTYPE_OUTGOING;
-                break;
-            }
-            case Calls.MISSED_TYPE: {
-                callLogTypeStr = VCARD_PROPERTY_CALLTYPE_MISSED;
-                break;
-            }
-            default: {
-                Log.w(LOG_TAG, "Call log type not correct.");
-                return;
-            }
-        }
-
-        final long dateAsLong = mCursor.getLong(DATE_COLUMN_INDEX);
-        builder.appendLine(VCARD_PROPERTY_X_TIMESTAMP,
-                Arrays.asList(callLogTypeStr), toRfc2455Format(dateAsLong));
-    }
-
-    private String createOneCallLogEntryInternal() {
-        final VCardBuilder builder = new VCardBuilder(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
-        String name = mCursor.getString(CALLER_NAME_COLUMN_INDEX);
-        if (TextUtils.isEmpty(name)) {
-            name = mCursor.getString(NUMBER_COLUMN_INDEX);
-        }
-        final boolean needCharset = !(VCardUtils.containsOnlyPrintableAscii(name));
-        builder.appendLine(VCardConstants.PROPERTY_FN, name, needCharset, false);
-        builder.appendLine(VCardConstants.PROPERTY_N, name, needCharset, false);
-
-        final String number = mCursor.getString(NUMBER_COLUMN_INDEX);
-        final int type = mCursor.getInt(CALLER_NUMBERTYPE_COLUMN_INDEX);
-        String label = mCursor.getString(CALLER_NUMBERLABEL_COLUMN_INDEX);
-        if (TextUtils.isEmpty(label)) {
-            label = Integer.toString(type);
-        }
-        builder.appendTelLine(type, label, number, false);
-        tryAppendCallHistoryTimeStampField(builder);
-        return builder.toString();
-    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 14e27eb..a020da4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -374,6 +374,11 @@
      * In some cases, a matching Activity may not exist, so ensure you
      * safeguard against this.
      * <p>
+     * The account types available to add via the add account button may be restricted by adding an
+     * {@link #EXTRA_AUTHORITIES} extra to this Intent with one or more syncable content provider's
+     * authorities. Only account types which can sync with that content provider will be offered to
+     * the user.
+     * <p>
      * Input: Nothing.
      * <p>
      * Output: Nothing.
@@ -383,6 +388,24 @@
             "android.settings.SYNC_SETTINGS";
 
     /**
+     * Activity Action: Show add account screen for creating a new account.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * The account types available to add may be restricted by adding an {@link #EXTRA_AUTHORITIES}
+     * extra to the Intent with one or more syncable content provider's authorities.  Only account
+     * types which can sync with that content provider will be offered to the user.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_ADD_ACCOUNT =
+            "android.settings.ADD_ACCOUNT_SETTINGS";
+
+    /**
      * Activity Action: Show settings for selecting the network operator.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -468,6 +491,19 @@
 
     // End of Intent actions for Settings
 
+    /**
+     * Activity Extra: Limit available options in launched activity based on the given authority.
+     * <p>
+     * This can be passed as an extra field in an Activity Intent with one or more syncable content
+     * provider's authorities as a String[]. This field is used by some intents to alter the
+     * behavior of the called activity.
+     * <p>
+     * Example: The {@link #ACTION_ADD_ACCOUNT} intent restricts the account types available based
+     * on the authority given.
+     */
+    public static final String EXTRA_AUTHORITIES =
+            "authorities";
+
     private static final String JID_RESOURCE_PREFIX = "android";
 
     public static final String AUTHORITY = "settings";
@@ -3033,11 +3069,11 @@
          * @hide
          */
         public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
-        
+
         /**
          * The {@link ComponentName} string of the service to be used as the voice recognition
          * service.
-         * 
+         *
          * @hide
          */
         public static final String VOICE_RECOGNITION_SERVICE = "voice_recognition_service";
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index fbc4a81..caa3144 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -16,20 +16,14 @@
 
 package android.server.search;
 
-import android.app.ActivityManagerNative;
-import android.app.IActivityWatcher;
+import com.android.internal.content.PackageMonitor;
+
 import android.app.ISearchManager;
-import android.app.ISearchManagerCallback;
 import android.app.SearchManager;
 import android.app.SearchableInfo;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
 import android.util.Log;
 
 import java.util.List;
@@ -42,13 +36,11 @@
 
     // general debugging support
     private static final String TAG = "SearchManagerService";
-    private static final boolean DBG = false;
 
     // Context that the service is running in.
     private final Context mContext;
 
-    // This field is initialized in ensureSearchablesCreated(), and then never modified.
-    // Only accessed by ensureSearchablesCreated() and getSearchables()
+    // This field is initialized lazily in getSearchables(), and then never modified.
     private Searchables mSearchables;
 
     /**
@@ -61,58 +53,28 @@
         mContext = context;
     }
 
-    private synchronized void ensureSearchablesCreated() {
-        if (mSearchables != null) return;  // already created
-
-        mSearchables = new Searchables(mContext);
-        mSearchables.buildSearchableList();
-
-        IntentFilter packageFilter = new IntentFilter();
-        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        packageFilter.addDataScheme("package");
-        mContext.registerReceiver(mPackageChangedReceiver, packageFilter);
-        // Register for events related to sdcard installation.
-        IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-        mContext.registerReceiver(mPackageChangedReceiver, sdFilter);
-    }
-
     private synchronized Searchables getSearchables() {
-        ensureSearchablesCreated();
+        if (mSearchables == null) {
+            mSearchables = new Searchables(mContext);
+            mSearchables.buildSearchableList();
+            new MyPackageMonitor().register(mContext, true);
+        }
         return mSearchables;
     }
 
     /**
      * Refreshes the "searchables" list when packages are added/removed.
      */
-    private BroadcastReceiver mPackageChangedReceiver = new BroadcastReceiver() {
+    class MyPackageMonitor extends PackageMonitor {
         @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-
-            if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
-                    Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
-                    Intent.ACTION_PACKAGE_CHANGED.equals(action) ||
-                    Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) ||
-                    Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
-                if (DBG) Log.d(TAG, "Got " + action);
-                // Update list of searchable activities
-                getSearchables().buildSearchableList();
-                broadcastSearchablesChanged();
-            }
+        public void onSomePackagesChanged() {
+            // Update list of searchable activities
+            getSearchables().buildSearchableList();
+            // Inform all listeners that the list of searchables has been updated.
+            Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
+            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+            mContext.sendBroadcast(intent);
         }
-    };
-
-    /**
-     * Informs all listeners that the list of searchables has been updated.
-     */
-    void broadcastSearchablesChanged() {
-        Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-        mContext.sendBroadcast(intent);
     }
 
     //
@@ -123,24 +85,15 @@
      * Returns the SearchableInfo for a given activity.
      *
      * @param launchActivity The activity from which we're launching this search.
-     * @param globalSearch If false, this will only launch the search that has been specifically
-     * defined by the application (which is usually defined as a local search).  If no default
-     * search is defined in the current application or activity, no search will be launched.
-     * If true, this will always launch a platform-global (e.g. web-based) search instead.
      * @return Returns a SearchableInfo record describing the parameters of the search,
      * or null if no searchable metadata was available.
      */
-    public SearchableInfo getSearchableInfo(final ComponentName launchActivity,
-            final boolean globalSearch) {
-        if (globalSearch) {
-            return getSearchables().getDefaultSearchable();
-        } else {
-            if (launchActivity == null) {
-                Log.e(TAG, "getSearchableInfo(), activity == null");
-                return null;
-            }
-            return getSearchables().getSearchableInfo(launchActivity);
+    public SearchableInfo getSearchableInfo(final ComponentName launchActivity) {
+        if (launchActivity == null) {
+            Log.e(TAG, "getSearchableInfo(), activity == null");
+            return null;
         }
+        return getSearchables().getSearchableInfo(launchActivity);
     }
 
     /**
@@ -151,27 +104,17 @@
     }
 
     /**
-     * Returns a list of the searchable activities that handle web searches.
-     * Can be called from any thread.
+     * Gets the name of the global search activity.
      */
-    public List<SearchableInfo> getSearchablesForWebSearch() {
-        return getSearchables().getSearchablesForWebSearchList();
+    public ComponentName getGlobalSearchActivity() {
+        return getSearchables().getGlobalSearchActivity();
     }
 
     /**
-     * Returns the default searchable activity for web searches.
-     * Can be called from any thread.
+     * Gets the name of the web search activity.
      */
-    public SearchableInfo getDefaultSearchableForWebSearch() {
-        return getSearchables().getDefaultSearchableForWebSearch();
+    public ComponentName getWebSearchActivity() {
+        return getSearchables().getWebSearchActivity();
     }
 
-    /**
-     * Sets the default searchable activity for web searches.
-     * Can be called from any thread.
-     */
-    public void setDefaultWebSearch(final ComponentName component) {
-        getSearchables().setDefaultWebSearch(component);
-        broadcastSearchablesChanged();
-    }
 }
diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java
index cbb63a5..279c17d 100644
--- a/core/java/android/server/search/Searchables.java
+++ b/core/java/android/server/search/Searchables.java
@@ -16,14 +16,12 @@
 
 package android.server.search;
 
-import com.android.internal.app.ResolverActivity;
-
+import android.Manifest;
 import android.app.SearchManager;
 import android.app.SearchableInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -52,9 +50,8 @@
     private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null;
     private ArrayList<SearchableInfo> mSearchablesList = null;
     private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null;
-    private ArrayList<SearchableInfo> mSearchablesForWebSearchList = null;
-    private SearchableInfo mDefaultSearchable = null;
-    private SearchableInfo mDefaultSearchableForWebSearch = null;
+    private ComponentName mGlobalSearchActivity = null;
+    private ComponentName mWebSearchActivity = null;
 
     public static String GOOGLE_SEARCH_COMPONENT_NAME =
             "com.android.googlesearch/.GoogleSearch";
@@ -131,10 +128,9 @@
             // Irrespective of source, if a reference was found, follow it.
             if (refActivityName != null)
             {
-                // An app or activity can declare that we should simply launch
-                // "system default search" if search is invoked.
+                // This value is deprecated, return null
                 if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) {
-                    return getDefaultSearchable();
+                    return null;
                 }
                 String pkg = activity.getPackageName();
                 ComponentName referredActivity;
@@ -164,20 +160,6 @@
     }
 
     /**
-     * Provides the system-default search activity, which you can use
-     * whenever getSearchableInfo() returns null;
-     *
-     * @return Returns the system-default search activity, null if never defined
-     */
-    public synchronized SearchableInfo getDefaultSearchable() {
-        return mDefaultSearchable;
-    }
-
-    public synchronized boolean isDefaultSearchable(SearchableInfo searchable) {
-        return searchable == mDefaultSearchable;
-    }
-
-    /**
      * Builds an entire list (suitable for display) of
      * activities that are searchable, by iterating the entire set of
      * ACTION_SEARCH & ACTION_WEB_SEARCH intents.
@@ -205,8 +187,6 @@
                                 = new ArrayList<SearchableInfo>();
         ArrayList<SearchableInfo> newSearchablesInGlobalSearchList
                                 = new ArrayList<SearchableInfo>();
-        ArrayList<SearchableInfo> newSearchablesForWebSearchList
-                                = new ArrayList<SearchableInfo>();
 
         final PackageManager pm = mContext.getPackageManager();
 
@@ -244,127 +224,71 @@
             }
         }
 
-        if (webSearchInfoList != null) {
-            for (int i = 0; i < webSearchInfoList.size(); ++i) {
-                ActivityInfo ai = webSearchInfoList.get(i).activityInfo;
-                ComponentName component = new ComponentName(ai.packageName, ai.name);
-                SearchableInfo searchable = newSearchablesMap.get(component);
-                if (searchable == null) {
-                    Log.w(LOG_TAG, "did not find component in searchables: " + component);
-                } else {
-                    newSearchablesForWebSearchList.add(searchable);
-                }
-            }
-        }
+        // Find the global search activity
+        ComponentName newGlobalSearchActivity = findGlobalSearchActivity();
 
-        // Find the global search provider
-        Intent globalSearchIntent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
-        ComponentName globalSearchActivity = globalSearchIntent.resolveActivity(pm);
-        SearchableInfo newDefaultSearchable = newSearchablesMap.get(globalSearchActivity);
-
-        if (newDefaultSearchable == null) {
-            Log.w(LOG_TAG, "No searchable info found for new default searchable activity "
-                    + globalSearchActivity);
-        }
-
-        // Find the default web search provider.
-        ComponentName webSearchActivity = getPreferredWebSearchActivity(mContext);
-        SearchableInfo newDefaultSearchableForWebSearch = null;
-        if (webSearchActivity != null) {
-            newDefaultSearchableForWebSearch = newSearchablesMap.get(webSearchActivity);
-        }
-        if (newDefaultSearchableForWebSearch == null) {
-            Log.w(LOG_TAG, "No searchable info found for new default web search activity "
-                    + webSearchActivity);
-        }
+        // Find the web search activity
+        ComponentName newWebSearchActivity = findWebSearchActivity(newGlobalSearchActivity);
 
         // Store a consistent set of new values
         synchronized (this) {
             mSearchablesMap = newSearchablesMap;
             mSearchablesList = newSearchablesList;
             mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList;
-            mSearchablesForWebSearchList = newSearchablesForWebSearchList;
-            mDefaultSearchable = newDefaultSearchable;
-            mDefaultSearchableForWebSearch = newDefaultSearchableForWebSearch;
+            mGlobalSearchActivity = newGlobalSearchActivity;
+            mWebSearchActivity = newWebSearchActivity;
         }
     }
 
     /**
-     * Checks if the given activity component is present in the system and if so makes it the
-     * preferred activity for handling ACTION_WEB_SEARCH.
-     * @param component Name of the component to check and set as preferred.
-     * @param action Intent action for which this activity is to be set as preferred.
-     * @return true if component was detected and set as preferred activity, false if not.
+     * Finds the global search activity.
+     *
+     * This is currently implemented by returning the first activity that handles
+     * the GLOBAL_SEARCH intent and has the GLOBAL_SEARCH permission. If we allow
+     * more than one global search activity to be installed, this code must be changed.
      */
-    private static boolean setPreferredActivity(Context context,
-            ComponentName component, String action) {
-        Log.d(LOG_TAG, "Checking component " + component);
-        PackageManager pm = context.getPackageManager();
-        ActivityInfo ai;
-        try {
-            ai = pm.getActivityInfo(component, 0);
-        } catch (PackageManager.NameNotFoundException e) {
-            return false;
+    private ComponentName findGlobalSearchActivity() {
+        Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
+        PackageManager pm = mContext.getPackageManager();
+        List<ResolveInfo> activities =
+                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+        int count = activities == null ? 0 : activities.size();
+        for (int i = 0; i < count; i++) {
+            ActivityInfo ai = activities.get(i).activityInfo;
+            if (pm.checkPermission(Manifest.permission.GLOBAL_SEARCH,
+                    ai.packageName) == PackageManager.PERMISSION_GRANTED) {
+                return new ComponentName(ai.packageName, ai.name);
+            } else {
+                Log.w(LOG_TAG, "Package " + ai.packageName + " wants to handle GLOBAL_SEARCH, "
+                        + "but does not have the GLOBAL_SEARCH permission.");
+            }
         }
-
-        // The code here to find the value for bestMatch is heavily inspired by the code
-        // in ResolverActivity where the preferred activity is set.
-        Intent intent = new Intent(action);
-        intent.addCategory(Intent.CATEGORY_DEFAULT);
-        List<ResolveInfo> webSearchActivities = pm.queryIntentActivities(intent, 0);
-        ComponentName set[] = new ComponentName[webSearchActivities.size()];
-        int bestMatch = 0;
-        for (int i = 0; i < webSearchActivities.size(); ++i) {
-            ResolveInfo ri = webSearchActivities.get(i);
-            set[i] = new ComponentName(ri.activityInfo.packageName,
-                                       ri.activityInfo.name);
-            if (ri.match > bestMatch) bestMatch = ri.match;
-        }
-
-        Log.d(LOG_TAG, "Setting preferred web search activity to " + component);
-        IntentFilter filter = new IntentFilter(action);
-        filter.addCategory(Intent.CATEGORY_DEFAULT);
-        pm.replacePreferredActivity(filter, bestMatch, set, component);
-        return true;
+        Log.w(LOG_TAG, "No global search activity found");
+        return null;
     }
 
-    private static ComponentName getPreferredWebSearchActivity(Context context) {
-        // Check if we have a preferred web search activity.
-        Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
-        PackageManager pm = context.getPackageManager();
-        ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
-
-        if (ri == null || ri.activityInfo.name.equals(ResolverActivity.class.getName())) {
-            Log.d(LOG_TAG, "No preferred activity set for action web search.");
-
-            // The components in the providers array are checked in the order of declaration so the
-            // first one has the highest priority. If the component exists in the system it is set
-            // as the preferred activity to handle intent action web search.
-            String[] preferredActivities = context.getResources().getStringArray(
-                    com.android.internal.R.array.default_web_search_providers);
-            for (String componentName : preferredActivities) {
-                ComponentName component = ComponentName.unflattenFromString(componentName);
-                if (setPreferredActivity(context, component, Intent.ACTION_WEB_SEARCH)) {
-                    return component;
-                }
-            }
-        } else {
-            // If the current preferred activity is GoogleSearch, and we detect
-            // EnhancedGoogleSearch installed as well, set the latter as preferred since that
-            // is a superset and provides more functionality.
-            ComponentName cn = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
-            if (cn.flattenToShortString().equals(GOOGLE_SEARCH_COMPONENT_NAME)) {
-                ComponentName enhancedGoogleSearch = ComponentName.unflattenFromString(
-                        ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME);
-                if (setPreferredActivity(context, enhancedGoogleSearch,
-                        Intent.ACTION_WEB_SEARCH)) {
-                    return enhancedGoogleSearch;
-                }
-            }
+    /**
+     * Finds the web search activity.
+     *
+     * Only looks in the package of the global search activity.
+     */
+    private ComponentName findWebSearchActivity(ComponentName globalSearchActivity) {
+        if (globalSearchActivity == null) {
+            return null;
         }
-
-        if (ri == null) return null;
-        return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
+        Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+        intent.setPackage(globalSearchActivity.getPackageName());
+        PackageManager pm = mContext.getPackageManager();
+        List<ResolveInfo> activities =
+                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+        int count = activities == null ? 0 : activities.size();
+        for (int i = 0; i < count; i++) {
+            ActivityInfo ai = activities.get(i).activityInfo;
+            // TODO: do some sanity checks here?
+            return new ComponentName(ai.packageName, ai.name);
+        }
+        Log.w(LOG_TAG, "No web search activity found");
+        return null;
     }
 
     /**
@@ -383,24 +307,16 @@
     }
 
     /**
-     * Returns a list of the searchable activities that handle web searches.
+     * Gets the name of the global search activity.
      */
-    public synchronized ArrayList<SearchableInfo> getSearchablesForWebSearchList() {
-        return new ArrayList<SearchableInfo>(mSearchablesForWebSearchList);
+    public synchronized ComponentName getGlobalSearchActivity() {
+        return mGlobalSearchActivity;
     }
 
     /**
-     * Returns the default searchable activity for web searches.
+     * Gets the name of the web search activity.
      */
-    public synchronized SearchableInfo getDefaultSearchableForWebSearch() {
-        return mDefaultSearchableForWebSearch;
-    }
-
-    /**
-     * Sets the default searchable activity for web searches.
-     */
-    public synchronized void setDefaultWebSearch(ComponentName component) {
-        setPreferredActivity(mContext, component, Intent.ACTION_WEB_SEARCH);
-        buildSearchableList();
+    public synchronized ComponentName getWebSearchActivity() {
+        return mWebSearchActivity;
     }
 }
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 1023036..38ac9b7 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1936,6 +1936,11 @@
 
     public static final int DIR_LEFT_TO_RIGHT = 1;
     public static final int DIR_RIGHT_TO_LEFT = -1;
+    
+    /* package */ static final int DIR_REQUEST_LTR = 1;
+    /* package */ static final int DIR_REQUEST_RTL = -1;
+    /* package */ static final int DIR_REQUEST_DEFAULT_LTR = 2;
+    /* package */ static final int DIR_REQUEST_DEFAULT_RTL = -2;
 
     public enum Alignment {
         ALIGN_NORMAL,
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 6c89f92..600ec7e 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -234,215 +234,9 @@
             }
 
             if (!easy) {
-                AndroidCharacter.getDirectionalities(chs, chdirs, end - start);
-
-                /*
-                 * Determine primary paragraph direction
-                 */
-
-                for (int j = start; j < end; j++) {
-                    int d = chdirs[j - start];
-
-                    if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT) {
-                        dir = DIR_LEFT_TO_RIGHT;
-                        break;
-                    }
-                    if (d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
-                        dir = DIR_RIGHT_TO_LEFT;
-                        break;
-                    }
-                }
-
-                /*
-                 * XXX Explicit overrides should go here
-                 */
-
-                /*
-                 * Weak type resolution
-                 */
-
-                final byte SOR = dir == DIR_LEFT_TO_RIGHT ?
-                                    Character.DIRECTIONALITY_LEFT_TO_RIGHT :
-                                    Character.DIRECTIONALITY_RIGHT_TO_LEFT;
-
-                // dump(chdirs, n, "initial");
-
-                // W1 non spacing marks
-                for (int j = 0; j < n; j++) {
-                    if (chdirs[j] == Character.NON_SPACING_MARK) {
-                        if (j == 0)
-                            chdirs[j] = SOR;
-                        else
-                            chdirs[j] = chdirs[j - 1];
-                    }
-                }
-
-                // dump(chdirs, n, "W1");
-
-                // W2 european numbers
-                byte cur = SOR;
-                for (int j = 0; j < n; j++) {
-                    byte d = chdirs[j];
-
-                    if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
-                        d == Character.DIRECTIONALITY_RIGHT_TO_LEFT ||
-                        d == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
-                        cur = d;
-                    else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) {
-                         if (cur ==
-                            Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
-                            chdirs[j] = Character.DIRECTIONALITY_ARABIC_NUMBER;
-                    }
-                }
-
-                // dump(chdirs, n, "W2");
-
-                // W3 arabic letters
-                for (int j = 0; j < n; j++) {
-                    if (chdirs[j] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
-                        chdirs[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
-                }
-
-                // dump(chdirs, n, "W3");
-
-                // W4 single separator between numbers
-                for (int j = 1; j < n - 1; j++) {
-                    byte d = chdirs[j];
-                    byte prev = chdirs[j - 1];
-                    byte next = chdirs[j + 1];
-
-                    if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR) {
-                        if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER &&
-                            next == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
-                            chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
-                    } else if (d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) {
-                        if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER &&
-                            next == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
-                            chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
-                        if (prev == Character.DIRECTIONALITY_ARABIC_NUMBER &&
-                            next == Character.DIRECTIONALITY_ARABIC_NUMBER)
-                            chdirs[j] = Character.DIRECTIONALITY_ARABIC_NUMBER;
-                    }
-                }
-
-                // dump(chdirs, n, "W4");
-
-                // W5 european number terminators
-                boolean adjacent = false;
-                for (int j = 0; j < n; j++) {
-                    byte d = chdirs[j];
-
-                    if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
-                        adjacent = true;
-                    else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR && adjacent)
-                        chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
-                    else
-                        adjacent = false;
-                }
-
-                //dump(chdirs, n, "W5");
-
-                // W5 european number terminators part 2,
-                // W6 separators and terminators
-                adjacent = false;
-                for (int j = n - 1; j >= 0; j--) {
-                    byte d = chdirs[j];
-
-                    if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
-                        adjacent = true;
-                    else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR) {
-                        if (adjacent)
-                            chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
-                        else
-                            chdirs[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS;
-                    }
-                    else {
-                        adjacent = false;
-
-                        if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR ||
-                            d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR ||
-                            d == Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR ||
-                            d == Character.DIRECTIONALITY_SEGMENT_SEPARATOR)
-                            chdirs[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS;
-                    }
-                }
-
-                // dump(chdirs, n, "W6");
-
-                // W7 strong direction of european numbers
-                cur = SOR;
-                for (int j = 0; j < n; j++) {
-                    byte d = chdirs[j];
-
-                    if (d == SOR ||
-                        d == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
-                        d == Character.DIRECTIONALITY_RIGHT_TO_LEFT)
-                        cur = d;
-
-                    if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
-                        chdirs[j] = cur;
-                }
-
-                // dump(chdirs, n, "W7");
-
-                // N1, N2 neutrals
-                cur = SOR;
-                for (int j = 0; j < n; j++) {
-                    byte d = chdirs[j];
-
-                    if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
-                        d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
-                        cur = d;
-                    } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER ||
-                               d == Character.DIRECTIONALITY_ARABIC_NUMBER) {
-                        cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
-                    } else {
-                        byte dd = SOR;
-                        int k;
-
-                        for (k = j + 1; k < n; k++) {
-                            dd = chdirs[k];
-
-                            if (dd == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
-                                dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
-                                break;
-                            }
-                            if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER ||
-                                dd == Character.DIRECTIONALITY_ARABIC_NUMBER) {
-                                dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
-                                break;
-                            }
-                        }
-
-                        for (int y = j; y < k; y++) {
-                            if (dd == cur)
-                                chdirs[y] = cur;
-                            else
-                                chdirs[y] = SOR;
-                        }
-
-                        j = k - 1;
-                    }
-                }
-
-                // dump(chdirs, n, "final");
-
-                // extra: enforce that all tabs and surrogate characters go the
-                // primary direction
-                // TODO: actually do directions right for surrogates
-
-                for (int j = 0; j < n; j++) {
-                    char c = chs[j];
-
-                    if (c == '\t' || (c >= 0xD800 && c <= 0xDFFF)) {
-                        chdirs[j] = SOR;
-                    }
-                }
-
-                // extra: enforce that object replacements go to the
-                // primary direction
-                // and that none of the underlying characters are treated
-                // as viable breakpoints
+                // Ensure that none of the underlying characters are treated
+                // as viable breakpoints, and that the entire run gets the
+                // same bidi direction.
 
                 if (source instanceof Spanned) {
                     Spanned sp = (Spanned) source;
@@ -453,12 +247,14 @@
                         int b = sp.getSpanEnd(spans[y]);
 
                         for (int x = a; x < b; x++) {
-                            chdirs[x - start] = SOR;
                             chs[x - start] = '\uFFFC';
                         }
                     }
                 }
 
+                // XXX put override flags, etc. into chdirs
+                dir = bidi(dir, chs, chdirs, n, false);
+
                 // Do mirroring for right-to-left segments
 
                 for (int i = 0; i < n; i++) {
@@ -810,6 +606,239 @@
         }
     }
 
+    /**
+     * Runs the unicode bidi algorithm on the first n chars in chs, returning
+     * the char dirs in chInfo and the base line direction of the first
+     * paragraph.
+     * 
+     * XXX change result from dirs to levels
+     *  
+     * @param dir the direction flag, either DIR_REQUEST_LTR,
+     * DIR_REQUEST_RTL, DIR_REQUEST_DEFAULT_LTR, or DIR_REQUEST_DEFAULT_RTL.
+     * @param chs the text to examine
+     * @param chInfo on input, if hasInfo is true, override and other flags 
+     * representing out-of-band embedding information. On output, the generated 
+     * dirs of the text.
+     * @param n the length of the text/information in chs and chInfo
+     * @param hasInfo true if chInfo has input information, otherwise the
+     * input data in chInfo is ignored.
+     * @return the resolved direction level of the first paragraph, either
+     * DIR_LEFT_TO_RIGHT or DIR_RIGHT_TO_LEFT.
+     */
+    /* package */ static int bidi(int dir, char[] chs, byte[] chInfo, int n, 
+            boolean hasInfo) {
+        
+        AndroidCharacter.getDirectionalities(chs, chInfo, n);
+
+        /*
+         * Determine primary paragraph direction if not specified
+         */
+        if (dir != DIR_REQUEST_LTR && dir != DIR_REQUEST_RTL) {
+            // set up default
+            dir = dir >= 0 ? DIR_LEFT_TO_RIGHT : DIR_RIGHT_TO_LEFT;
+            for (int j = 0; j < n; j++) {
+                int d = chInfo[j];
+
+                if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT) {
+                    dir = DIR_LEFT_TO_RIGHT;
+                    break;
+                }
+                if (d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
+                    dir = DIR_RIGHT_TO_LEFT;
+                    break;
+                }
+            }
+        }
+
+        final byte SOR = dir == DIR_LEFT_TO_RIGHT ?
+                Character.DIRECTIONALITY_LEFT_TO_RIGHT :
+                Character.DIRECTIONALITY_RIGHT_TO_LEFT;
+
+        /*
+         * XXX Explicit overrides should go here
+         */
+
+        /*
+         * Weak type resolution
+         */
+
+        // dump(chdirs, n, "initial");
+
+        // W1 non spacing marks
+        for (int j = 0; j < n; j++) {
+            if (chInfo[j] == Character.NON_SPACING_MARK) {
+                if (j == 0)
+                    chInfo[j] = SOR;
+                else
+                    chInfo[j] = chInfo[j - 1];
+            }
+        }
+
+        // dump(chdirs, n, "W1");
+
+        // W2 european numbers
+        byte cur = SOR;
+        for (int j = 0; j < n; j++) {
+            byte d = chInfo[j];
+
+            if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
+                d == Character.DIRECTIONALITY_RIGHT_TO_LEFT ||
+                d == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
+                cur = d;
+            else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) {
+                 if (cur ==
+                    Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
+                    chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER;
+            }
+        }
+
+        // dump(chdirs, n, "W2");
+
+        // W3 arabic letters
+        for (int j = 0; j < n; j++) {
+            if (chInfo[j] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
+                chInfo[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
+        }
+
+        // dump(chdirs, n, "W3");
+
+        // W4 single separator between numbers
+        for (int j = 1; j < n - 1; j++) {
+            byte d = chInfo[j];
+            byte prev = chInfo[j - 1];
+            byte next = chInfo[j + 1];
+
+            if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR) {
+                if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER &&
+                    next == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
+                    chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
+            } else if (d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) {
+                if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER &&
+                    next == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
+                    chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
+                if (prev == Character.DIRECTIONALITY_ARABIC_NUMBER &&
+                    next == Character.DIRECTIONALITY_ARABIC_NUMBER)
+                    chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER;
+            }
+        }
+
+        // dump(chdirs, n, "W4");
+
+        // W5 european number terminators
+        boolean adjacent = false;
+        for (int j = 0; j < n; j++) {
+            byte d = chInfo[j];
+
+            if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
+                adjacent = true;
+            else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR && adjacent)
+                chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
+            else
+                adjacent = false;
+        }
+
+        //dump(chdirs, n, "W5");
+
+        // W5 european number terminators part 2,
+        // W6 separators and terminators
+        adjacent = false;
+        for (int j = n - 1; j >= 0; j--) {
+            byte d = chInfo[j];
+
+            if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
+                adjacent = true;
+            else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR) {
+                if (adjacent)
+                    chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
+                else
+                    chInfo[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS;
+            }
+            else {
+                adjacent = false;
+
+                if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR ||
+                    d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR ||
+                    d == Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR ||
+                    d == Character.DIRECTIONALITY_SEGMENT_SEPARATOR)
+                    chInfo[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS;
+            }
+        }
+
+        // dump(chdirs, n, "W6");
+
+        // W7 strong direction of european numbers
+        cur = SOR;
+        for (int j = 0; j < n; j++) {
+            byte d = chInfo[j];
+
+            if (d == SOR ||
+                d == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
+                d == Character.DIRECTIONALITY_RIGHT_TO_LEFT)
+                cur = d;
+
+            if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
+                chInfo[j] = cur;
+        }
+
+        // dump(chdirs, n, "W7");
+
+        // N1, N2 neutrals
+        cur = SOR;
+        for (int j = 0; j < n; j++) {
+            byte d = chInfo[j];
+
+            if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
+                d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
+                cur = d;
+            } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER ||
+                       d == Character.DIRECTIONALITY_ARABIC_NUMBER) {
+                cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
+            } else {
+                byte dd = SOR;
+                int k;
+
+                for (k = j + 1; k < n; k++) {
+                    dd = chInfo[k];
+
+                    if (dd == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
+                        dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
+                        break;
+                    }
+                    if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER ||
+                        dd == Character.DIRECTIONALITY_ARABIC_NUMBER) {
+                        dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
+                        break;
+                    }
+                }
+
+                for (int y = j; y < k; y++) {
+                    if (dd == cur)
+                        chInfo[y] = cur;
+                    else
+                        chInfo[y] = SOR;
+                }
+
+                j = k - 1;
+            }
+        }
+
+        // dump(chdirs, n, "final");
+
+        // extra: enforce that all tabs and surrogate characters go the
+        // primary direction
+        // TODO: actually do directions right for surrogates
+
+        for (int j = 0; j < n; j++) {
+            char c = chs[j];
+
+            if (c == '\t' || (c >= 0xD800 && c <= 0xDFFF)) {
+                chInfo[j] = SOR;
+            }
+        }
+        
+        return dir;
+    }
+
     private static final char FIRST_CJK = '\u2E80';
     /**
      * Returns true if the specified character is one of those specified
@@ -1012,12 +1041,12 @@
         int extra;
 
         if (needMultiply) {
-            // XXX: this looks like it is using the +0.5 and the cast to int
-            // to do rounding, but this I expect this isn't doing the intended
-            // thing when spacingmult < 1.  An intended extra of, say, -1.2
-            // will get 'rounded' to -.7 and then truncated to 0.
-            extra = (int) ((below - above) * (spacingmult - 1)
-                           + spacingadd + 0.5);
+            double ex = (below - above) * (spacingmult - 1) + spacingadd;
+            if (ex >= 0) {
+                extra = (int)(ex + 0.5);
+            } else {
+                extra = -(int)(-ex + 0.5);
+            }
         } else {
             extra = 0;
         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2eb633f..f6f5235 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3650,14 +3650,27 @@
     }
 
     /**
-     * This is called when a container is going to temporarily detach a child
-     * that currently has focus, with
+     * @hide
+     */
+    public void dispatchStartTemporaryDetach() {
+        onStartTemporaryDetach();
+    }
+
+    /**
+     * This is called when a container is going to temporarily detach a child, with
      * {@link ViewGroup#detachViewFromParent(View) ViewGroup.detachViewFromParent}.
      * It will either be followed by {@link #onFinishTemporaryDetach()} or
-     * {@link #onDetachedFromWindow()} when the container is done.  Generally
-     * this is currently only done ListView for a view with focus.
+     * {@link #onDetachedFromWindow()} when the container is done.
      */
     public void onStartTemporaryDetach() {
+        removeUnsetPressCallback();
+    }
+
+    /**
+     * @hide
+     */
+    public void dispatchFinishTemporaryDetach() {
+        onFinishTemporaryDetach();
     }
 
     /**
@@ -4362,6 +4375,16 @@
     }
     
     /**
+     * Remove the prepress detection timer.
+     */
+    private void removeUnsetPressCallback() {
+        if ((mPrivateFlags & PRESSED) != 0 && mUnsetPressedState != null) {
+            setPressed(false);
+            removeCallbacks(mUnsetPressedState);
+        }
+    }
+
+    /**
      * Remove the tap detection timer.
      */
     private void removeTapCallback() {
@@ -5886,6 +5909,7 @@
      * @see #onAttachedToWindow()
      */
     protected void onDetachedFromWindow() {
+        removeUnsetPressCallback();
         removeLongPressCallback();
         destroyDrawingCache();
     }
@@ -8731,7 +8755,7 @@
             boolean clampedX, boolean clampedY) {
         // Intentionally empty.
     }
-    
+
     /**
      * A MeasureSpec encapsulates the layout requirements passed from parent to child.
      * Each MeasureSpec represents a requirement for either the width or the height.
@@ -9182,12 +9206,6 @@
         boolean mRecomputeGlobalAttributes;
 
         /**
-         * Set to true when attributes (like mKeepScreenOn) need to be
-         * recomputed.
-         */
-        boolean mAttributesChanged;
-
-        /**
          * Set during a traveral if any views want to keep the screen on.
          */
         boolean mKeepScreenOn;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0663215..aac1068 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1065,6 +1065,32 @@
         }
         return false;
     }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void dispatchStartTemporaryDetach() {
+        super.dispatchStartTemporaryDetach();
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            children[i].dispatchStartTemporaryDetach();
+        }
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void dispatchFinishTemporaryDetach() {
+        super.dispatchFinishTemporaryDetach();
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            children[i].dispatchFinishTemporaryDetach();
+        }
+    }
 
     /**
      * {@inheritDoc}
diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java
index cdcb662..df7d0c4 100644
--- a/core/java/android/webkit/PluginManager.java
+++ b/core/java/android/webkit/PluginManager.java
@@ -165,6 +165,7 @@
                     continue;
                 }
 
+/*              temporarily disable signatures checking
                 // check to ensure the plugin is properly signed
                 Signature signatures[] = pkgInfo.signatures;
                 if (signatures == null) {
@@ -184,7 +185,7 @@
                         continue;
                     }
                 }
-
+*/
                 // determine the type of plugin from the manifest
                 if (serviceInfo.metaData == null) {
                     Log.e(LOGTAG, "The plugin '" + serviceInfo.name + "' has no type defined");
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index adae0cb..67543aa 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -375,6 +375,7 @@
     private static final int PREVENT_DRAG_NO = 0;
     private static final int PREVENT_DRAG_MAYBE_YES = 1;
     private static final int PREVENT_DRAG_YES = 2;
+    private static final int PREVENT_DRAG_CANCEL = 3;
     private int mPreventDrag = PREVENT_DRAG_NO;
 
     // by default mPreventLongPress is false. If it is true, long press event
@@ -3351,23 +3352,31 @@
         InputMethodManager imm = (InputMethodManager)
                 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
 
+        // bring it back to the default scale so that user can enter text
+        boolean zoom = mActualScale < mDefaultScale;
+        if (zoom) {
+            mInZoomOverview = false;
+            mZoomCenterX = mLastTouchX;
+            mZoomCenterY = mLastTouchY;
+            // do not change text wrap scale so that there is no reflow
+            setNewZoomScale(mDefaultScale, false, false);
+        }
         if (isTextView) {
             rebuildWebTextView();
-            if (!inEditingMode()) return;
-            imm.showSoftInput(mWebTextView, 0);
-            // bring it back to the default scale so that user can enter text
-            if (mActualScale < mDefaultScale) {
-                mInZoomOverview = false;
-                mZoomCenterX = mLastTouchX;
-                mZoomCenterY = mLastTouchY;
-                // do not change text wrap scale so that there is no reflow
-                setNewZoomScale(mDefaultScale, false, false);
-                didUpdateTextViewBounds(true);
+            if (inEditingMode()) {
+                imm.showSoftInput(mWebTextView, 0);
+                if (zoom) {
+                    didUpdateTextViewBounds(true);
+                }
+                return;
             }
         }
-        else { // used by plugins
-            imm.showSoftInput(this, 0);
-        }
+        // Used by plugins.
+        // Also used if the navigation cache is out of date, and
+        // does not recognize that a textfield is in focus.  In that
+        // case, use WebView as the targeted view.
+        // see http://b/issue?id=2457459
+        imm.showSoftInput(this, 0);
     }
 
     // Called by WebKit to instruct the UI to hide the keyboard
@@ -4429,8 +4438,11 @@
         }
 
         // pass the touch events from UI thread to WebCore thread
-        if (mForwardTouchEvents && (action != MotionEvent.ACTION_MOVE
-                || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) {
+        if (mForwardTouchEvents
+                && (action != MotionEvent.ACTION_MOVE || eventTime
+                        - mLastSentTouchTime > mCurrentTouchInterval)
+                && (action == MotionEvent.ACTION_DOWN
+                        || mPreventDrag != PREVENT_DRAG_CANCEL)) {
             WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
             ted.mAction = action;
             ted.mX = viewToContentX((int) x + mScrollX);
@@ -5711,12 +5723,17 @@
                     break;
                 }
                 case SWITCH_TO_SHORTPRESS: {
-                    // if mPreventDrag is not confirmed, treat it as no so that
-                    // it won't block panning the page.
+                    // if mPreventDrag is not confirmed, cancel it so that it
+                    // won't block panning the page.
                     if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
-                        mPreventDrag = PREVENT_DRAG_NO;
+                        mPreventDrag = PREVENT_DRAG_CANCEL;
                         mPreventLongPress = false;
                         mPreventDoubleTap = false;
+                        // remove the pending TOUCH_EVENT and send a cancel
+                        mWebViewCore.removeMessages(EventHub.TOUCH_EVENT);
+                        WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
+                        ted.mAction = MotionEvent.ACTION_CANCEL;
+                        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                     }
                     if (mTouchMode == TOUCH_INIT_MODE) {
                         mTouchMode = mFullScreenHolder == null
@@ -5743,7 +5760,7 @@
                         // don't set it.
                         ted.mMetaState = 0;
                         mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
-                    } else if (mPreventDrag == PREVENT_DRAG_NO) {
+                    } else if (mPreventDrag != PREVENT_DRAG_YES) {
                         mTouchMode = TOUCH_DONE_MODE;
                         if (mFullScreenHolder == null) {
                             performLongClick();
@@ -5754,13 +5771,18 @@
                 }
                 case RELEASE_SINGLE_TAP: {
                     if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
-                        // if mPreventDrag is not confirmed, treat it as
-                        // no so that it won't block tap.
-                        mPreventDrag = PREVENT_DRAG_NO;
+                        // if mPreventDrag is not confirmed, cancel it so that
+                        // it won't block panning the page.
+                        mPreventDrag = PREVENT_DRAG_CANCEL;
                         mPreventLongPress = false;
                         mPreventDoubleTap = false;
+                        // remove the pending TOUCH_EVENT and send a cancel
+                        mWebViewCore.removeMessages(EventHub.TOUCH_EVENT);
+                        WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
+                        ted.mAction = MotionEvent.ACTION_CANCEL;
+                        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                     }
-                    if (mPreventDrag == PREVENT_DRAG_NO) {
+                    if (mPreventDrag != PREVENT_DRAG_YES) {
                         mTouchMode = TOUCH_DONE_MODE;
                         doShortPress();
                     }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index a79bbee..9ddfeff 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1314,7 +1314,8 @@
                             position, -1);
                 }
             } else {
-                isScrap[0] = true;                
+                isScrap[0] = true;
+                child.dispatchFinishTemporaryDetach();
             }
         } else {
             child = mAdapter.getView(position, null, this);
@@ -1960,7 +1961,6 @@
             if (getHeight() > 0 && getChildCount() > 0) {
                 // We do not lose focus initiating a touch (since AbsListView is focusable in
                 // touch mode). Force an initial layout to get rid of the selection.
-                mLayoutMode = LAYOUT_NORMAL;
                 layoutChildren();
             }
         } else {
@@ -3118,7 +3118,9 @@
 
     void hideSelector() {
         if (mSelectedPosition != INVALID_POSITION) {
-            mResurrectToPosition = mSelectedPosition;
+            if (mLayoutMode != LAYOUT_SPECIFIC) {
+                mResurrectToPosition = mSelectedPosition;
+            }
             if (mNextSelectedPosition >= 0 && mNextSelectedPosition != mSelectedPosition) {
                 mResurrectToPosition = mNextSelectedPosition;
             }
@@ -4144,8 +4146,10 @@
             }
 
             if (mViewTypeCount == 1) {
+                scrap.dispatchStartTemporaryDetach();
                 mCurrentScrap.add(scrap);
             } else {
+                scrap.dispatchStartTemporaryDetach();
                 mScrapViews[viewType].add(scrap);
             }
 
@@ -4164,7 +4168,7 @@
 
             ArrayList<View> scrapViews = mCurrentScrap;
             final int count = activeViews.length;
-            for (int i = 0; i < count; ++i) {
+            for (int i = count - 1; i >= 0; i--) {
                 final View victim = activeViews[i];
                 if (victim != null) {
                     int whichScrap = ((AbsListView.LayoutParams) victim.getLayoutParams()).viewType;
@@ -4180,6 +4184,7 @@
                     if (multipleScraps) {
                         scrapViews = mScrapViews[whichScrap];
                     }
+                    victim.dispatchStartTemporaryDetach();
                     scrapViews.add(victim);
 
                     if (hasListener) {
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index c939e3f..2b3b98d 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -28,8 +28,6 @@
 import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.animation.Interpolator;
-
 
 /**
  * An abstract base class for spinner widgets. SDK users will probably not
@@ -38,24 +36,21 @@
  * @attr ref android.R.styleable#AbsSpinner_entries
  */
 public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
-
     SpinnerAdapter mAdapter;
 
     int mHeightMeasureSpec;
     int mWidthMeasureSpec;
     boolean mBlockLayoutRequests;
+
     int mSelectionLeftPadding = 0;
     int mSelectionTopPadding = 0;
     int mSelectionRightPadding = 0;
     int mSelectionBottomPadding = 0;
-    Rect mSpinnerPadding = new Rect();
-    View mSelectedView = null;
-    Interpolator mInterpolator;
+    final Rect mSpinnerPadding = new Rect();
 
-    RecycleBin mRecycler = new RecycleBin();
+    final RecycleBin mRecycler = new RecycleBin();
     private DataSetObserver mDataSetObserver;
 
-
     /** Temporary frame to hold a child View's frame rectangle */
     private Rect mTouchFrame;
 
@@ -95,7 +90,6 @@
         setWillNotDraw(false);
     }
 
-
     /**
      * The Adapter is used to provide the data which backs this Spinner.
      * It also provides methods to transform spinner items based on their position
@@ -190,7 +184,7 @@
         boolean needsMeasuring = true;
         
         int selectedPosition = getSelectedItemPosition();
-        if (selectedPosition >= 0 && mAdapter != null) {
+        if (selectedPosition >= 0 && mAdapter != null && selectedPosition < mAdapter.getCount()) {
             // Try looking in the recycler. (Maybe we were measured once already)
             View view = mRecycler.get(selectedPosition);
             if (view == null) {
@@ -237,7 +231,6 @@
         mWidthMeasureSpec = widthMeasureSpec;
     }
 
-    
     int getChildHeight(View child) {
         return child.getMeasuredHeight();
     }
@@ -254,26 +247,17 @@
     }
     
     void recycleAllViews() {
-        int childCount = getChildCount();
+        final int childCount = getChildCount();
         final AbsSpinner.RecycleBin recycleBin = mRecycler;
+        final int position = mFirstPosition;
 
         // All views go in recycler
-        for (int i=0; i<childCount; i++) {
+        for (int i = 0; i < childCount; i++) {
             View v = getChildAt(i);
-            int index = mFirstPosition + i;
+            int index = position + i;
             recycleBin.put(index, v);
         }  
     }
-    
-    @Override
-    void handleDataChanged() {
-        // FIXME -- this is called from both measure and layout.
-        // This is harmless right now, but we don't want to do redundant work if
-        // this gets more complicated
-       super.handleDataChanged();
-    }
-    
-  
 
     /**
      * Jump directly to a specific item in the adapter data.
@@ -284,7 +268,6 @@
                 position <= mFirstPosition + getChildCount() - 1;
         setSelectionInt(position, shouldAnimate);
     }
-    
 
     @Override
     public void setSelection(int position) {
@@ -335,8 +318,6 @@
         }
     }
 
- 
-
     @Override
     public SpinnerAdapter getAdapter() {
         return mAdapter;
@@ -452,7 +433,7 @@
     }
 
     class RecycleBin {
-        private SparseArray<View> mScrapHeap = new SparseArray<View>();
+        private final SparseArray<View> mScrapHeap = new SparseArray<View>();
 
         public void put(int position, View v) {
             mScrapHeap.put(position, v);
@@ -469,12 +450,7 @@
             }
             return result;
         }
-        
-        View peek(int position) {
-            // System.out.print("Looking for " + position);
-            return mScrapHeap.get(position);
-        }
-        
+
         void clear() {
             final SparseArray<View> scrapHeap = mScrapHeap;
             final int count = scrapHeap.size();
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index aa9062b..bf63607 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -24,6 +24,7 @@
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.Gravity;
+import android.view.ViewDebug;
 import android.view.accessibility.AccessibilityEvent;
 
 
@@ -73,7 +74,8 @@
     public void toggle() {
         setChecked(!mChecked);
     }
-    
+
+    @ViewDebug.ExportedProperty
     public boolean isChecked() {
         return mChecked;
     }
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 98b0976..bf02ad3 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -26,6 +26,7 @@
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.view.Gravity;
+import android.view.ViewDebug;
 import android.view.accessibility.AccessibilityEvent;
 
 /**
@@ -98,6 +99,7 @@
         return super.performClick();
     }
 
+    @ViewDebug.ExportedProperty
     public boolean isChecked() {
         return mChecked;
     }
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 2feed03..8d688a5 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1485,7 +1485,6 @@
             }
 
             // Clear out old views
-            //removeAllViewsInLayout();
             detachAllViewsFromParent();
 
             switch (mLayoutMode) {
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 1dcb203..6dc9f78 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -36,6 +36,7 @@
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
@@ -335,6 +336,7 @@
      *
      * @return true if the progress bar is in indeterminate mode
      */
+    @ViewDebug.ExportedProperty
     public synchronized boolean isIndeterminate() {
         return mIndeterminate;
     }
@@ -607,6 +609,7 @@
      * @see #setMax(int)
      * @see #getMax()
      */
+    @ViewDebug.ExportedProperty
     public synchronized int getProgress() {
         return mIndeterminate ? 0 : mProgress;
     }
@@ -623,6 +626,7 @@
      * @see #setMax(int)
      * @see #getMax()
      */
+    @ViewDebug.ExportedProperty
     public synchronized int getSecondaryProgress() {
         return mIndeterminate ? 0 : mSecondaryProgress;
     }
@@ -636,6 +640,7 @@
      * @see #getProgress()
      * @see #getSecondaryProgress()
      */
+    @ViewDebug.ExportedProperty
     public synchronized int getMax() {
         return mMax;
     }
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 52ed11d..bb3b07e 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -22,6 +22,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.FocusFinder;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -51,6 +52,8 @@
  * <p>ScrollView only supports vertical scrolling.
  */
 public class ScrollView extends FrameLayout {
+    private static final String TAG = "ScrollView";
+
     static final int ANIMATED_SCROLL_GAP = 250;
 
     static final float MAX_SCROLL_FACTOR = 0.5f;
@@ -360,6 +363,17 @@
         return handled;
     }
 
+    private boolean inChild(int x, int y) {
+        if (getChildCount() > 0) {
+            final View child = getChildAt(0);
+            return !(y < child.getTop()
+                    || y >= child.getBottom()
+                    || x < child.getLeft()
+                    || x >= child.getRight());
+        }
+        return false;
+    }
+
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         /*
@@ -399,6 +413,11 @@
                 break;
 
             case MotionEvent.ACTION_DOWN:
+                if (!inChild((int)ev.getX(), (int)y)) {
+                    mIsBeingDragged = false;
+                    break;
+                }
+
                 /* Remember location of down touch */
                 mLastMotionY = y;
 
@@ -451,36 +470,45 @@
                     mScroller.abortAnimation();
                 }
 
+                if (!inChild((int)ev.getX(), (int)y)) {
+                    mIsBeingDragged = false;
+                    return false;
+                }
+
                 // Remember where the motion event started
                 mLastMotionY = y;
                 break;
             case MotionEvent.ACTION_MOVE:
-                // Scroll to follow the motion event
-                final int deltaY = (int) (mLastMotionY - y);
-                mLastMotionY = y;
+                if (mIsBeingDragged) {
+                    // Scroll to follow the motion event
+                    final int deltaY = (int) (mLastMotionY - y);
+                    mLastMotionY = y;
 
-                overscrollBy(0, deltaY, 0, mScrollY, 0, getScrollRange(),
-                        0, getOverscrollMax());
-                break;
+                    overscrollBy(0, deltaY, 0, mScrollY, 0, getScrollRange(),
+                            0, getOverscrollMax());
+                    break;
+                }
             case MotionEvent.ACTION_UP:
-                final VelocityTracker velocityTracker = mVelocityTracker;
-                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                int initialVelocity = (int) velocityTracker.getYVelocity();
+                if (mIsBeingDragged) {
+                    final VelocityTracker velocityTracker = mVelocityTracker;
+                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+                    int initialVelocity = (int) velocityTracker.getYVelocity();
 
-                if (getChildCount() > 0) {
-                    if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
-                        fling(-initialVelocity);
-                    } else {
-                        final int bottom = getScrollRange();
-                        if (mScroller.springback(mScrollX, mScrollY, 0, 0, 0, bottom)) {
-                            invalidate();
+                    if (getChildCount() > 0) {
+                        if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+                            fling(-initialVelocity);
+                        } else {
+                            final int bottom = getScrollRange();
+                            if (mScroller.springback(mScrollX, mScrollY, 0, 0, 0, bottom)) {
+                                invalidate();
+                            }
                         }
                     }
-                }
 
-                if (mVelocityTracker != null) {
-                    mVelocityTracker.recycle();
-                    mVelocityTracker = null;
+                    if (mVelocityTracker != null) {
+                        mVelocityTracker.recycle();
+                        mVelocityTracker = null;
+                    }
                 }
         }
         return true;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 7ba0fa1..cb44fa8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -198,6 +198,7 @@
     private boolean mFreezesText;
     private boolean mFrozenWithFocus;
     private boolean mTemporaryDetach;
+    private boolean mDispatchTemporaryDetach;
 
     private boolean mEatTouchRelease = false;
     private boolean mScrolled = false;
@@ -5731,6 +5732,7 @@
     /**
      * Convenience for {@link Selection#getSelectionStart}.
      */
+    @ViewDebug.ExportedProperty
     public int getSelectionStart() {
         return Selection.getSelectionStart(getText());
     }
@@ -5738,6 +5740,7 @@
     /**
      * Convenience for {@link Selection#getSelectionEnd}.
      */
+    @ViewDebug.ExportedProperty
     public int getSelectionEnd() {
         return Selection.getSelectionEnd(getText());
     }
@@ -6370,13 +6373,26 @@
     }
 
     @Override
+    public void dispatchFinishTemporaryDetach() {
+        mDispatchTemporaryDetach = true;
+        super.dispatchFinishTemporaryDetach();
+        mDispatchTemporaryDetach = false;
+    }
+
+    @Override
     public void onStartTemporaryDetach() {
-        mTemporaryDetach = true;
+        super.onStartTemporaryDetach();
+        // Only track when onStartTemporaryDetach() is called directly,
+        // usually because this instance is an editable field in a list
+        if (!mDispatchTemporaryDetach) mTemporaryDetach = true;
     }
     
     @Override
     public void onFinishTemporaryDetach() {
-        mTemporaryDetach = false;
+        super.onFinishTemporaryDetach();
+        // Only track when onStartTemporaryDetach() is called directly,
+        // usually because this instance is an editable field in a list
+        if (!mDispatchTemporaryDetach) mTemporaryDetach = false;
     }
     
     @Override
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 906bca1..c2517a8 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -62,6 +62,8 @@
     private static final int STATE_PLAYING            = 3;
     private static final int STATE_PAUSED             = 4;
     private static final int STATE_PLAYBACK_COMPLETED = 5;
+    private static final int STATE_SUSPEND            = 6;
+    private static final int STATE_RESUME             = 7;
 
     // mCurrentState is a VideoView object's current state.
     // mTargetState is the state that a method caller intends to reach.
@@ -87,6 +89,7 @@
     private boolean     mCanPause;
     private boolean     mCanSeekBack;
     private boolean     mCanSeekForward;
+    private int         mStateWhenSuspended;  //state before calling suspend()
 
     public VideoView(Context context) {
         super(context);
@@ -466,7 +469,14 @@
         public void surfaceCreated(SurfaceHolder holder)
         {
             mSurfaceHolder = holder;
-            openVideo();
+            //resume() was called before surfaceCreated()
+            if (mMediaPlayer != null && mCurrentState == STATE_SUSPEND
+                   && mTargetState == STATE_RESUME) {
+                mMediaPlayer.setDisplay(mSurfaceHolder);
+                resume();
+            } else {
+                openVideo();
+            }
         }
 
         public void surfaceDestroyed(SurfaceHolder holder)
@@ -474,7 +484,6 @@
             // after we return from this we can't use the surface any more
             mSurfaceHolder = null;
             if (mMediaController != null) mMediaController.hide();
-            release(true);
         }
     };
 
@@ -567,7 +576,36 @@
         mTargetState = STATE_PAUSED;
     }
 
-    // cache duration as mDuration for faster access
+    public void suspend() {
+        if (isInPlaybackState()) {
+            if (mMediaPlayer.suspend()) {
+                mStateWhenSuspended = mCurrentState;
+                mCurrentState = STATE_SUSPEND;
+                mTargetState = STATE_SUSPEND;
+            } else {
+                Log.w(TAG, "Unable to suspend video");
+                mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
+            }
+        }
+    }
+
+    public void resume() {
+        if (mSurfaceHolder == null && mCurrentState == STATE_SUSPEND){
+            mTargetState = STATE_RESUME;
+            return;
+        }
+        if (mMediaPlayer != null && mCurrentState == STATE_SUSPEND) {
+            if (mMediaPlayer.resume()) {
+                mCurrentState = mStateWhenSuspended;
+                mTargetState = mStateWhenSuspended;
+            } else {
+                Log.w(TAG, "Unable to resume video");
+                mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
+            }
+        }
+    }
+
+   // cache duration as mDuration for faster access
     public int getDuration() {
         if (isInPlaybackState()) {
             if (mDuration > 0) {
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index f56b15c..107b145 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -26,6 +26,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.text.TextUtils;
+import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -48,7 +49,6 @@
 import android.widget.SimpleCursorAdapter;
 import android.widget.TextView;
 import android.widget.AdapterView.OnItemClickListener;
-import android.util.AttributeSet;
 
 import com.android.internal.R;
 
@@ -322,24 +322,24 @@
     public Button getButton(int whichButton) {
         switch (whichButton) {
             case DialogInterface.BUTTON_POSITIVE:
-                return mButtonPositiveMessage != null ? mButtonPositive : null;
+                return mButtonPositive;
             case DialogInterface.BUTTON_NEGATIVE:
-                return mButtonNegativeMessage != null ? mButtonNegative : null;
+                return mButtonNegative;
             case DialogInterface.BUTTON_NEUTRAL:
-                return mButtonNeutralMessage != null ? mButtonNeutral : null;
+                return mButtonNeutral;
             default:
                 return null;
         }
     }
     
+    @SuppressWarnings({"UnusedDeclaration"})
     public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (mScrollView != null && mScrollView.executeKeyEvent(event)) return true;
-        return false;
+        return mScrollView != null && mScrollView.executeKeyEvent(event);
     }
 
+    @SuppressWarnings({"UnusedDeclaration"})
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (mScrollView != null && mScrollView.executeKeyEvent(event)) return true;
-        return false;
+        return mScrollView != null && mScrollView.executeKeyEvent(event);
     }
     
     private void setupView() {
@@ -469,7 +469,6 @@
     }
 
     private boolean setupButtons() {
-        View defaultButton = null;
         int BIT_BUTTON_POSITIVE = 1;
         int BIT_BUTTON_NEGATIVE = 2;
         int BIT_BUTTON_NEUTRAL = 4;
@@ -482,7 +481,6 @@
         } else {
             mButtonPositive.setText(mButtonPositiveText);
             mButtonPositive.setVisibility(View.VISIBLE);
-            defaultButton = mButtonPositive;
             whichButtons = whichButtons | BIT_BUTTON_POSITIVE;
         }
 
@@ -495,9 +493,6 @@
             mButtonNegative.setText(mButtonNegativeText);
             mButtonNegative.setVisibility(View.VISIBLE);
 
-            if (defaultButton == null) {
-                defaultButton = mButtonNegative;
-            }
             whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
         }
 
@@ -510,9 +505,6 @@
             mButtonNeutral.setText(mButtonNeutralText);
             mButtonNeutral.setVisibility(View.VISIBLE);
 
-            if (defaultButton == null) {
-                defaultButton = mButtonNeutral;
-            }
             whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
         }
 
@@ -565,8 +557,6 @@
                 R.styleable.AlertDialog_bottomBright, R.drawable.popup_bottom_bright);
         int bottomMedium = a.getResourceId(
                 R.styleable.AlertDialog_bottomMedium, R.drawable.popup_bottom_medium);
-        int centerMedium = a.getResourceId(
-                R.styleable.AlertDialog_centerMedium, R.drawable.popup_center_medium);
         
         /*
          * We now set the background of all of the sections of the alert.
@@ -596,7 +586,7 @@
          */
         views[pos] = (contentPanel.getVisibility() == View.GONE) 
                 ? null : contentPanel;
-        light[pos] = mListView == null ? false : true;
+        light[pos] = mListView != null;
         pos++;
         if (customPanel != null) {
             views[pos] = customPanel;
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index bc7dbf4..c5db83f 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -36,7 +36,7 @@
     public static final int RECOMMEND_INSTALL_EXTERNAL = 2;
     public static final int RECOMMEND_FAILED_INSUFFICIENT_STORAGE = -1;
     public static final int RECOMMEND_FAILED_INVALID_APK = -2;
-    private static final boolean DEBUG_SD_INSTALL = true;
+    private static final boolean localLOGV = true;
     private static final String TAG = "PackageHelper";
 
     public static IMountService getMountService() {
@@ -58,7 +58,7 @@
         if ((len - (mbLen * 1024 * 1024)) > 0) {
             mbLen++;
         }
-        if (DEBUG_SD_INSTALL) Log.i(TAG, "Size of resource " + mbLen);
+        if (localLOGV) Log.i(TAG, "Size of resource " + mbLen);
 
         try {
             int rc = mountService.createSecureContainer(
@@ -68,7 +68,7 @@
                 return null;
             }
             String cachePath = mountService.getSecureContainerPath(cid);
-            if (DEBUG_SD_INSTALL) Log.i(TAG, "Created secure container " + cid +
+            if (localLOGV) Log.i(TAG, "Created secure container " + cid +
                     " at " + cachePath);
                 return cachePath;
         } catch (RemoteException e) {
@@ -93,7 +93,7 @@
 
    public static boolean unMountSdDir(String cid) {
     try {
-        int rc = getMountService().unmountSecureContainer(cid, false);
+        int rc = getMountService().unmountSecureContainer(cid, true);
         if (rc != StorageResultCode.OperationSucceeded) {
             Log.e(TAG, "Failed to unmount " + cid + " with rc " + rc);
             return false;
@@ -148,7 +148,8 @@
 
     public static boolean destroySdDir(String cid) {
         try {
-            int rc = getMountService().destroySecureContainer(cid, false);
+            if (localLOGV) Log.i(TAG, "Forcibly destroying container " + cid);
+            int rc = getMountService().destroySecureContainer(cid, true);
             if (rc != StorageResultCode.OperationSucceeded) {
                 Log.i(TAG, "Failed to destroy container " + cid);
                 return false;
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
new file mode 100644
index 0000000..343041f
--- /dev/null
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -0,0 +1,287 @@
+package com.android.internal.content;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+
+import java.util.HashSet;
+
+/**
+ * Helper class for monitoring the state of packages: adding, removing,
+ * updating, and disappearing and reappearing on the SD card.
+ */
+public abstract class PackageMonitor extends android.content.BroadcastReceiver {
+    static final IntentFilter sPackageFilt = new IntentFilter();
+    static final IntentFilter sNonDataFilt = new IntentFilter();
+    static final IntentFilter sExternalFilt = new IntentFilter();
+    
+    static {
+        sPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
+        sPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        sPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
+        sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+        sPackageFilt.addAction(Intent.ACTION_UID_REMOVED);
+        sPackageFilt.addDataScheme("package");
+        sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
+        sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+    }
+    
+    final HashSet<String> mUpdatingPackages = new HashSet<String>();
+    
+    Context mRegisteredContext;
+    String[] mDisappearingPackages;
+    String[] mAppearingPackages;
+    String[] mModifiedPackages;
+    int mChangeType;
+    boolean mSomePackagesChanged;
+    
+    String[] mTempArray = new String[1];
+    
+    public void register(Context context, boolean externalStorage) {
+        if (mRegisteredContext != null) {
+            throw new IllegalStateException("Already registered");
+        }
+        mRegisteredContext = context;
+        context.registerReceiver(this, sPackageFilt);
+        context.registerReceiver(this, sNonDataFilt);
+        if (externalStorage) {
+            context.registerReceiver(this, sExternalFilt);
+        }
+    }
+    
+    public void unregister() {
+        if (mRegisteredContext == null) {
+            throw new IllegalStateException("Not registered");
+        }
+        mRegisteredContext.unregisterReceiver(this);
+        mRegisteredContext = null;
+    }
+    
+    //not yet implemented
+    boolean isPackageUpdating(String packageName) {
+        synchronized (mUpdatingPackages) {
+            return mUpdatingPackages.contains(packageName);
+        }
+    }
+    
+    public void onBeginPackageChanges() {
+    }
+    
+    public void onPackageAdded(String packageName, int uid) {
+    }
+    
+    public void onPackageRemoved(String packageName, int uid) {
+    }
+    
+    public void onPackageUpdateStarted(String packageName, int uid) {
+    }
+    
+    public void onPackageUpdateFinished(String packageName, int uid) {
+    }
+    
+    public void onPackageChanged(String packageName, int uid, String[] components) {
+    }
+    
+    public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
+        return false;
+    }
+    
+    public void onUidRemoved(int uid) {
+    }
+    
+    public void onPackagesAvailable(String[] packages) {
+    }
+    
+    public void onPackagesUnavailable(String[] packages) {
+    }
+    
+    public static final int PACKAGE_UNCHANGED = 0;
+    public static final int PACKAGE_UPDATING = 1;
+    public static final int PACKAGE_TEMPORARY_CHANGE = 2;
+    public static final int PACKAGE_PERMANENT_CHANGE = 3;
+    
+    public void onPackageDisappeared(String packageName, int reason) {
+    }
+    
+    public void onPackageAppeared(String packageName, int reason) {
+    }
+    
+    public void onPackageModified(String packageName) {
+    }
+    
+    public boolean didSomePackagesChange() {
+        return mSomePackagesChanged;
+    }
+    
+    public int isPackageAppearing(String packageName) {
+        if (mAppearingPackages != null) {
+            for (int i=mAppearingPackages.length-1; i>=0; i--) {
+                if (packageName.equals(mAppearingPackages[i])) {
+                    return mChangeType;
+                }
+            }
+        }
+        return PACKAGE_UNCHANGED;
+    }
+    
+    public boolean anyPackagesAppearing() {
+        return mAppearingPackages != null;
+    }
+    
+    public int isPackageDisappearing(String packageName) {
+        if (mDisappearingPackages != null) {
+            for (int i=mDisappearingPackages.length-1; i>=0; i--) {
+                if (packageName.equals(mDisappearingPackages[i])) {
+                    return mChangeType;
+                }
+            }
+        }
+        return PACKAGE_UNCHANGED;
+    }
+    
+    public boolean anyPackagesDisappearing() {
+        return mDisappearingPackages != null;
+    }
+    
+    public boolean isPackageModified(String packageName) {
+        if (mModifiedPackages != null) {
+            for (int i=mModifiedPackages.length-1; i>=0; i--) {
+                if (packageName.equals(mModifiedPackages[i])) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    
+    public void onSomePackagesChanged() {
+    }
+    
+    public void onFinishPackageChanges() {
+    }
+    
+    String getPackageName(Intent intent) {
+        Uri uri = intent.getData();
+        String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
+        return pkg;
+    }
+    
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        onBeginPackageChanges();
+        
+        mDisappearingPackages = mAppearingPackages = null;
+        mSomePackagesChanged = false;
+        
+        String action = intent.getAction();
+        if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+            String pkg = getPackageName(intent);
+            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
+            // We consider something to have changed regardless of whether
+            // this is just an update, because the update is now finished
+            // and the contents of the package may have changed.
+            mSomePackagesChanged = true;
+            if (pkg != null) {
+                mAppearingPackages = mTempArray;
+                mTempArray[0] = pkg;
+                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                    mModifiedPackages = mTempArray;
+                    mChangeType = PACKAGE_UPDATING;
+                    onPackageUpdateFinished(pkg, uid);
+                    onPackageModified(pkg);
+                } else {
+                    mChangeType = PACKAGE_PERMANENT_CHANGE;
+                    onPackageAdded(pkg, uid);
+                }
+                onPackageAppeared(pkg, mChangeType);
+                if (mChangeType == PACKAGE_UPDATING) {
+                    synchronized (mUpdatingPackages) {
+                        mUpdatingPackages.remove(pkg);
+                    }
+                }
+            }
+        } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+            String pkg = getPackageName(intent);
+            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
+            if (pkg != null) {
+                mDisappearingPackages = mTempArray;
+                mTempArray[0] = pkg;
+                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                    mChangeType = PACKAGE_UPDATING;
+                    synchronized (mUpdatingPackages) {
+                        //not used for now
+                        //mUpdatingPackages.add(pkg);
+                    }
+                    onPackageUpdateStarted(pkg, uid);
+                } else {
+                    mChangeType = PACKAGE_PERMANENT_CHANGE;
+                    // We only consider something to have changed if this is
+                    // not a replace; for a replace, we just need to consider
+                    // it when it is re-added.
+                    mSomePackagesChanged = true;
+                    onPackageRemoved(pkg, uid);
+                }
+                onPackageDisappeared(pkg, mChangeType);
+            }
+        } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+            String pkg = getPackageName(intent);
+            int uid = intent.getIntExtra(Intent.EXTRA_UID, 0);
+            String[] components = intent.getStringArrayExtra(
+                    Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+            if (pkg != null) {
+                mModifiedPackages = mTempArray;
+                mTempArray[0] = pkg;
+                onPackageChanged(pkg, uid, components);
+                // XXX Don't want this to always cause mSomePackagesChanged,
+                // since it can happen a fair amount.
+                onPackageModified(pkg);
+            }
+        } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
+            mDisappearingPackages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
+            mChangeType = PACKAGE_TEMPORARY_CHANGE;
+            boolean canRestart = onHandleForceStop(intent,
+                    mDisappearingPackages,
+                    intent.getIntExtra(Intent.EXTRA_UID, 0), false);
+            if (canRestart) setResultCode(Activity.RESULT_OK);
+        } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
+            mDisappearingPackages = new String[] {getPackageName(intent)};
+            mChangeType = PACKAGE_TEMPORARY_CHANGE;
+            onHandleForceStop(intent, mDisappearingPackages,
+                    intent.getIntExtra(Intent.EXTRA_UID, 0), true);
+        } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
+            onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0));
+        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+            String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            mAppearingPackages = pkgList;
+            mChangeType = PACKAGE_TEMPORARY_CHANGE;
+            mSomePackagesChanged = true;
+            if (pkgList != null) {
+                onPackagesAvailable(pkgList);
+                for (int i=0; i<pkgList.length; i++) {
+                    onPackageAppeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE);
+                }
+            }
+        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+            String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            mDisappearingPackages = pkgList;
+            mChangeType = PACKAGE_TEMPORARY_CHANGE;
+            mSomePackagesChanged = true;
+            if (pkgList != null) {
+                onPackagesUnavailable(pkgList);
+                for (int i=0; i<pkgList.length; i++) {
+                    onPackageDisappeared(pkgList[i], PACKAGE_TEMPORARY_CHANGE);
+                }
+            }
+        }
+        
+        if (mSomePackagesChanged) {
+            onSomePackagesChanged();
+        }
+        
+        onFinishPackageChanges();
+    }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index dfcc8f7..71ccb3b 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -59,6 +59,13 @@
     // Current on-disk Parcel version
     private static final int VERSION = 42;
 
+    // The maximum number of names wakelocks we will keep track of
+    // per uid; once the limit is reached, we batch the remaining wakelocks
+    // in to one common name.
+    private static final int MAX_WAKELOCKS_PER_UID = 20;
+    
+    private static final String BATCHED_WAKELOCK_NAME = "*overflow*";
+    
     private static int sNumSpeedSteps;
 
     private final File mFile;
@@ -1757,7 +1764,12 @@
                 String wakelockName = in.readString();
                 Uid.Wakelock wakelock = new Wakelock();
                 wakelock.readFromParcelLocked(unpluggables, in);
-                mWakelockStats.put(wakelockName, wakelock);
+                if (mWakelockStats.size() < MAX_WAKELOCKS_PER_UID) {
+                    // We will just drop some random set of wakelocks if
+                    // the previous run of the system was an older version
+                    // that didn't impose a limit.
+                    mWakelockStats.put(wakelockName, wakelock);
+                }
             }
 
             int numSensors = in.readInt();
@@ -2583,8 +2595,14 @@
         public StopwatchTimer getWakeTimerLocked(String name, int type) {
             Wakelock wl = mWakelockStats.get(name);
             if (wl == null) {
-                wl = new Wakelock();
-                mWakelockStats.put(name, wl);
+                if (mWakelockStats.size() > MAX_WAKELOCKS_PER_UID) {
+                    name = BATCHED_WAKELOCK_NAME;
+                    wl = mWakelockStats.get(name);
+                }
+                if (wl == null) {
+                    wl = new Wakelock();
+                    mWakelockStats.put(name, wl);
+                }
             }
             StopwatchTimer t = null;
             switch (type) {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index f074b80..9713c27f 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -104,14 +104,24 @@
     private static String sLockPatternFilename;
     private static String sLockPasswordFilename;
 
+    DevicePolicyManager getDevicePolicyManager() {
+        if (mDevicePolicyManager == null) {
+            mDevicePolicyManager =
+                (DevicePolicyManager)mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+            if (mDevicePolicyManager == null) {
+                Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?",
+                        new IllegalStateException("Stack trace:"));
+            }
+        }
+        return mDevicePolicyManager;
+    }
     /**
      * @param contentResolver Used to look up and save settings.
      */
     public LockPatternUtils(Context context) {
         mContext = context;
         mContentResolver = context.getContentResolver();
-        mDevicePolicyManager =
-                (DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        mDevicePolicyManager = getDevicePolicyManager();
         // Initialize the location of gesture lock file
         if (sLockPatternFilename == null) {
             sLockPatternFilename = android.os.Environment.getDataDirectory()
@@ -123,7 +133,7 @@
     }
 
     public int getRequestedMinimumPasswordLength() {
-        return mDevicePolicyManager.getPasswordMinimumLength(null);
+        return getDevicePolicyManager().getPasswordMinimumLength(null);
     }
 
     /**
@@ -133,7 +143,7 @@
      * @return
      */
     public int getRequestedPasswordMode() {
-        int policyMode = mDevicePolicyManager.getPasswordQuality(null);
+        int policyMode = getDevicePolicyManager().getPasswordQuality(null);
         switch (policyMode) {
             case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
                 return MODE_PASSWORD;
@@ -151,11 +161,11 @@
      * @return
      */
     public void reportFailedPasswordAttempt() {
-        mDevicePolicyManager.reportFailedPasswordAttempt();
+        getDevicePolicyManager().reportFailedPasswordAttempt();
     }
 
     public void reportSuccessfulPasswordAttempt() {
-        mDevicePolicyManager.reportSuccessfulPasswordAttempt();
+        getDevicePolicyManager().reportSuccessfulPasswordAttempt();
     }
 
     public void setActivePasswordState(int mode, int length) {
@@ -171,7 +181,7 @@
                 policyMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
                 break;
         }
-        mDevicePolicyManager.setActivePasswordState(policyMode, length);
+        getDevicePolicyManager().setActivePasswordState(policyMode, length);
     }
 
     /**
@@ -279,7 +289,7 @@
         saveLockPattern(null);
         setLong(PASSWORD_TYPE_KEY, MODE_PATTERN);
     }
-    
+
     /**
      * Save a lock pattern.
      * @param pattern The new pattern to save.
@@ -334,7 +344,7 @@
                 hasNonDigit = true;
             }
         }
-        
+
         // First check if it is sufficient.
         switch (reqMode) {
             case MODE_PASSWORD: {
@@ -342,19 +352,19 @@
                     return MODE_UNSPECIFIED;
                 }
             } break;
-            
+
             case MODE_PIN:
             case MODE_PATTERN: {
                 // Whatever we have is acceptable; we may need to promote the
                 // mode later.
             } break;
-            
+
             default:
                 // If it isn't a mode we specifically know, then fail fast.
                 Log.w(TAG, "adjustPasswordMode: unknown mode " + reqMode);
                 return MODE_UNSPECIFIED;
         }
-        
+
         // Do we need to promote?
         if (hasNonDigit) {
             if (reqMode < MODE_PASSWORD) {
@@ -366,10 +376,10 @@
                 reqMode = MODE_PIN;
             }
         }
-        
+
         return reqMode;
     }
-    
+
     /**
      * Save a lock password.  Does not ensure that the pattern is as good
      * as the requested mode, but will adjust the mode to be as good as the
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index a82a21e..5afa0342 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -1466,19 +1466,25 @@
     for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
         value = bag->map.value;
         jstring str = NULL;
-        
+
         // Take care of resolving the found resource to its final value.
         ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
         if (value.dataType == Res_value::TYPE_STRING) {
-            const char16_t* str16 = res.getTableStringBlock(block)->stringAt(value.data, &strLen);
-            str = env->NewString(str16, strLen);
-            if (str == NULL) {
-                doThrow(env, "java/lang/OutOfMemoryError");
-                res.unlockBag(startOfBag);
-                return NULL;
+            const ResStringPool* pool = res.getTableStringBlock(block);
+            const char* str8 = pool->string8At(value.data, &strLen);
+            if (str8 != NULL) {
+                str = env->NewStringUTF(str8);
+            } else {
+                const char16_t* str16 = pool->stringAt(value.data, &strLen);
+                str = env->NewString(str16, strLen);
+                if (str == NULL) {
+                    doThrow(env, "java/lang/OutOfMemoryError");
+                    res.unlockBag(startOfBag);
+                    return NULL;
+                }
             }
         }
-        
+
         env->SetObjectArrayElement(array, i, str);
     }
     res.unlockBag(startOfBag);
diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp
index ffb271c..641fbce 100644
--- a/core/jni/android_util_StringBlock.cpp
+++ b/core/jni/android_util_StringBlock.cpp
@@ -89,6 +89,11 @@
     }
 
     size_t len;
+    const char* str8 = osb->string8At(idx, &len);
+    if (str8 != NULL) {
+        return env->NewStringUTF(str8);
+    }
+
     const char16_t* str = osb->stringAt(idx, &len);
     if (str == NULL) {
         doThrow(env, "java/lang/IndexOutOfBoundsException");
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png b/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png
new file mode 100644
index 0000000..c6503c7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png b/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png
new file mode 100644
index 0000000..152de8b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png
index 7acd0df..72faccf 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png
index a8b843a..369be10 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png
index cfbc092..7e996ec 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png
index 3d0d16e..44668b3 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png
index 2ccd3da..3a4571e 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png
index 966ea44..60dc632 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinnerbox_arrow_first.9.png b/core/res/res/drawable-hdpi/spinnerbox_arrow_first.9.png
deleted file mode 100644
index af80855..0000000
--- a/core/res/res/drawable-hdpi/spinnerbox_arrow_first.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinnerbox_arrow_last.9.png b/core/res/res/drawable-hdpi/spinnerbox_arrow_last.9.png
deleted file mode 100644
index dc47275..0000000
--- a/core/res/res/drawable-hdpi/spinnerbox_arrow_last.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinnerbox_arrow_middle.9.png b/core/res/res/drawable-hdpi/spinnerbox_arrow_middle.9.png
deleted file mode 100644
index 007f279..0000000
--- a/core/res/res/drawable-hdpi/spinnerbox_arrow_middle.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinnerbox_arrow_single.9.png b/core/res/res/drawable-hdpi/spinnerbox_arrow_single.9.png
deleted file mode 100644
index 24592a3..0000000
--- a/core/res/res/drawable-hdpi/spinnerbox_arrow_single.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dropdown_disabled.9.png b/core/res/res/drawable-mdpi/btn_dropdown_disabled.9.png
new file mode 100644
index 0000000..f7464c7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_dropdown_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dropdown_disabled_focused.9.png b/core/res/res/drawable-mdpi/btn_dropdown_disabled_focused.9.png
new file mode 100644
index 0000000..ffe219f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_dropdown_disabled_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png
index febf222..437fbc7 100644
--- a/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png
+++ b/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png
index 70a200b..a679426 100644
--- a/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png
index 6f2989f..eb95f22 100644
--- a/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinnerbox_arrow_first.9.png b/core/res/res/drawable-mdpi/spinnerbox_arrow_first.9.png
deleted file mode 100644
index d8e268d..0000000
--- a/core/res/res/drawable-mdpi/spinnerbox_arrow_first.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinnerbox_arrow_last.9.png b/core/res/res/drawable-mdpi/spinnerbox_arrow_last.9.png
deleted file mode 100644
index 087e650..0000000
--- a/core/res/res/drawable-mdpi/spinnerbox_arrow_last.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinnerbox_arrow_middle.9.png b/core/res/res/drawable-mdpi/spinnerbox_arrow_middle.9.png
deleted file mode 100644
index f1f2ff5..0000000
--- a/core/res/res/drawable-mdpi/spinnerbox_arrow_middle.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinnerbox_arrow_single.9.png b/core/res/res/drawable-mdpi/spinnerbox_arrow_single.9.png
deleted file mode 100644
index f537b3b..0000000
--- a/core/res/res/drawable-mdpi/spinnerbox_arrow_single.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/btn_circle.xml b/core/res/res/drawable/btn_circle.xml
index 9208010..243f506 100644
--- a/core/res/res/drawable/btn_circle.xml
+++ b/core/res/res/drawable/btn_circle.xml
@@ -19,6 +19,8 @@
         android:drawable="@drawable/btn_circle_normal" />
     <item android:state_window_focused="false" android:state_enabled="false"
         android:drawable="@drawable/btn_circle_disable" />
+    <item android:state_pressed="true" android:state_enabled="false"
+        android:drawable="@drawable/btn_circle_disable" />
     <item android:state_pressed="true" 
         android:drawable="@drawable/btn_circle_pressed" />
     <item android:state_focused="true" android:state_enabled="true"
diff --git a/core/res/res/drawable/btn_dropdown.xml b/core/res/res/drawable/btn_dropdown.xml
index 8ec8ece..34a0504 100644
--- a/core/res/res/drawable/btn_dropdown.xml
+++ b/core/res/res/drawable/btn_dropdown.xml
@@ -15,10 +15,24 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item  android:state_window_focused="false" android:drawable="@drawable/btn_dropdown_normal" />
-    <item android:state_pressed="true" android:drawable="@drawable/btn_dropdown_pressed" />
-    <item android:state_focused="true" android:state_pressed="false"
+    <item
+        android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_dropdown_normal" />
+    <item
+        android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_dropdown_disabled" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/btn_dropdown_pressed" />
+    <item
+        android:state_focused="true" android:state_enabled="true"
         android:drawable="@drawable/btn_dropdown_selected" />
-    <item android:drawable="@drawable/btn_dropdown_normal" />
+    <item
+        android:state_enabled="true"
+        android:drawable="@drawable/btn_dropdown_normal" />
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/btn_dropdown_disabled_focused" />
+    <item
+        android:drawable="@drawable/btn_dropdown_disabled" />
 </selector>
-
diff --git a/core/res/res/drawable/spinnerbox_arrows.xml b/core/res/res/drawable/spinnerbox_arrows.xml
deleted file mode 100644
index 276a0f0..0000000
--- a/core/res/res/drawable/spinnerbox_arrows.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/res/drawable/spinnerbox_arrows.xml
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_single="true" android:drawable="@drawable/spinnerbox_arrow_single" />
-    <item android:state_first="true" android:drawable="@drawable/spinnerbox_arrow_first" />
-    <item android:state_last="true" android:drawable="@drawable/spinnerbox_arrow_last" />
-    <item android:state_middle="true" android:drawable="@drawable/spinnerbox_arrow_middle" />
-    <item android:state_pressed="true" android:drawable="@drawable/spinnerbox_arrow_single" />
-</selector>
diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml
index 7ae68f9..25a41f8 100644
--- a/core/res/res/layout/alert_dialog.xml
+++ b/core/res/res/layout/alert_dialog.xml
@@ -80,7 +80,8 @@
             android:paddingTop="2dip"
             android:paddingBottom="12dip"
             android:paddingLeft="14dip"
-            android:paddingRight="10dip">
+            android:paddingRight="10dip"
+            android:overscrollMode="ifContentScrolls">
             <TextView android:id="@+id/message"
                 style="?android:attr/textAppearanceMedium"
                 android:layout_width="match_parent"
diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml
index cf246ba..7935e2a 100644
--- a/core/res/res/layout/search_bar.xml
+++ b/core/res/res/layout/search_bar.xml
@@ -34,7 +34,7 @@
         android:layout_height="wrap_content"
         android:orientation="vertical"
         android:paddingLeft="12dip"
-        android:paddingRight="12dip"
+        android:paddingRight="6dip"
         android:paddingTop="7dip"
         android:paddingBottom="16dip"
         android:background="@drawable/search_plate_global" >
@@ -95,7 +95,10 @@
                 android:id="@+id/search_voice_btn"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:layout_marginLeft="8dip"
+                android:layout_marginLeft="0dip"
+                android:layout_marginTop="-6.5dip"
+                android:layout_marginBottom="-7dip"
+                android:layout_marginRight="-5dip"
                 android:background="@drawable/btn_search_dialog_voice"
                 android:src="@android:drawable/ic_btn_speak_now"
             />
diff --git a/core/res/res/layout/status_bar_expanded.xml b/core/res/res/layout/status_bar_expanded.xml
index a0cd11d..30138a7 100644
--- a/core/res/res/layout/status_bar_expanded.xml
+++ b/core/res/res/layout/status_bar_expanded.xml
@@ -20,9 +20,9 @@
 
 <com.android.server.status.ExpandedView xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
-    android:background="@drawable/status_bar_background"
     android:focusable="true"
-    android:descendantFocusability="afterDescendants">
+    android:descendantFocusability="afterDescendants"
+    >
 
     <LinearLayout
         android:layout_width="match_parent"
@@ -79,19 +79,18 @@
     <FrameLayout
         android:layout_width="match_parent" 
         android:layout_height="wrap_content"
+        android:layout_weight="1"
         >
         <ScrollView
             android:id="@+id/scroll"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
+            android:layout_height="match_parent"
             android:fadingEdge="none"
             >
             <com.android.server.status.NotificationLinearLayout
                 android:id="@+id/notificationLinearLayout"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_weight="1"
                 android:orientation="vertical"
                 >
                 
@@ -136,7 +135,7 @@
 
         <ImageView
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:src="@drawable/title_bar_shadow"
             android:scaleType="fitXY"
         />
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index cdc15c2..0c065ef 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -127,15 +127,4 @@
         <item><xliff:g id="id">ime</xliff:g></item>
     </string-array>
 
-    <!-- Do not translate. Each string points to the component name of an ACTION_WEB_SEARCH
-         handling activity. On startup if there were no preferred ACTION_WEB_SEARCH handlers,
-         the first component from this list which is found to be installed is set as the
-         preferred activity. -->
-    <string-array name="default_web_search_providers">
-        <item>com.google.android.googlequicksearchbox/.google.GoogleSearch</item>
-        <item>com.android.quicksearchbox/.google.GoogleSearch</item>
-        <item>com.google.android.providers.enhancedgooglesearch/.Launcher</item>
-        <item>com.android.googlesearch/.GoogleSearch</item>
-        <item>com.android.websearch/.Search.1</item>
-    </string-array>
 </resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 33b509b..d66f513 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -302,7 +302,7 @@
     <!-- Specify whether an activity should be finished when a "close system
          windows" request has been made.  This happens, for example, when
          the home key is pressed, when the device is locked, when a system
-         dialog like recent apps is displayed, etc. -->
+         dialog showing recent applications is displayed, etc. -->
     <attr name="finishOnCloseSystemDialogs" format="boolean" />
     
     <!-- Specify whether an activity's task should be cleared when it
@@ -487,7 +487,7 @@
              the display will rotate based on how the user moves the device. -->
         <enum name="sensor" value="4" />
         <!-- Always ignore orientation determined by orientation sensor:
-             tthe display will not rotate when the user moves the device. -->
+             the display will not rotate when the user moves the device. -->
         <enum name="nosensor" value="5" />
     </attr>
     
@@ -523,7 +523,7 @@
         <flag name="keyboard" value="0x0010" />
         <!-- The keyboard or navigation accessibility has changed, for example
              the user has slid the keyboard out to expose it.  Note that
-             inspite of its name, this applied to any accessibility: keyboard
+             despite its name, this applied to any accessibility: keyboard
              or navigation. -->
         <flag name="keyboardHidden" value="0x0020" />
         <!-- The navigation type has changed.  Should never normally happen. -->
@@ -592,11 +592,16 @@
          backup and restore of the application's settings to external storage. -->
     <attr name="backupAgent" format="string" />
 
-    <!-- This is not the attribute you are looking for. -->
+    <!-- Whether to allow the application to participate in backup
+         infrastructure.  If this attribute is set to <code>false</code>, no backup
+         of the application will ever be performed, even by a full-system backup that
+         would otherwise cause all application data to be saved via adb.  The
+         default value of this attribute is <code>true</code>. -->
     <attr name="allowBackup" format="boolean" />
 
     <!-- Whether the application in question should be terminated after its
-         settings have been restored.  The default is to do so. -->
+         settings have been restored.  The default is <code>true</code>,
+         which means to do so. -->
     <attr name="killAfterRestore" format="boolean" />
 
     <!-- Whether the application needs to have its own Application subclass
@@ -604,15 +609,25 @@
          Application class to avoid interference with application logic. -->
     <attr name="restoreNeedsApplication" format="boolean" />
 
+    <!-- Indicate that the application is prepared to attempt a restore of any
+         backed-up dataset, even if the backup is apparently from a newer version
+         of the application than is currently installed on the device.  Setting
+         this attribute to <code>true</code> will permit the Backup Manager to
+         attempt restore even when a version mismatch suggests that the data are
+         incompatible.  <em>Use with caution!</em>
+
+         <p>The default value of this attribute is <code>false</code>. -->
+    <attr name="restoreAnyVersion" format="boolean" />
+
     <!-- The default install location defined by an application. -->
     <attr name="installLocation">
         <!-- Let the system decide ideal install location -->
         <enum name="auto" value="0" />
-        <!-- Explicitly request to be installed on internal phone storate
+        <!-- Explicitly request to be installed on internal phone storage
              only. -->
         <enum name="internalOnly" value="1" />
-        <!-- Prefer to be installed on sdcard. There is no guarantee that
-             the system will honour this request. The application might end
+        <!-- Prefer to be installed on SD card. There is no guarantee that
+             the system will honor this request. The application might end
              up being installed on internal storage if external media
              is unavailable or too full. -->
         <enum name="preferExternal" value="2" />
@@ -698,6 +713,7 @@
         <attr name="allowBackup" />
         <attr name="killAfterRestore" />
         <attr name="restoreNeedsApplication" />
+        <attr name="restoreAnyVersion" />
         <attr name="neverEncrypt" />
     </declare-styleable>
     
@@ -813,7 +829,7 @@
     <!-- The <code>uses-feature</code> tag specifies
          a specific feature used by the application.
          For example an application might specify that it requires
-         specific version of open gl. Multiple such attribute
+         specific version of OpenGL. Multiple such attribute
          values can be specified by the application.
 
          <p>This appears as a child tag of the root
@@ -1359,7 +1375,7 @@
             {@link android.content.Intent#setData Intent.setData()}.
             <p><em>Note: scheme and host name matching in the Android framework is
             case-sensitive, unlike the formal RFC.  As a result,
-            Uris here should always be normalized to use lower case letters
+            URIs here should always be normalized to use lower case letters
             for these elements (as well as other proper Uri normalization).</em></p> -->
         <attr name="data" format="string" />
         <!-- The MIME type name to assign to the Intent, as per
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 5cea28db..8a92757 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -38,4 +38,6 @@
     <dimen name="password_keyboard_key_height">56dip</dimen>
     <!-- Default correction for the space key in the password keyboard -->
     <dimen name="password_keyboard_spacebar_vertical_correction">4dip</dimen>
+    <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
+    <dimen name="status_bar_edge_ignore">5dp</dimen>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b334337..5da8e85 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1231,6 +1231,7 @@
   <public type="attr" name="safeMode" id="0x010102b8" />
   <public type="attr" name="webTextViewStyle" id="0x010102b9" />
   <public type="attr" name="overscrollMode" id="0x010102ba" />
+  <public type="attr" name="restoreAnyVersion" id="0x010102bb" />
 
   <public type="anim" name="cycle_interpolator" id="0x010a000c" />
     
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f1501ae..5d2d272 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -779,18 +779,18 @@
         applications can use this to read phone owner data.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_readCalendar">read calendar data</string>
+    <string name="permlab_readCalendar">read calendar events</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_readCalendar">Allows an application to read all
         of the calendar events stored on your phone. Malicious applications
         can use this to send your calendar events to other people.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_writeCalendar">write calendar data</string>
+    <string name="permlab_writeCalendar">add or modify calendar events and send email to guests</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_writeCalendar">Allows an application to modify the
-        calendar events stored on your phone. Malicious
-        applications can use this to erase or modify your calendar data.</string>
+    <string name="permdesc_writeCalendar">Allows an application to add or change the 
+        events on your calendar, which may send email to guests. Malicious applications can use this 
+	to erase or modify your calendar events or to send email to guests.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_accessMockLocation">mock location sources for testing</string>
diff --git a/core/tests/coretests/src/android/app/SearchablesTest.java b/core/tests/coretests/src/android/app/SearchablesTest.java
index 9b4520e..6cb31d4 100644
--- a/core/tests/coretests/src/android/app/SearchablesTest.java
+++ b/core/tests/coretests/src/android/app/SearchablesTest.java
@@ -64,39 +64,7 @@
      *  findActionKey works
      *  getIcon works
      */
-    
-    /**
-     * The goal of this test is to confirm proper operation of the 
-     * SearchableInfo helper class.
-     * 
-     * TODO:  The metadata source needs to be mocked out because adding
-     * searchability metadata via this test is causing it to leak into the
-     * real system.  So for now I'm just going to test for existence of the
-     * GlobalSearch app (which is searchable).
-     */
-    public void testSearchableGlobalSearch() {
-        // test basic array & hashmap
-        Searchables searchables = new Searchables(mContext);
-        searchables.buildSearchableList();
 
-        // test linkage from another activity
-        // TODO inject this via mocking into the package manager.
-        // TODO for now, just check for searchable GlobalSearch app (this isn't really a unit test)
-        ComponentName thisActivity = new ComponentName(
-                "com.android.globalsearch",
-                "com.android.globalsearch.GlobalSearch");
-
-        SearchableInfo si = searchables.getSearchableInfo(thisActivity);
-        assertNotNull(si);
-        assertEquals(thisActivity, si.getSearchActivity());
-        
-        Context appContext = si.getActivityContext(mContext);
-        assertNotNull(appContext);
-        MoreAsserts.assertNotEqual(appContext, mContext);
-        assertEquals("Quick Search Box", appContext.getString(si.getHintId()));
-        assertEquals("Quick Search Box", appContext.getString(si.getLabelId()));
-    }
-    
     /**
      * Test that non-searchable activities return no searchable info (this would typically
      * trigger the use of the default searchable e.g. contacts)
@@ -113,18 +81,7 @@
         SearchableInfo si = searchables.getSearchableInfo(nonActivity);
         assertNull(si);
     }
-    
-    /**
-     * Test that there is a default searchable (aka global search provider).
-     */
-    public void testDefaultSearchable() {
-        Searchables searchables = new Searchables(mContext);
-        searchables.buildSearchableList();
-        SearchableInfo si = searchables.getDefaultSearchable();
-        checkSearchable(si);
-        assertTrue(searchables.isDefaultSearchable(si));
-    }
-    
+
     /**
      * This is an attempt to run the searchable info list with a mocked context.  Here are some
      * things I'd like to test.
@@ -371,7 +328,8 @@
         public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
             assertNotNull(intent);
             assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH)
-                    || intent.getAction().equals(Intent.ACTION_WEB_SEARCH));
+                    || intent.getAction().equals(Intent.ACTION_WEB_SEARCH)
+                    || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH));
             switch (mSearchablesMode) {
             case SEARCHABLES_PASSTHROUGH:
                 return mRealPackageManager.queryIntentActivities(intent, flags);
@@ -472,6 +430,20 @@
                 throw new UnsupportedOperationException();
             }
         }
+
+        @Override
+        public int checkPermission(String permName, String pkgName) {
+            assertNotNull(permName);
+            assertNotNull(pkgName);
+            switch (mSearchablesMode) {
+                case SEARCHABLES_PASSTHROUGH:
+                    return mRealPackageManager.checkPermission(permName, pkgName);
+                case SEARCHABLES_MOCK_ZERO:
+                    return PackageManager.PERMISSION_DENIED;
+                default:
+                    throw new UnsupportedOperationException();
+                }
+        }
     }
 }
 
diff --git a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
new file mode 100644
index 0000000..ccd0dae
--- /dev/null
+++ b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.text;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests StaticLayout bidi implementation.
+ */
+public class StaticLayoutBidiTest extends TestCase {
+    
+    public static final int REQ_DL = 2; // Layout.DIR_REQUEST_DEFAULT_LTR;
+    public static final int REQ_DR = -2; // Layout.DIR_REQUEST_DEFAULT_RTL;
+    public static final int REQ_L = 1; // Layout.DIR_REQUEST_LTR;
+    public static final int REQ_R = -1; // Layout.DIR_REQUEST_RTL;
+    public static final int L = Layout.DIR_LEFT_TO_RIGHT;
+    public static final int R = Layout.DIR_RIGHT_TO_LEFT;
+    
+    public static final String SP = " ";
+    public static final String ALEF = "\u05d0";
+    public static final String BET = "\u05d1";
+    public static final String GIMEL = "\u05d2";
+    public static final String DALET = "\u05d3";
+    
+    @SmallTest
+    public void testAllLtr() {
+        expectBidi(REQ_DL, "a test", "000000", L);
+    }
+    
+    @SmallTest
+    public void testLtrRtl() {
+        expectBidi(REQ_DL, "abc " + ALEF + BET + GIMEL, "0000111", L);
+    }
+    
+    @SmallTest
+    public void testAllRtl() {
+        expectBidi(REQ_DL, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", R);
+    }
+    
+    @SmallTest
+    public void testRtlLtr() {
+        expectBidi(REQ_DL,  ALEF + BET + GIMEL + " abc", "1111000", R);
+    }
+    
+    @SmallTest
+    public void testRAllLtr() {
+        expectBidi(REQ_R, "a test", "000000", R);
+    }
+    
+    @SmallTest
+    public void testRLtrRtl() {
+        expectBidi(REQ_R, "abc " + ALEF + BET + GIMEL, "0001111", R);
+    }
+    
+    @SmallTest
+    public void testLAllRtl() {
+        expectBidi(REQ_L, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", L);
+    }
+    
+    @SmallTest
+    public void testLRtlLtr() {
+        expectBidi(REQ_L,  ALEF + BET + GIMEL + " abc", "1110000", L);
+    }
+    
+    private void expectBidi(int dir, String text, 
+            String expectedLevels, int expectedDir) {
+        char[] chs = text.toCharArray();
+        int n = chs.length;
+        byte[] chInfo = new byte[n];
+        
+        int resultDir = StaticLayout.bidi(dir, chs, chInfo, n, false);
+        
+        {
+            StringBuilder sb = new StringBuilder("xdirs:");
+            for (int i = 0; i < n; ++i) {
+                sb.append(" ").append(String.valueOf(chInfo[i]));
+            }
+            Log.i("BIDI", sb.toString());
+        }
+        
+        char[] resultLevelChars = new char[n];
+        for (int i = 0; i < n; ++i) {
+            resultLevelChars[i] = (char)('0' + chInfo[i]);
+        }
+        String resultLevels = new String(resultLevelChars);
+        assertEquals("direction", expectedDir, resultDir);
+        assertEquals("levels", expectedLevels, resultLevels);
+    }
+}
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index 1e4db3d..7511ec1 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -213,13 +213,13 @@
         }
 
         public int scale(float height) {
-            int altVal = (int)(height * sMult + sAdd + 0.5); // existing impl
+            int altVal = (int)(height * sMult + sAdd + 0.5);
             int rndVal = Math.round(height * sMult + sAdd);
             if (altVal != rndVal) {
                 Log.i("Scale", "expected scale: " + rndVal +
                         " != returned scale: " + altVal);
             }
-            return altVal; // existing implementation
+            return rndVal;
         }
     }
 
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index ba6c711..66c34b2 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -30,6 +30,7 @@
 class ProcessState;
 class String8;
 class TextOutput;
+class Flattenable;
 
 struct flat_binder_object;  // defined in support_p/binder_module.h
 
@@ -81,6 +82,7 @@
     status_t            writeString16(const char16_t* str, size_t len);
     status_t            writeStrongBinder(const sp<IBinder>& val);
     status_t            writeWeakBinder(const wp<IBinder>& val);
+    status_t            write(const Flattenable& val);
 
     // Place a native_handle into the parcel (the native_handle's file-
     // descriptors are dup'ed, so it is safe to delete the native_handle
@@ -119,7 +121,7 @@
     const char16_t*     readString16Inplace(size_t* outLen) const;
     sp<IBinder>         readStrongBinder() const;
     wp<IBinder>         readWeakBinder() const;
-
+    status_t            read(Flattenable& val) const;
     
     // Retrieve native_handle from the parcel. This returns a copy of the
     // parcel's native_handle (the caller takes ownership). The caller
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 8e5f05f..ea15a5c 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -61,7 +61,7 @@
     status_t seekTo(int64_t time_us);
 
     bool isSeeking();
-    bool reachedEOS();
+    bool reachedEOS(status_t *finalStatus);
 
 private:
     sp<MediaSource> mSource;
@@ -81,6 +81,7 @@
 
     bool mSeeking;
     bool mReachedEOS;
+    status_t mFinalStatus;
     int64_t mSeekTimeUs;
 
     bool mStarted;
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index f8bc7ab..24c2f46 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -96,6 +96,7 @@
         kRequiresFlushBeforeShutdown         = 64,
         kDefersOutputBufferAllocation        = 128,
         kDecoderLiesAboutNumberOfChannels    = 256,
+        kInputBufferSizesAreBogus            = 512,
     };
 
     struct BufferInfo {
@@ -131,6 +132,7 @@
     PortStatus mPortStatus[2];
     bool mInitialBufferSubmit;
     bool mSignalledEOS;
+    status_t mFinalStatus;
     bool mNoMoreOutputData;
     bool mOutputPortSettingsHaveChanged;
     int64_t mSeekTimeUs;
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index b9c491be..e72b6b3 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -23,6 +23,7 @@
 #include <ui/android_native_buffer.h>
 #include <ui/PixelFormat.h>
 #include <ui/Rect.h>
+#include <utils/Flattenable.h>
 #include <pixelflinger/pixelflinger.h>
 
 struct android_native_buffer_t;
@@ -30,7 +31,6 @@
 namespace android {
 
 class GraphicBufferMapper;
-class Parcel;
 
 // ===========================================================================
 // GraphicBuffer
@@ -40,7 +40,7 @@
     : public EGLNativeBase<
         android_native_buffer_t, 
         GraphicBuffer, 
-        LightRefBase<GraphicBuffer> >
+        LightRefBase<GraphicBuffer> >, public Flattenable
 {
 public:
 
@@ -97,7 +97,6 @@
     uint32_t getVerticalStride() const;
 
 protected:
-    GraphicBuffer(const Parcel& reply);
     virtual ~GraphicBuffer();
 
     enum {
@@ -122,8 +121,16 @@
     status_t initSize(uint32_t w, uint32_t h, PixelFormat format, 
             uint32_t usage);
 
-    static status_t writeToParcel(Parcel* reply, 
-            android_native_buffer_t const* buffer);
+    void free_handle();
+
+    // Flattenable interface
+    size_t getFlattenedSize() const;
+    size_t getFdCount() const;
+    status_t flatten(void* buffer, size_t size,
+            int fds[], size_t count) const;
+    status_t unflatten(void const* buffer, size_t size,
+            int fds[], size_t count);
+
 
     GraphicBufferMapper& mBufferMapper;
     ssize_t mInitCheck;
diff --git a/include/utils/Flattenable.h b/include/utils/Flattenable.h
new file mode 100644
index 0000000..852be3b
--- /dev/null
+++ b/include/utils/Flattenable.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UTILS_FLATTENABLE_H
+#define ANDROID_UTILS_FLATTENABLE_H
+
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class Flattenable
+{
+public:
+    // size in bytes of the flattened object
+    virtual size_t getFlattenedSize() const = 0;
+
+    // number of file descriptors to flatten
+    virtual size_t getFdCount() const = 0;
+
+    // flattens the object into buffer.
+    // size should be at least of getFlattenedSize()
+    // file descriptors are written in the fds[] array but ownership is
+    // not transfered (ie: they must be dupped by the caller of
+    // flatten() if needed).
+    virtual status_t flatten(void* buffer, size_t size,
+            int fds[], size_t count) const = 0;
+
+    // unflattens the object from buffer.
+    // size should be equal to the value of getFlattenedSize() when the
+    // object was flattened.
+    // unflattened file descriptors are found in the fds[] array and
+    // don't need to be dupped(). ie: the caller of unflatten doesn't
+    // keep ownership. If a fd is not retained by unflatten() it must be
+    // explicitly closed.
+    virtual status_t unflatten(void const* buffer, size_t size,
+            int fds[], size_t count) = 0;
+
+protected:
+    virtual ~Flattenable() = 0;
+
+};
+
+}; // namespace android
+
+
+#endif /* ANDROID_UTILS_FLATTENABLE_H */
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index 13ea27e..cd657e8 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -447,6 +447,8 @@
     }
     const char16_t* stringAt(size_t idx, size_t* outLen) const;
 
+    const char* string8At(size_t idx, size_t* outLen) const;
+
     const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const;
     const ResStringPool_span* styleAt(size_t idx) const;
 
diff --git a/keystore/java/android/security/SystemKeyStore.java b/keystore/java/android/security/SystemKeyStore.java
index 61a4293..abdb0ae 100644
--- a/keystore/java/android/security/SystemKeyStore.java
+++ b/keystore/java/android/security/SystemKeyStore.java
@@ -17,6 +17,7 @@
 package android.security;
 
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.Process;
 
 import java.io.File;
@@ -92,6 +93,8 @@
             fos.write(retKey);
             fos.flush();
             fos.close();
+            FileUtils.setPermissions(keyFile.getName(), (FileUtils.S_IRUSR | FileUtils.S_IWUSR),
+                -1, -1);
         } catch (IOException ioe) {
             return null;
         }
diff --git a/libs/audioflinger/AudioPolicyManagerBase.cpp b/libs/audioflinger/AudioPolicyManagerBase.cpp
index 096aa73..42b6508 100644
--- a/libs/audioflinger/AudioPolicyManagerBase.cpp
+++ b/libs/audioflinger/AudioPolicyManagerBase.cpp
@@ -295,13 +295,31 @@
     if (oldState == AudioSystem::MODE_IN_CALL && newDevice == 0) {
         newDevice = hwOutputDesc->device();
     }
+
+    // when changing from ring tone to in call mode, mute the ringing tone
+    // immediately and delay the route change to avoid sending the ring tone
+    // tail into the earpiece or headset.
+    int delayMs = 0;
+    if (state == AudioSystem::MODE_IN_CALL && oldState == AudioSystem::MODE_RINGTONE) {
+        // delay the device change command by twice the output latency to have some margin
+        // and be sure that audio buffers not yet affected by the mute are out when
+        // we actually apply the route change
+        delayMs = hwOutputDesc->mLatency*2;
+        setStreamMute(AudioSystem::RING, true, mHardwareOutput);
+    }
+
     // change routing is necessary
-    setOutputDevice(mHardwareOutput, newDevice, force);
+    setOutputDevice(mHardwareOutput, newDevice, force, delayMs);
 
     // if entering in call state, handle special case of active streams
     // pertaining to sonification strategy see handleIncallSonification()
     if (state == AudioSystem::MODE_IN_CALL) {
         LOGV("setPhoneState() in call state management: new state is %d", state);
+        // unmute the ringing tone after a sufficient delay if it was muted before
+        // setting output device above
+        if (oldState == AudioSystem::MODE_RINGTONE) {
+            setStreamMute(AudioSystem::RING, false, mHardwareOutput, MUTE_TIME_MS);
+        }
         for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
             handleIncallSonification(stream, true, true);
         }
@@ -1207,10 +1225,10 @@
         return INVALID_OPERATION;
     }
 
-    // mute media during 2 seconds to avoid outputing sound on hardware output while music stream
+    // mute media strategy to avoid outputting sound on hardware output while music stream
     // is switched from A2DP output and before music is paused by music application
     setStrategyMute(STRATEGY_MEDIA, true, mHardwareOutput);
-    setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, 2000);
+    setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, MUTE_TIME_MS);
 
     if (!a2dpUsedForSonification()) {
         // unmute music on A2DP output if a notification or ringtone is playing
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index e397bce..00d2210 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -28,6 +28,7 @@
 #include <utils/String16.h>
 #include <utils/TextOutput.h>
 #include <utils/misc.h>
+#include <utils/Flattenable.h>
 
 #include <private/binder/binder_module.h>
 
@@ -675,6 +676,42 @@
     return writeObject(obj, true);
 }
 
+status_t Parcel::write(const Flattenable& val)
+{
+    status_t err;
+
+    // size if needed
+    size_t len = val.getFlattenedSize();
+    size_t fd_count = val.getFdCount();
+
+    err = this->writeInt32(len);
+    if (err) return err;
+
+    err = this->writeInt32(fd_count);
+    if (err) return err;
+
+    // payload
+    void* buf = this->writeInplace(PAD_SIZE(len));
+    if (buf == NULL)
+        return BAD_VALUE;
+
+    int* fds = NULL;
+    if (fd_count) {
+        fds = new int[fd_count];
+    }
+
+    err = val.flatten(buf, len, fds, fd_count);
+    for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
+        err = this->writeDupFileDescriptor( fds[i] );
+    }
+
+    if (fd_count) {
+        delete [] fds;
+    }
+
+    return err;
+}
+
 status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
 {
     const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
@@ -713,7 +750,6 @@
     goto restart_write;
 }
 
-
 void Parcel::remove(size_t start, size_t amt)
 {
     LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!");
@@ -940,6 +976,38 @@
     return BAD_TYPE;
 }
 
+status_t Parcel::read(Flattenable& val) const
+{
+    // size
+    const size_t len = this->readInt32();
+    const size_t fd_count = this->readInt32();
+
+    // payload
+    void const* buf = this->readInplace(PAD_SIZE(len));
+    if (buf == NULL)
+        return BAD_VALUE;
+
+    int* fds = NULL;
+    if (fd_count) {
+        fds = new int[fd_count];
+    }
+
+    status_t err = NO_ERROR;
+    for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
+        fds[i] = dup(this->readFileDescriptor());
+        if (fds[i] < 0) err = BAD_VALUE;
+    }
+
+    if (err == NO_ERROR) {
+        err = val.unflatten(buf, len, fds, fd_count);
+    }
+
+    if (fd_count) {
+        delete [] fds;
+    }
+
+    return err;
+}
 const flat_binder_object* Parcel::readObject(bool nullMetaData) const
 {
     const size_t DPOS = mDataPos;
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index dec993a..cc3a74fb 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -546,6 +546,8 @@
 
             glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mGL.mMaxFragmentTextureImageUnits);
             glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGL.mMaxFragmentUniformVectors);
+
+            mGL.OES_texture_npot = NULL != strstr((const char *)mGL.mExtensions, "GL_OES_texture_npot");
         }
 
     }
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 03e65f1..04bd748 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -163,6 +163,8 @@
 
     mutable const ObjectBase * mObjHead;
 
+    bool ext_OES_texture_npot() const {return mGL.OES_texture_npot;}
+
 protected:
     Device *mDev;
 
@@ -196,6 +198,8 @@
         int32_t mMaxVertexAttribs;
         int32_t mMaxVertexUniformVectors;
         int32_t mMaxVertexTextureUnits;
+
+        bool OES_texture_npot;
     } mGL;
 
     uint32_t mWidth;
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index 15f3269..c17b94c 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -109,7 +109,7 @@
         }
 
         if (mSamplers[ct].get()) {
-            mSamplers[ct]->setupGL(rsc);
+            mSamplers[ct]->setupGL(rsc, mTextures[ct]->getType()->getIsNp2());
         } else {
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -159,7 +159,7 @@
         glBindTexture(GL_TEXTURE_2D, mTextures[ct]->getTextureID());
         rsc->checkError("ProgramFragment::setupGL2 tex bind");
         if (mSamplers[ct].get()) {
-            mSamplers[ct]->setupGL(rsc);
+            mSamplers[ct]->setupGL(rsc, mTextures[ct]->getType()->getIsNp2());
         } else {
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp
index 7552d54..71f508f 100644
--- a/libs/rs/rsSampler.cpp
+++ b/libs/rs/rsSampler.cpp
@@ -53,7 +53,7 @@
 {
 }
 
-void Sampler::setupGL(const Context *rsc)
+void Sampler::setupGL(const Context *rsc, bool npot)
 {
     GLenum trans[] = {
         GL_NEAREST, //RS_SAMPLER_NEAREST,
@@ -64,11 +64,21 @@
 
     };
 
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
+    bool forceNonMip = false;
+    if (!rsc->ext_OES_texture_npot() && npot) {
+        forceNonMip = true;
+    }
+
+    if ((mMinFilter == RS_SAMPLER_LINEAR_MIP_LINEAR) && forceNonMip) {
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    } else {
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
+    }
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, trans[mWrapS]);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, trans[mWrapT]);
 
+
     rsc->checkError("ProgramFragment::setupGL2 tex env");
 }
 
diff --git a/libs/rs/rsSampler.h b/libs/rs/rsSampler.h
index 9e20a2f..0506081 100644
--- a/libs/rs/rsSampler.h
+++ b/libs/rs/rsSampler.h
@@ -41,7 +41,7 @@
     virtual ~Sampler();
 
     void bind(Allocation *);
-    void setupGL(const Context *);
+    void setupGL(const Context *, bool npot);
 
     void bindToContext(SamplerState *, uint32_t slot);
     void unbindFromContext(SamplerState *);
diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp
index 9d24c6c..c09e979 100644
--- a/libs/rs/rsType.cpp
+++ b/libs/rs/rsType.cpp
@@ -283,6 +283,24 @@
     mElement->dumpLOGV(buf);
 }
 
+bool Type::getIsNp2() const
+{
+    uint32_t x = getDimX();
+    uint32_t y = getDimY();
+    uint32_t z = getDimZ();
+
+    if (x && (x & (x-1))) {
+        return true;
+    }
+    if (y && (y & (y-1))) {
+        return true;
+    }
+    if (z && (z & (z-1))) {
+        return true;
+    }
+    return false;
+}
+
 
 //////////////////////////////////////////////////
 //
diff --git a/libs/rs/rsType.h b/libs/rs/rsType.h
index 28e6274..c25577c 100644
--- a/libs/rs/rsType.h
+++ b/libs/rs/rsType.h
@@ -56,6 +56,7 @@
     uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const;
 
     uint32_t getLODCount() const {return mLODCount;}
+    bool getIsNp2() const;
 
 
     void setElement(const Element *e) {mElement.set(e);}
@@ -65,6 +66,7 @@
     void setDimFaces(bool v) {mFaces = v;}
     void setDimLOD(bool v) {mDimLOD = v;}
 
+
     void clear();
     void compute();
 
diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk
index 395a937..86eb78d 100644
--- a/libs/surfaceflinger/Android.mk
+++ b/libs/surfaceflinger/Android.mk
@@ -35,7 +35,6 @@
 	libpixelflinger \
 	libhardware \
 	libutils \
-	libskia \
 	libEGL \
 	libGLESv1_CM \
 	libbinder \
diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h
index d1bbd04..6aacd82 100644
--- a/libs/surfaceflinger/LayerBase.h
+++ b/libs/surfaceflinger/LayerBase.h
@@ -22,6 +22,7 @@
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
+#include <GLES/gl.h>
 
 #include <utils/RefBase.h>
 
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 2d6152e..b408779 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -1496,8 +1496,8 @@
                     layer->needsBlending(), layer->needsDithering(),
                     layer->contentDirty,
                     s.alpha, s.flags,
-                    s.transform[0], s.transform[1],
-                    s.transform[2], s.transform[3]);
+                    s.transform[0][0], s.transform[0][1],
+                    s.transform[1][0], s.transform[1][1]);
             result.append(buffer);
             buffer[0] = 0;
             /*** LayerBaseClient ***/
@@ -1833,27 +1833,25 @@
 
 status_t GraphicPlane::orientationToTransfrom(
         int orientation, int w, int h, Transform* tr)
-{    
-    float a, b, c, d, x, y;
+{
+    uint32_t flags = 0;
     switch (orientation) {
     case ISurfaceComposer::eOrientationDefault:
-        // make sure the default orientation is optimal
-        tr->reset();
-        return NO_ERROR;
+        flags = Transform::ROT_0;
+        break;
     case ISurfaceComposer::eOrientation90:
-        a=0; b=-1; c=1; d=0; x=w; y=0;
+        flags = Transform::ROT_90;
         break;
     case ISurfaceComposer::eOrientation180:
-        a=-1; b=0; c=0; d=-1; x=w; y=h;
+        flags = Transform::ROT_180;
         break;
     case ISurfaceComposer::eOrientation270:
-        a=0; b=1; c=-1; d=0; x=0; y=h;
+        flags = Transform::ROT_270;
         break;
     default:
         return BAD_VALUE;
     }
-    tr->set(a, b, c, d);
-    tr->set(x, y);
+    tr->set(flags, w, h);
     return NO_ERROR;
 }
 
@@ -1869,24 +1867,13 @@
     mHeight = int(h);
 
     Transform orientationTransform;
-    if (UNLIKELY(orientation == 42)) {
-        float a, b, c, d, x, y;
-        const float r = (3.14159265f / 180.0f) * 42.0f;
-        const float si = sinf(r);
-        const float co = cosf(r);
-        a=co; b=-si; c=si; d=co;
-        x = si*(h*0.5f) + (1-co)*(w*0.5f);
-        y =-si*(w*0.5f) + (1-co)*(h*0.5f);
-        orientationTransform.set(a, b, c, d);
-        orientationTransform.set(x, y);
-    } else {
-        GraphicPlane::orientationToTransfrom(orientation, w, h,
-                &orientationTransform);
-        if (orientation & ISurfaceComposer::eOrientationSwapMask) {
-            mWidth = int(h);
-            mHeight = int(w);
-        }
+    GraphicPlane::orientationToTransfrom(orientation, w, h,
+            &orientationTransform);
+    if (orientation & ISurfaceComposer::eOrientationSwapMask) {
+        mWidth = int(h);
+        mHeight = int(w);
     }
+
     mOrientation = orientation;
     mGlobalTransform = mDisplayTransform * orientationTransform;
     return NO_ERROR;
diff --git a/libs/surfaceflinger/Transform.cpp b/libs/surfaceflinger/Transform.cpp
index ab6f7ba..b2d5856 100644
--- a/libs/surfaceflinger/Transform.cpp
+++ b/libs/surfaceflinger/Transform.cpp
@@ -14,180 +14,257 @@
  * limitations under the License.
  */
 
+#include <math.h>
+
+#include <cutils/compiler.h>
+#include <utils/String8.h>
 #include <ui/Region.h>
 
-#include <private/pixelflinger/ggl_fixed.h>
-
 #include "Transform.h"
 
 // ---------------------------------------------------------------------------
 
-#define LIKELY( exp )       (__builtin_expect( (exp) != 0, true  ))
-#define UNLIKELY( exp )     (__builtin_expect( (exp) != 0, false ))
-
-// ---------------------------------------------------------------------------
-
 namespace android {
 
 // ---------------------------------------------------------------------------
 
-Transform::Transform()
-    : mType(0)
-{
-    mTransform.reset();
+template <typename T> inline T min(T a, T b) {
+    return a<b ? a : b;
+}
+template <typename T> inline T min(T a, T b, T c) {
+    return min(a, min(b, c));
+}
+template <typename T> inline T min(T a, T b, T c, T d) {
+    return min(a, b, min(c, d));
+}
+
+template <typename T> inline T max(T a, T b) {
+    return a>b ? a : b;
+}
+template <typename T> inline T max(T a, T b, T c) {
+    return max(a, max(b, c));
+}
+template <typename T> inline T max(T a, T b, T c, T d) {
+    return max(a, b, max(c, d));
+}
+
+// ---------------------------------------------------------------------------
+
+Transform::Transform() {
+    reset();
 }
 
 Transform::Transform(const Transform&  other)
-    : mTransform(other.mTransform), mType(other.mType)
-{
+    : mMatrix(other.mMatrix), mType(other.mType) {
 }
 
-Transform::Transform(int32_t flags) {
-    mTransform.reset();
-    int sx = (flags & FLIP_H) ? -1 : 1;
-    int sy = (flags & FLIP_V) ? -1 : 1;
-    if (flags & ROT_90) {
-        this->set(0, -sy, sx, 0);
-    } else {
-        this->set(sx, 0, 0, sy);
-    }
+Transform::Transform(uint32_t orientation) {
+    set(orientation, 0, 0);
 }
 
 Transform::~Transform() {
 }
 
+
+bool Transform::absIsOne(float f) {
+    return fabs(f) == 1.0f;
+}
+
+bool Transform::isZero(float f) {
+    return fabs(f) == 0.0f;
+}
+
+bool Transform::absEqual(float a, float b) {
+    return fabs(a) == fabs(b);
+}
+
 Transform Transform::operator * (const Transform& rhs) const
 {
-    if (LIKELY(mType == 0))
+    if (CC_LIKELY(mType == IDENTITY))
         return rhs;
 
     Transform r(*this);
-    r.mTransform.preConcat(rhs.mTransform);
+    if (rhs.mType == IDENTITY)
+        return r;
+
+    // TODO: we could use mType to optimize the matrix multiply
+    const mat33& A(mMatrix);
+    const mat33& B(rhs.mMatrix);
+          mat33& D(r.mMatrix);
+    for (int i=0 ; i<3 ; i++) {
+        const float v0 = A[0][i];
+        const float v1 = A[1][i];
+        const float v2 = A[2][i];
+        D[0][i] = v0*B[0][0] + v1*B[0][1] + v2*B[0][2];
+        D[1][i] = v0*B[1][0] + v1*B[1][1] + v2*B[1][2];
+        D[2][i] = v0*B[2][0] + v1*B[2][1] + v2*B[2][2];
+    }
     r.mType |= rhs.mType;
+
+    // TODO: we could recompute this value from r and rhs
+    r.mType &= 0xFF;
+    r.mType |= UNKNOWN_TYPE;
     return r;
 }
 
-float Transform::operator [] (int i) const
-{
-    float r = 0;
-    switch(i) {
-        case 0: r = SkScalarToFloat( mTransform[SkMatrix::kMScaleX] );  break;
-        case 1: r = SkScalarToFloat( mTransform[SkMatrix::kMSkewX] );   break;
-        case 2: r = SkScalarToFloat( mTransform[SkMatrix::kMSkewY] );   break;
-        case 3: r = SkScalarToFloat( mTransform[SkMatrix::kMScaleY] );  break;
-    }
-    return r;
-}
-
-uint8_t Transform::type() const
-{
-    if (UNLIKELY(mType & 0x80000000)) {
-        mType = mTransform.getType();
-    }
-    return uint8_t(mType & 0xFF);
+float const* Transform::operator [] (int i) const {
+    return mMatrix[i].v;
 }
 
 bool Transform::transformed() const {
-    return type() > SkMatrix::kTranslate_Mask;
+    return type() > TRANSLATE;
 }
 
 int Transform::tx() const {
-    return SkScalarRound( mTransform[SkMatrix::kMTransX] );
+    return floorf(mMatrix[2][0] + 0.5f);
 }
 
 int Transform::ty() const {
-    return SkScalarRound( mTransform[SkMatrix::kMTransY] );
+    return floorf(mMatrix[2][1] + 0.5f);
 }
 
 void Transform::reset() {
-    mTransform.reset();
-    mType = 0;
-}
-
-void Transform::set( float xx, float xy,
-                     float yx, float yy)
-{
-    mTransform.set(SkMatrix::kMScaleX, SkFloatToScalar(xx));
-    mTransform.set(SkMatrix::kMSkewX, SkFloatToScalar(xy));
-    mTransform.set(SkMatrix::kMSkewY, SkFloatToScalar(yx));
-    mTransform.set(SkMatrix::kMScaleY, SkFloatToScalar(yy));
-    mType |= 0x80000000;
-}
-
-void Transform::set(float radian, float x, float y)
-{
-    float r00 = cosf(radian);    float r01 = -sinf(radian);
-    float r10 = sinf(radian);    float r11 =  cosf(radian);
-    mTransform.set(SkMatrix::kMScaleX, SkFloatToScalar(r00));
-    mTransform.set(SkMatrix::kMSkewX, SkFloatToScalar(r01));
-    mTransform.set(SkMatrix::kMSkewY, SkFloatToScalar(r10));
-    mTransform.set(SkMatrix::kMScaleY, SkFloatToScalar(r11));
-    mTransform.set(SkMatrix::kMTransX, SkIntToScalar(x - r00*x - r01*y));
-    mTransform.set(SkMatrix::kMTransY, SkIntToScalar(y - r10*x - r11*y));
-    mType |= 0x80000000 | SkMatrix::kTranslate_Mask;
-}
-
-void Transform::scale(float s, float x, float y)
-{
-    mTransform.postScale(s, s, x, y); 
-    mType |= 0x80000000;
-}
-
-void Transform::set(int tx, int ty)
-{
-    if (tx | ty) {
-        mTransform.set(SkMatrix::kMTransX, SkIntToScalar(tx));
-        mTransform.set(SkMatrix::kMTransY, SkIntToScalar(ty));
-        mType |= SkMatrix::kTranslate_Mask;
-    } else {
-        mTransform.set(SkMatrix::kMTransX, 0);
-        mTransform.set(SkMatrix::kMTransY, 0);
-        mType &= ~SkMatrix::kTranslate_Mask;
+    mType = IDENTITY;
+    for(int i=0 ; i<3 ; i++) {
+        vec3& v(mMatrix[i]);
+        for (int j=0 ; j<3 ; j++)
+            v[j] = ((i==j) ? 1.0f : 0.0f);
     }
 }
 
-void Transform::transform(GLfixed* point, int x, int y) const
+void Transform::set(float tx, float ty)
 {
-    SkPoint s;
-    mTransform.mapXY(SkIntToScalar(x), SkIntToScalar(y), &s);
-    point[0] = SkScalarToFixed(s.fX);
-    point[1] = SkScalarToFixed(s.fY);
+    mMatrix[2][0] = tx;
+    mMatrix[2][1] = ty;
+    mMatrix[2][2] = 1.0f;
+
+    if (isZero(tx) && isZero(ty)) {
+        mType &= ~TRANSLATE;
+    } else {
+        mType |= TRANSLATE;
+    }
+}
+
+void Transform::set(float a, float b, float c, float d)
+{
+    mat33& M(mMatrix);
+    M[0][0] = a;    M[1][0] = b;
+    M[0][1] = c;    M[1][1] = d;
+    M[0][2] = 0;    M[1][2] = 0;
+    mType = UNKNOWN_TYPE;
+}
+
+void Transform::set(uint32_t flags, float w, float h)
+{
+    mType = flags << 8;
+    float sx = (flags & FLIP_H) ? -1 : 1;
+    float sy = (flags & FLIP_V) ? -1 : 1;
+    float a=0, b=0, c=0, d=0, x=0, y=0;
+    int xmask = 0;
+
+    // computation of x,y
+    // x y
+    // 0 0  0
+    // w 0  ROT90
+    // w h  FLIPH|FLIPV
+    // 0 h  FLIPH|FLIPV|ROT90
+
+    if (flags & ROT_90) {
+        mType |= ROTATE;
+        b = -sy;
+        c = sx;
+        xmask = 1;
+    } else {
+        a = sx;
+        d = sy;
+    }
+
+    if (flags & FLIP_H) {
+        mType ^= SCALE;
+        xmask ^= 1;
+    }
+
+    if (flags & FLIP_V) {
+        mType ^= SCALE;
+        y = h;
+    }
+
+    if ((flags & ROT_180) == ROT_180) {
+        mType |= ROTATE;
+    }
+
+    if (xmask) {
+        x = w;
+    }
+
+    if (!isZero(x) || !isZero(y)) {
+        mType |= TRANSLATE;
+    }
+
+    mat33& M(mMatrix);
+    M[0][0] = a;    M[1][0] = b;    M[2][0] = x;
+    M[0][1] = c;    M[1][1] = d;    M[2][1] = y;
+    M[0][2] = 0;    M[1][2] = 0;    M[2][2] = 1;
+}
+
+Transform::vec2 Transform::transform(const vec2& v) const {
+    vec2 r;
+    const mat33& M(mMatrix);
+    r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0];
+    r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1];
+    return r;
+}
+
+Transform::vec3 Transform::transform(const vec3& v) const {
+    vec3 r;
+    const mat33& M(mMatrix);
+    r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]*v[2];
+    r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]*v[2];
+    r[2] = M[0][2]*v[0] + M[1][2]*v[1] + M[2][2]*v[2];
+    return r;
+}
+
+void Transform::transform(fixed1616* point, int x, int y) const
+{
+    const float toFixed = 65536.0f;
+    const mat33& M(mMatrix);
+    vec2 v(x, y);
+    v = transform(v);
+    point[0] = v[0] * toFixed;
+    point[1] = v[1] * toFixed;
 }
 
 Rect Transform::makeBounds(int w, int h) const
 {
-    Rect r;
-    SkRect d, s;
-    s.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
-    mTransform.mapRect(&d, s);
-    r.left   = SkScalarRound( d.fLeft );
-    r.top    = SkScalarRound( d.fTop );
-    r.right  = SkScalarRound( d.fRight );
-    r.bottom = SkScalarRound( d.fBottom );
-    return r;
+    return transform( Rect(w, h) );
 }
 
 Rect Transform::transform(const Rect& bounds) const
 {
     Rect r;
-    SkRect d, s;
-    s.set(  SkIntToScalar( bounds.left ),
-            SkIntToScalar( bounds.top ),
-            SkIntToScalar( bounds.right ),
-            SkIntToScalar( bounds.bottom ));
-    mTransform.mapRect(&d, s);
-    r.left   = SkScalarRound( d.fLeft );
-    r.top    = SkScalarRound( d.fTop );
-    r.right  = SkScalarRound( d.fRight );
-    r.bottom = SkScalarRound( d.fBottom );
+    vec2 lt( bounds.left,  bounds.top    );
+    vec2 rt( bounds.right, bounds.top    );
+    vec2 lb( bounds.left,  bounds.bottom );
+    vec2 rb( bounds.right, bounds.bottom );
+
+    lt = transform(lt);
+    rt = transform(rt);
+    lb = transform(lb);
+    rb = transform(rb);
+
+    r.left   = floorf(min(lt[0], rt[0], lb[0], rb[0]) + 0.5f);
+    r.top    = floorf(min(lt[1], rt[1], lb[1], rb[1]) + 0.5f);
+    r.right  = floorf(max(lt[0], rt[0], lb[0], rb[0]) + 0.5f);
+    r.bottom = floorf(max(lt[1], rt[1], lb[1], rb[1]) + 0.5f);
+
     return r;
 }
 
 Region Transform::transform(const Region& reg) const
 {
     Region out;
-    if (UNLIKELY(transformed())) {
-        if (LIKELY(preserveRects())) {
+    if (CC_UNLIKELY(transformed())) {
+        if (CC_LIKELY(preserveRects())) {
             Region::const_iterator it = reg.begin();
             Region::const_iterator const end = reg.end();
             while (it != end) {
@@ -202,31 +279,108 @@
     return out;
 }
 
-int32_t Transform::getOrientation() const
+uint32_t Transform::type() const
 {
-    uint32_t flags = 0;
-    if (UNLIKELY(transformed())) {
-        SkScalar a = mTransform[SkMatrix::kMScaleX];
-        SkScalar b = mTransform[SkMatrix::kMSkewX];
-        SkScalar c = mTransform[SkMatrix::kMSkewY];
-        SkScalar d = mTransform[SkMatrix::kMScaleY];
-        if (b==0 && c==0 && a && d) {
-            if (a<0)    flags |= FLIP_H;
-            if (d<0)    flags |= FLIP_V;
-        } else if (b && c && a==0 && d==0) {
-            flags |= ROT_90;
-            if (b>0)    flags |= FLIP_H;
-            if (c<0)    flags |= FLIP_V;
+    if (mType & UNKNOWN_TYPE) {
+        // recompute what this transform is
+
+        const mat33& M(mMatrix);
+        const float a = M[0][0];
+        const float b = M[1][0];
+        const float c = M[0][1];
+        const float d = M[1][1];
+        const float x = M[2][0];
+        const float y = M[2][1];
+
+        bool scale = false;
+        uint32_t flags = ROT_0;
+        if (isZero(b) && isZero(c)) {
+            if (absEqual(a, d)) {
+                if (a<0)    flags |= FLIP_H;
+                if (d<0)    flags |= FLIP_V;
+                if (!absIsOne(a) || !absIsOne(d)) {
+                    scale = true;
+                }
+            } else {
+                flags = ROT_INVALID;
+            }
+        } else if (isZero(a) && isZero(d)) {
+            if (absEqual(b, c)) {
+                flags |= ROT_90;
+                if (b>0)    flags |= FLIP_H;
+                if (c<0)    flags |= FLIP_V;
+                if (!absIsOne(b) || !absIsOne(c)) {
+                    scale = true;
+                }
+            } else {
+                flags = ROT_INVALID;
+            }
         } else {
-            flags = 0x80000000;
+            flags = ROT_INVALID;
         }
+
+        mType = flags << 8;
+        if (flags & ROT_INVALID) {
+            mType |= UNKNOWN;
+        } else {
+            if ((flags & ROT_90) || ((flags & ROT_180) == ROT_180))
+                mType |= ROTATE;
+            if (flags & FLIP_H)
+                mType ^= SCALE;
+            if (flags & FLIP_V)
+                mType ^= SCALE;
+            if (scale)
+                mType |= SCALE;
+        }
+
+        if (!isZero(x) || !isZero(y))
+            mType |= TRANSLATE;
     }
-    return flags;
+    return mType;
+}
+
+uint32_t Transform::getType() const {
+    return type() & 0xFF;
+}
+
+uint32_t Transform::getOrientation() const
+{
+    return (type() >> 8) & 0xFF;
 }
 
 bool Transform::preserveRects() const
 {
-    return mTransform.rectStaysRect();
+    return (type() & ROT_INVALID) ? false : true;
+}
+
+void Transform::dump(const char* name) const
+{
+    type(); // updates the type
+
+    String8 flags, type;
+    const mat33& m(mMatrix);
+    uint32_t orient = mType >> 8;
+
+    if (orient&ROT_INVALID)
+        flags.append("ROT_INVALID ");
+    if (orient&ROT_90)
+        flags.append("ROT_90 ");
+    if (orient&FLIP_V)
+        flags.append("FLIP_V ");
+    if (orient&FLIP_H)
+        flags.append("FLIP_H ");
+
+    if (mType&SCALE)
+        type.append("SCALE ");
+    if (mType&ROTATE)
+        type.append("ROTATE ");
+    if (mType&TRANSLATE)
+        type.append("TRANSLATE ");
+
+    LOGD("%s (%s, %s)", name, flags.string(), type.string());
+    LOGD("%.2f  %.2f  %.2f", m[0][0], m[1][0], m[2][0]);
+    LOGD("%.2f  %.2f  %.2f", m[0][1], m[1][1], m[2][1]);
+    LOGD("%.2f  %.2f  %.2f", m[0][2], m[1][2], m[2][2]);
 }
 
 // ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/Transform.h b/libs/surfaceflinger/Transform.h
index ddab404..51d3e3f 100644
--- a/libs/surfaceflinger/Transform.h
+++ b/libs/surfaceflinger/Transform.h
@@ -23,10 +23,6 @@
 #include <ui/Point.h>
 #include <ui/Rect.h>
 
-#include <GLES/gl.h>
-
-#include <core/SkMatrix.h>
-
 namespace android {
 
 class Region;
@@ -38,9 +34,12 @@
 public:
                     Transform();
                     Transform(const Transform&  other);
-                    Transform(int32_t flags);
+           explicit Transform(uint32_t orientation);
                     ~Transform();
 
+            typedef int32_t fixed1616;
+
+            // FIXME: must match OVERLAY_TRANSFORM_*, pull from hardware.h
             enum orientation_flags {
                 ROT_0   = 0x00000000,
                 FLIP_H  = 0x00000001,
@@ -48,48 +47,79 @@
                 ROT_90  = 0x00000004,
                 ROT_180 = FLIP_H|FLIP_V,
                 ROT_270 = ROT_180|ROT_90,
-                ROT_INVALID = 0x80000000
+                ROT_INVALID = 0x80
             };
 
             enum type_mask {
                 IDENTITY            = 0,
                 TRANSLATE           = 0x1,
-                SCALE               = 0x2,
-                AFFINE              = 0x4,
-                PERSPECTIVE         = 0x8
+                ROTATE              = 0x2,
+                SCALE               = 0x4,
+                UNKNOWN             = 0x8
             };
 
-            bool    transformed() const;
-            int32_t getOrientation() const;
-            bool    preserveRects() const;
-            
+            // query the transform
+            bool        transformed() const;
+            bool        preserveRects() const;
+            uint32_t    getType() const;
+            uint32_t    getOrientation() const;
+
+            float const* operator [] (int i) const;  // returns column i
             int     tx() const;
             int     ty() const;
-        
+
+            // modify the transform
             void    reset();
-            void    set(float xx, float xy, float yx, float yy);
-            void    set(int tx, int ty);
-            void    set(float radian, float x, float y);
-            void    scale(float s, float x, float y);
-            
+            void    set(float tx, float ty);
+            void    set(float a, float b, float c, float d);
+            void    set(uint32_t flags, float w, float h);
+
+            // transform data
             Rect    makeBounds(int w, int h) const;
-            void    transform(GLfixed* point, int x, int y) const;
+            void    transform(fixed1616* point, int x, int y) const;
             Region  transform(const Region& reg) const;
-            Rect    transform(const Rect& bounds) const;
-
             Transform operator * (const Transform& rhs) const;
-            float operator [] (int i) const;
-
-    inline uint32_t getType() const { return type(); }
-            
-    inline Transform(bool) : mType(0xFF) { };
 
 private:
-    uint8_t     type() const;
+    struct vec3 {
+        float v[3];
+        inline vec3() { }
+        inline vec3(float a, float b, float c) {
+            v[0] = a; v[1] = b; v[2] = c;
+        }
+        inline float operator [] (int i) const { return v[i]; }
+        inline float& operator [] (int i) { return v[i]; }
+    };
+    struct vec2 {
+        float v[2];
+        inline vec2() { }
+        inline vec2(float a, float b) {
+            v[0] = a; v[1] = b;
+        }
+        inline float operator [] (int i) const { return v[i]; }
+        inline float& operator [] (int i) { return v[i]; }
+    };
+    struct mat33 {
+        vec3 v[3];
+        inline const vec3& operator [] (int i) const { return v[i]; }
+        inline vec3& operator [] (int i) { return v[i]; }
+    };
 
-private:
-            SkMatrix    mTransform;
-    mutable uint32_t    mType;      
+    enum { UNKNOWN_TYPE = 0x80000000 };
+
+    // assumes the last row is < 0 , 0 , 1 >
+    vec2 transform(const vec2& v) const;
+    vec3 transform(const vec3& v) const;
+    Rect transform(const Rect& bounds) const;
+    uint32_t type() const;
+    static bool absIsOne(float f);
+    static bool absEqual(float a, float b);
+    static bool isZero(float f);
+
+    void dump(const char* name) const;
+
+    mat33               mMatrix;
+    mutable uint32_t    mType;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger_client/ISurface.cpp b/libs/surfaceflinger_client/ISurface.cpp
index 9125146..bb86199 100644
--- a/libs/surfaceflinger_client/ISurface.cpp
+++ b/libs/surfaceflinger_client/ISurface.cpp
@@ -78,7 +78,8 @@
         data.writeInt32(bufferIdx);
         data.writeInt32(usage);
         remote()->transact(REQUEST_BUFFER, data, &reply);
-        sp<GraphicBuffer> buffer = new GraphicBuffer(reply);
+        sp<GraphicBuffer> buffer = new GraphicBuffer();
+        reply.read(*buffer);
         return buffer;
     }
 
@@ -141,7 +142,9 @@
             int bufferIdx = data.readInt32();
             int usage = data.readInt32();
             sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, usage));
-            return GraphicBuffer::writeToParcel(reply, buffer.get());
+            if (buffer == NULL)
+                return BAD_VALUE;
+            return reply->write(*buffer);
         }
         case REGISTER_BUFFERS: {
             CHECK_INTERFACE(ISurface, data, reply);
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 6a5c8a9..ba1fd9c 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "GraphicBuffer"
+
 #include <stdlib.h>
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <binder/Parcel.h>
-
 #include <utils/Errors.h>
 #include <utils/Log.h>
 
@@ -77,34 +77,21 @@
     handle = inHandle;
 }
 
-GraphicBuffer::GraphicBuffer(const Parcel& data) 
-    : BASE(), mOwner(ownHandle), mBufferMapper(GraphicBufferMapper::get()),
-      mInitCheck(NO_ERROR),  mVStride(0), mIndex(-1)
-{
-    // we own the handle in this case
-    width  = data.readInt32();
-    if (width < 0) {
-        width = height = stride = format = usage = 0;
-        handle = 0;
-    } else {
-        height = data.readInt32();
-        stride = data.readInt32();
-        format = data.readInt32();
-        usage  = data.readInt32();
-        handle = data.readNativeHandle();
-    }
-}
-
 GraphicBuffer::~GraphicBuffer()
 {
     if (handle) {
-        if (mOwner == ownHandle) {
-            native_handle_close(handle);
-            native_handle_delete(const_cast<native_handle*>(handle));
-        } else if (mOwner == ownData) {
-            GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
-            allocator.free(handle);
-        }
+        free_handle();
+    }
+}
+
+void GraphicBuffer::free_handle()
+{
+    if (mOwner == ownHandle) {
+        native_handle_close(handle);
+        native_handle_delete(const_cast<native_handle*>(handle));
+    } else if (mOwner == ownData) {
+        GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
+        allocator.free(handle);
     }
 }
 
@@ -192,29 +179,83 @@
     return res;
 }
 
+size_t GraphicBuffer::getFlattenedSize() const {
+    return (8 + (handle ? handle->numInts : 0))*sizeof(int);
+}
 
-status_t GraphicBuffer::writeToParcel(Parcel* reply, 
-        android_native_buffer_t const* buffer)
+size_t GraphicBuffer::getFdCount() const {
+    return handle ? handle->numFds : 0;
+}
+
+status_t GraphicBuffer::flatten(void* buffer, size_t size,
+        int fds[], size_t count) const
 {
-    if (buffer == NULL)
-        return BAD_VALUE;
+    size_t sizeNeeded = GraphicBuffer::getFlattenedSize();
+    if (size < sizeNeeded) return NO_MEMORY;
 
-    if (buffer->width < 0 || buffer->height < 0)
-        return BAD_VALUE;
+    size_t fdCountNeeded = GraphicBuffer::getFdCount();
+    if (count < fdCountNeeded) return NO_MEMORY;
 
-    status_t err = NO_ERROR;
-    if (buffer->handle == NULL) {
-        // this buffer doesn't have a handle
-        reply->writeInt32(NO_MEMORY);
-    } else {
-        reply->writeInt32(buffer->width);
-        reply->writeInt32(buffer->height);
-        reply->writeInt32(buffer->stride);
-        reply->writeInt32(buffer->format);
-        reply->writeInt32(buffer->usage);
-        err = reply->writeNativeHandle(buffer->handle);
+    int* buf = static_cast<int*>(buffer);
+    buf[0] = 'GBFR';
+    buf[1] = width;
+    buf[2] = height;
+    buf[3] = stride;
+    buf[4] = format;
+    buf[5] = usage;
+    buf[6] = 0;
+    buf[7] = 0;
+
+    if (handle) {
+        buf[6] = handle->numFds;
+        buf[7] = handle->numInts;
+        native_handle_t const* const h = handle;
+        memcpy(fds,     h->data,             h->numFds*sizeof(int));
+        memcpy(&buf[8], h->data + h->numFds, h->numInts*sizeof(int));
     }
-    return err;
+
+    return NO_ERROR;
+}
+
+status_t GraphicBuffer::unflatten(void const* buffer, size_t size,
+        int fds[], size_t count)
+{
+    if (size < 8*sizeof(int)) return NO_MEMORY;
+
+    int const* buf = static_cast<int const*>(buffer);
+    if (buf[0] != 'GBFR') return BAD_TYPE;
+
+    const size_t numFds  = buf[6];
+    const size_t numInts = buf[7];
+
+    const size_t sizeNeeded = (8 + numInts) * sizeof(int);
+    if (size < sizeNeeded) return NO_MEMORY;
+
+    size_t fdCountNeeded = 0;
+    if (count < fdCountNeeded) return NO_MEMORY;
+
+    if (handle) {
+        // free previous handle if any
+        free_handle();
+    }
+
+    if (numFds || numInts) {
+        width  = buf[1];
+        height = buf[2];
+        stride = buf[3];
+        format = buf[4];
+        usage  = buf[5];
+        native_handle* h = native_handle_create(numFds, numInts);
+        memcpy(h->data,          fds,     numFds*sizeof(int));
+        memcpy(h->data + numFds, &buf[8], numInts*sizeof(int));
+        handle = h;
+    } else {
+        width = height = stride = format = usage = 0;
+        handle = NULL;
+    }
+
+    mOwner = ownHandle;
+    return NO_ERROR;
 }
 
 
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index d2cfd3b..d0eedb4 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -25,6 +25,7 @@
 	CallStack.cpp \
 	Debug.cpp \
 	FileMap.cpp \
+	Flattenable.cpp \
 	RefBase.cpp \
 	ResourceTypes.cpp \
 	SharedBuffer.cpp \
diff --git a/libs/utils/Flattenable.cpp b/libs/utils/Flattenable.cpp
new file mode 100644
index 0000000..1f2ffaa
--- /dev/null
+++ b/libs/utils/Flattenable.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/Flattenable.h>
+
+namespace android {
+
+Flattenable::~Flattenable() {
+}
+
+}; // namespace android
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index e8bd5cf..38600b9 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -497,6 +497,34 @@
     return NULL;
 }
 
+const char* ResStringPool::string8At(size_t idx, size_t* outLen) const
+{
+    if (mError == NO_ERROR && idx < mHeader->stringCount) {
+        const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0;
+        const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t));
+        if (off < (mStringPoolSize-1)) {
+            if (isUTF8) {
+                const uint8_t* strings = (uint8_t*)mStrings;
+                const uint8_t* str = strings+off;
+                DECODE_LENGTH(str, sizeof(uint8_t), *outLen)
+                size_t encLen;
+                DECODE_LENGTH(str, sizeof(uint8_t), encLen)
+                if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
+                    return (const char*)str;
+                } else {
+                    LOGW("Bad string block: string #%d extends to %d, past end at %d\n",
+                            (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
+                }
+            }
+        } else {
+            LOGW("Bad string block: string #%d entry is at %d, past end at %d\n",
+                    (int)idx, (int)(off*sizeof(uint16_t)),
+                    (int)(mStringPoolSize*sizeof(uint16_t)));
+        }
+    }
+    return NULL;
+}
+
 const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const
 {
     return styleAt(ref.index);
@@ -4018,14 +4046,19 @@
         printf("(attribute) 0x%08x\n", value.data);
     } else if (value.dataType == Res_value::TYPE_STRING) {
         size_t len;
-        const char16_t* str = pkg->header->values.stringAt(
+        const char* str8 = pkg->header->values.string8At(
                 value.data, &len);
-        if (str == NULL) {
-            printf("(string) null\n");
+        if (str8 != NULL) {
+            printf("(string8) \"%s\"\n", str8);
         } else {
-            printf("(string%d) \"%s\"\n",
-                    pkg->header->values.isUTF8()?8:16,
-                    String8(str, len).string());
+            const char16_t* str16 = pkg->header->values.stringAt(
+                    value.data, &len);
+            if (str16 != NULL) {
+                printf("(string16) \"%s\"\n",
+                    String8(str16, len).string());
+            } else {
+                printf("(string) null\n");
+            }
         } 
     } else if (value.dataType == Res_value::TYPE_FLOAT) {
         printf("(float) %g\n", *(const float*)&value.data);
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 8e84106..90b50cc 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -321,7 +321,9 @@
     public GpsLocationProvider(Context context, ILocationManager locationManager) {
         mContext = context;
         mLocationManager = locationManager;
-        mNIHandler= new GpsNetInitiatedHandler(context, this);
+        mNIHandler = new GpsNetInitiatedHandler(context, this);
+
+        mLocation.setExtras(mLocationExtras);
 
         // Create a wake lock
         PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -759,7 +761,7 @@
                 positionMode = GPS_POSITION_MODE_STANDALONE;
             }
 
-            if (!native_start(positionMode, false, mFixInterval)) {
+            if (!native_start(positionMode, false, 1)) {
                 mStarted = false;
                 Log.e(TAG, "native_start failed in startNavigating()");
                 return;
@@ -870,7 +872,12 @@
         }
 
         if (mStarted && mStatus != LocationProvider.AVAILABLE) {
-            mAlarmManager.cancel(mTimeoutIntent);
+            // we still want to time out if we do not receive MIN_FIX_COUNT
+            // within the time out and we are requesting infrequent fixes
+            if (mFixInterval < NO_FIX_TIMEOUT) {
+                mAlarmManager.cancel(mTimeoutIntent);
+            }
+
             // send an intent to notify that the GPS is receiving fixes.
             Intent intent = new Intent(GPS_FIX_CHANGE_ACTION);
             intent.putExtra(EXTRA_ENABLED, true);
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 2294069..9d1d420 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -17,13 +17,16 @@
 package android.media;
 
 import android.content.ContentValues;
-import android.os.SystemProperties;
 import android.provider.MediaStore.Audio;
 import android.provider.MediaStore.Images;
 import android.provider.MediaStore.Video;
+import android.media.DecoderCapabilities;
+import android.media.DecoderCapabilities.VideoDecoder;
+import android.media.DecoderCapabilities.AudioDecoder;
 
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 
 /**
  * MediaScanner helper class.
@@ -98,13 +101,34 @@
         sFileTypeMap.put(extension, new MediaFileType(fileType, mimeType));
         sMimeTypeMap.put(mimeType, Integer.valueOf(fileType));
     }
+
+    private static boolean isWMAEnabled() {
+        List<AudioDecoder> decoders = DecoderCapabilities.getAudioDecoders();
+        for (AudioDecoder decoder: decoders) {
+            if (decoder == AudioDecoder.AUDIO_DECODER_WMA) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean isWMVEnabled() {
+        List<VideoDecoder> decoders = DecoderCapabilities.getVideoDecoders();
+        for (VideoDecoder decoder: decoders) {
+            if (decoder == VideoDecoder.VIDEO_DECODER_WMV) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     static {
         addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg");
         addFileType("M4A", FILE_TYPE_M4A, "audio/mp4");
         addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav");
         addFileType("AMR", FILE_TYPE_AMR, "audio/amr");
         addFileType("AWB", FILE_TYPE_AWB, "audio/amr-wb");
-        if (SystemProperties.getInt("ro.media.dec.aud.wma.enabled", 0) != 0) {
+        if (isWMAEnabled()) {
             addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma");
         }
         addFileType("OGG", FILE_TYPE_OGG, "application/ogg");
@@ -127,7 +151,7 @@
         addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp");
         addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2");
         addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2");
-        if (SystemProperties.getInt("ro.media.dec.vid.wmv.enabled", 0) != 0) {
+        if (isWMVEnabled()) {
             addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv");
             addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf");
         }
diff --git a/media/jni/android_media_ResampleInputStream.cpp b/media/jni/android_media_ResampleInputStream.cpp
index f248557..d965d9a 100644
--- a/media/jni/android_media_ResampleInputStream.cpp
+++ b/media/jni/android_media_ResampleInputStream.cpp
@@ -92,7 +92,7 @@
          jint jNpoints) {
     
     // safety first!
-    if (nFir21 + jNpoints > BUF_SIZE) {
+    if (nFir21 + jNpoints * 2 > BUF_SIZE) {
         throwException(env, "java/lang/IllegalArgumentException",
                 "FIR+data too long %d", nFir21 + jNpoints);
         return;
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index c5dfbb5..3f9c6d6 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -709,7 +709,9 @@
 
 bool AudioSystem::isLowVisibility(stream_type stream)
 {
-    if (stream == AudioSystem::SYSTEM || stream == AudioSystem::NOTIFICATION) {
+    if (stream == AudioSystem::SYSTEM ||
+        stream == AudioSystem::NOTIFICATION ||
+        stream == AudioSystem::RING) {
         return true;
     } else {
         return false;
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 7997cd6..57f58be 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -38,6 +38,7 @@
       mPositionTimeRealUs(-1),
       mSeeking(false),
       mReachedEOS(false),
+      mFinalStatus(OK),
       mStarted(false),
       mAudioSink(audioSink) {
 }
@@ -168,6 +169,7 @@
     mPositionTimeRealUs = -1;
     mSeeking = false;
     mReachedEOS = false;
+    mFinalStatus = OK;
     mStarted = false;
 }
 
@@ -181,8 +183,11 @@
     return mSeeking;
 }
 
-bool AudioPlayer::reachedEOS() {
+bool AudioPlayer::reachedEOS(status_t *finalStatus) {
+    *finalStatus = OK;
+
     Mutex::Autolock autoLock(mLock);
+    *finalStatus = mFinalStatus;
     return mReachedEOS;
 }
 
@@ -245,6 +250,7 @@
 
             if (err != OK) {
                 mReachedEOS = true;
+                mFinalStatus = err;
                 break;
             }
 
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index b3a73b0..ab65b44 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -434,14 +434,22 @@
     }
     mStreamDoneEventPending = false;
 
-    if (mFlags & LOOPING) {
+    if (mStreamDoneStatus == ERROR_END_OF_STREAM && (mFlags & LOOPING)) {
         seekTo_l(0);
 
         if (mVideoSource != NULL) {
             postVideoEvent_l();
         }
     } else {
-        notifyListener_l(MEDIA_PLAYBACK_COMPLETE);
+        if (mStreamDoneStatus == ERROR_END_OF_STREAM) {
+            LOGV("MEDIA_PLAYBACK_COMPLETE");
+            notifyListener_l(MEDIA_PLAYBACK_COMPLETE);
+        } else {
+            LOGV("MEDIA_ERROR %d", mStreamDoneStatus);
+
+            notifyListener_l(
+                    MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, mStreamDoneStatus);
+        }
 
         pause_l();
 
@@ -802,7 +810,7 @@
                     continue;
                 }
 
-                postStreamDoneEvent_l();
+                postStreamDoneEvent_l(err);
                 return;
             }
 
@@ -904,11 +912,13 @@
     mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 : delayUs);
 }
 
-void AwesomePlayer::postStreamDoneEvent_l() {
+void AwesomePlayer::postStreamDoneEvent_l(status_t status) {
     if (mStreamDoneEventPending) {
         return;
     }
     mStreamDoneEventPending = true;
+
+    mStreamDoneStatus = status;
     mQueue.postEvent(mStreamDoneEvent);
 }
 
@@ -947,9 +957,10 @@
         notifyListener_l(MEDIA_SEEK_COMPLETE);
     }
 
-    if (mWatchForAudioEOS && mAudioPlayer->reachedEOS()) {
+    status_t finalStatus;
+    if (mWatchForAudioEOS && mAudioPlayer->reachedEOS(&finalStatus)) {
         mWatchForAudioEOS = false;
-        postStreamDoneEvent_l();
+        postStreamDoneEvent_l(finalStatus);
     }
 
     postCheckAudioStatusEvent_l();
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 16635d3..165ac09 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -463,7 +463,10 @@
                 return err;
             }
         }
-        CHECK_EQ(*offset, stop_offset);
+
+        if (*offset != stop_offset) {
+            return ERROR_MALFORMED;
+        }
 
         return OK;
     }
@@ -496,6 +499,23 @@
                 }
             }
 
+            if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
+                Track *track = new Track;
+                track->next = NULL;
+                if (mLastTrack) {
+                    mLastTrack->next = track;
+                } else {
+                    mFirstTrack = track;
+                }
+                mLastTrack = track;
+
+                track->meta = new MetaData;
+                track->includes_expensive_metadata = false;
+                track->timescale = 0;
+                track->sampleTable = new SampleTable(mDataSource);
+                track->meta->setCString(kKeyMIMEType, "application/octet-stream");
+            }
+
             off_t stop_offset = *offset + chunk_size;
             *offset = data_offset;
             while (*offset < stop_offset) {
@@ -504,9 +524,18 @@
                     return err;
                 }
             }
-            CHECK_EQ(*offset, stop_offset);
 
-            if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
+
+            if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
+                status_t err = verifyTrack(mLastTrack);
+
+                if (err != OK) {
+                    return err;
+                }
+            } else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
                 mHaveMetadata = true;
 
                 return UNKNOWN_ERROR;  // Return a dummy error.
@@ -516,7 +545,9 @@
 
         case FOURCC('t', 'k', 'h', 'd'):
         {
-            CHECK(chunk_data_size >= 4);
+            if (chunk_data_size < 4) {
+                return ERROR_MALFORMED;
+            }
 
             uint8_t version;
             if (mDataSource->readAt(data_offset, &version, 1) < 1) {
@@ -562,21 +593,6 @@
                 height = U32_AT(&buffer[80]);
             }
 
-            Track *track = new Track;
-            track->next = NULL;
-            if (mLastTrack) {
-                mLastTrack->next = track;
-            } else {
-                mFirstTrack = track;
-            }
-            mLastTrack = track;
-
-            track->meta = new MetaData;
-            track->includes_expensive_metadata = false;
-            track->timescale = 0;
-            track->sampleTable = new SampleTable(mDataSource);
-            track->meta->setCString(kKeyMIMEType, "application/octet-stream");
-
             *offset += chunk_size;
             break;
         }
@@ -670,7 +686,10 @@
             }
 
             uint8_t buffer[8];
-            CHECK(chunk_data_size >= (off_t)sizeof(buffer));
+            if (chunk_data_size < (off_t)sizeof(buffer)) {
+                return ERROR_MALFORMED;
+            }
+
             if (mDataSource->readAt(
                         data_offset, buffer, 8) < 8) {
                 return ERROR_IO;
@@ -696,7 +715,10 @@
                     return err;
                 }
             }
-            CHECK_EQ(*offset, stop_offset);
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
             break;
         }
 
@@ -748,7 +770,10 @@
                     return err;
                 }
             }
-            CHECK_EQ(*offset, stop_offset);
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
             break;
         }
 
@@ -792,7 +817,10 @@
                     return err;
                 }
             }
-            CHECK_EQ(*offset, stop_offset);
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
             break;
         }
 
@@ -942,7 +970,10 @@
         case FOURCC('m', 'e', 't', 'a'):
         {
             uint8_t buffer[4];
-            CHECK(chunk_data_size >= (off_t)sizeof(buffer));
+            if (chunk_data_size < (off_t)sizeof(buffer)) {
+                return ERROR_MALFORMED;
+            }
+
             if (mDataSource->readAt(
                         data_offset, buffer, 4) < 4) {
                 return ERROR_IO;
@@ -961,7 +992,10 @@
                     return err;
                 }
             }
-            CHECK_EQ(*offset, stop_offset);
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
             break;
         }
 
@@ -995,8 +1029,9 @@
             int64_t creationTime;
             if (header[0] == 1) {
                 creationTime = U64_AT(&header[4]);
+            } else if (header[0] != 0) {
+                return ERROR_MALFORMED;
             } else {
-                CHECK_EQ(header[0], 0);
                 creationTime = U32_AT(&header[4]);
             }
 
@@ -1174,6 +1209,30 @@
             track->meta, mDataSource, track->timescale, track->sampleTable);
 }
 
+// static
+status_t MPEG4Extractor::verifyTrack(Track *track) {
+    const char *mime;
+    CHECK(track->meta->findCString(kKeyMIMEType, &mime));
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        if (!track->meta->findData(kKeyAVCC, &type, &data, &size)
+                || type != kTypeAVCC) {
+            return ERROR_MALFORMED;
+        }
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
+            || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        if (!track->meta->findData(kKeyESDS, &type, &data, &size)
+                || type != kTypeESDS) {
+            return ERROR_MALFORMED;
+        }
+    }
+
+    return OK;
+}
+
 status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio(
         const void *esds_data, size_t esds_size) {
     ESDS esds(esds_data, esds_size);
@@ -1391,6 +1450,14 @@
                 &sampleIndex, SampleTable::kSyncSample_Flag);
 
         if (err != OK) {
+            if (err == ERROR_OUT_OF_RANGE) {
+                // An attempt to seek past the end of the stream would
+                // normally cause this ERROR_OUT_OF_RANGE error. Propagating
+                // this all the way to the MediaPlayer would cause abnormal
+                // termination. Legacy behaviour appears to be to behave as if
+                // we had seeked to the end of stream, ending normally.
+                err = ERROR_END_OF_STREAM;
+            }
             return err;
         }
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 6cf7cff..974413d 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -298,6 +298,10 @@
         quirks |= kRequiresAllocateBufferOnOutputPorts;
     }
 
+    if (!strcmp(componentName, "OMX.TI.Video.Decoder")) {
+        quirks |= kInputBufferSizesAreBogus;
+    }
+
     return quirks;
 }
 
@@ -561,7 +565,8 @@
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
     CHECK_EQ(err, OK);
 
-    if (def.nBufferSize < size) {
+    if ((portIndex == kPortIndexInput && (mQuirks & kInputBufferSizesAreBogus))
+        || (def.nBufferSize < size)) {
         def.nBufferSize = size;
     }
 
@@ -574,7 +579,12 @@
     CHECK_EQ(err, OK);
 
     // Make sure the setting actually stuck.
-    CHECK(def.nBufferSize >= size);
+    if (portIndex == kPortIndexInput
+            && (mQuirks & kInputBufferSizesAreBogus)) {
+        CHECK_EQ(def.nBufferSize, size);
+    } else {
+        CHECK(def.nBufferSize >= size);
+    }
 }
 
 status_t OMXCodec::setVideoPortFormatType(
@@ -1923,6 +1933,7 @@
         CODEC_LOGV("signalling end of input stream.");
         flags |= OMX_BUFFERFLAG_EOS;
 
+        mFinalStatus = err;
         mSignalledEOS = true;
     } else {
         mNoMoreOutputData = false;
@@ -2401,7 +2412,7 @@
     }
 
     if (mFilledBuffers.empty()) {
-        return ERROR_END_OF_STREAM;
+        return mSignalledEOS ? mFinalStatus : ERROR_END_OF_STREAM;
     }
 
     if (mOutputPortSettingsHaveChanged) {
diff --git a/media/libstagefright/Prefetcher.cpp b/media/libstagefright/Prefetcher.cpp
index 363e121..9c73f4a 100644
--- a/media/libstagefright/Prefetcher.cpp
+++ b/media/libstagefright/Prefetcher.cpp
@@ -55,6 +55,7 @@
     size_t mIndex;
     bool mStarted;
     bool mReachedEOS;
+    status_t mFinalStatus;
     int64_t mSeekTimeUs;
     int64_t mCacheDurationUs;
     bool mPrefetcherStopped;
@@ -306,7 +307,7 @@
     }
 
     if (mCachedBuffers.empty()) {
-        return ERROR_END_OF_STREAM;
+        return mReachedEOS ? mFinalStatus : ERROR_END_OF_STREAM;
     }
 
     *out = *mCachedBuffers.begin();
@@ -353,6 +354,7 @@
 
     if (err != OK) {
         mReachedEOS = true;
+        mFinalStatus = err;
         mCondition.signal();
 
         return;
diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
index f1f7194..6d6e408 100644
--- a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
+++ b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
@@ -160,14 +160,25 @@
     mConfig->outputFrameSize = buffer->size() / sizeof(int16_t);
     mConfig->pOutputBuffer = static_cast<int16_t *>(buffer->data());
 
-    if (pvmp3_framedecoder(mConfig, mDecoderBuf) != NO_DECODING_ERROR) {
-        buffer->release();
-        buffer = NULL;
+    ERROR_CODE decoderErr;
+    if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf))
+            != NO_DECODING_ERROR) {
+        LOGV("mp3 decoder returned error %d", decoderErr);
 
-        mInputBuffer->release();
-        mInputBuffer = NULL;
+        if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR) {
+            buffer->release();
+            buffer = NULL;
 
-        return UNKNOWN_ERROR;
+            mInputBuffer->release();
+            mInputBuffer = NULL;
+
+            return UNKNOWN_ERROR;
+        }
+
+        // This is recoverable, just ignore the current frame and
+        // play silence instead.
+        memset(buffer->data(), 0, mConfig->outputFrameSize);
+        mConfig->inputBufferUsedLength = mInputBuffer->range_length();
     }
 
     buffer->set_range(
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index ce8eeae..3590987 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -145,10 +145,11 @@
     Condition mPreparedCondition;
     bool mIsAsyncPrepare;
     status_t mPrepareResult;
+    status_t mStreamDoneStatus;
 
     void postVideoEvent_l(int64_t delayUs = -1);
     void postBufferingEvent_l();
-    void postStreamDoneEvent_l();
+    void postStreamDoneEvent_l(status_t status);
     void postCheckAudioStatusEvent_l();
     status_t getPosition_l(int64_t *positionUs);
     status_t play_l();
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 3a63e88..9d35e0c 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -68,6 +68,8 @@
     status_t updateAudioTrackInfoFromESDS_MPEG4Audio(
             const void *esds_data, size_t esds_size);
 
+    static status_t verifyTrack(Track *track);
+
     MPEG4Extractor(const MPEG4Extractor &);
     MPEG4Extractor &operator=(const MPEG4Extractor &);
 };
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 9ca060d..ff8757d 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -401,6 +401,33 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+struct SharedVideoRenderer : public VideoRenderer {
+    SharedVideoRenderer(void *libHandle, VideoRenderer *obj)
+        : mLibHandle(libHandle),
+          mObj(obj) {
+    }
+
+    virtual ~SharedVideoRenderer() {
+        delete mObj;
+        mObj = NULL;
+
+        dlclose(mLibHandle);
+        mLibHandle = NULL;
+    }
+
+    virtual void render(
+            const void *data, size_t size, void *platformPrivate) {
+        return mObj->render(data, size, platformPrivate);
+    }
+
+private:
+    void *mLibHandle;
+    VideoRenderer *mObj;
+
+    SharedVideoRenderer(const SharedVideoRenderer &);
+    SharedVideoRenderer &operator=(const SharedVideoRenderer &);
+};
+
 sp<IOMXRenderer> OMX::createRenderer(
         const sp<ISurface> &surface,
         const char *componentName,
@@ -411,11 +438,7 @@
 
     VideoRenderer *impl = NULL;
 
-    static void *libHandle = NULL;
-
-    if (!libHandle) {
-        libHandle = dlopen("libstagefrighthw.so", RTLD_NOW);
-    }
+    void *libHandle = dlopen("libstagefrighthw.so", RTLD_NOW);
 
     if (libHandle) {
         typedef VideoRenderer *(*CreateRendererFunc)(
@@ -434,6 +457,16 @@
         if (func) {
             impl = (*func)(surface, componentName, colorFormat,
                     displayWidth, displayHeight, encodedWidth, encodedHeight);
+
+            if (impl) {
+                impl = new SharedVideoRenderer(libHandle, impl);
+                libHandle = NULL;
+            }
+        }
+
+        if (libHandle) {
+            dlclose(libHandle);
+            libHandle = NULL;
         }
     }
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java
index 717f7ba..d7cf069 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java
@@ -1,28 +1,35 @@
 package com.android.mediaframeworktest;
 
 import android.media.MediaRecorder;
+import android.media.EncoderCapabilities;
+import android.media.EncoderCapabilities.VideoEncoderCap;
+import android.media.EncoderCapabilities.AudioEncoderCap;
+import android.media.DecoderCapabilities;
+import android.media.DecoderCapabilities.VideoDecoder;
+import android.media.DecoderCapabilities.AudioDecoder;
+
 import android.os.SystemProperties;
+import java.util.List;
 import java.util.HashMap;
 
-public class MediaProfileReader {
+public class MediaProfileReader
+{
+    private static final List<VideoDecoder> videoDecoders = DecoderCapabilities.getVideoDecoders();
+    private static final List<AudioDecoder> audioDecoders = DecoderCapabilities.getAudioDecoders();
+    private static final List<VideoEncoderCap> videoEncoders = EncoderCapabilities.getVideoEncoders();
+    private static final List<AudioEncoderCap> audioEncoders = EncoderCapabilities.getAudioEncoders();
+    private static final HashMap<Integer, String> encoderMap = new HashMap<Integer, String>();
 
-    public static final HashMap<String, Integer>
-    OUTPUT_FORMAT_TABLE = new HashMap<String, Integer>();
-    public static String MEDIA_ENC_VID = "ro.media.enc.vid.";
-    public static String MEDIA_AUD_VID = "ro.media.enc.aud.";
-    public static String[] VIDEO_ENCODER_PROPERTY = {".width", ".height", ".bps", ".fps",};
-    public static String[] AUDIO_ENCODER_PROPERTY = {".bps", ".hz", ".ch",};
+    static {
+        initEncoderMap();
+    };
 
-    public static String getVideoCodecProperty() {
-        String s;
-        s = SystemProperties.get("ro.media.enc.vid.codec");
-        return s;
+    public static List<VideoEncoderCap> getVideoEncoders() {
+        return videoEncoders;
     }
 
-    public static String getAudioCodecProperty() {
-        String s;
-        s = SystemProperties.get("ro.media.enc.aud.codec");
-        return s;
+    public static List<AudioEncoderCap> getAudioEncoders() {
+        return audioEncoders;
     }
 
     public static String getDeviceType() {
@@ -33,78 +40,56 @@
     }
 
     public static boolean getWMAEnable() {
-        // push all the property into one big table
-        int wmaEnable = 1;
-        wmaEnable = SystemProperties.getInt("ro.media.dec.aud.wma.enabled",
-                wmaEnable);
-        if (wmaEnable == 1) {
-            return true;
-        } else {
-            return false;
+        for (AudioDecoder decoder: audioDecoders) {
+            if (decoder == AudioDecoder.AUDIO_DECODER_WMA) {
+                return true;
+            }
         }
+        return false;
     }
 
     public static boolean getWMVEnable(){
-        int wmvEnable = 1;
-        wmvEnable = SystemProperties.getInt("ro.media.dec.vid.wmv.enabled",
-                wmvEnable);
-        if (wmvEnable == 1) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    public static void createVideoProfileTable() {
-        // push all the property into one big table
-        String encoderType = getVideoCodecProperty();
-        if (encoderType.length() != 0) {
-            String encoder[] = encoderType.split(",");
-            for (int i = 0; i < encoder.length; i++) {
-                for (int j = 0; j < VIDEO_ENCODER_PROPERTY.length; j++) {
-                    String propertyName = MEDIA_ENC_VID + encoder[i] + VIDEO_ENCODER_PROPERTY[j];
-                    String prop = SystemProperties.get(propertyName);
-                    // push to the table
-                    String propRange[] = prop.split(",");
-                    OUTPUT_FORMAT_TABLE.put((encoder[i] + VIDEO_ENCODER_PROPERTY[j] + "_low"),
-                            Integer.parseInt(propRange[0]));
-                    OUTPUT_FORMAT_TABLE.put((encoder[i] + VIDEO_ENCODER_PROPERTY[j] + "_high"),
-                            Integer.parseInt(propRange[1]));
-                }
-
+        for (VideoDecoder decoder: videoDecoders) {
+            if (decoder == VideoDecoder.VIDEO_DECODER_WMV) {
+                return true;
             }
         }
+        return false;
     }
 
-    public static void createAudioProfileTable() {
-        // push all the property into one big table
-        String audioType = getAudioCodecProperty();
-        String encoder[] = audioType.split(",");
-        if (audioType.length() != 0) {
-            for (int i = 0; i < encoder.length; i++) {
-                for (int j = 0; j < AUDIO_ENCODER_PROPERTY.length; j++) {
-                    String propertyName = MEDIA_AUD_VID + encoder[i] + AUDIO_ENCODER_PROPERTY[j];
-                    String prop = SystemProperties.get(propertyName);
-                    // push to the table
-                    String propRange[] = prop.split(",");
-                    OUTPUT_FORMAT_TABLE.put((encoder[i] + AUDIO_ENCODER_PROPERTY[j] + "_low"),
-                            Integer.parseInt(propRange[0]));
-                    OUTPUT_FORMAT_TABLE.put((encoder[i] + AUDIO_ENCODER_PROPERTY[j] + "_high"),
-                            Integer.parseInt(propRange[1]));
-                }
-            }
+    public static String getVideoCodecName(int videoEncoder) {
+        if (videoEncoder != MediaRecorder.VideoEncoder.H263 &&
+            videoEncoder != MediaRecorder.VideoEncoder.H264 &&
+            videoEncoder != MediaRecorder.VideoEncoder.MPEG_4_SP) {
+            throw new IllegalArgumentException("Unsupported video encoder " + videoEncoder);
         }
+        return encoderMap.get(videoEncoder);
     }
 
-    public static void createEncoderTable(){
-        OUTPUT_FORMAT_TABLE.put("h263", MediaRecorder.VideoEncoder.H263);
-        OUTPUT_FORMAT_TABLE.put("h264", MediaRecorder.VideoEncoder.H264);
-        OUTPUT_FORMAT_TABLE.put("m4v", MediaRecorder.VideoEncoder.MPEG_4_SP);
-        OUTPUT_FORMAT_TABLE.put("amrnb", MediaRecorder.AudioEncoder.AMR_NB);
-        OUTPUT_FORMAT_TABLE.put("amrwb", MediaRecorder.AudioEncoder.AMR_WB);
-        OUTPUT_FORMAT_TABLE.put("aac", MediaRecorder.AudioEncoder.AAC);
-        OUTPUT_FORMAT_TABLE.put("aacplus", MediaRecorder.AudioEncoder.AAC_PLUS);
-        OUTPUT_FORMAT_TABLE.put("eaacplus",
-                MediaRecorder.AudioEncoder.EAAC_PLUS);
+    public static String getAudioCodecName(int audioEncoder) {
+        if (audioEncoder != MediaRecorder.AudioEncoder.AMR_NB &&
+            audioEncoder != MediaRecorder.AudioEncoder.AMR_WB &&
+            audioEncoder != MediaRecorder.AudioEncoder.AAC &&
+            audioEncoder != MediaRecorder.AudioEncoder.AAC_PLUS &&
+            audioEncoder != MediaRecorder.AudioEncoder.EAAC_PLUS) {
+            throw new IllegalArgumentException("Unsupported audio encodeer " + audioEncoder);
+        }
+        return encoderMap.get(audioEncoder);
+    }
+
+    private MediaProfileReader() {} // Don't call me
+
+    private static void initEncoderMap() {
+        // video encoders
+        encoderMap.put(MediaRecorder.VideoEncoder.H263, "h263");
+        encoderMap.put(MediaRecorder.VideoEncoder.H264, "h264");
+        encoderMap.put(MediaRecorder.VideoEncoder.MPEG_4_SP, "m4v");
+
+        // audio encoders
+        encoderMap.put(MediaRecorder.AudioEncoder.AMR_NB, "amrnb");
+        encoderMap.put(MediaRecorder.AudioEncoder.AMR_WB, "amrwb");
+        encoderMap.put(MediaRecorder.AudioEncoder.AAC, "aac");
+        encoderMap.put(MediaRecorder.AudioEncoder.AAC_PLUS, "aacplus");
+        encoderMap.put(MediaRecorder.AudioEncoder.EAAC_PLUS, "eaacplus");
     }
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
index fdc5970..39caccd 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
@@ -25,6 +25,9 @@
 import android.hardware.Camera;
 import android.media.MediaPlayer;
 import android.media.MediaRecorder;
+import android.media.EncoderCapabilities;
+import android.media.EncoderCapabilities.VideoEncoderCap;
+import android.media.EncoderCapabilities.AudioEncoderCap;
 import android.test.ActivityInstrumentationTestCase;
 import android.util.Log;
 import android.view.SurfaceHolder;
@@ -33,6 +36,7 @@
 
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.Suppress;
+import java.util.List;
 
 
 /**
@@ -99,31 +103,29 @@
         }
     }
     
-    private boolean recordVideoWithPara(String encoder, String audio, String quality){
+    private boolean recordVideoWithPara(VideoEncoderCap videoCap, AudioEncoderCap audioCap, boolean highQuality){
         boolean recordSuccess = false;
-        int videoEncoder = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(encoder);
-        int audioEncoder = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(audio);
-        int videoWidth = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(encoder + ".width_" + quality);
-        int videoHeight =
-                MediaProfileReader.OUTPUT_FORMAT_TABLE.get(encoder + ".height_" + quality);
-        int videoFps = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(encoder + ".fps_" + quality);
-        int videoBitrate = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(encoder + ".bps_" + quality);
-        int audioBitrate = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(audio + ".bps_" + quality);
-        int audioChannels = MediaProfileReader.OUTPUT_FORMAT_TABLE.get(audio + ".ch_" + quality);
-        int audioSamplingRate =
-                MediaProfileReader.OUTPUT_FORMAT_TABLE.get(audio + ".hz_" + quality);
+        int videoEncoder = videoCap.mCodec;
+        int audioEncoder = audioCap.mCodec;
+        int videoWidth = highQuality? videoCap.mMaxFrameWidth: videoCap.mMinFrameWidth;
+        int videoHeight = highQuality? videoCap.mMaxFrameHeight: videoCap.mMinFrameHeight;
+        int videoFps = highQuality? videoCap.mMaxFrameRate: videoCap.mMinFrameRate;
+        int videoBitrate = highQuality? videoCap.mMaxBitRate: videoCap.mMinBitRate;
+        int audioBitrate = highQuality? audioCap.mMaxBitRate: audioCap.mMinBitRate;
+        int audioChannels = highQuality? audioCap.mMaxChannels: audioCap.mMinChannels ;
+        int audioSamplingRate = highQuality? audioCap.mMaxSampleRate: audioCap.mMinSampleRate;
 
         if (videoFps < MIN_VIDEO_FPS) {
             videoFps = MIN_VIDEO_FPS;
         }
         mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
-        String filename = ("/sdcard/" + encoder + "_" + audio + "_" + quality + ".3gp");
+        String filename = ("/sdcard/" + videoEncoder + "_" + audioEncoder + "_" + highQuality + ".3gp");
         try {
             Log.v(TAG, "video encoder :" + videoEncoder);
             Log.v(TAG, "audio encoder :" + audioEncoder);
-            Log.v(TAG, "quality : " + quality);
-            Log.v(TAG, "encoder : " + encoder);
-            Log.v(TAG, "audio : " + audio);
+            Log.v(TAG, "quality : " + (highQuality?"high": "low"));
+            Log.v(TAG, "encoder : " + MediaProfileReader.getVideoCodecName(videoEncoder));
+            Log.v(TAG, "audio : " + MediaProfileReader.getAudioCodecName(audioEncoder));
             Log.v(TAG, "videoWidth : " + videoWidth);
             Log.v(TAG, "videoHeight : " + videoHeight);
             Log.v(TAG, "videoFPS : " + videoFps);
@@ -434,35 +436,26 @@
         boolean recordSuccess = false;
         String deviceType = MediaProfileReader.getDeviceType();
         Log.v(TAG, "deviceType = " + deviceType);
-        // Test cases are device specified
-        MediaProfileReader.createVideoProfileTable();
-        MediaProfileReader.createAudioProfileTable();
-        MediaProfileReader.createEncoderTable();
-        String encoderType = MediaProfileReader.getVideoCodecProperty();
-        String audioType = MediaProfileReader.getAudioCodecProperty();
-        if ((encoderType.length() != 0) || (audioType.length() != 0)) {
-            String audio[] = audioType.split(",");
-            String encoder[] = encoderType.split(",");
-            for (int k = 0; k < 2; k++) {
-                for (int i = 0; i < encoder.length; i++) {
-                    for (int j = 0; j < audio.length; j++) {
-                        if (k == 0) {
-                            recordSuccess = recordVideoWithPara(encoder[i], audio[j], "high");
-                        } else {
-                            recordSuccess = recordVideoWithPara(encoder[i], audio[j], "low");
-                        }
-                        if (!recordSuccess) {
-                            Log.v(TAG, "testDeviceSpecificCodec failed");
-                            Log.v(TAG, "Encoder = " + encoder[i] + "Audio Encoder = " + audio[j]);
-                            noOfFailure++;
-                        }
-                        // assertTrue((encoder[i] + audio[j]), recordSuccess);
+        List<VideoEncoderCap> videoEncoders = MediaProfileReader.getVideoEncoders();
+        List<AudioEncoderCap> audioEncoders = MediaProfileReader.getAudioEncoders();
+        for (int k = 0; k < 2; k++) {
+            for (VideoEncoderCap videoEncoder: videoEncoders) {
+                for (AudioEncoderCap audioEncoder: audioEncoders) {
+                    if (k == 0) {
+                        recordSuccess = recordVideoWithPara(videoEncoder, audioEncoder, true);
+                    } else {
+                        recordSuccess = recordVideoWithPara(videoEncoder, audioEncoder, false);
+                    }
+                    if (!recordSuccess) {
+                        Log.v(TAG, "testDeviceSpecificCodec failed");
+                        Log.v(TAG, "Encoder = " + videoEncoder.mCodec + "Audio Encoder = " + audioEncoder.mCodec);
+                        noOfFailure++;
                     }
                 }
             }
-            if (noOfFailure != 0) {
-                assertTrue("testDeviceSpecificCodec", false);
-            }
+        }
+        if (noOfFailure != 0) {
+            assertTrue("testDeviceSpecificCodec", false);
         }
     }
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
index 8750098..4c259f1 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
@@ -39,9 +39,8 @@
     public static void testAlbumArt() throws Exception {
         Log.v(TAG, "testAlbumArt starts.");
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        MediaProfileReader reader = new MediaProfileReader();
-        boolean supportWMA = reader.getWMAEnable();
-        boolean supportWMV = reader.getWMVEnable();
+        boolean supportWMA = MediaProfileReader.getWMAEnable();
+        boolean supportWMV = MediaProfileReader.getWMVEnable();
         retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
         for (int i = 0, n = MediaNames.ALBUMART_TEST_FILES.length; i < n; ++i) {
             try {
@@ -74,9 +73,8 @@
     @LargeTest
     public static void testThumbnailCapture() throws Exception {
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        MediaProfileReader reader = new MediaProfileReader();
-        boolean supportWMA = reader.getWMAEnable();
-        boolean supportWMV = reader.getWMVEnable();
+        boolean supportWMA = MediaProfileReader.getWMAEnable();
+        boolean supportWMV = MediaProfileReader.getWMVEnable();
         Log.v(TAG, "Thumbnail processing starts");
         long startedAt = System.currentTimeMillis();
         for(int i = 0, n = MediaNames.THUMBNAIL_CAPTURE_TEST_FILES.length; i < n; ++i) {
@@ -110,9 +108,8 @@
     
     @LargeTest
     public static void testMetadataRetrieval() throws Exception {
-        MediaProfileReader reader = new MediaProfileReader();
-        boolean supportWMA = reader.getWMAEnable();
-        boolean supportWMV = reader.getWMVEnable();
+        boolean supportWMA = MediaProfileReader.getWMAEnable();
+        boolean supportWMV = MediaProfileReader.getWMVEnable();
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
         retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
         for(int i = 0, n = MediaNames.METADATA_RETRIEVAL_TEST_FILES.length; i < n; ++i) {
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 95ab684..a79f0cd 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -39,7 +39,7 @@
  */
 public class DefaultContainerService extends IntentService {
     private static final String TAG = "DefContainer";
-    private static final boolean localLOGV = false;
+    private static final boolean localLOGV = true;
 
     private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
         /*
@@ -211,6 +211,8 @@
                 if (PackageHelper.isContainerMounted(newCid)) {
                     if (localLOGV) Log.i(TAG, "Unmounting " + newCid +
                             " at path " + newCachePath + " after " + errMsg);
+                    // Force a gc to avoid being killed.
+                    Runtime.getRuntime().gc();
                     PackageHelper.unMountSdDir(newCid);
                 } else {
                     if (localLOGV) Log.i(TAG, "Container " + newCid + " not mounted");
@@ -328,8 +330,8 @@
         boolean auto = true;
         // To make final copy
         long reqInstallSize = pkgLen;
-        // For dex files
-        long reqInternalSize = 1 * pkgLen;
+        // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now.
+        long reqInternalSize = 0;
         boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
         boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalFlashSize);
         boolean fitsOnSd = (reqInstallSize < availSDSize) && intThresholdOk &&
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 654ca32..1e9c312 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -60,14 +60,14 @@
 
     <!-- user interface sound effects -->
     <integer name="def_power_sounds_enabled">1</integer>
-    <string name="def_low_battery_sound">/system/media/ui/LowBattery.ogg</string>
-    <integer name="def_dock_sounds_enabled">1</integer>
-    <string name="def_desk_dock_sound">/system/media/audio/ui/dock.ogg</string>
-    <string name="def_desk_undock_sound">/system/media/audio/ui/undock.ogg</string>
-    <string name="def_car_dock_sound">/system/media/audio/ui/Dock.ogg</string>
-    <string name="def_car_undock_sound">/system/media/audio/ui/Undock.ogg</string>
-    <integer name="def_lockscreen_sounds_enabled">1</integer>
-    <string name="def_lock_sound">/system/media/audio/ui/Lock.ogg</string>
-    <string name="def_unlock_sound">/system/media/audio/ui/Unlock.ogg</string>
+    <string name="def_low_battery_sound" translatable="false">/system/media/ui/LowBattery.ogg</string>
+    <integer name="def_dock_sounds_enabled">0</integer>
+    <string name="def_desk_dock_sound" translatable="false">/system/media/audio/ui/Dock.ogg</string>
+    <string name="def_desk_undock_sound" translatable="false">/system/media/audio/ui/Undock.ogg</string>
+    <string name="def_car_dock_sound" translatable="false">/system/media/audio/ui/Dock.ogg</string>
+    <string name="def_car_undock_sound" translatable="false">/system/media/audio/ui/Undock.ogg</string>
+    <integer name="def_lockscreen_sounds_enabled">0</integer>
+    <string name="def_lock_sound" translatable="false">/system/media/audio/ui/Lock.ogg</string>
+    <string name="def_unlock_sound" translatable="false">/system/media/audio/ui/Unlock.ogg</string>
 
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index cf34d4e..8036e52 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -76,7 +76,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 50;
+    private static final int DATABASE_VERSION = 51;
 
     private Context mContext;
 
@@ -618,18 +618,9 @@
 
        if (upgradeVersion == 48) {
            /*
-            * Adding a new setting for which voice recognition service to use.
+            * Default recognition service no longer initialized here,
+            * moved to RecognitionManagerService.
             */
-           db.beginTransaction();
-           try {
-               SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
-                       + " VALUES(?,?);");
-               loadVoiceRecognitionServiceSetting(stmt);
-               stmt.close();
-               db.setTransactionSuccessful();
-           } finally {
-               db.endTransaction();
-           }
            upgradeVersion = 49;
        }
 
@@ -651,6 +642,25 @@
            upgradeVersion = 50;
        }
 
+       if (upgradeVersion == 50) {
+           /*
+            * New settings for set install location UI.
+            */
+           db.beginTransaction();
+           try {
+                SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)"
+                        + " VALUES(?,?);");
+                loadBooleanSetting(stmt, Settings.System.SET_INSTALL_LOCATION,
+                        R.bool.set_install_location);
+                stmt.close();
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+
+           upgradeVersion = 51;
+       }
+
        if (upgradeVersion != currentVersion) {
             Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion
                     + ", must wipe the settings provider");
@@ -1028,8 +1038,6 @@
         loadBooleanSetting(stmt, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED,
                 R.bool.def_mount_ums_notify_enabled);
 
-        loadVoiceRecognitionServiceSetting(stmt);
-
         stmt.close();
     }
 
@@ -1041,32 +1049,6 @@
                 R.string.def_backup_transport);
     }
 
-    /**
-     * Introduced in database version 49.
-     */
-    private void loadVoiceRecognitionServiceSetting(SQLiteStatement stmt) {
-        String selectedService = null;
-        List<ResolveInfo> availableRecognitionServices =
-                mContext.getPackageManager().queryIntentServices(
-                        new Intent(RecognitionService.SERVICE_INTERFACE), 0);
-        int numAvailable = availableRecognitionServices.size();
-
-        if (numAvailable == 0) {
-            Log.w(TAG, "no available voice recognition services found");
-        } else {
-            if (numAvailable > 1) {
-                Log.w(TAG, "more than one voice recognition service found, picking first");
-            }
-
-            ServiceInfo serviceInfo = availableRecognitionServices.get(0).serviceInfo;
-            selectedService =
-                    new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToString();
-        }
-
-        loadSetting(stmt, Settings.Secure.VOICE_RECOGNITION_SERVICE,
-                selectedService == null ? "" : selectedService);
-    }
-
     private void loadSetting(SQLiteStatement stmt, String key, Object value) {
         stmt.bindString(1, key);
         stmt.bindString(2, value.toString());
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4080a6a..db802d3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -20,7 +20,6 @@
 import java.io.UnsupportedEncodingException;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
-import java.util.Random;
 
 import android.backup.BackupManager;
 import android.content.ContentProvider;
@@ -30,14 +29,11 @@
 import android.content.pm.PackageManager;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
-import android.database.SQLException;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteQueryBuilder;
-import android.media.Ringtone;
 import android.media.RingtoneManager;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.DrmStore;
 import android.provider.MediaStore;
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java
index 477ea0c..c55dcb3 100644
--- a/services/java/com/android/server/AccessibilityManagerService.java
+++ b/services/java/com/android/server/AccessibilityManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.HandlerCaller.SomeArgs;
 
@@ -56,6 +57,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -145,13 +147,58 @@
     private void registerPackageChangeAndBootCompletedBroadcastReceiver() {
         Context context = mContext;
 
-        BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+        PackageMonitor monitor = new PackageMonitor() {
             @Override
-            public void onReceive(Context context, Intent intent) {
+            public void onSomePackagesChanged() {
                 synchronized (mLock) {
                     populateAccessibilityServiceListLocked();
+                    manageServicesLocked();
+                }
+            }
+            
+            @Override
+            public boolean onHandleForceStop(Intent intent, String[] packages,
+                    int uid, boolean doit) {
+                synchronized (mLock) {
+                    boolean changed = false;
+                    Iterator<ComponentName> it = mEnabledServices.iterator();
+                    while (it.hasNext()) {
+                        ComponentName comp = it.next();
+                        String compPkg = comp.getPackageName();
+                        for (String pkg : packages) {
+                            if (compPkg.equals(pkg)) {
+                                if (!doit) {
+                                    return true;
+                                }
+                                it.remove();
+                                changed = true;
+                            }
+                        }
+                    }
+                    if (changed) {
+                        it = mEnabledServices.iterator();
+                        StringBuilder str = new StringBuilder();
+                        while (it.hasNext()) {
+                            if (str.length() > 0) {
+                                str.append(':');
+                            }
+                            str.append(it.next().flattenToShortString());
+                        }
+                        Settings.Secure.putString(mContext.getContentResolver(),
+                                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                                str.toString());
+                        manageServicesLocked();
+                    }
+                    return false;
+                }
+            }
+            
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
+                    synchronized (mLock) {
+                        populateAccessibilityServiceListLocked();
 
-                    if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
                         // get the accessibility enabled setting on boot
                         mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
                                 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
@@ -160,29 +207,23 @@
                         if (mIsEnabled) {
                             updateClientsLocked();
                         }
-                    }
 
-                    manageServicesLocked();
+                        manageServicesLocked();
+                    }
+                    
+                    return;
                 }
+                
+                super.onReceive(context, intent);
             }
         };
 
         // package changes
-        IntentFilter packageFilter = new IntentFilter();
-        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
-        packageFilter.addDataScheme("package");
-        context.registerReceiver(broadcastReceiver, packageFilter);
-        // Register for events related to sdcard installation.
-        IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-        mContext.registerReceiver(broadcastReceiver, sdFilter);
+        monitor.register(context, true);
 
         // boot completed
         IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
-        mContext.registerReceiver(broadcastReceiver, bootFiler);
+        mContext.registerReceiver(monitor, bootFiler);
     }
 
     /**
@@ -529,8 +570,14 @@
             TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
             splitter.setString(servicesValue);
             while (splitter.hasNext()) {
-                ComponentName enabledService = ComponentName.unflattenFromString(splitter.next());
-                enabledServices.add(enabledService);
+                String str = splitter.next();
+                if (str == null || str.length() <= 0) {
+                    continue;
+                }
+                ComponentName enabledService = ComponentName.unflattenFromString(str);
+                if (enabledService != null) {
+                    enabledServices.add(enabledService);
+                }
             }
         }
     }
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 44cc0bb..f480209 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.app.Activity;
 import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
 import android.app.IAlarmManager;
@@ -344,6 +345,22 @@
         }
     }
     
+    public boolean lookForPackageLocked(String packageName) {
+        return lookForPackageLocked(mRtcWakeupAlarms, packageName)
+                || lookForPackageLocked(mRtcAlarms, packageName)
+                || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName)
+                || lookForPackageLocked(mElapsedRealtimeAlarms, packageName);
+    }
+
+    private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) {
+        for (int i=alarmList.size()-1; i>=0; i--) {
+            if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
     private ArrayList<Alarm> getAlarmList(int type) {
         switch (type) {
             case AlarmManager.RTC_WAKEUP:              return mRtcWakeupAlarms;
@@ -778,6 +795,7 @@
             IntentFilter filter = new IntentFilter();
             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+            filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
             filter.addDataScheme("package");
             mContext.registerReceiver(this, filter);
              // Register for events related to sdcard installation.
@@ -791,7 +809,16 @@
             synchronized (mLock) {
                 String action = intent.getAction();
                 String pkgList[] = null;
-                if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+                if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
+                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
+                    for (String packageName : pkgList) {
+                        if (lookForPackageLocked(packageName)) {
+                            setResultCode(Activity.RESULT_OK);
+                            return;
+                        }
+                    }
+                    return;
+                } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                 } else {
                     Uri data = intent.getData();
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index f79a02a..d4b28e2 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1647,11 +1647,22 @@
                     }
 
                     if (metaInfo.versionCode > packageInfo.versionCode) {
-                        String message = "Version " + metaInfo.versionCode
-                                + " > installed version " + packageInfo.versionCode;
-                        Log.w(TAG, "Package " + packageName + ": " + message);
-                        EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, message);
-                        continue;
+                        // Data is from a "newer" version of the app than we have currently
+                        // installed.  If the app has not declared that it is prepared to
+                        // handle this case, we do not attempt the restore.
+                        if ((packageInfo.applicationInfo.flags
+                                & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
+                            String message = "Version " + metaInfo.versionCode
+                                    + " > installed version " + packageInfo.versionCode;
+                            Log.w(TAG, "Package " + packageName + ": " + message);
+                            EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
+                                    packageName, message);
+                            continue;
+                        } else {
+                            if (DEBUG) Log.v(TAG, "Version " + metaInfo.versionCode
+                                    + " > installed " + packageInfo.versionCode
+                                    + " but restoreAnyVersion");
+                        }
                     }
 
                     if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
@@ -1695,8 +1706,10 @@
                         // The agent was probably running with a stub Application object,
                         // which isn't a valid run mode for the main app logic.  Shut
                         // down the app so that next time it's launched, it gets the
-                        // usual full initialization.
-                        if ((packageInfo.applicationInfo.flags
+                        // usual full initialization.  Note that this is only done for
+                        // full-system restores: when a single app has requested a restore,
+                        // it is explicitly not killed following that operation.
+                        if (mTargetPackage == null && (packageInfo.applicationInfo.flags
                                 & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
                             if (DEBUG) Log.d(TAG, "Restore complete, killing host process of "
                                     + packageInfo.applicationInfo.processName);
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index a267e0f..ac65aa9 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -17,6 +17,8 @@
 package com.android.server;
 
 import com.android.common.FastXmlSerializer;
+import com.android.common.XmlUtils;
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.widget.LockPatternUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -34,6 +36,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.IPowerManager;
@@ -58,9 +61,10 @@
  * Implementation of the device policy APIs.
  */
 public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
-    private static final String TAG = "DevicePolicyManagerService";
+    static final String TAG = "DevicePolicyManagerService";
     
-    private final Context mContext;
+    final Context mContext;
+    final MyPackageMonitor mMonitor;
 
     IPowerManager mIPowerManager;
     
@@ -89,6 +93,9 @@
         
         void writeToXml(XmlSerializer out)
                 throws IllegalArgumentException, IllegalStateException, IOException {
+            out.startTag(null, "policies");
+            info.writePoliciesToXml(out);
+            out.endTag(null, "policies");
             if (passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
                 out.startTag(null, "password-quality");
                 out.attribute(null, "value", Integer.toString(passwordQuality));
@@ -121,7 +128,9 @@
                     continue;
                 }
                 String tag = parser.getName();
-                if ("password-quality".equals(tag)) {
+                if ("policies".equals(tag)) {
+                    info.readPoliciesFromXml(parser);
+                } else if ("password-quality".equals(tag)) {
                     passwordQuality = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
                 } else if ("min-password-length".equals(tag)) {
@@ -133,6 +142,35 @@
                 } else if ("max-failed-password-wipe".equals(tag)) {
                     maximumFailedPasswordsForWipe = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
+                } else {
+                    Log.w(TAG, "Unknown admin tag: " + tag);
+                }
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+    }
+    
+    class MyPackageMonitor extends PackageMonitor {
+        public void onSomePackagesChanged() {
+            synchronized (DevicePolicyManagerService.this) {
+                for (int i=mAdminList.size()-1; i>=0; i--) {
+                    ActiveAdmin aa = mAdminList.get(i);
+                    int change = isPackageDisappearing(aa.info.getPackageName()); 
+                    if (change == PACKAGE_PERMANENT_CHANGE
+                            || change == PACKAGE_TEMPORARY_CHANGE) {
+                        Log.w(TAG, "Admin unexpectedly uninstalled: "
+                                + aa.info.getComponent());
+                        mAdminList.remove(i);
+                    } else if (isPackageModified(aa.info.getPackageName())) {
+                        try {
+                            mContext.getPackageManager().getReceiverInfo(
+                                    aa.info.getComponent(), 0);
+                        } catch (NameNotFoundException e) {
+                            Log.w(TAG, "Admin package change removed component: "
+                                    + aa.info.getComponent());
+                            mAdminList.remove(i);
+                        }
+                    }
                 }
             }
         }
@@ -143,6 +181,8 @@
      */
     public DevicePolicyManagerService(Context context) {
         mContext = context;
+        mMonitor = new MyPackageMonitor();
+        mMonitor.register(context, true);
     }
 
     private IPowerManager getIPowerManager() {
@@ -336,6 +376,10 @@
                 } else if ("failed-password-attempts".equals(tag)) {
                     mFailedPasswordAttempts = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
+                    XmlUtils.skipCurrentTag(parser);
+                } else {
+                    Log.w(TAG, "Unknown tag: " + tag);
+                    XmlUtils.skipCurrentTag(parser);
                 }
             }
         } catch (NullPointerException e) {
@@ -420,6 +464,18 @@
         }
     }
     
+    public boolean packageHasActiveAdmins(String packageName) {
+        synchronized (this) {
+            final int N = mAdminList.size();
+            for (int i=0; i<N; i++) {
+                if (mAdminList.get(i).info.getPackageName().equals(packageName)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+    
     public void removeActiveAdmin(ComponentName adminReceiver) {
         synchronized (this) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index 4791718..027f35c 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -41,6 +41,7 @@
 import android.location.LocationManager;
 import android.location.LocationProvider;
 import android.os.Binder;
+import android.media.AudioManager;
 import android.media.Ringtone;
 import android.media.RingtoneManager;
 import android.net.Uri;
@@ -379,7 +380,10 @@
                                     final Uri soundUri = Uri.parse("file://" + soundPath);
                                     if (soundUri != null) {
                                         final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
-                                        if (sfx != null) sfx.play();
+                                        if (sfx != null) {
+                                            sfx.setStreamType(AudioManager.STREAM_SYSTEM);
+                                            sfx.play();
+                                        }
                                     }
                                 }
                             }
@@ -412,7 +416,7 @@
                         mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
                                 LOCATION_UPDATE_MS, LOCATION_UPDATE_DISTANCE_METER, mLocationListener);
                         retrieveLocation();
-                        if (mLocation != null) {
+                        if (mCarModeEnabled && mLocation != null && mNightMode == MODE_NIGHT_AUTO) {
                             try {
                                 DockObserver.this.updateTwilight();
                             } catch (RemoteException e) {
@@ -458,8 +462,8 @@
             if (location == null) {
                 Time currentTime = new Time();
                 currentTime.set(System.currentTimeMillis());
-                double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE * currentTime.gmtoff
-                        - (currentTime.isDst > 0 ? 3600 : 0);
+                double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
+                        (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
                 location = new Location("fake");
                 location.setLongitude(lngOffset);
                 location.setLatitude(59.95);
@@ -583,20 +587,26 @@
             }
 
             // schedule next update
-            final int mLastTwilightState = tw.mState;
-            // add some extra time to be on the save side.
-            long nextUpdate = DateUtils.MINUTE_IN_MILLIS;
-            if (currentTime > tw.mSunset) {
-                // next update should be on the following day
-                tw.calculateTwilight(currentTime
-                        + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(),
-                        mLocation.getLongitude());
-            }
-
-            if (mLastTwilightState == TwilightCalculator.NIGHT) {
-                nextUpdate += tw.mSunrise;
+            long nextUpdate = 0;
+            if (tw.mSunrise == -1 || tw.mSunset == -1) {
+                // In the case the day or night never ends the update is scheduled 12 hours later.
+                nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS;
             } else {
-                nextUpdate += tw.mSunset;
+                final int mLastTwilightState = tw.mState;
+                // add some extra time to be on the save side.
+                nextUpdate += DateUtils.MINUTE_IN_MILLIS;
+                if (currentTime > tw.mSunset) {
+                    // next update should be on the following day
+                    tw.calculateTwilight(currentTime
+                            + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(),
+                            mLocation.getLongitude());
+                }
+
+                if (mLastTwilightState == TwilightCalculator.NIGHT) {
+                    nextUpdate += tw.mSunrise;
+                } else {
+                    nextUpdate += tw.mSunset;
+                }
             }
 
             Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE);
@@ -605,8 +615,11 @@
             mAlarmManager.cancel(pendingIntent);
             mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
 
-            // set current mode
-            setMode(Configuration.UI_MODE_TYPE_CAR, nightMode << 4);
+            // Make sure that we really set the new mode only if we're in car mode and
+            // automatic switching is enables.
+            if (mCarModeEnabled && mNightMode == MODE_NIGHT_AUTO) {
+                setMode(Configuration.UI_MODE_TYPE_CAR, nightMode << 4);
+            }
         }
     }
 
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 5e96a2f..59d4c9b 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethod;
@@ -48,7 +49,6 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
-import android.net.Uri;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -332,51 +332,68 @@
         }
     }
 
-    class PackageReceiver extends android.content.BroadcastReceiver {
+    class MyPackageMonitor extends PackageMonitor {
+        
         @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            String pkgList[] = null;
-            if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
-                    Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                Uri uri = intent.getData();
-                String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
-                if (pkg != null) {
-                    pkgList = new String[] { pkg };
-                }
-            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) ||
-                    Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
-                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-            }
-            if (pkgList == null || pkgList.length == 0) {
-                return;
-            }
+        public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
             synchronized (mMethodMap) {
-                buildInputMethodListLocked(mMethodList, mMethodMap);
-
-                InputMethodInfo curIm = null;
-                String curInputMethodId = Settings.Secure.getString(context
+                String curInputMethodId = Settings.Secure.getString(mContext
                         .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
                 final int N = mMethodList.size();
                 if (curInputMethodId != null) {
                     for (int i=0; i<N; i++) {
-                        if (mMethodList.get(i).getId().equals(curInputMethodId)) {
-                            curIm = mMethodList.get(i);
+                        InputMethodInfo imi = mMethodList.get(i);
+                        if (imi.getId().equals(curInputMethodId)) {
+                            for (String pkg : packages) {
+                                if (imi.getPackageName().equals(pkg)) {
+                                    if (!doit) {
+                                        return true;
+                                    }
+                                    
+                                    Settings.Secure.putString(mContext.getContentResolver(),
+                                            Settings.Secure.DEFAULT_INPUT_METHOD, "");
+                                    chooseNewDefaultIMELocked();
+                                    return true;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public void onSomePackagesChanged() {
+            synchronized (mMethodMap) {
+                InputMethodInfo curIm = null;
+                String curInputMethodId = Settings.Secure.getString(mContext
+                        .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+                final int N = mMethodList.size();
+                if (curInputMethodId != null) {
+                    for (int i=0; i<N; i++) {
+                        InputMethodInfo imi = mMethodList.get(i);
+                        if (imi.getId().equals(curInputMethodId)) {
+                            curIm = imi;
+                        }
+                        int change = isPackageDisappearing(imi.getPackageName());
+                        if (change == PACKAGE_TEMPORARY_CHANGE
+                                || change == PACKAGE_PERMANENT_CHANGE) {
+                            Log.i(TAG, "Input method uninstalled, disabling: "
+                                    + imi.getComponent());
+                            setInputMethodEnabledLocked(imi.getId(), false);
                         }
                     }
                 }
 
+                buildInputMethodListLocked(mMethodList, mMethodMap);
+
                 boolean changed = false;
 
                 if (curIm != null) {
-                    boolean foundPkg = false;
-                    for (String pkg : pkgList) {
-                        if (curIm.getPackageName().equals(pkg)) {
-                            foundPkg = true;
-                            break;
-                        }
-                    }
-                    if (foundPkg) {
+                    int change = isPackageDisappearing(curIm.getPackageName()); 
+                    if (change == PACKAGE_TEMPORARY_CHANGE
+                            || change == PACKAGE_PERMANENT_CHANGE) {
                         ServiceInfo si = null;
                         try {
                             si = mContext.getPackageManager().getServiceInfo(
@@ -387,7 +404,7 @@
                             // Uh oh, current input method is no longer around!
                             // Pick another one...
                             Log.i(TAG, "Current input method removed: " + curInputMethodId);
-                            if (!chooseNewDefaultIME()) {
+                            if (!chooseNewDefaultIMELocked()) {
                                 changed = true;
                                 curIm = null;
                                 curInputMethodId = "";
@@ -397,16 +414,17 @@
                                         curInputMethodId);
                             }
                         }
-
-                    } else if (curIm == null) {
-                        // We currently don't have a default input method... is
-                        // one now available?
-                        changed = chooseNewDefaultIME();
                     }
+                }
+                
+                if (curIm == null) {
+                    // We currently don't have a default input method... is
+                    // one now available?
+                    changed = chooseNewDefaultIMELocked();
+                }
 
-                    if (changed) {
-                        updateFromSettingsLocked();
-                    }
+                if (changed) {
+                    updateFromSettingsLocked();
                 }
             }
         }
@@ -438,19 +456,7 @@
             }
         });
 
-        PackageReceiver mBroadcastReceiver = new PackageReceiver();
-        IntentFilter packageFilt = new IntentFilter();
-        packageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
-        packageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        packageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        packageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
-        packageFilt.addDataScheme("package");
-        mContext.registerReceiver(mBroadcastReceiver, packageFilt);
-        // Register for events related to sdcard installation.
-        IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+        (new MyPackageMonitor()).register(mContext, true);
 
         IntentFilter screenOnOffFilt = new IntentFilter();
         screenOnOffFilt.addAction(Intent.ACTION_SCREEN_ON);
@@ -1385,7 +1391,7 @@
                 & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
 
-    private boolean chooseNewDefaultIME() {
+    private boolean chooseNewDefaultIMELocked() {
         List<InputMethodInfo> enabled = getEnabledInputMethodListLocked();
         if (enabled != null && enabled.size() > 0) {
             Settings.Secure.putString(mContext.getContentResolver(),
@@ -1446,7 +1452,7 @@
         String defaultIme = Settings.Secure.getString(mContext
                 .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
         if (!map.containsKey(defaultIme)) {
-            if (chooseNewDefaultIME()) {
+            if (chooseNewDefaultIMELocked()) {
                 updateFromSettingsLocked();
             }
         }
@@ -1557,91 +1563,95 @@
                         "Requires permission "
                         + android.Manifest.permission.WRITE_SECURE_SETTINGS);
             }
-
+            
             long ident = Binder.clearCallingIdentity();
             try {
-                // Make sure this is a valid input method.
-                InputMethodInfo imm = mMethodMap.get(id);
-                if (imm == null) {
-                    if (imm == null) {
-                        throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
-                    }
-                }
-
-                StringBuilder builder = new StringBuilder(256);
-
-                boolean removed = false;
-                String firstId = null;
-
-                // Look through the currently enabled input methods.
-                String enabledStr = Settings.Secure.getString(mContext.getContentResolver(),
-                        Settings.Secure.ENABLED_INPUT_METHODS);
-                if (enabledStr != null) {
-                    final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
-                    splitter.setString(enabledStr);
-                    while (splitter.hasNext()) {
-                        String curId = splitter.next();
-                        if (curId.equals(id)) {
-                            if (enabled) {
-                                // We are enabling this input method, but it is
-                                // already enabled.  Nothing to do.  The previous
-                                // state was enabled.
-                                return true;
-                            }
-                            // We are disabling this input method, and it is
-                            // currently enabled.  Skip it to remove from the
-                            // new list.
-                            removed = true;
-                        } else if (!enabled) {
-                            // We are building a new list of input methods that
-                            // doesn't contain the given one.
-                            if (firstId == null) firstId = curId;
-                            if (builder.length() > 0) builder.append(':');
-                            builder.append(curId);
-                        }
-                    }
-                }
-
-                if (!enabled) {
-                    if (!removed) {
-                        // We are disabling the input method but it is already
-                        // disabled.  Nothing to do.  The previous state was
-                        // disabled.
-                        return false;
-                    }
-                    // Update the setting with the new list of input methods.
-                    Settings.Secure.putString(mContext.getContentResolver(),
-                            Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
-                    // We the disabled input method is currently selected, switch
-                    // to another one.
-                    String selId = Settings.Secure.getString(mContext.getContentResolver(),
-                            Settings.Secure.DEFAULT_INPUT_METHOD);
-                    if (id.equals(selId)) {
-                        Settings.Secure.putString(mContext.getContentResolver(),
-                                Settings.Secure.DEFAULT_INPUT_METHOD,
-                                firstId != null ? firstId : "");
-                    }
-                    // Previous state was enabled.
-                    return true;
-                }
-
-                // Add in the newly enabled input method.
-                if (enabledStr == null || enabledStr.length() == 0) {
-                    enabledStr = id;
-                } else {
-                    enabledStr = enabledStr + ':' + id;
-                }
-
-                Settings.Secure.putString(mContext.getContentResolver(),
-                        Settings.Secure.ENABLED_INPUT_METHODS, enabledStr);
-
-                // Previous state was disabled.
-                return false;
+                return setInputMethodEnabledLocked(id, enabled);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
         }
     }
+    
+    boolean setInputMethodEnabledLocked(String id, boolean enabled) {
+        // Make sure this is a valid input method.
+        InputMethodInfo imm = mMethodMap.get(id);
+        if (imm == null) {
+            if (imm == null) {
+                throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
+            }
+        }
+
+        StringBuilder builder = new StringBuilder(256);
+
+        boolean removed = false;
+        String firstId = null;
+
+        // Look through the currently enabled input methods.
+        String enabledStr = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.ENABLED_INPUT_METHODS);
+        if (enabledStr != null) {
+            final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+            splitter.setString(enabledStr);
+            while (splitter.hasNext()) {
+                String curId = splitter.next();
+                if (curId.equals(id)) {
+                    if (enabled) {
+                        // We are enabling this input method, but it is
+                        // already enabled.  Nothing to do.  The previous
+                        // state was enabled.
+                        return true;
+                    }
+                    // We are disabling this input method, and it is
+                    // currently enabled.  Skip it to remove from the
+                    // new list.
+                    removed = true;
+                } else if (!enabled) {
+                    // We are building a new list of input methods that
+                    // doesn't contain the given one.
+                    if (firstId == null) firstId = curId;
+                    if (builder.length() > 0) builder.append(':');
+                    builder.append(curId);
+                }
+            }
+        }
+
+        if (!enabled) {
+            if (!removed) {
+                // We are disabling the input method but it is already
+                // disabled.  Nothing to do.  The previous state was
+                // disabled.
+                return false;
+            }
+            // Update the setting with the new list of input methods.
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
+            // We the disabled input method is currently selected, switch
+            // to another one.
+            String selId = Settings.Secure.getString(mContext.getContentResolver(),
+                    Settings.Secure.DEFAULT_INPUT_METHOD);
+            if (id.equals(selId)) {
+                Settings.Secure.putString(mContext.getContentResolver(),
+                        Settings.Secure.DEFAULT_INPUT_METHOD,
+                        firstId != null ? firstId : "");
+            }
+            // Previous state was enabled.
+            return true;
+        }
+
+        // Add in the newly enabled input method.
+        if (enabledStr == null || enabledStr.length() == 0) {
+            enabledStr = id;
+        } else {
+            enabledStr = enabledStr + ':' + id;
+        }
+
+        Settings.Secure.putString(mContext.getContentResolver(),
+                Settings.Secure.ENABLED_INPUT_METHODS, enabledStr);
+
+        // Previous state was disabled.
+        return false;
+    }
 
     // ----------------------------------------------------------------------
 
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 004fcf13..c0dcdf9 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -27,6 +27,7 @@
 import java.util.Observer;
 import java.util.Set;
 
+import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -484,14 +485,17 @@
         super();
         mContext = context;
 
-        Thread thread = new Thread(null, this, "LocationManagerService");
-        thread.start();
-
         if (LOCAL_LOGV) {
             Log.v(TAG, "Constructed LocationManager Service");
         }
     }
 
+    void systemReady() {
+        // we defer starting up the service until the system is ready 
+        Thread thread = new Thread(null, this, "LocationManagerService");
+        thread.start();
+    }
+
     private void initialize() {
         // Create a wake lock, needs to be done before calling loadProviders() below
         PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -506,6 +510,7 @@
         // Register for Package Manager updates
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+        intentFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
         mContext.registerReceiver(mBroadcastReceiver, intentFilter);
         IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
@@ -1539,8 +1544,9 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-
-            if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
+            boolean queryRestart = action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART);
+            if (queryRestart
+                    || action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
                     || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                 synchronized (mLock) {
@@ -1560,6 +1566,10 @@
                                 for (int j=i.size()-1; j>=0; j--) {
                                     UpdateRecord ur = i.get(j);
                                     if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
+                                        if (queryRestart) {
+                                            setResultCode(Activity.RESULT_OK);
+                                            return;
+                                        }
                                         if (removedRecs == null) {
                                             removedRecs = new ArrayList<Receiver>();
                                         }
@@ -1572,6 +1582,10 @@
                             ArrayList<ProximityAlert> removedAlerts = null;
                             for (ProximityAlert i : mProximityAlerts.values()) {
                                 if (i.mUid == uid) {
+                                    if (queryRestart) {
+                                        setResultCode(Activity.RESULT_OK);
+                                        return;
+                                    }
                                     if (removedAlerts == null) {
                                         removedAlerts = new ArrayList<ProximityAlert>();
                                     }
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 2a78806..2dc12f6 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -202,7 +202,7 @@
         String vs = getVolumeState(path);
         if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
             mUmsEnabling = enable; // Override for isUsbMassStorageEnabled()
-            int rc = doUnmountVolume(path, false);
+            int rc = doUnmountVolume(path, true);
             mUmsEnabling = false; // Clear override
             if (rc != StorageResultCode.OperationSucceeded) {
                 Log.e(TAG, String.format("Failed to unmount before enabling UMS (%d)", rc));
@@ -1003,7 +1003,11 @@
         warnOnNotMounted();
 
         synchronized (mAsecMountSet) {
-            if (mAsecMountSet.contains(oldId)) {
+            /*
+             * Because a mounted container has active internal state which cannot be 
+             * changed while active, we must ensure both ids are not currently mounted.
+             */
+            if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
                 return StorageResultCode.OperationFailedStorageMounted;
             }
         }
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 958d089..7c555e2 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -256,14 +256,14 @@
             Log.e(TAG, "Failed to parse netmask", uhe);
             cfg.netmask = 0;
         }
-        cfg.interfaceFlags = st.nextToken("]");
+        cfg.interfaceFlags = st.nextToken("]").trim() +"]";
         Log.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
         return cfg;
     }
 
     public void setInterfaceConfig(
             String iface, InterfaceConfiguration cfg) throws IllegalStateException {
-        String cmd = String.format("interface setcfg %s %s %s", iface,
+        String cmd = String.format("interface setcfg %s %s %s %s", iface,
                 intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags);
         mConnector.doCommand(cmd);
     }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 3657133..3c43352 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -307,6 +307,8 @@
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
 
+            boolean queryRestart = false;
+            
             if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
                 boolean batteryCharging = (intent.getIntExtra("plugged", 0) != 0);
                 int level = intent.getIntExtra("level", -1);
@@ -330,10 +332,13 @@
                 updateAdbNotification();
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
+                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
                     || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                 String pkgList[] = null;
                 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                } else if (queryRestart) {
+                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
                 } else {
                     Uri uri = intent.getData();
                     if (uri == null) {
@@ -347,7 +352,7 @@
                 }
                 if (pkgList != null && (pkgList.length > 0)) {
                     for (String pkgName : pkgList) {
-                        cancelAllNotificationsInt(pkgName, 0, 0);
+                        cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart);
                     }
                 }
             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
@@ -436,11 +441,15 @@
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
         filter.addAction(Intent.ACTION_UMS_CONNECTED);
         filter.addAction(Intent.ACTION_UMS_DISCONNECTED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
         filter.addAction(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         mContext.registerReceiver(mIntentReceiver, filter);
+        IntentFilter pkgFilter = new IntentFilter();
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
+        pkgFilter.addDataScheme("package");
+        mContext.registerReceiver(mIntentReceiver, pkgFilter);
         IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mIntentReceiver, sdFilter);
 
@@ -920,8 +929,8 @@
      * Cancels all notifications from a given package that have all of the
      * {@code mustHaveFlags}.
      */
-    void cancelAllNotificationsInt(String pkg, int mustHaveFlags,
-            int mustNotHaveFlags) {
+    boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
+            int mustNotHaveFlags, boolean doit) {
         EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags);
 
         synchronized (mNotificationList) {
@@ -938,13 +947,17 @@
                 if (!r.pkg.equals(pkg)) {
                     continue;
                 }
+                canceledSomething = true;
+                if (!doit) {
+                    return true;
+                }
                 mNotificationList.remove(i);
                 cancelNotificationLocked(r);
-                canceledSomething = true;
             }
             if (canceledSomething) {
                 updateLightsLocked();
             }
+            return canceledSomething;
         }
     }
 
@@ -966,7 +979,7 @@
 
         // Calling from user space, don't allow the canceling of actively
         // running foreground services.
-        cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE);
+        cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true);
     }
 
     void checkIncomingCall(String pkg) {
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 38d8615..9e0d623 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -28,7 +28,9 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.app.ActivityManagerNative;
+import android.app.DevicePolicyManager;
 import android.app.IActivityManager;
+import android.app.IDevicePolicyManager;
 import android.backup.IBackupManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -4856,42 +4858,67 @@
                 String oldCodePath) {
             String newCacheId = getNextCodePath(oldCodePath, pkgName, "/" + RES_FILE_NAME);
             String newCachePath = null;
-            final int RENAME_FAILED = 1;
-            final int MOUNT_FAILED = 2;
-            final int PASS = 4;
-            int errCode = RENAME_FAILED;
-            String errMsg = "RENAME_FAILED";
-            boolean mounted = PackageHelper.isContainerMounted(cid);
-            if (mounted) {
-                // Unmount the container
-                if (!PackageHelper.unMountSdDir(cid)) {
-                    Log.i(TAG, "Failed to unmount " + cid + " before renaming");
+            boolean enableRename = false;
+            if (enableRename) {
+                if (PackageHelper.isContainerMounted(cid)) {
+                    // Unmount the container
+                    if (!PackageHelper.unMountSdDir(cid)) {
+                        Log.i(TAG, "Failed to unmount " + cid + " before renaming");
+                        return false;
+                    }
+                }
+                if (!PackageHelper.renameSdDir(cid, newCacheId)) {
+                    Log.e(TAG, "Failed to rename " + cid + " to " + newCacheId);
                     return false;
                 }
-                mounted = false;
-            }
-            if (PackageHelper.renameSdDir(cid, newCacheId)) {
-                errCode = MOUNT_FAILED;
-                errMsg = "MOUNT_FAILED";
-                if ((newCachePath = PackageHelper.mountSdDir(newCacheId,
-                        getEncryptKey(), Process.SYSTEM_UID)) != null) {
-                    errCode = PASS;
-                    errMsg = "PASS";
+                if (!PackageHelper.isContainerMounted(newCacheId)) {
+                    Log.w(TAG, "Mounting container " + newCacheId);
+                    newCachePath = PackageHelper.mountSdDir(newCacheId,
+                            getEncryptKey(), Process.SYSTEM_UID);
+                } else {
+                    newCachePath = PackageHelper.getSdDir(newCacheId);
                 }
-            }
-            if (errCode != PASS) {
-                Log.i(TAG, "Failed to rename " + cid + " to " + newCacheId +
-                        " at path: " + cachePath + " to new path: " + newCachePath +
-                        "err = " + errMsg);
+                if (newCachePath == null) {
+                    Log.w(TAG, "Failed to get cache path for  " + newCacheId);
+                    return false;
+                }
                 // Mount old container?
-                return false;
+                Log.i(TAG, "Succesfully renamed " + cid +
+                        " at path: " + cachePath + " to " + newCacheId +
+                        " at new path: " + newCachePath);
+                cid = newCacheId;
+                cachePath = newCachePath;
+                return true;
             } else {
-                Log.i(TAG, "Succesfully renamed " + cid + " to " + newCacheId +
-                        " at path: " + cachePath + " to new path: " + newCachePath);
+                // STOPSHIP work around for rename
+                Log.i(TAG, "Copying instead of renaming");
+                File srcFile = new File(getCodePath());
+                // Create new container
+                newCachePath = PackageHelper.createSdDir(srcFile, newCacheId,
+                        getEncryptKey(), Process.SYSTEM_UID);
+                Log.i(TAG, "Created rename container " + newCacheId);
+                File destFile = new File(newCachePath + "/" + RES_FILE_NAME);
+                if (!FileUtils.copyFile(srcFile, destFile)) {
+                    Log.e(TAG, "Failed to copy " + srcFile + " to " + destFile);
+                    return false;
+                }
+                Log.i(TAG, "Successfully copied resource to " + newCachePath);
+                if (!PackageHelper.finalizeSdDir(newCacheId)) {
+                    Log.e(TAG, "Failed to finalize " + newCacheId);
+                    PackageHelper.destroySdDir(newCacheId);
+                    return false;
+                }
+                Log.i(TAG, "Finalized " + newCacheId);
+                Runtime.getRuntime().gc();
+                // Unmount first
+                PackageHelper.unMountSdDir(cid);
+                // Delete old container
+                PackageHelper.destroySdDir(cid);
+                // Dont have to mount. Already mounted.
+                cid = newCacheId;
+                cachePath = newCachePath;
+                return true;
             }
-            cid = newCacheId;
-            cachePath = newCachePath;
-            return true;
         }
 
         int doPostInstall(int status) {
@@ -5401,6 +5428,7 @@
             res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
             return;
         }
+
         if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {
             res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
             return;
@@ -5594,6 +5622,16 @@
         PackageRemovedInfo info = new PackageRemovedInfo();
         boolean res;
 
+        IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
+                ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+        try {
+            if (dpm != null && dpm.packageHasActiveAdmins(packageName)) {
+                Log.w(TAG, "Not removing package " + packageName + ": has active device admin");
+                return false;
+            }
+        } catch (RemoteException e) {
+        }
+        
         synchronized (mInstallLock) {
             res = deletePackageLI(packageName, deleteCodeAndResources, flags, info);
         }
@@ -5692,6 +5730,18 @@
                 // remove permissions associated with package
                 mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids);
             }
+            if (deletedPs != null) {
+                // remove from preferred activities.
+                ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
+                for (PreferredActivity pa : mSettings.mPreferredActivities.filterSet()) {
+                    if (pa.mActivity.getPackageName().equals(deletedPs.name)) {
+                        removed.add(pa);
+                    }
+                }
+                for (PreferredActivity pa : removed) {
+                    mSettings.mPreferredActivities.removeFilter(pa);
+                }
+            }
             // Save settings now
             mSettings.writeLP();
         }
@@ -7449,9 +7499,9 @@
                         Log.w(TAG, "Trying to update system app code path from " +
                                 p.codePathString + " to " + codePath.toString());
                     } else {
-                        // Let the app continue with previous uid if code path changes.
-                        reportSettingsProblem(Log.WARN,
-                                "Package " + name + " codePath changed from " + p.codePath
+                        // Just a change in the code path is not an issue, but
+                        // let's log a message about it.
+                        Log.i(TAG, "Package " + name + " codePath changed from " + p.codePath
                                 + " to " + codePath + "; Retaining data and using new");
                     }
                 }
@@ -8785,7 +8835,7 @@
     }
 
    static String getTempContainerId() {
-       String prefix = "smdl1tmp";
+       String prefix = "smdl2tmp";
        int tmpIdx = 1;
        String list[] = PackageHelper.getSecureContainerList();
        if (list != null) {
@@ -8827,30 +8877,45 @@
        return prefix + tmpIdx;
    }
 
-   public void updateExternalMediaStatus(final boolean mediaStatus) {
+   public boolean updateExternalMediaStatus(final boolean mediaStatus) {
        synchronized (mPackages) {
            if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
                    mediaStatus+", mMediaMounted=" + mMediaMounted);
            if (mediaStatus == mMediaMounted) {
-               return;
+               return false;
            }
            mMediaMounted = mediaStatus;
+           final HashMap<SdInstallArgs, String> processCids =
+                   new HashMap<SdInstallArgs, String>();
+           final int[] uidArr = getExternalMediaPackages(mediaStatus, processCids);
+           if (processCids.size() == 0) {
+               return false;
+           }
             // Queue up an async operation since the package installation may take a little while.
            mHandler.post(new Runnable() {
                public void run() {
                    mHandler.removeCallbacks(this);
-                   updateExternalMediaStatusInner(mediaStatus);
+                   if (mediaStatus) {
+                       if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
+                       loadMediaPackages(processCids, uidArr);
+                       startCleaningPackages();
+                   } else {
+                       if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
+                       unloadMediaPackages(processCids, uidArr);
+                   }
                }
            });
+           return true;
        }
    }
 
-   void updateExternalMediaStatusInner(boolean mediaStatus) {
+    private int[] getExternalMediaPackages(boolean mediaStatus,
+            Map<SdInstallArgs, String> processCids) {
        final String list[] = PackageHelper.getSecureContainerList();
        if (list == null || list.length == 0) {
-           return;
+           return null;
        }
-       HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
+
        int uidList[] = new int[list.length];
        int num = 0;
        synchronized (mPackages) {
@@ -8892,14 +8957,7 @@
                }
            }
        }
-       if (mediaStatus) {
-           if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
-           loadMediaPackages(processCids, uidArr);
-           startCleaningPackages();
-       } else {
-           if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
-           unloadMediaPackages(processCids, uidArr);
-       }
+       return uidArr;
    }
 
    private void sendResourcesChangedBroadcast(boolean mediaStatus,
@@ -8919,7 +8977,7 @@
        }
    }
 
-   void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
+   private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
        ArrayList<String> pkgList = new ArrayList<String>();
        Set<SdInstallArgs> keys = processCids.keySet();
        for (SdInstallArgs args : keys) {
@@ -8969,7 +9027,7 @@
        }
    }
 
-   void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
+   private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
        if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages");
        ArrayList<String> pkgList = new ArrayList<String>();
        ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
diff --git a/services/java/com/android/server/RecognitionManagerService.java b/services/java/com/android/server/RecognitionManagerService.java
new file mode 100644
index 0000000..7305b07
--- /dev/null
+++ b/services/java/com/android/server/RecognitionManagerService.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import com.android.internal.content.PackageMonitor;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Binder;
+import android.provider.Settings;
+import android.speech.RecognitionService;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.List;
+
+public class RecognitionManagerService extends Binder {
+    final static String TAG = "RecognitionManagerService";
+    
+    final Context mContext;
+    final MyPackageMonitor mMonitor;
+    
+    class MyPackageMonitor extends PackageMonitor {
+        public void onSomePackagesChanged() {
+            ComponentName comp = getCurRecognizer();
+            if (comp == null) {
+                if (anyPackagesAppearing()) {
+                    comp = findAvailRecognizer(null);
+                    if (comp != null) {
+                        setCurRecognizer(comp);
+                    }
+                }
+                return;
+            }
+            
+            int change = isPackageDisappearing(comp.getPackageName()); 
+            if (change == PACKAGE_PERMANENT_CHANGE
+                    || change == PACKAGE_TEMPORARY_CHANGE) {
+                setCurRecognizer(findAvailRecognizer(null));
+                
+            } else if (isPackageModified(comp.getPackageName())) {
+                setCurRecognizer(findAvailRecognizer(comp.getPackageName()));
+            }
+        }
+    }
+    
+    RecognitionManagerService(Context context) {
+        mContext = context;
+        mMonitor = new MyPackageMonitor();
+        mMonitor.register(context, true);
+    }
+    
+    public void systemReady() {
+        ComponentName comp = getCurRecognizer();
+        if (comp != null) {
+            // See if the current recognizer is no longer available.
+            try {
+                mContext.getPackageManager().getServiceInfo(comp, 0);
+            } catch (NameNotFoundException e) {
+                setCurRecognizer(null);
+            }
+        } else {
+            comp = findAvailRecognizer(null);
+            if (comp != null) {
+                setCurRecognizer(comp);
+            }
+        }
+    }
+    
+    ComponentName findAvailRecognizer(String prefPackage) {
+        List<ResolveInfo> available =
+                mContext.getPackageManager().queryIntentServices(
+                        new Intent(RecognitionService.SERVICE_INTERFACE), 0);
+        int numAvailable = available.size();
+    
+        if (numAvailable == 0) {
+            Log.w(TAG, "no available voice recognition services found");
+            return null;
+        } else {
+            if (prefPackage != null) {
+                for (int i=0; i<numAvailable; i++) {
+                    ServiceInfo serviceInfo = available.get(i).serviceInfo;
+                    if (prefPackage.equals(serviceInfo.packageName)) {
+                        return new ComponentName(serviceInfo.packageName, serviceInfo.name);
+                    }
+                }
+            }
+            if (numAvailable > 1) {
+                Log.w(TAG, "more than one voice recognition service found, picking first");
+            }
+    
+            ServiceInfo serviceInfo = available.get(0).serviceInfo;
+            return new ComponentName(serviceInfo.packageName, serviceInfo.name);
+        }
+    }
+    
+    ComponentName getCurRecognizer() {
+        String curRecognizer = Settings.Secure.getString(
+                mContext.getContentResolver(),
+                Settings.Secure.VOICE_RECOGNITION_SERVICE);
+        if (TextUtils.isEmpty(curRecognizer)) {
+            return null;
+        }
+        return ComponentName.unflattenFromString(curRecognizer);
+    }
+    
+    void setCurRecognizer(ComponentName comp) {
+        Settings.Secure.putString(mContext.getContentResolver(),
+                Settings.Secure.VOICE_RECOGNITION_SERVICE,
+                comp != null ? comp.flattenToShortString() : "");
+    }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a09896a..1f46faf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -97,6 +97,7 @@
         BluetoothA2dpService bluetoothA2dp = null;
         HeadsetObserver headset = null;
         DockObserver dock = null;
+        RecognitionManagerService recognition = null;
 
         // Critical services...
         try {
@@ -208,6 +209,7 @@
         AppWidgetService appWidget = null;
         NotificationManagerService notification = null;
         WallpaperManagerService wallpaper = null;
+        LocationManagerService location = null;
 
         if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
             try {
@@ -302,8 +304,8 @@
 
             try {
                 Log.i(TAG, "Location Manager");
-                ServiceManager.addService(Context.LOCATION_SERVICE,
-                        new LocationManagerService(context));
+                location = new LocationManagerService(context);
+                ServiceManager.addService(Context.LOCATION_SERVICE, location);
             } catch (Throwable e) {
                 Log.e(TAG, "Failure starting Location Manager", e);
             }
@@ -377,6 +379,13 @@
             }
 
             try {
+                Log.i(TAG, "Recognition Service");
+                recognition = new RecognitionManagerService(context);
+            } catch (Throwable e) {
+                Log.e(TAG, "Failure starting Recognition Service", e);
+            }
+
+            try {
                 com.android.server.status.StatusBarPolicy.installIcons(context, statusBar);
             } catch (Throwable e) {
                 Log.e(TAG, "Failure installing status bar icons", e);
@@ -435,6 +444,8 @@
         final AppWidgetService appWidgetF = appWidget;
         final WallpaperManagerService wallpaperF = wallpaper;
         final InputMethodManagerService immF = imm;
+        final RecognitionManagerService recognitionF = recognition;
+        final LocationManagerService locationF = location;
 
         // We now tell the activity manager it is okay to run third party
         // code.  It will call back into us once it has gotten to the state
@@ -449,6 +460,7 @@
                 if (batteryF != null) batteryF.systemReady();
                 if (connectivityF != null) connectivityF.systemReady();
                 if (dockF != null) dockF.systemReady();
+                if (recognitionF != null) recognitionF.systemReady();
                 Watchdog.getInstance().start();
 
                 // It is now okay to let the various system services start their
@@ -457,6 +469,7 @@
                 if (appWidgetF != null) appWidgetF.systemReady(safeMode);
                 if (wallpaperF != null) wallpaperF.systemReady();
                 if (immF != null) immF.systemReady();
+                if (locationF != null) locationF.systemReady();
             }
         });
 
diff --git a/services/java/com/android/server/TwilightCalculator.java b/services/java/com/android/server/TwilightCalculator.java
index a8f67d8..a5c93b5 100644
--- a/services/java/com/android/server/TwilightCalculator.java
+++ b/services/java/com/android/server/TwilightCalculator.java
@@ -46,10 +46,16 @@
     // Java time on Jan 1, 2000 12:00 UTC.
     private static final long UTC_2000 = 946728000000L;
 
-    /** Time of sunset (civil twilight) in milliseconds. */
+    /**
+     * Time of sunset (civil twilight) in milliseconds or -1 in the case the day
+     * or night never ends.
+     */
     public long mSunset;
 
-    /** Time of sunrise (civil twilight) in milliseconds. */
+    /**
+     * Time of sunrise (civil twilight) in milliseconds or -1 in the case the
+     * day or night never ends.
+     */
     public long mSunrise;
 
     /** Current state */
@@ -85,10 +91,24 @@
         double solarDec = Math.asin(FloatMath.sin(solarLng) * FloatMath.sin(OBLIQUITY));
 
         final double latRad = latiude * DEGREES_TO_RADIANS;
-        float hourAngle = (float) (Math
-                .acos((FloatMath.sin(ALTIDUTE_CORRECTION_CIVIL_TWILIGHT) - Math.sin(latRad)
-                        * Math.sin(solarDec))
-                        / (Math.cos(latRad) * Math.cos(solarDec))) / (2 * Math.PI));
+
+        double cosHourAngle = (FloatMath.sin(ALTIDUTE_CORRECTION_CIVIL_TWILIGHT) - Math.sin(latRad)
+                * Math.sin(solarDec)) / (Math.cos(latRad) * Math.cos(solarDec));
+        // The day or night never ends for the given date and location, if this value is out of
+        // range.
+        if (cosHourAngle >= 1) {
+            mState = NIGHT;
+            mSunset = -1;
+            mSunrise = -1;
+            return;
+        } else if (cosHourAngle <= -1) {
+            mState = DAY;
+            mSunset = -1;
+            mSunrise = -1;
+            return;
+        }
+
+        float hourAngle = (float) (Math.acos(cosHourAngle) / (2 * Math.PI));
 
         mSunset = Math.round((solarTransitJ2000 + hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000;
         mSunrise = Math.round((solarTransitJ2000 - hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000;
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 81255ee..481e6a4 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -65,7 +65,10 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.service.wallpaper.ImageWallpaper;
+import com.android.server.DevicePolicyManagerService.ActiveAdmin;
+import com.android.server.DevicePolicyManagerService.MyPackageMonitor;
 import com.android.common.FastXmlSerializer;
 
 class WallpaperManagerService extends IWallpaperManager.Stub {
@@ -122,6 +125,7 @@
     
     final Context mContext;
     final IWindowManager mIWindowManager;
+    final MyPackageMonitor mMonitor;
 
     int mWidth = -1;
     int mHeight = -1;
@@ -150,6 +154,7 @@
     
     WallpaperConnection mWallpaperConnection;
     long mLastDiedTime;
+    boolean mWallpaperUpdating;
     
     class WallpaperConnection extends IWallpaperConnection.Stub
             implements ServiceConnection {
@@ -165,6 +170,7 @@
         public void onServiceConnected(ComponentName name, IBinder service) {
             synchronized (mLock) {
                 if (mWallpaperConnection == this) {
+                    mLastDiedTime = SystemClock.uptimeMillis();
                     mService = IWallpaperService.Stub.asInterface(service);
                     attachServiceLocked(this);
                     // XXX should probably do saveSettingsLocked() later
@@ -182,8 +188,8 @@
                 mEngine = null;
                 if (mWallpaperConnection == this) {
                     Log.w(TAG, "Wallpaper service gone: " + mWallpaperComponent);
-                    if ((mLastDiedTime+MIN_WALLPAPER_CRASH_TIME)
-                            < SystemClock.uptimeMillis()) {
+                    if (!mWallpaperUpdating && (mLastDiedTime+MIN_WALLPAPER_CRASH_TIME)
+                                > SystemClock.uptimeMillis()) {
                         Log.w(TAG, "Reverting to built-in wallpaper!");
                         bindWallpaperComponentLocked(null);
                     }
@@ -205,11 +211,92 @@
         }
     }
     
+    class MyPackageMonitor extends PackageMonitor {
+        @Override
+        public void onPackageUpdateFinished(String packageName, int uid) {
+            synchronized (mLock) {
+                if (mWallpaperComponent != null &&
+                        mWallpaperComponent.getPackageName().equals(packageName)) {
+                    mWallpaperUpdating = false;
+                    ComponentName comp = mWallpaperComponent;
+                    clearWallpaperComponentLocked();
+                    bindWallpaperComponentLocked(comp);
+                }
+            }
+        }
+
+        @Override
+        public void onPackageUpdateStarted(String packageName, int uid) {
+            synchronized (mLock) {
+                if (mWallpaperComponent != null &&
+                        mWallpaperComponent.getPackageName().equals(packageName)) {
+                    mWallpaperUpdating = true;
+                }
+            }
+        }
+
+        @Override
+        public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
+            return doPackagesChanged(doit);
+        }
+
+        @Override
+        public void onSomePackagesChanged() {
+            doPackagesChanged(true);
+        }
+        
+        boolean doPackagesChanged(boolean doit) {
+            boolean changed = false;
+            synchronized (mLock) {
+                if (mWallpaperComponent != null) {
+                    int change = isPackageDisappearing(mWallpaperComponent.getPackageName());
+                    if (change == PACKAGE_PERMANENT_CHANGE
+                            || change == PACKAGE_TEMPORARY_CHANGE) {
+                        changed = true;
+                        if (doit) {
+                            Log.w(TAG, "Wallpaper uninstalled, removing: " + mWallpaperComponent);
+                            clearWallpaperLocked();
+                        }
+                    }
+                }
+                if (mNextWallpaperComponent != null) {
+                    int change = isPackageDisappearing(mNextWallpaperComponent.getPackageName());
+                    if (change == PACKAGE_PERMANENT_CHANGE
+                            || change == PACKAGE_TEMPORARY_CHANGE) {
+                        mNextWallpaperComponent = null;
+                    }
+                }
+                if (mWallpaperComponent != null
+                        && isPackageModified(mWallpaperComponent.getPackageName())) {
+                    try {
+                        mContext.getPackageManager().getServiceInfo(
+                                mWallpaperComponent, 0);
+                    } catch (NameNotFoundException e) {
+                        Log.w(TAG, "Wallpaper component gone, removing: " + mWallpaperComponent);
+                        clearWallpaperLocked();
+                    }
+                }
+                if (mNextWallpaperComponent != null
+                        && isPackageModified(mNextWallpaperComponent.getPackageName())) {
+                    try {
+                        mContext.getPackageManager().getServiceInfo(
+                                mNextWallpaperComponent, 0);
+                    } catch (NameNotFoundException e) {
+                        mNextWallpaperComponent = null;
+                    }
+                }
+            }
+            return changed;
+        }
+    }
+    
     public WallpaperManagerService(Context context) {
         if (DEBUG) Log.v(TAG, "WallpaperService startup");
         mContext = context;
         mIWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService(Context.WINDOW_SERVICE));
+        mMonitor = new MyPackageMonitor();
+        mMonitor.register(context, true);
         WALLPAPER_DIR.mkdirs();
         loadSettingsLocked();
         mWallpaperObserver.startWatching();
@@ -241,16 +328,20 @@
     public void clearWallpaper() {
         if (DEBUG) Log.v(TAG, "clearWallpaper");
         synchronized (mLock) {
-            File f = WALLPAPER_FILE;
-            if (f.exists()) {
-                f.delete();
-            }
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                bindWallpaperComponentLocked(null);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            clearWallpaperLocked();
+        }
+    }
+
+    public void clearWallpaperLocked() {
+        File f = WALLPAPER_FILE;
+        if (f.exists()) {
+            f.delete();
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            bindWallpaperComponentLocked(null);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -499,7 +590,9 @@
                     mWidth, mHeight);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed attaching wallpaper; clearing", e);
-            bindWallpaperComponentLocked(null);
+            if (!mWallpaperUpdating) {
+                bindWallpaperComponentLocked(null);
+            }
         }
     }
     
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 45c3f00..7b64704 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -47,6 +47,7 @@
 import android.app.Service;
 import android.backup.IBackupManager;
 import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -93,7 +94,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.Config;
 import android.util.EventLog;
 import android.util.Log;
@@ -107,7 +107,6 @@
 
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -136,6 +135,7 @@
     static final boolean DEBUG_VISBILITY = localLOGV || false;
     static final boolean DEBUG_PROCESSES = localLOGV || false;
     static final boolean DEBUG_PROVIDER = localLOGV || false;
+    static final boolean DEBUG_URI_PERMISSION = localLOGV || false;
     static final boolean DEBUG_USER_LEAVING = localLOGV || false;
     static final boolean DEBUG_RESULTS = localLOGV || false;
     static final boolean DEBUG_BACKUP = localLOGV || false;
@@ -296,12 +296,6 @@
     // Memory pages are 4K.
     static final int PAGE_SIZE = 4*1024;
     
-    // System property defining error report receiver for system apps
-    static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps";
-
-    // System property defining default error report receiver
-    static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default";
-
     // Corresponding memory levels for above adjustments.
     static final int EMPTY_APP_MEM;
     static final int HIDDEN_APP_MEM;
@@ -975,6 +969,7 @@
     static final int PROC_START_TIMEOUT_MSG = 20;
     static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
     static final int KILL_APPLICATION_MSG = 22;
+    static final int FINALIZE_PENDING_INTENT_MSG = 23;
 
     AlertDialog mUidAlert;
 
@@ -1193,9 +1188,12 @@
                     int uid = msg.arg1;
                     boolean restart = (msg.arg2 == 1);
                     String pkg = (String) msg.obj;
-                    forceStopPackageLocked(pkg, uid, restart, false);
+                    forceStopPackageLocked(pkg, uid, restart, false, true);
                 }
             } break;
+            case FINALIZE_PENDING_INTENT_MSG: {
+                ((PendingIntentRecord)msg.obj).completeFinalize();
+            } break;
             }
         }
     };
@@ -1396,7 +1394,8 @@
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
             ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
 
-        mConfiguration.makeDefault();
+        mConfiguration.setToDefaults();
+        mConfiguration.locale = Locale.getDefault();
         mProcessStats.init();
         
         // Add ourself to the Watchdog monitors.
@@ -3605,10 +3604,18 @@
         }
 
         synchronized(this) {
+            int callingPid;
+            int callingUid;
+            if (caller == null) {
+                callingPid = Binder.getCallingPid();
+                callingUid = Binder.getCallingUid();
+            } else {
+                callingPid = callingUid = -1;
+            }
             final long origId = Binder.clearCallingIdentity();
             int res = startActivityLocked(caller, intent, resolvedType,
                     grantedUriPermissions, grantedMode, aInfo,
-                    resultTo, resultWho, requestCode, -1, -1,
+                    resultTo, resultWho, requestCode, callingPid, callingUid,
                     onlyIfNeeded, componentSpecified);
             Binder.restoreCallingIdentity(origId);
             return res;
@@ -4846,7 +4853,7 @@
                     return;
                 }
                 killPackageProcessesLocked(packageName, pkgUid,
-                        SECONDARY_SERVER_ADJ, false);
+                        SECONDARY_SERVER_ADJ, false, true);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -4988,7 +4995,7 @@
     }
 
     private void forceStopPackageLocked(final String packageName, int uid) {
-        forceStopPackageLocked(packageName, uid, false, false);
+        forceStopPackageLocked(packageName, uid, false, false, true);
         Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
                 Uri.fromParts("package", packageName, null));
         intent.putExtra(Intent.EXTRA_UID, uid);
@@ -4997,8 +5004,8 @@
                 false, false, MY_PID, Process.SYSTEM_UID);
     }
     
-    private final void killPackageProcessesLocked(String packageName, int uid,
-            int minOomAdj, boolean callerWillRestart) {
+    private final boolean killPackageProcessesLocked(String packageName, int uid,
+            int minOomAdj, boolean callerWillRestart, boolean doit) {
         ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
 
         // Remove all processes this package may have touched: all with the
@@ -5010,11 +5017,16 @@
             for (int ia=0; ia<NA; ia++) {
                 ProcessRecord app = apps.valueAt(ia);
                 if (app.removed) {
-                    procs.add(app);
+                    if (doit) {
+                        procs.add(app);
+                    }
                 } else if ((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid)
                         || app.processName.equals(packageName)
                         || app.processName.startsWith(procNamePrefix)) {
                     if (app.setAdj >= minOomAdj) {
+                        if (!doit) {
+                            return true;
+                        }
                         app.removed = true;
                         procs.add(app);
                     }
@@ -5026,10 +5038,11 @@
         for (int i=0; i<N; i++) {
             removeProcessLocked(procs.get(i), callerWillRestart);
         }
+        return N > 0;
     }
 
-    private final void forceStopPackageLocked(String name, int uid,
-            boolean callerWillRestart, boolean purgeCache) {
+    private final boolean forceStopPackageLocked(String name, int uid,
+            boolean callerWillRestart, boolean purgeCache, boolean doit) {
         int i, N;
 
         if (uid < 0) {
@@ -5039,21 +5052,28 @@
             }
         }
 
-        Log.i(TAG, "Force stopping package " + name + " uid=" + uid);
+        if (doit) {
+            Log.i(TAG, "Force stopping package " + name + " uid=" + uid);
 
-        Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator();
-        while (badApps.hasNext()) {
-            SparseArray<Long> ba = badApps.next();
-            if (ba.get(uid) != null) {
-                badApps.remove();
+            Iterator<SparseArray<Long>> badApps = mProcessCrashTimes.getMap().values().iterator();
+            while (badApps.hasNext()) {
+                SparseArray<Long> ba = badApps.next();
+                if (ba.get(uid) != null) {
+                    badApps.remove();
+                }
             }
         }
-
-        killPackageProcessesLocked(name, uid, -100, callerWillRestart);
+        
+        boolean didSomething = killPackageProcessesLocked(name, uid, -100,
+                callerWillRestart, doit);
         
         for (i=mHistory.size()-1; i>=0; i--) {
             HistoryRecord r = (HistoryRecord)mHistory.get(i);
             if (r.packageName.equals(name)) {
+                if (!doit) {
+                    return true;
+                }
+                didSomething = true;
                 Log.i(TAG, "  Force finishing activity " + r);
                 if (r.app != null) {
                     r.app.removed = true;
@@ -5066,6 +5086,10 @@
         ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
         for (ServiceRecord service : mServices.values()) {
             if (service.packageName.equals(name)) {
+                if (!doit) {
+                    return true;
+                }
+                didSomething = true;
                 Log.i(TAG, "  Force stopping service " + service);
                 if (service.app != null) {
                     service.app.removed = true;
@@ -5080,13 +5104,17 @@
             bringDownServiceLocked(services.get(i), true);
         }
         
-        resumeTopActivityLocked(null);
-        if (purgeCache) {
-            AttributeCache ac = AttributeCache.instance();
-            if (ac != null) {
-                ac.removePackage(name);
+        if (doit) {
+            if (purgeCache) {
+                AttributeCache ac = AttributeCache.instance();
+                if (ac != null) {
+                    ac.removePackage(name);
+                }
             }
+            resumeTopActivityLocked(null);
         }
+        
+        return didSomething;
     }
 
     private final boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart) {
@@ -5583,19 +5611,38 @@
     }
 
     final void finishBooting() {
-        // Ensure that any processes we had put on hold are now started
-        // up.
-        final int NP = mProcessesOnHold.size();
-        if (NP > 0) {
-            ArrayList<ProcessRecord> procs =
-                new ArrayList<ProcessRecord>(mProcessesOnHold);
-            for (int ip=0; ip<NP; ip++) {
-                this.startProcessLocked(procs.get(ip), "on-hold", null);
+        IntentFilter pkgFilter = new IntentFilter();
+        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
+        pkgFilter.addDataScheme("package");
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
+                if (pkgs != null) {
+                    for (String pkg : pkgs) {
+                        if (forceStopPackageLocked(pkg, -1, false, false, false)) {
+                            setResultCode(Activity.RESULT_OK);
+                            return;
+                        }
+                    }
+                }
             }
-        }
-        if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
-            // Tell anyone interested that we are done booting!
-            synchronized (this) {
+        }, pkgFilter);
+        
+        synchronized (this) {
+            // Ensure that any processes we had put on hold are now started
+            // up.
+            final int NP = mProcessesOnHold.size();
+            if (NP > 0) {
+                ArrayList<ProcessRecord> procs =
+                    new ArrayList<ProcessRecord>(mProcessesOnHold);
+                for (int ip=0; ip<NP; ip++) {
+                    this.startProcessLocked(procs.get(ip), "on-hold", null);
+                }
+            }
+            
+            if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+                // Tell anyone interested that we are done booting!
                 broadcastIntentLocked(null, null,
                         new Intent(Intent.ACTION_BOOT_COMPLETED, null),
                         null, null, 0, null, null,
@@ -6132,10 +6179,15 @@
             return;
         }
 
+        if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                "Requested grant " + targetPkg + " permission to " + uri);
+        
         final IPackageManager pm = ActivityThread.getPackageManager();
 
         // If this is not a content: uri, we can't do anything with it.
         if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+            if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                    "Can't grant URI permission for non-content URI: " + uri);
             return;
         }
 
@@ -6161,6 +6213,8 @@
         try {
             targetUid = pm.getPackageUid(targetPkg);
             if (targetUid < 0) {
+                if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                        "Can't grant URI permission no uid for: " + targetPkg);
                 return;
             }
         } catch (RemoteException ex) {
@@ -6170,17 +6224,12 @@
         // First...  does the target actually need this permission?
         if (checkHoldingPermissionsLocked(pm, pi, targetUid, modeFlags)) {
             // No need to grant the target this permission.
+            if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                    "Target " + targetPkg + " already has full permission to " + uri);
             return;
         }
 
-        // Second...  maybe someone else has already granted the
-        // permission?
-        if (checkUriPermissionLocked(uri, targetUid, modeFlags)) {
-            // No need to grant the target this permission.
-            return;
-        }
-
-        // Third...  is the provider allowing granting of URI permissions?
+        // Second...  is the provider allowing granting of URI permissions?
         if (!pi.grantUriPermissions) {
             throw new SecurityException("Provider " + pi.packageName
                     + "/" + pi.name
@@ -6205,7 +6254,7 @@
             }
         }
 
-        // Fourth...  does the caller itself have permission to access
+        // Third...  does the caller itself have permission to access
         // this uri?
         if (!checkHoldingPermissionsLocked(pm, pi, callingUid, modeFlags)) {
             if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
@@ -6218,6 +6267,9 @@
         // to the uri, and the target doesn't.  Let's now give this to
         // the target.
 
+        if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                "Granting " + targetPkg + " permission to " + uri);
+        
         HashMap<Uri, UriPermission> targetUris
                 = mGrantedUriPermissions.get(targetUid);
         if (targetUris == null) {
@@ -6291,6 +6343,8 @@
             HashMap<Uri, UriPermission> perms
                     = mGrantedUriPermissions.get(perm.uid);
             if (perms != null) {
+                if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                        "Removing " + perm.uid + " permission to " + perm.uri);
                 perms.remove(perm.uri);
                 if (perms.size() == 0) {
                     mGrantedUriPermissions.remove(perm.uid);
@@ -6330,6 +6384,9 @@
             return;
         }
 
+        if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                "Revoking all granted permissions to " + uri);
+        
         final IPackageManager pm = ActivityThread.getPackageManager();
 
         final String authority = uri.getAuthority();
@@ -6388,6 +6445,8 @@
                             continue toploop;
                         }
                     }
+                    if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                            "Revoking " + perm.uid + " permission to " + perm.uri);
                     perm.clearModes(modeFlags);
                     if (perm.modeFlags == 0) {
                         it.remove();
@@ -7556,6 +7615,15 @@
                                     ? cpi.readPermission : cpi.writePermission);
                 }
 
+                if (!mSystemReady && !mDidUpdate && !mWaitingUpdate
+                        && !cpi.processName.equals("system")) {
+                    // If this content provider does not run in the system
+                    // process, and the system is not yet ready to run other
+                    // processes, then fail fast instead of hanging.
+                    throw new IllegalArgumentException(
+                            "Attempt to launch content provider before system ready");
+                }
+                
                 cpr = (ContentProviderRecord)mProvidersByClass.get(cpi.name);
                 final boolean firstClass = cpr == null;
                 if (firstClass) {
@@ -7803,6 +7871,16 @@
     public static final void installSystemProviders() {
         ProcessRecord app = mSelf.mProcessNames.get("system", Process.SYSTEM_UID);
         List providers = mSelf.generateApplicationProvidersLocked(app);
+        if (providers != null) {
+            for (int i=providers.size()-1; i>=0; i--) {
+                ProviderInfo pi = (ProviderInfo)providers.get(i);
+                if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
+                    Log.w(TAG, "Not installing system proc provider " + pi.name
+                            + ": not system .apk");
+                    providers.remove(i);
+                }
+            }
+        }
         mSystemThread.installSystemProviders(providers);
     }
 
@@ -8050,7 +8128,7 @@
             mDebugTransient = !persistent;
             if (packageName != null) {
                 final long origId = Binder.clearCallingIdentity();
-                forceStopPackageLocked(packageName, -1, false, false);
+                forceStopPackageLocked(packageName, -1, false, false, true);
                 Binder.restoreCallingIdentity(origId);
             }
         }
@@ -8339,7 +8417,6 @@
             mAlwaysFinishActivities = alwaysFinishActivities;
             // This happens before any activities are started, so we can
             // change mConfiguration in-place.
-            mConfiguration.locale = Locale.getDefault();
             mConfiguration.updateFrom(configuration);
             mConfigurationSeq = mConfiguration.seq = 1;
             if (DEBUG_CONFIGURATION) Log.v(TAG, "Initial config: " + mConfiguration);
@@ -8537,73 +8614,6 @@
         return handleAppCrashLocked(app);
     }
 
-    private ComponentName getErrorReportReceiver(ProcessRecord app) {
-        // check if error reporting is enabled in secure settings
-        int enabled = Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.SEND_ACTION_APP_ERROR, 0);
-        if (enabled == 0) {
-            return null;
-        }
-
-        IPackageManager pm = ActivityThread.getPackageManager();
-
-        try {
-            // look for receiver in the installer package
-            String candidate = pm.getInstallerPackageName(app.info.packageName);
-            ComponentName result = getErrorReportReceiver(pm, app.info.packageName, candidate);
-            if (result != null) {
-                return result;
-            }
-
-            // if the error app is on the system image, look for system apps
-            // error receiver
-            if ((app.info.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
-                candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
-                result = getErrorReportReceiver(pm, app.info.packageName, candidate);
-                if (result != null) {
-                    return result;
-                }
-            }
-
-            // if there is a default receiver, try that
-            candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
-            return getErrorReportReceiver(pm, app.info.packageName, candidate);
-        } catch (RemoteException e) {
-            // should not happen
-            Log.e(TAG, "error talking to PackageManager", e);
-            return null;
-        }
-    }
-
-    /**
-     * Return activity in receiverPackage that handles ACTION_APP_ERROR.
-     *
-     * @param pm PackageManager isntance
-     * @param errorPackage package which caused the error
-     * @param receiverPackage candidate package to receive the error
-     * @return activity component within receiverPackage which handles
-     * ACTION_APP_ERROR, or null if not found
-     */
-    private ComponentName getErrorReportReceiver(IPackageManager pm, String errorPackage,
-            String receiverPackage) throws RemoteException {
-        if (receiverPackage == null || receiverPackage.length() == 0) {
-            return null;
-        }
-
-        // break the loop if it's the error report receiver package that crashed
-        if (receiverPackage.equals(errorPackage)) {
-            return null;
-        }
-
-        Intent intent = new Intent(Intent.ACTION_APP_ERROR);
-        intent.setPackage(receiverPackage);
-        ResolveInfo info = pm.resolveIntent(intent, null, 0);
-        if (info == null || info.activityInfo == null) {
-            return null;
-        }
-        return new ComponentName(receiverPackage, info.activityInfo.name);
-    }
-
     private void makeAppNotRespondingLocked(ProcessRecord app,
             String activity, String shortMsg, String longMsg) {
         app.notResponding = true;
@@ -8717,7 +8727,8 @@
     }
 
     void startAppProblemLocked(ProcessRecord app) {
-        app.errorReportReceiver = getErrorReportReceiver(app);
+        app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
+                mContext, app.info.packageName, app.info.flags);
         skipCurrentReceiverLocked(app);
     }
 
@@ -11951,7 +11962,7 @@
                         String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                         if (list != null && (list.length > 0)) {
                             for (String pkg : list) {
-                                forceStopPackageLocked(pkg, -1, false, true);
+                                forceStopPackageLocked(pkg, -1, false, true, true);
                             }
                         }
                     } else {
@@ -11960,7 +11971,7 @@
                         if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
                             if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
                                 forceStopPackageLocked(ssp,
-                                        intent.getIntExtra(Intent.EXTRA_UID, -1), false, true);
+                                        intent.getIntExtra(Intent.EXTRA_UID, -1), false, true, true);
                             }
                         }
                     }
@@ -12931,7 +12942,7 @@
             }
 
             final long origId = Binder.clearCallingIdentity();
-            forceStopPackageLocked(ii.targetPackage, -1, true, false);
+            forceStopPackageLocked(ii.targetPackage, -1, true, false, true);
             ProcessRecord app = addAppLocked(ai);
             app.instrumentationClass = className;
             app.instrumentationInfo = ai;
@@ -12986,7 +12997,7 @@
         app.instrumentationProfileFile = null;
         app.instrumentationArguments = null;
 
-        forceStopPackageLocked(app.processName, -1, false, false);
+        forceStopPackageLocked(app.processName, -1, false, false, true);
     }
 
     public void finishInstrumentation(IApplicationThread target,
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index b3086d5..fac47d7 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -145,8 +145,9 @@
         
         public String toString() {
             return "Key{" + typeName() + " pkg=" + packageName
-                + " intent=" + requestIntent.toShortString(true, false) + " flags=0x"
-                + Integer.toHexString(flags) + "}";
+                + " intent="
+                + (requestIntent != null ? requestIntent.toShortString(true, false) : "<null>")
+                + " flags=0x" + Integer.toHexString(flags) + "}";
         }
         
         String typeName() {
@@ -262,17 +263,26 @@
     }
     
     protected void finalize() throws Throwable {
-        if (!canceled) {
-            synchronized(owner) {
-                WeakReference<PendingIntentRecord> current =
-                        owner.mIntentSenderRecords.get(key);
-                if (current == ref) {
-                    owner.mIntentSenderRecords.remove(key);
-                }
+        try {
+            if (!canceled) {
+                owner.mHandler.sendMessage(owner.mHandler.obtainMessage(
+                        ActivityManagerService.FINALIZE_PENDING_INTENT_MSG, this));
             }
+        } finally {
+            super.finalize();
         }
     }
 
+    public void completeFinalize() {
+        synchronized(owner) {
+            WeakReference<PendingIntentRecord> current =
+                    owner.mIntentSenderRecords.get(key);
+            if (current == ref) {
+                owner.mIntentSenderRecords.remove(key);
+            }
+        }
+    }
+    
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("uid="); pw.print(uid);
                 pw.print(" packageName="); pw.print(key.packageName);
@@ -286,8 +296,10 @@
             pw.print(prefix); pw.print("requestCode="); pw.print(key.requestCode);
                     pw.print(" requestResolvedType="); pw.println(key.requestResolvedType);
         }
-        pw.print(prefix); pw.print("requestIntent=");
-                pw.println(key.requestIntent.toShortString(true, true));
+        if (key.requestIntent != null) {
+            pw.print(prefix); pw.print("requestIntent=");
+                    pw.println(key.requestIntent.toShortString(true, true));
+        }
         if (sent || canceled) {
             pw.print(prefix); pw.print("sent="); pw.print(sent);
                     pw.print(" canceled="); pw.println(canceled);
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 2f2cc32..5a02c40 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -269,8 +269,12 @@
                         inm.enqueueNotification(localPackageName, localForegroundId,
                                 localForegroundNoti, outId);
                     } catch (RuntimeException e) {
-                        Log.w(ActivityManagerService.TAG, "Error showing notification for service",
-                            e);
+                        Log.w(ActivityManagerService.TAG,
+                                "Error showing notification for service", e);
+                        // If it gave us a garbage notification, it doesn't
+                        // get to be foreground.
+                        ams.setServiceForeground(name, ServiceRecord.this,
+                                localForegroundId, null, true);
                     } catch (RemoteException e) {
                     }
                 }
@@ -293,8 +297,8 @@
                     try {
                         inm.cancelNotification(localPackageName, localForegroundId);
                     } catch (RuntimeException e) {
-                        Log.w(ActivityManagerService.TAG, "Error canceling notification for"
-                            + " service", e);
+                        Log.w(ActivityManagerService.TAG,
+                                "Error canceling notification for service", e);
                     } catch (RemoteException e) {
                     }
                 }
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 1d20074..10d6e3a 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
+import android.net.InterfaceConfiguration;
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
 import android.net.NetworkInfo;
@@ -56,6 +57,7 @@
  *
  * TODO - look for parent classes and code sharing
  */
+
 public class Tethering extends INetworkManagementEventObserver.Stub {
 
     private Notification mTetheringNotification;
@@ -80,6 +82,10 @@
 
     private String mUpstreamIfaceName;
 
+    // turning on/off RNDIS resets the interface generating and extra discon/conn cycle
+    // count how many to ignore..  Self correcting if you plug/unplug a bunch of times.
+    private int mUsbResetExpected = 0;
+
     HierarchicalStateMachine mTetherMasterSM;
 
     public Tethering(Context context) {
@@ -113,7 +119,7 @@
                 com.android.internal.R.array.config_tether_dhcp_range);
         if (mDhcpRange.length == 0) {
             mDhcpRange = new String[2];
-            mDhcpRange[0] = new String("169.254.2.1");
+            mDhcpRange[0] = new String("169.254.2.2");
             mDhcpRange[1] = new String("169.254.2.64");
         } else if(mDhcpRange.length == 1) {
             String[] tmp = new String[2];
@@ -127,16 +133,6 @@
         mTetherableWifiRegexs = context.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_wifi_regexs);
 
-        String[] ifaces = new String[0];
-        try {
-            ifaces = service.listInterfaces();
-        } catch (Exception e) {
-            Log.e(TAG, "Error listing Interfaces :" + e);
-        }
-        for (String iface : ifaces) {
-            interfaceAdded(iface);
-        }
-
         // TODO - remove and rely on real notifications of the current iface
         mDnsServers = new String[2];
         mDnsServers[0] = "8.8.8.8";
@@ -147,6 +143,7 @@
     public void interfaceLinkStatusChanged(String iface, boolean link) {
         Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link);
         boolean found = false;
+        boolean usb = false;
         for (String regex : mTetherableWifiRegexs) {
             if (iface.matches(regex)) {
                 found = true;
@@ -156,6 +153,7 @@
         for (String regex: mTetherableUsbRegexs) {
             if (iface.matches(regex)) {
                 found = true;
+                usb = true;
                 break;
             }
         }
@@ -165,7 +163,7 @@
             TetherInterfaceSM sm = mIfaces.get(iface);
             if (link) {
                 if (sm == null) {
-                    sm = new TetherInterfaceSM(iface);
+                    sm = new TetherInterfaceSM(iface, usb);
                     mIfaces.put(iface, sm);
                     sm.start();
                 }
@@ -179,7 +177,10 @@
     }
 
     public void interfaceAdded(String iface) {
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
         boolean found = false;
+        boolean usb = false;
         for (String regex : mTetherableWifiRegexs) {
             if (iface.matches(regex)) {
                 found = true;
@@ -189,6 +190,7 @@
         for (String regex : mTetherableUsbRegexs) {
             if (iface.matches(regex)) {
                 found = true;
+                usb = true;
                 break;
             }
         }
@@ -196,13 +198,14 @@
             Log.d(TAG, iface + " is not a tetherable iface, ignoring");
             return;
         }
+
         synchronized (mIfaces) {
             TetherInterfaceSM sm = mIfaces.get(iface);
             if (sm != null) {
                 Log.e(TAG, "active iface (" + iface + ") reported as added, ignoring");
                 return;
             }
-            sm = new TetherInterfaceSM(iface);
+            sm = new TetherInterfaceSM(iface, usb);
             mIfaces.put(iface, sm);
             sm.start();
         }
@@ -314,8 +317,8 @@
         if (tellUser) {
             for (Object o : availableList) {
                 String s = (String)o;
-                for (Object matchObject : mTetherableUsbRegexs) {
-                    if (s.matches((String)matchObject)) {
+                for (String match : mTetherableUsbRegexs) {
+                    if (s.matches(match)) {
                         showTetherAvailableNotification();
                         return;
                     }
@@ -414,20 +417,32 @@
         }
     }
 
-
-
-
     private class StateReceiver extends BroadcastReceiver {
         public void onReceive(Context content, Intent intent) {
             String action = intent.getAction();
             if (action.equals(Intent.ACTION_UMS_CONNECTED)) {
-                Tethering.this.handleTtyConnect();
+                Log.w(TAG, "got UMS connected");
+                synchronized (Tethering.this) {
+                    if(mUsbResetExpected != 0) {
+                        Log.w(TAG, "mUsbResetExpected == " + mUsbResetExpected + ", ignored");
+                        mUsbResetExpected--;
+                        return;
+                    }
+                }
+                Tethering.this.toggleUsbIfaces(true); // add them
             } else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) {
-                Tethering.this.handleTtyDisconnect();
+                Log.w(TAG, "got UMS disconneded broadcast");
+                synchronized (Tethering.this) {
+                    if(mUsbResetExpected != 0) {
+                        Log.w(TAG, "mUsbResetExpected == " + mUsbResetExpected + ", ignored");
+                        mUsbResetExpected--;
+                        return;
+                    }
+                }
+                Tethering.this.toggleUsbIfaces(false); // remove them
             } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                 IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                IConnectivityManager service =
-                        IConnectivityManager.Stub.asInterface(b);
+                IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
                 try {
                     NetworkInfo info = service.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_DUN);
                     int msg;
@@ -442,6 +457,114 @@
         }
     }
 
+    // used on cable insert/remove
+    private void toggleUsbIfaces(boolean add) {
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+        String[] ifaces = new String[0];
+        try {
+            ifaces = service.listInterfaces();
+        } catch (Exception e) {
+            Log.e(TAG, "Error listing Interfaces :" + e);
+            return;
+        }
+        for (String iface : ifaces) {
+            for (String regex : mTetherableUsbRegexs) {
+                if (iface.matches(regex)) {
+                    if (add) {
+                        interfaceAdded(iface);
+                    } else {
+                        interfaceRemoved(iface);
+                    }
+                }
+            }
+        }
+    }
+
+    // toggled when we enter/leave the fully teathered state
+    private boolean enableRndisUsb(boolean enabled) {
+        Log.d(TAG, "enableRndisUsb(" + enabled + ")");
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+        try {
+            if (enabled) {
+                // turning this on will reset USB and generate two bogus events - ignore them
+                synchronized (this) {
+                    if (!service.isUsbRNDISStarted()) {
+                        mUsbResetExpected += 2;
+                        service.startUsbRNDIS();
+                    }
+                }
+            } else {
+                if (service.isUsbRNDISStarted()) {
+                    service.stopUsbRNDIS();
+                }
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Error toggling usb RNDIS :" + e);
+            return false;
+        }
+        return true;
+    }
+
+    // configured when we start tethering and unconfig'd on error or conclusion
+    private boolean configureUsb(boolean enabled) {
+        Log.d(TAG, "configureUsb(" + enabled + ")");
+
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+        // bring toggle the interfaces
+        String[] ifaces = new String[0];
+        try {
+            ifaces = service.listInterfaces();
+        } catch (Exception e) {
+            Log.e(TAG, "Error listing Interfaces :" + e);
+            return false;
+        }
+        for (String iface : ifaces) {
+            for (String regex : mTetherableUsbRegexs) {
+                if (iface.matches(regex)) {
+                    InterfaceConfiguration ifcg = null;
+                    try {
+                        ifcg = service.getInterfaceConfig(iface);
+                        if (ifcg != null) {
+                            ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 1;
+                            ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0;
+                            if (enabled) {
+                                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
+                            } else {
+                                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
+                            }
+                            service.setInterfaceConfig(iface, ifcg);
+                        }
+                    } catch (Exception e) {
+                        Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
+                        return false;
+                    }
+                }
+            }
+        }
+
+        if (!enabled) {
+            // turn off ndis
+            try {
+                synchronized (this) {
+                    if (service.isUsbRNDISStarted()) {
+                        mUsbResetExpected += 2;
+                        service.stopUsbRNDIS();
+                    }
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Error stopping usb RNDIS :" + e);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     private void handleTtyConnect() {
         Log.d(TAG, "handleTtyConnect");
         // for each of the available Tty not already supported by a ppp session,
@@ -609,6 +732,7 @@
         private HierarchicalState mUntetherInterfaceErrorState;
         private HierarchicalState mEnableNatErrorState;
         private HierarchicalState mDisableNatErrorState;
+        private HierarchicalState mUsbConfigurationErrorState;
 
         private HierarchicalState mUnavailableState;
 
@@ -617,10 +741,12 @@
         private boolean mTethered;
 
         String mIfaceName;
+        boolean mUsb;
 
-        TetherInterfaceSM(String name) {
+        TetherInterfaceSM(String name, boolean usb) {
             super(name);
             mIfaceName = name;
+            mUsb = usb;
 
             mInitialState = new InitialState();
             addState(mInitialState);
@@ -638,6 +764,8 @@
             addState(mEnableNatErrorState);
             mDisableNatErrorState = new DisableNatErrorState();
             addState(mDisableNatErrorState);
+            mUsbConfigurationErrorState = new UsbConfigurationErrorState();
+            addState(mUsbConfigurationErrorState);
             mUnavailableState = new UnavailableState();
             addState(mUnavailableState);
 
@@ -656,6 +784,7 @@
             if (current == mUntetherInterfaceErrorState) res += "UntetherInterfaceErrorState";
             if (current == mEnableNatErrorState) res += "EnableNatErrorState";
             if (current == mDisableNatErrorState) res += "DisableNatErrorState";
+            if (current == mUsbConfigurationErrorState) res += "UsbConfigurationErrorState";
             if (current == mUnavailableState) res += "UnavailableState";
             if (mAvailable) res += " - Available";
             if (mTethered) res += " - Tethered";
@@ -686,8 +815,15 @@
             return mErrored;
         }
 
-        private synchronized void setErrored(boolean errored) {
-            mErrored = errored;
+        private void setErrored(boolean errored) {
+            synchronized (this) {
+                mErrored = errored;
+            }
+            if (errored && mUsb) {
+                // note everything's been unwound by this point so nothing to do on
+                // further error..
+                Tethering.this.configureUsb(false);
+            }
         }
 
         class InitialState extends HierarchicalState {
@@ -726,6 +862,19 @@
             @Override
             public void enter() {
                 setAvailable(false);
+                if (mUsb) {
+                    if (!Tethering.this.configureUsb(true)) {
+                        Message m = mTetherMasterSM.obtainMessage(
+                                TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
+                        m.obj = TetherInterfaceSM.this;
+                        mTetherMasterSM.sendMessage(m);
+
+                        m = obtainMessage(CMD_TRANSITION_TO_ERROR);
+                        m.obj = mUsbConfigurationErrorState;
+                        sendMessageAtFrontOfQueue(m);
+                        return;
+                    }
+                }
                 sendTetherStateChangedBroadcast();
             }
             @Override
@@ -739,6 +888,12 @@
                                 TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
                         m.obj = TetherInterfaceSM.this;
                         mTetherMasterSM.sendMessage(m);
+                        if (mUsb) {
+                            if (!Tethering.this.configureUsb(false)) {
+                                transitionTo(mUsbConfigurationErrorState);
+                                break;
+                            }
+                        }
                         transitionTo(mInitialState);
                         break;
                     case CMD_TETHER_MODE_ALIVE:
@@ -759,6 +914,10 @@
                         mTetherMasterSM.sendMessage(m);
                         transitionTo(mUnavailableState);
                         break;
+                   case CMD_TRANSITION_TO_ERROR:
+                       HierarchicalState s = (HierarchicalState)(message.obj);
+                       transitionTo(s);
+                       break;
                     default:
                         retValue = false;
                 }
@@ -788,12 +947,17 @@
                     sendMessageAtFrontOfQueue(m);
                     return;
                 }
+                if (mUsb) Tethering.this.enableRndisUsb(true);
                 Log.d(TAG, "Tethered " + mIfaceName);
                 setAvailable(false);
                 setTethered(true);
                 sendTetherStateChangedBroadcast();
             }
             @Override
+            public void exit() {
+                if(mUsb) Tethering.this.enableRndisUsb(false);
+            }
+            @Override
             public boolean processMessage(Message message) {
                 Log.d(TAG, "TetheredState.processMessage what=" + message.what);
                 boolean retValue = true;
@@ -821,7 +985,15 @@
                         m.obj = TetherInterfaceSM.this;
                         mTetherMasterSM.sendMessage(m);
                         if (message.what == CMD_TETHER_UNREQUESTED) {
-                            transitionTo(mInitialState);
+                            if (mUsb) {
+                                if (!Tethering.this.configureUsb(false)) {
+                                    transitionTo(mUsbConfigurationErrorState);
+                                } else {
+                                    transitionTo(mInitialState);
+                                }
+                            } else {
+                                transitionTo(mInitialState);
+                            }
                         } else if (message.what == CMD_INTERFACE_DOWN) {
                             transitionTo(mUnavailableState);
                         }
@@ -857,6 +1029,12 @@
                         }
                         Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
                         sendTetherStateChangedBroadcast();
+                        if (mUsb) {
+                            if (!Tethering.this.configureUsb(false)) {
+                                transitionTo(mUsbConfigurationErrorState);
+                                break;
+                            }
+                        }
                         transitionTo(mInitialState);
                         break;
                     case CMD_TRANSITION_TO_ERROR:
@@ -974,6 +1152,15 @@
                 sendTetherStateChangedBroadcast();
             }
         }
+
+        class UsbConfigurationErrorState extends ErrorState {
+            @Override
+            public void enter() {
+                Log.e(TAG, "Error trying to configure USB " + mIfaceName);
+                setAvailable(false);
+                setErrored(true);
+            }
+        }
     }
 
     class TetherMasterSM extends HierarchicalStateMachine {
@@ -1179,6 +1366,7 @@
         class CellDunAliveState extends HierarchicalState {
             @Override
             public void enter() {
+                Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
                 sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
             }
 
@@ -1228,6 +1416,8 @@
                         transitionTo(mInitialState);
                         break;
                     case CMD_CELL_DUN_RENEW:
+                        Log.d(TAG, "renewing dun connection - requeuing for another " +
+                                CELL_DUN_RENEW_MS + "ms");
                         b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
                         IConnectivityManager cservice = IConnectivityManager.Stub.asInterface(b);
                         try {
@@ -1248,6 +1438,8 @@
         class TetherModeAliveState extends HierarchicalState {
             @Override
             public void enter() {
+                Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
+                sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
                 for (Object o : mNotifyList) {
                     TetherInterfaceSM sm = (TetherInterfaceSM)o;
                     sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_MODE_ALIVE));
@@ -1298,6 +1490,18 @@
                         }
                         transitionTo(mInitialState);
                         break;
+                    case CMD_CELL_DUN_RENEW:
+                        Log.d(TAG, "renewing dun connection - requeuing for another " +
+                                CELL_DUN_RENEW_MS + "ms");
+                        b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+                        IConnectivityManager cservice = IConnectivityManager.Stub.asInterface(b);
+                        try {
+                            cservice.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+                                    Phone.FEATURE_ENABLE_DUN, new Binder());
+                        } catch (Exception e) {
+                        }
+                        sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
+                        break;
                     default:
                        retValue = false;
                        break;
diff --git a/services/java/com/android/server/status/ExpandedView.java b/services/java/com/android/server/status/ExpandedView.java
index d0f14cb..33ac8c1 100644
--- a/services/java/com/android/server/status/ExpandedView.java
+++ b/services/java/com/android/server/status/ExpandedView.java
@@ -11,17 +11,11 @@
 
 
 public class ExpandedView extends LinearLayout {
-    final Display mDisplay;
     StatusBarService mService;
-    boolean mTracking;
-    int mStartX, mStartY;
-    int mMaxHeight = 0;
     int mPrevHeight = -1;
 
     public ExpandedView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mDisplay = ((WindowManager)context.getSystemService(
-                Context.WINDOW_SERVICE)).getDefaultDisplay();
     }
 
     @Override
@@ -36,12 +30,6 @@
     }
 
     @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec,
-                MeasureSpec.makeMeasureSpec(mMaxHeight, MeasureSpec.AT_MOST));
-    }
-
-    @Override
      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
          super.onLayout(changed, left, top, right, bottom);
          int height = bottom - top;
@@ -51,11 +39,4 @@
              mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
          }
      }
-
-    void setMaxHeight(int h) {
-        if (h != mMaxHeight) {
-            mMaxHeight = h;
-            requestLayout();
-        }
-    }
 }
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index d13f9d3..f1ccb9b 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -826,7 +826,10 @@
                 final Uri soundUri = Uri.parse("file://" + soundPath);
                 if (soundUri != null) {
                     final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
-                    if (sfx != null) sfx.play();
+                    if (sfx != null) {
+                        sfx.setStreamType(AudioManager.STREAM_SYSTEM);
+                        sfx.play();
+                    }
                 }
             }
         }
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index dab8b72..3f77291 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -187,8 +187,9 @@
     TextView mSpnLabel;
     TextView mPlmnLabel;
     TextView mClearButton;
+    View mExpandedContents;
     CloseDragHandle mCloseView;
-    int[] mCloseLocation = new int[2];
+    int[] mPositionTmp = new int[2];
     boolean mExpanded;
     boolean mExpandedVisible;
 
@@ -198,7 +199,7 @@
     // the tracker view
     TrackingView mTrackingView;
     WindowManager.LayoutParams mTrackingParams;
-    int mTrackingPosition;
+    int mTrackingPosition; // the position of the top of the tracking view.
 
     // ticker
     private Ticker mTicker;
@@ -206,6 +207,7 @@
     private boolean mTicking;
     
     // Tracking finger for opening/closing.
+    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
     boolean mTracking;
     VelocityTracker mVelocityTracker;
     
@@ -273,6 +275,7 @@
 
         mExpandedDialog = new ExpandedDialog(context);
         mExpandedView = expanded;
+        mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
         mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
         mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
         mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
@@ -299,6 +302,8 @@
         mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
         mCloseView.mService = this;
 
+        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
+
         // add the more icon for the notifications
         IconData moreData = IconData.makeIcon(null, context.getPackageName(),
                 R.drawable.stat_notify_more, 0, 42);
@@ -1204,8 +1209,10 @@
     }
     
     boolean interceptTouchEvent(MotionEvent event) {
-        if (SPEW) Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
+        if (SPEW) {
+            Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
                 + mDisabled);
+        }
 
         if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
             return false;
@@ -1214,7 +1221,7 @@
         final int statusBarSize = mStatusBarView.getHeight();
         final int hitSize = statusBarSize*2;
         if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            int y = (int)event.getRawY();
+            final int y = (int)event.getRawY();
 
             if (!mExpanded) {
                 mViewDelta = statusBarSize - y;
@@ -1224,8 +1231,16 @@
             }
             if ((!mExpanded && y < hitSize) ||
                     (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
-                prepareTracking(y, !mExpanded); // opening if we're not already fully visible
-                mVelocityTracker.addMovement(event);
+
+                // We drop events at the edge of the screen to make the windowshade come
+                // down by accident less, especially when pushing open a device with a keyboard
+                // that rotates (like g1 and droid)
+                int x = (int)event.getRawX();
+                final int edgeBorder = mEdgeBorder;
+                if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
+                    prepareTracking(y, !mExpanded);// opening if we're not already fully visible
+                    mVelocityTracker.addMovement(event);
+                }
             }
         } else if (mTracking) {
             mVelocityTracker.addMovement(event);
@@ -1517,20 +1532,11 @@
 
         /// ---------- Expanded View --------------
         pixelFormat = PixelFormat.TRANSLUCENT;
-        bg = mExpandedView.getBackground();
-        if (bg != null) {
-            pixelFormat = bg.getOpacity();
-            if (pixelFormat != PixelFormat.TRANSLUCENT) {
-                // we want good-looking gradients, so we force a 8-bits per
-                // pixel format.
-                pixelFormat = PixelFormat.RGBX_8888;
-            }
-        }
 
         final int disph = mDisplay.getHeight();
         lp = mExpandedDialog.getWindow().getAttributes();
         lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
-        lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+        lp.height = getExpandedHeight();
         lp.x = 0;
         mTrackingPosition = lp.y = -disph; // sufficiently large negative
         lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
@@ -1549,10 +1555,10 @@
         mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
         mExpandedDialog.setContentView(mExpandedView,
                 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                                           ViewGroup.LayoutParams.WRAP_CONTENT));
+                                           ViewGroup.LayoutParams.MATCH_PARENT));
+        mExpandedDialog.getWindow().setBackgroundDrawable(null);
         mExpandedDialog.show();
         FrameLayout hack = (FrameLayout)mExpandedView.getParent();
-        hack.setForeground(null);
     }
 
     void setDateViewVisibility(boolean visible, int anim) {
@@ -1617,11 +1623,15 @@
         mTrackingParams.height = disph-h;
         WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
 
-        mCloseView.getLocationInWindow(mCloseLocation);
-
         if (mExpandedParams != null) {
+            mCloseView.getLocationInWindow(mPositionTmp);
+            final int closePos = mPositionTmp[1];
+
+            mExpandedContents.getLocationInWindow(mPositionTmp);
+            final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
+
             mExpandedParams.y = pos + mTrackingView.getHeight()
-                    - (mTrackingParams.height-mCloseLocation[1]) - mExpandedView.getHeight();
+                    - (mTrackingParams.height-closePos) - contentsBottom;
             int max = h;
             if (mExpandedParams.y > max) {
                 mExpandedParams.y = max;
@@ -1631,13 +1641,13 @@
                 mExpandedParams.y = min;
             }
 
-            /*
-            Log.d(TAG, "mTrackingPosition=" + mTrackingPosition
-                    + " mTrackingView.height=" + mTrackingView.getHeight()
-                    + " diff=" + (mTrackingPosition + mTrackingView.getHeight())
-                    + " h=" + h);
-            */
-            panelSlightlyVisible((mTrackingPosition + mTrackingView.getHeight()) > h);
+            boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
+            if (!visible) {
+                // if the contents aren't visible, move the expanded view way off screen
+                // because the window itself extends below the content view.
+                mExpandedParams.y = -disph;
+            }
+            panelSlightlyVisible(visible);
             mExpandedDialog.getWindow().setAttributes(mExpandedParams);
         }
 
@@ -1645,16 +1655,19 @@
             Log.d(TAG, "updateExpandedViewPos after  expandedPosition=" + expandedPosition
                     + " mTrackingParams.y=" + mTrackingParams.y
                     + " mTrackingPosition=" + mTrackingPosition
-                    + " mExpandedParams.y=" + mExpandedParams.y);
+                    + " mExpandedParams.y=" + mExpandedParams.y
+                    + " mExpandedParams.height=" + mExpandedParams.height);
         }
     }
 
-    void updateAvailableHeight() {
+    int getExpandedHeight() {
+        return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
+    }
+
+    void updateExpandedHeight() {
         if (mExpandedView != null) {
-            int disph = mDisplay.getHeight();
-            int h = mStatusBarView.getHeight();
-            int max = disph - (mCloseView.getHeight() + h);
-            mExpandedView.setMaxHeight(max);
+            mExpandedParams.height = getExpandedHeight();
+            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
         }
     }
 
@@ -1771,10 +1784,15 @@
      * meantime, just update the things that we know change.
      */
     void updateResources() {
+        Resources res = mContext.getResources();
+
         mClearButton.setText(mContext.getText(R.string.status_bar_clear_all_button));
         mOngoingTitle.setText(mContext.getText(R.string.status_bar_ongoing_events_title));
         mLatestTitle.setText(mContext.getText(R.string.status_bar_latest_events_title));
         mNoNotificationsTitle.setText(mContext.getText(R.string.status_bar_no_notifications_title));
+
+        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
+
         if (false) Log.v(TAG, "updateResources");
     }
 
diff --git a/services/java/com/android/server/status/StorageNotification.java b/services/java/com/android/server/status/StorageNotification.java
index 3b79049..e0b288d 100644
--- a/services/java/com/android/server/status/StorageNotification.java
+++ b/services/java/com/android/server/status/StorageNotification.java
@@ -36,6 +36,7 @@
 import android.os.storage.StorageEventListener;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageResultCode;
+import android.provider.Settings;
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
@@ -46,6 +47,8 @@
 public class StorageNotification extends StorageEventListener {
     private static final String TAG = "StorageNotification";
 
+    private static final boolean POP_UMS_ACTIVITY_ON_CONNECT = true;
+
     /**
      * Binder context for this service
      */
@@ -239,12 +242,28 @@
             Intent intent = new Intent();
             intent.setClass(mContext, com.android.server.status.UsbStorageActivity.class);
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            final boolean adbOn = 1 == Settings.Secure.getInt(
+                mContext.getContentResolver(),
+                Settings.Secure.ADB_ENABLED,
+                0);
+
             PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
             setUsbStorageNotification(
                     com.android.internal.R.string.usb_storage_notification_title,
                     com.android.internal.R.string.usb_storage_notification_message,
                     com.android.internal.R.drawable.stat_sys_data_usb,
                     false, true, pi);
+
+            if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) {
+                // We assume that developers don't want to enable UMS every
+                // time they attach a device to a USB host. The average user,
+                // however, is looking to charge the phone (in which case this
+                // is harmless) or transfer files (in which case this coaches
+                // the user about how to complete that task and saves several
+                // steps).
+                mContext.startActivity(intent);
+            }
         } else {
             setUsbStorageNotification(0, 0, 0, false, false, null);
         }
diff --git a/services/java/com/android/server/status/TrackingView.java b/services/java/com/android/server/status/TrackingView.java
index 722d10c..886d66d 100644
--- a/services/java/com/android/server/status/TrackingView.java
+++ b/services/java/com/android/server/status/TrackingView.java
@@ -23,7 +23,7 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        mService.updateAvailableHeight();
+        mService.updateExpandedHeight();
     }
 
     @Override
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 52c8b1f..cab7b81 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -440,10 +440,11 @@
             return Phone.APN_REQUEST_FAILED;
         }
 
-        if(DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = "
+        if (DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = "
                 + isApnTypeActive(type) + " and state = " + state);
 
         if (!isApnTypeAvailable(type)) {
+            if (DBG) Log.d(LOG_TAG, "type not available");
             return Phone.APN_TYPE_NOT_AVAILABLE;
         }
 
diff --git a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
index 31cf6a7..9f8e57f 100644
--- a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -22,10 +22,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ServiceManager;
-import android.telephony.PhoneNumberUtils;
-import android.util.Log;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -37,7 +34,7 @@
 
     protected PhoneBase phone;
     protected AdnRecordCache adnCache;
-    protected Object mLock = new Object();
+    protected final Object mLock = new Object();
     protected int recordSize[];
     protected boolean success;
     protected List<AdnRecord> records;
@@ -80,8 +77,7 @@
                     ar = (AsyncResult)msg.obj;
                     synchronized (mLock) {
                         if (ar.exception == null) {
-                            records = (List<AdnRecord>)
-                                    ((ArrayList<AdnRecord>) ar.result);
+                            records = (List<AdnRecord>) ar.result;
                         } else {
                             if(DBG) logd("Cannot load ADN records");
                             if (records != null) {
diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
index 19900c8..1ac2da3 100644
--- a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
+++ b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
@@ -39,6 +39,11 @@
     }
 
     protected void finalize() {
+        try {
+            super.finalize();
+        } catch (Throwable throwable) {
+            Log.e(LOG_TAG, "Error while finalizing:", throwable);
+        }
         Log.d(LOG_TAG, "PhoneSubInfo finalized");
     }
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java
index 78e89d5..6e12f24a 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java
@@ -16,22 +16,10 @@
 
 package com.android.internal.telephony.cdma;
 
-import android.content.pm.PackageManager;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.Message;
-import android.os.ServiceManager;
-import android.telephony.PhoneNumberUtils;
 import android.util.Log;
 
-import com.android.internal.telephony.AdnRecord;
-import com.android.internal.telephony.AdnRecordCache;
 import com.android.internal.telephony.IccPhoneBookInterfaceManager;
-import com.android.internal.telephony.PhoneProxy;
-
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * RuimPhoneBookInterfaceManager to provide an inter-process communication to
@@ -42,20 +30,6 @@
 public class RuimPhoneBookInterfaceManager extends IccPhoneBookInterfaceManager {
     static final String LOG_TAG = "CDMA";
 
-
-    Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            AsyncResult ar;
-
-            switch(msg.what) {
-                default:
-                    mBaseHandler.handleMessage(msg);
-                    break;
-            }
-        }
-    };
-
     public RuimPhoneBookInterfaceManager(CDMAPhone phone) {
         super(phone);
         adnCache = phone.mRuimRecords.getAdnCache();
@@ -67,6 +41,11 @@
     }
 
     protected void finalize() {
+        try {
+            super.finalize();
+        } catch (Throwable throwable) {
+            Log.e(LOG_TAG, "Error while finalizing:", throwable);
+        }
         if(DBG) Log.d(LOG_TAG, "RuimPhoneBookInterfaceManager finalized");
     }
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
index 9439359..cfcfd98 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
@@ -30,6 +30,7 @@
 import com.android.internal.telephony.SmsRawData;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
@@ -89,6 +90,11 @@
     }
 
     protected void finalize() {
+        try {
+            super.finalize();
+        } catch (Throwable throwable) {
+            Log.e(LOG_TAG, "Error while finalizing:", throwable);
+        }
         if(DBG) Log.d(LOG_TAG, "RuimSmsInterfaceManager finalized");
     }
 
@@ -143,7 +149,7 @@
     public boolean copyMessageToIccEf(int status, byte[] pdu, byte[] smsc) {
         //NOTE smsc not used in RUIM
         if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " +
-                "pdu=("+ pdu + ")");
+                "pdu=("+ Arrays.toString(pdu) + ")");
         enforceReceiveAndSend("Copying message to RUIM");
         synchronized(mLock) {
             mSuccess = false;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java
index 076da6b..feb508a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java
@@ -16,22 +16,10 @@
 
 package com.android.internal.telephony.gsm;
 
-import android.content.pm.PackageManager;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.Message;
-import android.os.ServiceManager;
-import android.telephony.PhoneNumberUtils;
 import android.util.Log;
 
-import com.android.internal.telephony.AdnRecord;
-import com.android.internal.telephony.AdnRecordCache;
 import com.android.internal.telephony.IccPhoneBookInterfaceManager;
-import com.android.internal.telephony.PhoneProxy;
-
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * SimPhoneBookInterfaceManager to provide an inter-process communication to
@@ -42,20 +30,6 @@
 public class SimPhoneBookInterfaceManager extends IccPhoneBookInterfaceManager {
     static final String LOG_TAG = "GSM";
 
-
-    Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            AsyncResult ar;
-
-            switch(msg.what) {
-                default:
-                    mBaseHandler.handleMessage(msg);
-                    break;
-            }
-        }
-    };
-
     public SimPhoneBookInterfaceManager(GSMPhone phone) {
         super(phone);
         adnCache = phone.mSIMRecords.getAdnCache();
@@ -67,6 +41,11 @@
     }
 
     protected void finalize() {
+        try {
+            super.finalize();
+        } catch (Throwable throwable) {
+            Log.e(LOG_TAG, "Error while finalizing:", throwable);
+        }
         if(DBG) Log.d(LOG_TAG, "SimPhoneBookInterfaceManager finalized");
     }
 
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
index 875d8d0..2028ca4 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
@@ -25,10 +25,10 @@
 import com.android.internal.telephony.IccConstants;
 import com.android.internal.telephony.IccSmsInterfaceManager;
 import com.android.internal.telephony.IccUtils;
-import com.android.internal.telephony.PhoneProxy;
 import com.android.internal.telephony.SmsRawData;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
@@ -65,8 +65,7 @@
                     ar = (AsyncResult)msg.obj;
                     synchronized (mLock) {
                         if (ar.exception == null) {
-                            mSms  = (List<SmsRawData>)
-                                    buildValidRawData((ArrayList<byte[]>) ar.result);
+                            mSms  = buildValidRawData((ArrayList<byte[]>) ar.result);
                         } else {
                             if(DBG) log("Cannot load Sms records");
                             if (mSms != null)
@@ -88,6 +87,11 @@
     }
 
     protected void finalize() {
+        try {
+            super.finalize();
+        } catch (Throwable throwable) {
+            Log.e(LOG_TAG, "Error while finalizing:", throwable);
+        }
         if(DBG) Log.d(LOG_TAG, "SimSmsInterfaceManager finalized");
     }
 
@@ -106,7 +110,7 @@
     updateMessageOnIccEf(int index, int status, byte[] pdu) {
         if (DBG) log("updateMessageOnIccEf: index=" + index +
                 " status=" + status + " ==> " +
-                "("+ pdu + ")");
+                "("+ Arrays.toString(pdu) + ")");
         enforceReceiveAndSend("Updating message on SIM");
         synchronized(mLock) {
             mSuccess = false;
@@ -118,7 +122,7 @@
                 mPhone.mCM.deleteSmsOnSim(index, response);
             } else {
                 byte[] record = makeSmsRecordData(status, pdu);
-                ((SIMFileHandler)mPhone.getIccFileHandler()).updateEFLinearFixed(
+                mPhone.getIccFileHandler().updateEFLinearFixed(
                         IccConstants.EF_SMS,
                         index, record, null, response);
             }
@@ -142,7 +146,8 @@
      */
     public boolean copyMessageToIccEf(int status, byte[] pdu, byte[] smsc) {
         if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " +
-                "pdu=("+ pdu + "), smsm=(" + smsc +")");
+                "pdu=("+ Arrays.toString(pdu) +
+                "), smsm=(" + Arrays.toString(smsc) +")");
         enforceReceiveAndSend("Copying message to SIM");
         synchronized(mLock) {
             mSuccess = false;
@@ -175,8 +180,7 @@
                 "Reading messages from SIM");
         synchronized(mLock) {
             Message response = mHandler.obtainMessage(EVENT_LOAD_DONE);
-            ((SIMFileHandler)mPhone.getIccFileHandler()).loadEFLinearFixedAll(IccConstants.EF_SMS,
-                    response);
+            mPhone.getIccFileHandler().loadEFLinearFixedAll(IccConstants.EF_SMS, response);
 
             try {
                 mLock.wait();
diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java
index 4ae98e6..ee6b89c 100644
--- a/test-runner/src/android/test/InstrumentationTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationTestRunner.java
@@ -19,6 +19,7 @@
 import static android.test.suitebuilder.TestPredicates.REJECT_PERFORMANCE;
 
 import com.android.internal.util.Predicate;
+import com.android.internal.util.Predicates;
 
 import android.app.Activity;
 import android.app.Instrumentation;
@@ -31,11 +32,14 @@
 import android.test.suitebuilder.TestMethod;
 import android.test.suitebuilder.TestPredicates;
 import android.test.suitebuilder.TestSuiteBuilder;
+import android.test.suitebuilder.annotation.HasAnnotation;
+import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.PrintStream;
+import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -93,6 +97,18 @@
  * -e size large
  * com.android.foo/android.test.InstrumentationTestRunner
  * <p/>
+ * <b>Filter test run to tests with given annotation:</b> adb shell am instrument -w
+ * -e annotation com.android.foo.MyAnnotation
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
+ * If used with other options, the resulting test run will contain the union of the two options.
+ * e.g. "-e size large -e annotation com.android.foo.MyAnnotation" will run only tests with both
+ * the {@link LargeTest} and "com.android.foo.MyAnnotation" annotations.
+ * <p/>
+ * <b>Filter test run to tests <i>without</i> given annotation:</b> adb shell am instrument -w
+ * -e notAnnotation com.android.foo.MyAnnotation
+ * com.android.foo/android.test.InstrumentationTestRunner
+ * <p/>
  * <b>Running a single testcase:</b> adb shell am instrument -w
  * -e class com.android.foo.FooTest
  * com.android.foo/android.test.InstrumentationTestRunner
@@ -161,6 +177,10 @@
     private static final String LARGE_SUITE = "large";
 
     private static final String ARGUMENT_LOG_ONLY = "log";
+    /** @hide */
+    static final String ARGUMENT_ANNOTATION = "annotation";
+    /** @hide */
+    static final String ARGUMENT_NOT_ANNOTATION = "notAnnotation";
 
     /**
      * This constant defines the maximum allowed runtime (in ms) for a test included in the "small"
@@ -274,6 +294,8 @@
         ClassPathPackageInfoSource.setApkPaths(apkPaths);
 
         Predicate<TestMethod> testSizePredicate = null;
+        Predicate<TestMethod> testAnnotationPredicate = null;
+        Predicate<TestMethod> testNotAnnotationPredicate = null;
         boolean includePerformance = false;
         String testClassesArg = null;
         boolean logOnly = false;
@@ -287,6 +309,11 @@
             mPackageOfTests = arguments.getString(ARGUMENT_TEST_PACKAGE);
             testSizePredicate = getSizePredicateFromArg(
                     arguments.getString(ARGUMENT_TEST_SIZE_PREDICATE));
+            testAnnotationPredicate = getAnnotationPredicate(
+                    arguments.getString(ARGUMENT_ANNOTATION));
+            testNotAnnotationPredicate = getNotAnnotationPredicate(
+                    arguments.getString(ARGUMENT_NOT_ANNOTATION));
+
             includePerformance = getBooleanArgument(arguments, ARGUMENT_INCLUDE_PERF);
             logOnly = getBooleanArgument(arguments, ARGUMENT_LOG_ONLY);
             mCoverage = getBooleanArgument(arguments, "coverage");
@@ -306,6 +333,12 @@
         if (testSizePredicate != null) {
             testSuiteBuilder.addRequirements(testSizePredicate);
         }
+        if (testAnnotationPredicate != null) {
+            testSuiteBuilder.addRequirements(testAnnotationPredicate);
+        }
+        if (testNotAnnotationPredicate != null) {
+            testSuiteBuilder.addRequirements(testNotAnnotationPredicate);
+        }
         if (!includePerformance) {
             testSuiteBuilder.addRequirements(REJECT_PERFORMANCE);
         }
@@ -406,6 +439,59 @@
         }
     }
 
+   /**
+    * Returns the test predicate object, corresponding to the annotation class value provided via
+    * the {@link ARGUMENT_ANNOTATION} argument.
+    *
+    * @return the predicate or <code>null</code>
+    */
+    private Predicate<TestMethod> getAnnotationPredicate(String annotationClassName) {
+        Class<? extends Annotation> annotationClass = getAnnotationClass(annotationClassName);
+        if (annotationClass != null) {
+            return new HasAnnotation(annotationClass);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the negative test predicate object, corresponding to the annotation class value
+     * provided via the {@link ARGUMENT_NOT_ANNOTATION} argument.
+     *
+     * @return the predicate or <code>null</code>
+     */
+     private Predicate<TestMethod> getNotAnnotationPredicate(String annotationClassName) {
+         Class<? extends Annotation> annotationClass = getAnnotationClass(annotationClassName);
+         if (annotationClass != null) {
+             return Predicates.not(new HasAnnotation(annotationClass));
+         }
+         return null;
+     }
+
+    /**
+     * Helper method to return the annotation class with specified name
+     *
+     * @param annotationClassName the fully qualified name of the class
+     * @return the annotation class or <code>null</code>
+     */
+    private Class<? extends Annotation> getAnnotationClass(String annotationClassName) {
+        if (annotationClassName == null) {
+            return null;
+        }
+        try {
+           Class<?> annotationClass = Class.forName(annotationClassName);
+           if (annotationClass.isAnnotation()) {
+               return (Class<? extends Annotation>)annotationClass;
+           } else {
+               Log.e(LOG_TAG, String.format("Provided annotation value %s is not an Annotation",
+                       annotationClassName));
+           }
+        } catch (ClassNotFoundException e) {
+            Log.e(LOG_TAG, String.format("Could not find class for specified annotation %s",
+                    annotationClassName));
+        }
+        return null;
+    }
+
     @Override
     public void onStart() {
         Looper.prepare();
@@ -471,7 +557,7 @@
         String coverageFilePath = getCoverageFilePath();
         java.io.File coverageFile = new java.io.File(coverageFilePath);
         try {
-            Class emmaRTClass = Class.forName("com.vladium.emma.rt.RT");
+            Class<?> emmaRTClass = Class.forName("com.vladium.emma.rt.RT");
             Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData",
                     coverageFile.getClass(), boolean.class, boolean.class);
 
diff --git a/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java b/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java
index d9afd54..6db72ad 100644
--- a/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java
+++ b/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java
@@ -109,6 +109,33 @@
         assertTrue(mStubAndroidTestRunner.isRun());
     }
 
+    /**
+     * Test that the -e {@link InstrumentationTestRunner.ARGUMENT_ANNOTATION} parameter properly
+     * selects tests.
+     */
+    public void testAnnotationParameter() throws Exception {
+        String expectedTestClassName = AnnotationTest.class.getName();
+        Bundle args = new Bundle();
+        args.putString(InstrumentationTestRunner.ARGUMENT_TEST_CLASS, expectedTestClassName);
+        args.putString(InstrumentationTestRunner.ARGUMENT_ANNOTATION, FlakyTest.class.getName());
+        mInstrumentationTestRunner.onCreate(args);
+        assertTestRunnerCalledWithExpectedParameters(expectedTestClassName, "testAnnotated");
+    }
+    
+    /**
+     * Test that the -e {@link InstrumentationTestRunner.ARGUMENT_NOT_ANNOTATION} parameter
+     * properly excludes tests.
+     */
+    public void testNotAnnotationParameter() throws Exception {
+        String expectedTestClassName = AnnotationTest.class.getName();
+        Bundle args = new Bundle();
+        args.putString(InstrumentationTestRunner.ARGUMENT_TEST_CLASS, expectedTestClassName);
+        args.putString(InstrumentationTestRunner.ARGUMENT_NOT_ANNOTATION,
+                FlakyTest.class.getName());
+        mInstrumentationTestRunner.onCreate(args);
+        assertTestRunnerCalledWithExpectedParameters(expectedTestClassName, "testNotAnnotated");
+    }
+
     private void assertContentsInOrder(List<TestDescriptor> actual, TestDescriptor... source) {
         TestDescriptor[] clonedSource = source.clone();
         assertEquals("Unexpected number of items.", clonedSource.length, actual.size());
@@ -269,4 +296,17 @@
 
         }
     }
+
+    /**
+     * Annotated test used for validation.
+     */
+    public static class AnnotationTest extends TestCase {
+
+        public void testNotAnnotated() throws Exception {
+        }
+
+        @FlakyTest
+        public void testAnnotated() throws Exception {
+        }
+    }
 }
diff --git a/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java
index 3bbb447..e85254d 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java
@@ -132,6 +132,14 @@
         return ms.destroySecureContainer(fullId, force);
     }
 
+    private boolean isContainerMounted(String localId) throws RemoteException {
+        Assert.assertTrue(isMediaMounted());
+        String fullId = "com.android.unittests.AsecTests." + localId;
+
+        IMountService ms = getMs();
+        return ms.isSecureContainerMounted(fullId);
+    }
+
     private IMountService getMs() {
         IBinder service = ServiceManager.getService("mount");
         if (service != null) {
@@ -300,7 +308,7 @@
         }
     }
 
-    public void testRenameMountedContainer() {
+    public void testRenameSrcMountedContainer() {
         try {
             Assert.assertEquals(StorageResultCode.OperationSucceeded,
                     createContainer("testRenameContainer.1", 4, "none"));
@@ -312,12 +320,15 @@
         }
     }
 
-    public void testRenameToExistingContainer() {
+    public void testRenameDstMountedContainer() {
         try {
             Assert.assertEquals(StorageResultCode.OperationSucceeded,
                     createContainer("testRenameContainer.1", 4, "none"));
 
             Assert.assertEquals(StorageResultCode.OperationSucceeded,
+                    unmountContainer("testRenameContainer.1", false));
+
+            Assert.assertEquals(StorageResultCode.OperationSucceeded,
                     createContainer("testRenameContainer.2", 4, "none"));
 
             Assert.assertEquals(StorageResultCode.OperationFailedStorageMounted,
@@ -326,4 +337,25 @@
             failStr(e);
         }
     }
+
+    public void testIsContainerMountedAfterRename() {
+        try {
+            Assert.assertEquals(StorageResultCode.OperationSucceeded,
+                    createContainer("testRenameContainer.1", 4, "none"));
+
+            Assert.assertEquals(StorageResultCode.OperationSucceeded,
+                    unmountContainer("testRenameContainer.1", false));
+
+            Assert.assertEquals(StorageResultCode.OperationSucceeded,
+                    renameContainer("testRenameContainer.1", "testRenameContainer.2"));
+
+            Assert.assertEquals(false, containerExists("testRenameContainer.1"));
+            Assert.assertEquals(true, containerExists("testRenameContainer.2"));
+            // Check if isContainerMounted returns valid value
+            Assert.assertEquals(true, isContainerMounted("testRenameContainer.2"));
+        } catch (Exception e) {
+            failStr(e);
+        }
+    }
+
 }
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index 9c5c44d..d161a88 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -57,17 +57,25 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.storage.IMountService;
+import android.os.storage.IMountServiceListener;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
 import android.os.storage.StorageResultCode;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StatFs;
 import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
 
 public class PackageManagerTests extends AndroidTestCase {
     private static final boolean localLOGV = true;
     public static final String TAG="PackageManagerTests";
     public final long MAX_WAIT_TIME=120*1000;
     public final long WAIT_TIME_INCR=20*1000;
+    private static final String SECURE_CONTAINERS_PREFIX = "/mnt/asec";
+    private static final int APP_INSTALL_AUTO = 0;
+    private static final int APP_INSTALL_DEVICE = 1;
+    private static final int APP_INSTALL_SDCARD = 2;
 
     void failStr(String errMsg) {
         Log.w(TAG, "errMsg="+errMsg);
@@ -244,9 +252,46 @@
         packageParser = null;
         return pkg;
     }
-
-    private void assertInstall(String pkgName, int flags) {
+    private boolean getInstallLoc(int flags, int expInstallLocation) {
+        // Flags explicitly over ride everything else.
+        if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0 ) {
+            return false;
+        } else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0 ) {
+            return true;
+        }
+        // Manifest option takes precedence next
+        if (expInstallLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
+            return true;
+        }
+        // TODO Out of memory checks here.
+        boolean checkSd = false;
+        int setLoc = 0;
         try {
+            setLoc = Settings.System.getInt(mContext.getContentResolver(), Settings.System.SET_INSTALL_LOCATION);
+        } catch (SettingNotFoundException e) {
+            failStr(e);
+        }
+        if (setLoc == 1) {
+            int userPref = APP_INSTALL_AUTO;
+            try {
+                userPref = Settings.System.getInt(mContext.getContentResolver(), Settings.System.DEFAULT_INSTALL_LOCATION);
+            } catch (SettingNotFoundException e) {
+                failStr(e);
+            }
+            if (userPref == APP_INSTALL_DEVICE) {
+                checkSd = false;
+            } else if (userPref == APP_INSTALL_SDCARD) {
+                checkSd = true;
+            } else if (userPref == APP_INSTALL_AUTO) {
+                // Might be determined dynamically. TODO fix this
+                checkSd = false;
+            }
+        }
+        return checkSd;
+    }
+    private void assertInstall(PackageParser.Package pkg, int flags, int expInstallLocation) {
+        try {
+            String pkgName = pkg.packageName;
         ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
         assertNotNull(info);
         assertEquals(pkgName, info.packageName);
@@ -264,15 +309,14 @@
             assertEquals(publicSrcPath, appInstallPath);
         } else {
             assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
-            if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
-                assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0);
-                // Hardcoded for now
-                assertTrue(srcPath.startsWith("/asec"));
-                assertTrue(publicSrcPath.startsWith("/asec"));
-            } else {
+            if (!getInstallLoc(flags, expInstallLocation)) {
                 assertEquals(srcPath, appInstallPath);
                 assertEquals(publicSrcPath, appInstallPath);
                 assertFalse((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0);
+            } else {
+                assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0);
+                assertTrue(srcPath.startsWith(SECURE_CONTAINERS_PREFIX));
+                assertTrue(publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX));
             }
         }
         } catch (NameNotFoundException e) {
@@ -300,7 +344,7 @@
 
     private InstallParams sampleInstallFromRawResource(int flags, boolean cleanUp) {
         return installFromRawResource("install.apk", R.raw.install, flags, cleanUp,
-                false, -1);
+                false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
     }
 
     public void clearSecureContainersForPkg(String pkgName) {
@@ -327,7 +371,8 @@
      * PackageManager api to install it.
      */
     private InstallParams installFromRawResource(String outFileName,
-            int rawResId, int flags, boolean cleanUp, boolean fail, int result) {
+            int rawResId, int flags, boolean cleanUp, boolean fail, int result,
+            int expInstallLocation) {
         File filesDir = mContext.getFilesDir();
         File outFile = new File(filesDir, outFileName);
         Uri packageURI = getInstallablePackage(rawResId, outFile);
@@ -337,9 +382,7 @@
         // Make sure the package doesn't exist
         getPm().deletePackage(pkg.packageName, null, 0);
         // Clean up the containers as well
-        if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
-            clearSecureContainersForPkg(pkg.packageName);
-        }
+        clearSecureContainersForPkg(pkg.packageName);
         try {
             try {
                 if (fail) {
@@ -351,7 +394,7 @@
                     assertTrue(invokeInstallPackage(packageURI, flags,
                             pkg.packageName, receiver));
                     // Verify installed information
-                    assertInstall(pkg.packageName, flags);
+                    assertInstall(pkg, flags, expInstallLocation);
                     ip = new InstallParams(pkg, outFileName, packageURI);
                 }
             } catch (Exception e) {
@@ -443,6 +486,7 @@
     public void replaceFromRawResource(int flags) {
         InstallParams ip = sampleInstallFromRawResource(flags, false);
         boolean replace = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0);
+        Log.i(TAG, "replace=" + replace);
         GenericReceiver receiver;
         if (replace) {
             receiver = new ReplaceReceiver(ip.pkg.packageName);
@@ -455,7 +499,7 @@
                 assertEquals(invokeInstallPackage(ip.packageURI, flags,
                         ip.pkg.packageName, receiver), replace);
                 if (replace) {
-                    assertInstall(ip.pkg.packageName, flags);
+                    assertInstall(ip.pkg, flags, ip.pkg.installLocation);
                 }
             } catch (Exception e) {
                 failStr("Failed with exception : " + e);
@@ -738,51 +782,73 @@
         }
     }
 
+    class StorageListener extends StorageEventListener {
+        String oldState;
+        String newState;
+        String path;
+        private boolean doneFlag = false;
+        @Override
+        public void onStorageStateChanged(String path, String oldState, String newState) {
+            if (localLOGV) Log.i(TAG, "Storage state changed from " + oldState + " to " + newState);
+            synchronized (this) {
+                this.oldState = oldState;
+                this.newState = newState;
+                this.path = path;
+                doneFlag = true;
+                notifyAll();
+            }
+        }
+
+        public boolean isDone() {
+            return doneFlag;
+        }
+    }
+
     private boolean unmountMedia() {
         if (!getMediaState()) {
             return true;
         }
+        String path = Environment.getExternalStorageDirectory().toString();
+        StorageListener observer = new StorageListener();
+        StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
+        sm.registerListener(observer);
         try {
-        String mPath = Environment.getExternalStorageDirectory().toString();
-        int ret = getMs().unmountVolume(mPath, false);
-        return ret == StorageResultCode.OperationSucceeded;
-        } catch (RemoteException e) {
-            return true;
+            // Wait on observer
+            synchronized(observer) {
+                getMs().unmountVolume(path, false);
+                long waitTime = 0;
+                while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+                    observer.wait(WAIT_TIME_INCR);
+                    waitTime += WAIT_TIME_INCR;
+                }
+                if(!observer.isDone()) {
+                    throw new Exception("Timed out waiting for packageInstalled callback");
+                }
+                return true;
+            }
+        } catch (Exception e) {
+            return false;
+        } finally {
+            sm.unregisterListener(observer);
         }
     }
 
-    /*
-     * Install package on sdcard. Unmount and then mount the media.
-     * (Use PackageManagerService private api for now)
-     * Make sure the installed package is available.
-     * STOPSHIP will uncomment when MountService api's to mount/unmount
-     * are made asynchronous.
-     */
-    public void xxxtestMountSdNormalInternal() {
-        assertTrue(mountFromRawResource());
-    }
-
     private boolean mountFromRawResource() {
         // Install pkg on sdcard
-        InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL |
-                PackageManager.INSTALL_REPLACE_EXISTING, false);
+        InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, false);
         if (localLOGV) Log.i(TAG, "Installed pkg on sdcard");
         boolean origState = getMediaState();
+        boolean registeredReceiver = false;
         SdMountReceiver receiver = new SdMountReceiver(new String[]{ip.pkg.packageName});
         try {
             if (localLOGV) Log.i(TAG, "Unmounting media");
             // Unmount media
             assertTrue(unmountMedia());
             if (localLOGV) Log.i(TAG, "Unmounted media");
-            try {
-                if (localLOGV) Log.i(TAG, "Sleeping for 10 second");
-                Thread.sleep(10*1000);
-            } catch (InterruptedException e) {
-                failStr(e);
-            }
             // Register receiver here
             PackageManager pm = getPm();
             mContext.registerReceiver(receiver, receiver.filter);
+            registeredReceiver = true;
 
             // Wait on receiver
             synchronized (receiver) {
@@ -807,7 +873,7 @@
             failStr(e);
             return false;
         } finally {
-            mContext.unregisterReceiver(receiver);
+            if (registeredReceiver) mContext.unregisterReceiver(receiver);
             // Restore original media state
             if (origState) {
                 mountMedia();
@@ -819,6 +885,17 @@
         }
     }
 
+    /*
+     * Install package on sdcard. Unmount and then mount the media.
+     * (Use PackageManagerService private api for now)
+     * Make sure the installed package is available.
+     * STOPSHIP will uncomment when MountService api's to mount/unmount
+     * are made asynchronous.
+     */
+    public void xxxtestMountSdNormalInternal() {
+        assertTrue(mountFromRawResource());
+    }
+
     void cleanUpInstall(InstallParams ip) {
         if (ip == null) {
             return;
@@ -834,28 +911,29 @@
 
     public void testManifestInstallLocationInternal() {
         installFromRawResource("install.apk", R.raw.install_loc_internal,
-                0, true, false, -1);
+                0, true, false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
     }
 
     public void testManifestInstallLocationSdcard() {
         installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                0, true, false, -1);
+                0, true, false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
     }
 
     public void testManifestInstallLocationAuto() {
         installFromRawResource("install.apk", R.raw.install_loc_auto,
-                0, true, false, -1);
+                0, true, false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
     }
 
     public void testManifestInstallLocationUnspecified() {
         installFromRawResource("install.apk", R.raw.install_loc_unspecified,
-                0, true, false, -1);
+                0, true, false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
     }
 
     public void testManifestInstallLocationFwdLockedSdcard() {
         installFromRawResource("install.apk", R.raw.install_loc_sdcard,
                 PackageManager.INSTALL_FORWARD_LOCK, true, true,
-                PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION);
+                PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+                PackageInfo.INSTALL_LOCATION_AUTO);
     }
 
     public void xxxtestClearAllSecureContainers() {
diff --git a/tests/CoreTests/android/core/MiscRegressionTest.java b/tests/CoreTests/android/core/MiscRegressionTest.java
index 8fe064c..8281db0 100644
--- a/tests/CoreTests/android/core/MiscRegressionTest.java
+++ b/tests/CoreTests/android/core/MiscRegressionTest.java
@@ -110,6 +110,16 @@
         Logger.global.finest("This has logging Level.FINEST, should become VERBOSE");
     }
 
+    // Regression test for Issue 5697:
+    // getContextClassLoader returns a non-application classloader
+    // http://code.google.com/p/android/issues/detail?id=5697
+    //
+    @MediumTest
+    public void testJavaContextClassLoader() throws Exception {
+        Assert.assertNotNull("Must hava a Java context ClassLoader",
+                             Thread.currentThread().getContextClassLoader());
+    }
+
     // Regression test for #1045939: Different output for Method.toString()
     @SmallTest
     public void testMethodToString() {
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index 8983612..634d683 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -339,14 +339,7 @@
         this.mTestList = new Vector<String>();
 
         // Read settings
-        try {
-            this.mTestPathPrefix =
-                (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getCanonicalPath();
-        } catch (IOException e) {
-            Log.e(LOGTAG, "Cannot find test path prefix: " + e.getMessage());
-            return;
-        }
-
+        this.mTestPathPrefix = (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getAbsolutePath();
         this.mRebaselineResults = runner.mRebaseline;
 
         int timeout = runner.mTimeoutInMillis;
@@ -395,11 +388,7 @@
         if (runner.mTestPath != null) {
             test_path += runner.mTestPath;
         }
-        try {
-            test_path = new File(test_path).getCanonicalPath();
-        } catch (IOException e) {
-            Log.e("LayoutTestsAutoTest", "Cannot get cannonical path " + e.getMessage());
-        }
+        test_path = new File(test_path).getAbsolutePath();
         Log.v("LayoutTestsAutoTest", " Test path : " + test_path);
 
         return test_path;
diff --git a/tests/LocationTracker/Android.mk b/tests/LocationTracker/Android.mk
new file mode 100644
index 0000000..b142d22
--- /dev/null
+++ b/tests/LocationTracker/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := LocationTracker
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/LocationTracker/AndroidManifest.xml b/tests/LocationTracker/AndroidManifest.xml
new file mode 100644
index 0000000..dc7ea99
--- /dev/null
+++ b/tests/LocationTracker/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.locationtracker">
+
+    <!-- Permissions for the Location Service -->
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+    <!--  Permission for wifi -->
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+
+    <!-- give the location tracker ability to induce device insomnia -->
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+    <!--  Permission for SD card -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application android:label="@string/app_label">
+        <activity android:name="TrackerActivity" android:label="Location Tracker">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <service android:name=".TrackerService" />
+        <activity android:label="@string/settings_menu" android:name="SettingsActivity" />
+        <provider android:name=".data.TrackerProvider"
+            android:authorities="com.android.locationtracker" />
+    </application>
+
+</manifest>
diff --git a/tests/LocationTracker/res/layout/entrylist_item.xml b/tests/LocationTracker/res/layout/entrylist_item.xml
new file mode 100644
index 0000000..d2a8033
--- /dev/null
+++ b/tests/LocationTracker/res/layout/entrylist_item.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2006-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/entrylist_item"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:textAppearance="?android:attr/textAppearanceSmall"
+/>
diff --git a/tests/LocationTracker/res/menu/menu.xml b/tests/LocationTracker/res/menu/menu.xml
new file mode 100644
index 0000000..05d13fd
--- /dev/null
+++ b/tests/LocationTracker/res/menu/menu.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+* Copyright (c) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/start_service_menu"
+          android:title="@string/start_service_menu" />
+    <item android:id="@+id/stop_service_menu"
+          android:title="@string/stop_service_menu" />
+    <item android:id="@+id/settings_menu"
+          android:title="@string/settings_menu" />
+    <item android:id="@+id/export_sub_menu"
+          android:title="@string/export_sub_menu">
+        <menu>
+            <item android:id="@+id/export_kml"
+                  android:title="@string/export_kml" />
+            <item android:id="@+id/export_csv"
+                  android:title="@string/export_csv" />
+        </menu>
+    </item>
+    <item android:title="@string/clear_data"
+          android:id="@+id/clear_data_menu"/>
+</menu>
diff --git a/tests/LocationTracker/res/values/strings.xml b/tests/LocationTracker/res/values/strings.xml
new file mode 100644
index 0000000..ea6bf2d
--- /dev/null
+++ b/tests/LocationTracker/res/values/strings.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<resources>
+    <string name="start_service_menu">Start Service</string>
+    <string name="stop_service_menu">Stop Service</string>
+    <string name="settings_menu">Settings</string>
+    <string name="update_preference">Update frequency</string>
+    <string name="title_mintime_preference">Minimum update time</string>
+    <string name="summary_mintime_preference">The suggested minimum time interval for location updates, in seconds</string>
+    <string name="dialog_title_mintime_preference">Minimum update time</string>
+    <string name="title_mindistance_preference">Minimum distance</string>
+    <string name="summary_mindistance_preference">Minimum distance interval for location updates, in meters</string>
+    <string name="dialog_title_mindistance_preference">Minimum distance</string>
+    <string name="provider_preferences">Location providers</string>
+    <string name="title_network_preference">Network location</string>
+    <string name="summary_network_preference">Listen for updates to network location (Wi-Fi/cellid)</string>
+    <string name="title_gps_preference">GPS location</string>
+    <string name="summary_gps_preference">Listen for updates to GPS location</string>
+    <string name="title_signal_preference">Signal strength</string>
+    <string name="summary_signal_preference">Listen for updates to signal strength</string>
+    <string name="advanced_preferences">Advanced</string>
+    <string name="title_advanced_log_preference">Location debug logging</string>
+    <string name="summary_advanced_preference">Logs detailed location data, only relevant for location/test engineers</string>
+    <string name="app_label">Location Tracker</string>
+    <string name="export_sub_menu">Export\u2026</string>
+    <string name="export_kml">Export As KML</string>
+    <string name="export_csv">Export As CSV</string>
+    <string name="clear_data">Clear data</string>
+    <string name="delete_confirm">All current tracking data will be deleted.</string>
+    <string name="confirm_title">Clear data</string>
+</resources>
diff --git a/tests/LocationTracker/res/xml/preferences.xml b/tests/LocationTracker/res/xml/preferences.xml
new file mode 100755
index 0000000..61d4817
--- /dev/null
+++ b/tests/LocationTracker/res/xml/preferences.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- The Location preferences UI -->
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <PreferenceCategory android:title="@string/update_preference">
+        <EditTextPreference android:key="mintime_preference"
+            android:defaultValue="0"
+            android:title="@string/title_mintime_preference"
+            android:summary="@string/summary_mintime_preference"
+            android:dialogTitle="@string/dialog_title_mintime_preference" />
+
+        <EditTextPreference android:key="mindistance_preference"
+            android:defaultValue="0"
+            android:title="@string/title_mindistance_preference"
+            android:summary="@string/summary_mindistance_preference"
+            android:dialogTitle="@string/dialog_title_mindistance_preference" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory android:title="@string/provider_preferences">
+
+        <CheckBoxPreference android:key="network_preference"
+            android:defaultValue="true"
+            android:title="@string/title_network_preference"
+            android:summary="@string/summary_network_preference" />
+
+        <CheckBoxPreference android:key="gps_preference"
+            android:defaultValue="true"
+            android:title="@string/title_gps_preference"
+            android:summary="@string/summary_gps_preference" />
+
+        <CheckBoxPreference android:key="signal_preference"
+            android:defaultValue="false"
+            android:title="@string/title_signal_preference"
+            android:summary="@string/summary_signal_preference" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory android:title="@string/advanced_preferences">
+
+        <CheckBoxPreference android:key="advanced_log_preference"
+            android:defaultValue="false"
+            android:title="@string/title_advanced_log_preference"
+            android:summary="@string/summary_advanced_preference" />
+    </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/tests/LocationTracker/src/com/android/locationtracker/SettingsActivity.java b/tests/LocationTracker/src/com/android/locationtracker/SettingsActivity.java
new file mode 100755
index 0000000..cb77118
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/SettingsActivity.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright (C) 2008 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.android.locationtracker;

+

+import android.os.Bundle;

+import android.preference.PreferenceActivity;

+

+/**

+ * Settings preference screen for location tracker

+ */

+public class SettingsActivity extends PreferenceActivity {

+

+    @Override

+    protected void onCreate(Bundle savedInstanceState) {

+        super.onCreate(savedInstanceState);

+

+        // Load the preferences from an XML resource

+        addPreferencesFromResource(R.xml.preferences);

+    }

+

+}

diff --git a/tests/LocationTracker/src/com/android/locationtracker/TrackerActivity.java b/tests/LocationTracker/src/com/android/locationtracker/TrackerActivity.java
new file mode 100644
index 0000000..98d0a50
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/TrackerActivity.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.locationtracker;
+
+import com.android.locationtracker.data.DateUtils;
+import com.android.locationtracker.data.TrackerDataHelper;
+import com.android.locationtracker.data.TrackerListHelper;
+
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.database.Cursor;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Activity for location tracker service
+ *
+ * Contains facilities for starting and
+ * stopping location tracker service, as well as displaying the current location
+ * data
+ *
+ * Future enhancements:
+ *   - export data as dB
+ *   - enable/disable "start service" and "stop service" menu items as
+ *     appropriate
+ */
+public class TrackerActivity extends ListActivity {
+
+    static final String LOG_TAG = "LocationTracker";
+
+    private TrackerListHelper mDataHelper;
+
+    /**
+     * Retrieves and displays the currently logged location data from file
+     *
+     * @param icicle
+     */
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mDataHelper = new TrackerListHelper(this);
+        mDataHelper.bindListUI(R.layout.entrylist_item);
+    }
+
+    /**
+     * Builds the menu
+     *
+     * @param menu - menu to add items to
+     */
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater menuInflater = getMenuInflater();
+        menuInflater.inflate(R.menu.menu, menu);
+        return true;
+    }
+
+    /**
+     * Handles menu item selection
+     *
+     * @param item - the selected menu item
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.start_service_menu: {
+                startService(new Intent(TrackerActivity.this,
+                        TrackerService.class));
+                break;
+            }
+            case R.id.stop_service_menu: {
+                stopService(new Intent(TrackerActivity.this,
+                        TrackerService.class));
+                break;
+            }
+            case R.id.settings_menu: {
+                launchSettings();
+                break;
+            }
+            case R.id.export_kml: {
+                exportKML();
+                break;
+            }
+            case R.id.export_csv: {
+                exportCSV();
+                break;
+            }
+            case R.id.clear_data_menu: {
+                clearData();
+                break;
+            }
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void clearData() {
+        Runnable clearAction = new Runnable() {
+            public void run() {
+                TrackerDataHelper helper =
+                    new TrackerDataHelper(TrackerActivity.this);
+                helper.deleteAll();
+            }
+        };
+        showConfirm(R.string.delete_confirm, clearAction);
+    }
+
+    private void showConfirm(int textId, final Runnable onConfirmAction) {
+        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
+        dialogBuilder.setTitle(R.string.confirm_title);
+        dialogBuilder.setMessage(textId);
+        dialogBuilder.setPositiveButton(android.R.string.ok,
+                new OnClickListener() {
+            public void onClick(DialogInterface dialog, int which) {
+                onConfirmAction.run();
+            }
+        });
+        dialogBuilder.setNegativeButton(android.R.string.cancel,
+                new OnClickListener() {
+            public void onClick(DialogInterface dialog, int which) {
+                // do nothing
+            }
+        });
+        dialogBuilder.show();
+    }
+
+    private void exportCSV() {
+        String exportFileName = getUniqueFileName("csv");
+        exportFile(null, exportFileName, new TrackerDataHelper(this,
+                TrackerDataHelper.CSV_FORMATTER));
+    }
+
+    private void exportKML() {
+        String exportFileName = getUniqueFileName(
+                LocationManager.NETWORK_PROVIDER + ".kml");
+        exportFile(LocationManager.NETWORK_PROVIDER, exportFileName,
+                new TrackerDataHelper(this, TrackerDataHelper.KML_FORMATTER));
+        exportFileName = getUniqueFileName(
+                LocationManager.GPS_PROVIDER + ".kml");
+        exportFile(LocationManager.GPS_PROVIDER, exportFileName,
+                new TrackerDataHelper(this, TrackerDataHelper.KML_FORMATTER));
+    }
+
+    private void exportFile(String tagFilter,
+                            String exportFileName,
+                            TrackerDataHelper trackerData) {
+        BufferedWriter exportWriter = null;
+        Cursor cursor = trackerData.query(tagFilter);
+        try {
+            exportWriter = new BufferedWriter(new FileWriter(exportFileName));
+            exportWriter.write(trackerData.getOutputHeader());
+
+            String line = null;
+
+            while ((line = trackerData.getNextOutput(cursor)) != null) {
+                exportWriter.write(line);
+            }
+            exportWriter.write(trackerData.getOutputFooter());
+            Toast.makeText(this, "Successfully exported data to " +
+                    exportFileName, Toast.LENGTH_SHORT).show();
+
+        } catch (IOException e) {
+            Toast.makeText(this, "Error exporting file: " +
+                    e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
+
+            Log.e(LOG_TAG, "Error exporting file", e);
+        } finally {
+            closeWriter(exportWriter);
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private void closeWriter(Writer exportWriter) {
+        if (exportWriter != null) {
+            try {
+                exportWriter.close();
+            } catch (IOException e) {
+                Log.e(LOG_TAG, "error closing file", e);
+            }
+        }
+    }
+
+    private String getUniqueFileName(String ext) {
+        File dir = new File("/sdcard/locationtracker");
+        if (!dir.exists()) {
+            dir.mkdir();
+        }
+        return "/sdcard/locationtracker/tracking-" +
+            DateUtils.getCurrentTimestamp() + "." + ext;
+    }
+
+    private void launchSettings() {
+        Intent settingsIntent = new Intent();
+        settingsIntent.setClass(this, SettingsActivity.class);
+        startActivity(settingsIntent);
+    }
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/TrackerService.java b/tests/LocationTracker/src/com/android/locationtracker/TrackerService.java
new file mode 100644
index 0000000..5b75653
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/TrackerService.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.locationtracker;
+
+import com.android.locationtracker.data.TrackerDataHelper;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.net.ConnectivityManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.preference.PreferenceManager;
+import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.gsm.GsmCellLocation;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Location Tracking service
+ *
+ * Records location updates for all registered location providers, and cell
+ * location updates
+ */
+public class TrackerService extends Service {
+
+    private List<LocationTrackingListener> mListeners;
+
+    private static final String LOG_TAG = TrackerActivity.LOG_TAG;
+
+    // controls which location providers to track
+    private Set<String> mTrackedProviders;
+
+    private TrackerDataHelper mTrackerData;
+
+    private TelephonyManager mTelephonyManager;
+    private Location mNetworkLocation;
+
+    // Handlers and Receivers for phone and network state
+    private NetworkStateBroadcastReceiver mNetwork;
+    private static final String CELL_PROVIDER_TAG = "cell";
+    // signal strength updates
+    private static final String SIGNAL_PROVIDER_TAG = "signal";
+    private static final String WIFI_PROVIDER_TAG = "wifi";
+    // tracking tag for data connectivity issues
+    private static final String DATA_CONN_PROVIDER_TAG = "data";
+
+    // preference constants
+    private static final String MIN_TIME_PREF = "mintime_preference";
+    private static final String MIN_DIS_PREF = "mindistance_preference";
+    private static final String GPS_PREF = "gps_preference";
+    private static final String NETWORK_PREF = "network_preference";
+    private static final String SIGNAL_PREF = "signal_preference";
+    private static final String DEBUG_PREF = "advanced_log_preference";
+
+    private PreferenceListener mPrefListener;
+
+    public TrackerService() {
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        // ignore - nothing to do
+        return null;
+    }
+
+    /**
+     * registers location listeners
+     *
+     * @param intent
+     * @param startId
+     */
+    @Override
+    public void onStart(Intent intent, int startId) {
+        super.onStart(intent, startId);
+        mNetworkLocation = null;
+
+        initLocationListeners();
+        Toast.makeText(this, "Tracking service started", Toast.LENGTH_SHORT);
+    }
+
+    private synchronized void initLocationListeners() {
+        mTrackerData = new TrackerDataHelper(this);
+        LocationManager lm = getLocationManager();
+
+        mTrackedProviders = getTrackedProviders();
+
+        List<String> locationProviders = lm.getAllProviders();
+        mListeners = new ArrayList<LocationTrackingListener>(
+                locationProviders.size());
+
+        long minUpdateTime = getLocationUpdateTime();
+        float minDistance = getLocationMinDistance();
+
+        for (String providerName : locationProviders) {
+            if (mTrackedProviders.contains(providerName)) {
+                Log.d(LOG_TAG, "Adding location listener for provider " +
+                        providerName);
+                if (doDebugLogging()) {
+                    mTrackerData.writeEntry("init", String.format(
+                            "start listening to %s : %d ms; %f meters",
+                            providerName, minUpdateTime, minDistance));
+                }    
+                LocationTrackingListener listener =
+                    new LocationTrackingListener();
+                lm.requestLocationUpdates(providerName, minUpdateTime,
+                        minDistance, listener);
+                mListeners.add(listener);
+            }
+        }
+        mTelephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
+
+        if (doDebugLogging()) {
+            // register for cell location updates
+            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_LOCATION);
+
+            // Register for Network (Wifi or Mobile) updates
+            mNetwork = new NetworkStateBroadcastReceiver();
+            IntentFilter mIntentFilter;
+            mIntentFilter = new IntentFilter();
+            mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+            mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+            mIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+            Log.d(LOG_TAG, "registering receiver");
+            registerReceiver(mNetwork, mIntentFilter);
+        }
+
+        if (trackSignalStrength()) {
+            mTelephonyManager.listen(mPhoneStateListener,
+                    PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+        }
+
+        // register for preference changes, so we can restart listeners on
+        // pref changes
+        mPrefListener = new PreferenceListener();
+        getPreferences().registerOnSharedPreferenceChangeListener(mPrefListener);
+    }
+
+    private Set<String> getTrackedProviders() {
+        Set<String> providerSet = new HashSet<String>();
+
+        if (trackGPS()) {
+            providerSet.add(LocationManager.GPS_PROVIDER);
+        }
+        if (trackNetwork()) {
+            providerSet.add(LocationManager.NETWORK_PROVIDER);
+        }
+        return providerSet;
+    }
+
+    private SharedPreferences getPreferences() {
+        return PreferenceManager.getDefaultSharedPreferences(this);
+    }
+
+    private boolean trackNetwork() {
+        return getPreferences().getBoolean(NETWORK_PREF, true);
+    }
+
+    private boolean trackGPS() {
+        return getPreferences().getBoolean(GPS_PREF, true);
+    }
+
+    private boolean doDebugLogging() {
+        return getPreferences().getBoolean(DEBUG_PREF, true);
+    }
+
+    private boolean trackSignalStrength() {
+        return getPreferences().getBoolean(SIGNAL_PREF, true);
+    }
+
+    private float getLocationMinDistance() {
+        try {
+            String disString = getPreferences().getString(MIN_DIS_PREF, "0");
+            return Float.parseFloat(disString);
+        }
+        catch (NumberFormatException e) {
+            Log.e(LOG_TAG, "Invalid preference for location min distance", e);
+        }
+        return 0;
+    }
+
+    private long getLocationUpdateTime() {
+        try {
+            String timeString = getPreferences().getString(MIN_TIME_PREF, "0");
+            long secondsTime = Long.valueOf(timeString);
+            return secondsTime * 1000;
+        }
+        catch (NumberFormatException e) {
+            Log.e(LOG_TAG, "Invalid preference for location min time", e);
+        }
+        return 0;
+    }
+
+    /**
+     * Shuts down this service
+     */
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.d(LOG_TAG, "Removing location listeners");
+        stopListeners();
+        Toast.makeText(this, "Tracking service stopped", Toast.LENGTH_SHORT);
+    }
+
+    /**
+     * De-registers all location listeners, closes persistent storage
+     */
+    protected synchronized void stopListeners() {
+        LocationManager lm = getLocationManager();
+        if (mListeners != null) {
+            for (LocationTrackingListener listener : mListeners) {
+                lm.removeUpdates(listener);
+            }
+            mListeners.clear();
+        }
+        mListeners = null;
+
+        // stop cell state listener
+        if (mTelephonyManager != null) {
+            mTelephonyManager.listen(mPhoneStateListener, 0);
+        }    
+        
+        // stop network/wifi listener
+        if (mNetwork != null) {
+            unregisterReceiver(mNetwork);
+        }
+        mNetwork = null;
+
+        mTrackerData = null;
+        if (mPrefListener != null) {
+            getPreferences().unregisterOnSharedPreferenceChangeListener(mPrefListener);
+            mPrefListener = null;
+        }
+    }
+
+    private LocationManager getLocationManager() {
+        return (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+    }
+
+    /**
+     * Determine the current distance from given location to the last
+     * approximated network location
+     *
+     * @param location - new location
+     *
+     * @return float distance in meters
+     */
+    private synchronized float getDistanceFromNetwork(Location location) {
+        float value = 0;
+        if (mNetworkLocation != null) {
+            value = location.distanceTo(mNetworkLocation);
+        }
+        if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
+            mNetworkLocation = location;
+        }
+        return value;
+    }
+
+    private class LocationTrackingListener implements LocationListener {
+
+        /**
+         * Writes details of location update to tracking file, including
+         * recording the distance between this location update and the last
+         * network location update
+         *
+         * @param location - new location
+         */
+        public void onLocationChanged(Location location) {
+            if (location == null) {
+                return;
+            }
+            float distance = getDistanceFromNetwork(location);
+            mTrackerData.writeEntry(location, distance);
+        }
+
+        /**
+         * Writes update to tracking file
+         *
+         * @param provider - name of disabled provider
+         */
+        public void onProviderDisabled(String provider) {
+            if (doDebugLogging()) {
+                mTrackerData.writeEntry(provider, "provider disabled");
+            }
+        }
+
+        /**
+         * Writes update to tracking file
+         * 
+         * @param provider - name of enabled provider
+         */
+        public void onProviderEnabled(String provider) {
+            if (doDebugLogging()) {
+                mTrackerData.writeEntry(provider,  "provider enabled");
+            }
+        }
+
+        /**
+         * Writes update to tracking file 
+         * 
+         * @param provider - name of provider whose status changed
+         * @param status - new status
+         * @param extras - optional set of extra status messages
+         */
+        public void onStatusChanged(String provider, int status, Bundle extras) {
+            if (doDebugLogging()) {
+                mTrackerData.writeEntry(provider,  "status change: " + status);
+            }
+        }
+    }
+
+    PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onCellLocationChanged(CellLocation location) {
+            try {
+                if (location instanceof GsmCellLocation) {
+                    GsmCellLocation cellLocation = (GsmCellLocation)location;
+                    String updateMsg = "cid=" + cellLocation.getCid() +
+                            ", lac=" + cellLocation.getLac();
+                    mTrackerData.writeEntry(CELL_PROVIDER_TAG, updateMsg);
+                } else if (location instanceof CdmaCellLocation) {
+                    CdmaCellLocation cellLocation = (CdmaCellLocation)location;
+                    String updateMsg = "BID=" + cellLocation.getBaseStationId() +
+                            ", SID=" + cellLocation.getSystemId() +
+                            ", NID=" + cellLocation.getNetworkId() +
+                            ", lat=" + cellLocation.getBaseStationLatitude() +
+                            ", long=" + cellLocation.getBaseStationLongitude() +
+                            ", SID=" + cellLocation.getSystemId() +
+                            ", NID=" + cellLocation.getNetworkId();
+                    mTrackerData.writeEntry(CELL_PROVIDER_TAG, updateMsg);
+                }
+            } catch (Exception e) {
+                Log.e(LOG_TAG, "Exception in CellStateHandler.handleMessage:", e);
+            }
+        }
+
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
+                String updateMsg = "cdma dBM=" + signalStrength.getCdmaDbm();
+                mTrackerData.writeEntry(SIGNAL_PROVIDER_TAG, updateMsg);
+            } else if  (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
+                String updateMsg = "gsm signal=" + signalStrength.getGsmSignalStrength();
+                mTrackerData.writeEntry(SIGNAL_PROVIDER_TAG, updateMsg);
+            }
+        }
+    };
+
+    /**
+     * Listener + recorder for mobile or wifi updates
+     */
+    private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+
+            if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+                WifiManager wifiManager =
+                    (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+                List<ScanResult> wifiScanResults = wifiManager.getScanResults();
+                String updateMsg = "num scan results=" +
+                    (wifiScanResults == null ? "0" : wifiScanResults.size());
+                mTrackerData.writeEntry(WIFI_PROVIDER_TAG, updateMsg);
+
+            } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+                String updateMsg;
+                boolean noConnectivity =
+                    intent.getBooleanExtra(
+                            ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+                if (noConnectivity) {
+                    updateMsg = "no connectivity";
+                }
+                else {
+                    updateMsg = "connection available";
+                }
+                mTrackerData.writeEntry(DATA_CONN_PROVIDER_TAG, updateMsg);
+
+            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+                int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+                    WifiManager.WIFI_STATE_UNKNOWN);
+
+                String stateString = "unknown";
+                switch (state) {
+                    case WifiManager.WIFI_STATE_DISABLED:
+                        stateString = "disabled";
+                        break;
+                    case WifiManager.WIFI_STATE_DISABLING:
+                        stateString = "disabling";
+                        break;
+                    case WifiManager.WIFI_STATE_ENABLED:
+                        stateString = "enabled";
+                        break;
+                    case WifiManager.WIFI_STATE_ENABLING:
+                        stateString = "enabling";
+                        break;
+                }
+                mTrackerData.writeEntry(WIFI_PROVIDER_TAG,
+                        "state = " + stateString);
+            }
+        }
+    }
+
+    private class PreferenceListener implements OnSharedPreferenceChangeListener {
+
+        public void onSharedPreferenceChanged(
+                SharedPreferences sharedPreferences, String key) {
+            Log.d(LOG_TAG, "restarting listeners due to preference change");
+            synchronized (TrackerService.this) {
+                stopListeners();
+                initLocationListeners();
+            }
+        }
+    }
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/CSVFormatter.java b/tests/LocationTracker/src/com/android/locationtracker/data/CSVFormatter.java
new file mode 100644
index 0000000..672ce28
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/CSVFormatter.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.locationtracker.data;
+
+import com.android.locationtracker.data.TrackerEntry.EntryType;
+
+/**
+ * Formats tracker data as CSV output
+ */
+class CSVFormatter implements IFormatter {
+
+    private static final String DELIMITER = ", ";
+
+    public String getHeader() {
+        StringBuilder csvBuilder = new StringBuilder();
+        for (String col : TrackerEntry.ATTRIBUTES) {
+            // skip type and id column
+            if (!TrackerEntry.ENTRY_TYPE.equals(col) &&
+                !TrackerEntry.ID_COL.equals(col)) {
+                csvBuilder.append(col);
+                csvBuilder.append(DELIMITER);
+            }
+        }
+        csvBuilder.append("\n");
+        return csvBuilder.toString();
+    }
+
+    public String getOutput(TrackerEntry entry) {
+        StringBuilder rowOutput = new StringBuilder();
+        // these must match order of columns added in getHeader
+        rowOutput.append(entry.getTimestamp());
+        rowOutput.append(DELIMITER);
+        rowOutput.append(entry.getTag());
+        rowOutput.append(DELIMITER);
+        //rowOutput.append(entry.getType());
+        //rowOutput.append(DELIMITER);
+        if (entry.getType() == EntryType.LOCATION_TYPE) {
+            if (entry.getLocation().hasAccuracy()) {
+                rowOutput.append(entry.getLocation().getAccuracy());
+            }
+            rowOutput.append(DELIMITER);
+            rowOutput.append(entry.getLocation().getLatitude());
+            rowOutput.append(DELIMITER);
+            rowOutput.append(entry.getLocation().getLongitude());
+            rowOutput.append(DELIMITER);
+            if (entry.getLocation().hasAltitude()) {
+                rowOutput.append(entry.getLocation().getAltitude());
+            }
+            rowOutput.append(DELIMITER);
+            if (entry.getLocation().hasSpeed()) {
+                rowOutput.append(entry.getLocation().getSpeed());
+            }
+            rowOutput.append(DELIMITER);
+            if (entry.getLocation().hasBearing()) {
+                rowOutput.append(entry.getLocation().getBearing());
+            }
+            rowOutput.append(DELIMITER);
+            rowOutput.append(entry.getDistFromNetLocation());
+            rowOutput.append(DELIMITER);
+            rowOutput.append(DateUtils.getKMLTimestamp(entry.getLocation()
+                    .getTime()));
+            rowOutput.append(DELIMITER);
+        }
+        rowOutput.append(entry.getLogMsg());
+        rowOutput.append("\n");
+        return rowOutput.toString();
+    }
+
+    public String getFooter() {
+        // not needed, return empty string
+        return "";
+    }
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/DateUtils.java b/tests/LocationTracker/src/com/android/locationtracker/data/DateUtils.java
new file mode 100644
index 0000000..13226bd
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/DateUtils.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.locationtracker.data;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * Provides formatting date as string utilities
+ */
+public class DateUtils {
+
+    private DateUtils() {
+
+    }
+
+    /**
+     * Returns timestamp given by param in KML format ie yyyy-mm-ddThh:mm:ssZ,
+     * where T is the separator between the date and the time and the time zone
+     * is Z (for UTC)
+     *
+     * @return KML timestamp as String
+     */
+    public static String getKMLTimestamp(long when) {
+        TimeZone tz = TimeZone.getTimeZone("GMT");
+        Calendar c = Calendar.getInstance(tz);
+        c.setTimeInMillis(when);
+        return String.format("%tY-%tm-%tdT%tH:%tM:%tSZ", c, c, c, c, c, c);
+    }
+
+    /**
+     * Helper version of getKMLTimestamp, that returns timestamp for current
+     * time
+     */
+    public static String getCurrentKMLTimestamp() {
+        return getKMLTimestamp(System.currentTimeMillis());
+    }
+
+    /**
+     * Returns timestamp in following format: yyyy-mm-dd-hh-mm-ss
+     */
+    public static String getCurrentTimestamp() {
+        Calendar c = Calendar.getInstance();
+        c.setTimeInMillis(System.currentTimeMillis());
+        return String.format("%tY-%tm-%td-%tH-%tM-%tS", c, c, c, c, c, c);
+    }
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/IFormatter.java b/tests/LocationTracker/src/com/android/locationtracker/data/IFormatter.java
new file mode 100644
index 0000000..af0b5ed
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/IFormatter.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.locationtracker.data;
+
+/**
+ * interface for formatting tracker data output
+ */
+interface IFormatter {
+
+    String getHeader();
+    String getOutput(TrackerEntry entry);
+    String getFooter();
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/KMLFormatter.java b/tests/LocationTracker/src/com/android/locationtracker/data/KMLFormatter.java
new file mode 100644
index 0000000..a5e1816
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/KMLFormatter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.locationtracker.data;
+
+import com.android.locationtracker.data.TrackerEntry.EntryType;
+
+import android.location.Location;
+
+/**
+ * Formats tracker data as KML output
+ */
+class KMLFormatter implements IFormatter {
+
+    public String getHeader() {
+        LineBuilder builder = new LineBuilder();
+        builder.addLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+        builder.addLine("<kml xmlns=\"http://earth.google.com/kml/2.2\">");
+        builder.addLine("<Document>");
+        return builder.toString();
+    }
+
+    public String getFooter() {
+        LineBuilder builder = new LineBuilder();
+        builder.addLine("</Document>");
+        builder.addLine("</kml>");
+        return builder.toString();
+    }
+
+    public String getOutput(TrackerEntry entry) {
+        LineBuilder builder = new LineBuilder();
+
+        if (entry.getType() == EntryType.LOCATION_TYPE) {
+
+            Location loc = entry.getLocation();
+            builder.addLine("<Placemark>");
+            builder.addLine("<description>");
+            builder.addLine("accuracy = " + loc.getAccuracy());
+            builder.addLine("distance from last network location  = "
+                    + entry.getDistFromNetLocation());
+            builder.addLine("</description>");
+            builder.addLine("<TimeStamp>");
+            builder.addLine("<when>" + entry.getTimestamp() + "</when>");
+            builder.addLine("</TimeStamp>");
+            builder.addLine("<Point>");
+            builder.addLine("<coordinates>");
+            builder.addLine(loc.getLongitude() + "," + loc.getLatitude() + ","
+                    + loc.getAltitude());
+            builder.addLine("</coordinates>");
+            builder.addLine("</Point>");
+            builder.addLine("</Placemark>");
+        }
+        return builder.toString();
+    }
+
+    private static class LineBuilder {
+        private StringBuilder mBuilder;
+
+        public LineBuilder() {
+            mBuilder = new StringBuilder();
+        }
+
+        public void addLine(String line) {
+            mBuilder.append(line);
+            mBuilder.append("\n");
+        }
+
+        @Override
+        public String toString() {
+            return mBuilder.toString();
+        }
+
+    }
+
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerDataHelper.java b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerDataHelper.java
new file mode 100644
index 0000000..a3838df
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerDataHelper.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.locationtracker.data;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.location.Location;
+
+/**
+ * Helper class for writing and retrieving data using the TrackerProvider
+ * content provider
+ *
+ */
+public class TrackerDataHelper {
+
+    private Context mContext;
+    /** formats data output */
+    protected IFormatter mFormatter;
+
+    /** formats output as Comma separated value CSV file */
+    public static final IFormatter CSV_FORMATTER = new CSVFormatter();
+    /** formats output as KML file */
+    public static final IFormatter KML_FORMATTER = new KMLFormatter();
+    /** provides no formatting */
+    public static final IFormatter NO_FORMATTER = new IFormatter() {
+        public String getFooter() {
+            return "";
+        }
+
+        public String getHeader() {
+            return "";
+        }
+
+        public String getOutput(TrackerEntry entry) {
+            return "";
+        }
+    };
+
+    /**
+     * Creates instance
+     *
+     * @param context - content context
+     * @param formatter - formats the output from the get*Output* methods
+     */
+    public TrackerDataHelper(Context context, IFormatter formatter) {
+        mContext = context;
+        mFormatter = formatter;
+    }
+
+    /**
+     * Creates a instance with no output formatting capabilities. Useful for
+     * clients that require write-only access
+     */
+    public TrackerDataHelper(Context context) {
+        this(context, NO_FORMATTER);
+    }
+
+    /**
+     * insert given TrackerEntry into content provider
+     */
+    void writeEntry(TrackerEntry entry) {
+        mContext.getContentResolver().insert(TrackerProvider.CONTENT_URI,
+                entry.getAsContentValues());
+    }
+
+    /**
+     * insert given location into tracker data
+     */
+    public void writeEntry(Location loc, float distFromNetLoc) {
+        writeEntry(TrackerEntry.createEntry(loc, distFromNetLoc));
+    }
+
+    /**
+     * insert given log message into tracker data
+     */
+    public void writeEntry(String tag, String logMsg) {
+        writeEntry(TrackerEntry.createEntry(tag, logMsg));
+    }
+
+    /**
+     * Deletes all tracker entries
+     */
+    public void deleteAll() {
+        mContext.getContentResolver().delete(TrackerProvider.CONTENT_URI, null,
+                null);
+    }
+
+    /**
+     * Query tracker data, filtering by given tag
+     *
+     * @param tag
+     * @return Cursor to data
+     */
+    public Cursor query(String tag, int limit) {
+        String selection = (tag == null ? null : TrackerEntry.TAG + "=?");
+        String[] selectionArgs = (tag == null ? null : new String[] {tag});
+        Cursor cursor = mContext.getContentResolver().query(
+                TrackerProvider.CONTENT_URI, TrackerEntry.ATTRIBUTES,
+                selection, selectionArgs, null);
+        if (cursor == null) {
+            return cursor;
+        }
+        int pos = (cursor.getCount() < limit ? 0 : cursor.getCount() - limit);
+        cursor.moveToPosition(pos);
+        return cursor;
+    }
+
+    /**
+     * Retrieves a cursor that starts at the last limit rows
+     *
+     * @param limit
+     * @return a cursor, null if bad things happened
+     */
+    public Cursor query(int limit) {
+        return query(null, limit);
+    }
+
+    /**
+     * Query tracker data, filtering by given tag. mo limit to number of rows
+     * returned
+     *
+     * @param tag
+     * @return Cursor to data
+     */
+    public Cursor query(String tag) {
+        return query(tag, Integer.MAX_VALUE);
+    }
+
+    /**
+     * Returns the output header particular to the associated formatter
+     */
+    public String getOutputHeader() {
+        return mFormatter.getHeader();
+    }
+
+    /**
+     * Returns the output footer particular to the associated formatter
+     */
+    public String getOutputFooter() {
+        return mFormatter.getFooter();
+    }
+
+    /**
+     * Helper method which converts row referenced by given cursor to a string
+     * output
+     *
+     * @param cursor
+     * @return CharSequence output, null if given cursor is invalid or no more
+     *         data
+     */
+    public String getNextOutput(Cursor cursor) {
+        if (cursor == null || cursor.isAfterLast()) {
+            return null;
+        }
+        String output = mFormatter.getOutput(TrackerEntry.createEntry(cursor));
+        cursor.moveToNext();
+        return output;
+    }
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerEntry.java b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerEntry.java
new file mode 100644
index 0000000..b2741f6
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerEntry.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.locationtracker.data;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.location.Location;
+
+
+/**
+ * Class that holds a tracker entry. An entry can be either a valid location, or
+ * a simple log msg
+ *
+ * It provides a concrete data structure to represent data stored in the
+ * TrackerProvider
+ */
+class TrackerEntry {
+
+    static final String TIMESTAMP = "Timestamp";
+    static final String TAG = "Tag";
+    static final String ENTRY_TYPE = "Type";
+
+    private Location mLocation;
+    private float mDistFromNetLocation;
+    private String mLogMsg;
+
+    static final String ID_COL = "_id";
+    static final String ACCURACY = "Accuracy";
+    static final String LATITUDE = "Latitude";
+    static final String LONGITUDE = "Longitude";
+    static final String ALTITUDE = "Altitude";
+    static final String SPEED = "Speed";
+    static final String BEARING = "Bearing";
+    static final String DIST_NET_LOCATION = "DistFromNetLocation";
+    static final String LOC_TIME = "LocationTime";
+    static final String DEBUG_INFO = "DebugInfo";
+
+    static final String STRING_DATA = "STRING";
+    static final String INT_DATA = "INTEGER";
+    static final String REAL_DATA = "REAL";
+    static final String BLOB_DATA = "BLOB";
+
+    static final String[] ATTRIBUTES = {
+            ID_COL, TIMESTAMP, TAG, ENTRY_TYPE, ACCURACY, LATITUDE, LONGITUDE,
+            ALTITUDE, SPEED, BEARING, DIST_NET_LOCATION, LOC_TIME, DEBUG_INFO};
+    static final String[] ATTRIBUTES_DATA_TYPE = {
+            INT_DATA + " PRIMARY KEY", STRING_DATA, STRING_DATA, STRING_DATA,
+            REAL_DATA, REAL_DATA, REAL_DATA, REAL_DATA, REAL_DATA, REAL_DATA,
+            REAL_DATA, INT_DATA, STRING_DATA};
+
+    // location extra keys used to retrieve debug info
+    private static final String NETWORK_LOCATION_SOURCE_KEY =
+        "networkLocationSource";
+    private static final String NETWORK_LOCATION_TYPE_KEY =
+        "networkLocationType";
+    private static final String[] LOCATION_DEBUG_KEYS = {
+            NETWORK_LOCATION_SOURCE_KEY, NETWORK_LOCATION_TYPE_KEY};
+
+    enum EntryType {
+        LOCATION_TYPE, LOG_TYPE
+    }
+
+    private String mTimestamp;
+    private String mTag;
+    private EntryType mType;
+
+    private TrackerEntry(String tag, EntryType type) {
+        mType = type;
+        mTag = tag;
+        mLocation = null;
+    }
+
+    private TrackerEntry(Location loc) {
+        this(loc.getProvider(), EntryType.LOCATION_TYPE);
+        mLocation = new Location(loc);
+    }
+
+    /**
+     * Creates a TrackerEntry from a Location
+     */
+    static TrackerEntry createEntry(Location loc, float distFromNetLocation) {
+        TrackerEntry entry = new TrackerEntry(loc);
+
+        String timestampVal = DateUtils.getCurrentKMLTimestamp();
+        entry.setTimestamp(timestampVal);
+        entry.setDistFromNetLocation(distFromNetLocation);
+        return entry;
+    }
+
+    /**
+     * Creates a TrackerEntry from a log msg
+     */
+    static TrackerEntry createEntry(String tag, String msg) {
+        TrackerEntry entry = new TrackerEntry(tag, EntryType.LOG_TYPE);
+        String timestampVal = DateUtils.getCurrentKMLTimestamp();
+        entry.setTimestamp(timestampVal);
+        entry.setLogMsg(msg);
+        return entry;
+    }
+
+    private void setTimestamp(String timestamp) {
+        mTimestamp = timestamp;
+    }
+
+    EntryType getType() {
+        return mType;
+    }
+
+    private void setDistFromNetLocation(float distFromNetLocation) {
+        mDistFromNetLocation = distFromNetLocation;
+    }
+
+    private void setLogMsg(String msg) {
+        mLogMsg = msg;
+    }
+
+    private void setLocation(Location location) {
+        mLocation = location;
+    }
+
+    String getTimestamp() {
+        return mTimestamp;
+    }
+
+    String getTag() {
+        return mTag;
+    }
+
+    Location getLocation() {
+        return mLocation;
+    }
+
+    String getLogMsg() {
+        return mLogMsg;
+    }
+
+    float getDistFromNetLocation() {
+        return mDistFromNetLocation;
+    }
+
+    static void buildCreationString(StringBuilder builder) {
+        if (ATTRIBUTES.length != ATTRIBUTES_DATA_TYPE.length) {
+            throw new IllegalArgumentException(
+                    "Attribute length does not match data type length");
+        }
+        for (int i = 0; i < ATTRIBUTES_DATA_TYPE.length; i++) {
+            if (i != 0) {
+                builder.append(", ");
+            }
+            builder.append(String.format("%s %s", ATTRIBUTES[i],
+                    ATTRIBUTES_DATA_TYPE[i]));
+        }
+    }
+
+    ContentValues getAsContentValues() {
+        ContentValues cValues = new ContentValues(ATTRIBUTES.length);
+        cValues.put(TIMESTAMP, mTimestamp);
+        cValues.put(TAG, mTag);
+        cValues.put(ENTRY_TYPE, mType.toString());
+        if (mType == EntryType.LOCATION_TYPE) {
+            cValues.put(LATITUDE, mLocation.getLatitude());
+            cValues.put(LONGITUDE, mLocation.getLongitude());
+            if (mLocation.hasAccuracy()) {
+                cValues.put(ACCURACY, mLocation.getAccuracy());
+            }
+            if (mLocation.hasAltitude()) {
+                cValues.put(ALTITUDE, mLocation.getAltitude());
+            }
+            if (mLocation.hasSpeed()) {
+                cValues.put(SPEED, mLocation.getSpeed());
+            }
+            if (mLocation.hasBearing()) {
+                cValues.put(BEARING, mLocation.getBearing());
+            }
+            cValues.put(DIST_NET_LOCATION, mDistFromNetLocation);
+            cValues.put(LOC_TIME, mLocation.getTime());
+            StringBuilder debugBuilder = new StringBuilder("");
+            if (mLocation.getExtras() != null) {
+                for (String key : LOCATION_DEBUG_KEYS) {
+                    Object val = mLocation.getExtras().get(key);
+                    if (val != null) {
+                        debugBuilder.append(String.format("%s=%s; ", key, val
+                                .toString()));
+                    }
+                }
+            }
+            cValues.put(DEBUG_INFO, debugBuilder.toString());
+        } else {
+            cValues.put(DEBUG_INFO, mLogMsg);
+        }
+        return cValues;
+    }
+
+    static TrackerEntry createEntry(Cursor cursor) {
+        String timestamp = cursor.getString(cursor.getColumnIndex(TIMESTAMP));
+        String tag = cursor.getString(cursor.getColumnIndex(TAG));
+        String sType = cursor.getString(cursor.getColumnIndex(ENTRY_TYPE));
+        TrackerEntry entry = new TrackerEntry(tag, EntryType.valueOf(sType));
+        entry.setTimestamp(timestamp);
+        if (entry.getType() == EntryType.LOCATION_TYPE) {
+            Location location = new Location(tag);
+            location.setLatitude(cursor.getFloat(cursor
+                    .getColumnIndexOrThrow(LATITUDE)));
+            location.setLongitude(cursor.getFloat(cursor
+                    .getColumnIndexOrThrow(LONGITUDE)));
+
+            Float accuracy = getNullableFloat(cursor, ACCURACY);
+            if (accuracy != null) {
+                location.setAccuracy(accuracy);
+            }
+            Float altitude = getNullableFloat(cursor, ALTITUDE);
+            if (altitude != null) {
+                location.setAltitude(altitude);
+            }
+            Float bearing = getNullableFloat(cursor, BEARING);
+            if (bearing != null) {
+                location.setBearing(bearing);
+            }
+            Float speed = getNullableFloat(cursor, SPEED);
+            if (speed != null) {
+                location.setSpeed(speed);
+            }
+            location.setTime(cursor.getLong(cursor.getColumnIndex(LOC_TIME)));
+            entry.setLocation(location);
+        }
+        entry.setLogMsg(cursor.getString(cursor.getColumnIndex(DEBUG_INFO)));
+
+        return entry;
+    }
+
+    private static Float getNullableFloat(Cursor cursor, String colName) {
+        Float retValue = null;
+        int colIndex = cursor.getColumnIndexOrThrow(colName);
+        if (!cursor.isNull(colIndex)) {
+            retValue = cursor.getFloat(colIndex);
+        }
+        return retValue;
+    }
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerListHelper.java b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerListHelper.java
new file mode 100644
index 0000000..55d4d1e
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerListHelper.java
@@ -0,0 +1,75 @@
+/*

+ * Copyright (C) 2008 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not

+ * use this file except in compliance with the License. You may obtain a copy of

+ * the License at

+ *

+ * http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

+ * License for the specific language governing permissions and limitations under

+ * the License.

+ */

+package com.android.locationtracker.data;

+

+import android.app.ListActivity;

+import android.content.Context;

+import android.database.Cursor;

+import android.view.View;

+import android.widget.ResourceCursorAdapter;

+import android.widget.TextView;

+

+import com.android.locationtracker.R;

+

+/**

+ * Used to bind Tracker data to a list view UI

+ */

+public class TrackerListHelper extends TrackerDataHelper {

+

+    private ListActivity mActivity;

+

+    // sort entries by most recent first

+    private static final String SORT_ORDER = TrackerEntry.ID_COL + " DESC";

+

+    public TrackerListHelper(ListActivity activity) {

+        super(activity, TrackerDataHelper.CSV_FORMATTER);

+        mActivity = activity;

+    }

+

+    /**

+     * Helper method for binding the list activities UI to the tracker data

+     * Tracker data will be sorted in most-recent first order

+     * Will enable automatic UI changes as tracker data changes

+     *

+     * @param layout - layout to populate data

+     */

+    public void bindListUI(int layout) {

+        Cursor cursor = mActivity.managedQuery(TrackerProvider.CONTENT_URI,

+                TrackerEntry.ATTRIBUTES, null, null, SORT_ORDER);

+        // Used to map tracker entries from the database to views

+        TrackerAdapter adapter = new TrackerAdapter(mActivity, layout, cursor);

+        mActivity.setListAdapter(adapter);

+        cursor.setNotificationUri(mActivity.getContentResolver(),

+                TrackerProvider.CONTENT_URI);

+

+    }

+

+    private class TrackerAdapter extends ResourceCursorAdapter {

+

+        public TrackerAdapter(Context context, int layout, Cursor c) {

+            super(context, layout, c);

+        }

+

+        @Override

+        public void bindView(View view, Context context, Cursor cursor) {

+            final TextView v = (TextView) view

+                    .findViewById(R.id.entrylist_item);

+            String rowText = mFormatter.getOutput(TrackerEntry

+                    .createEntry(cursor));

+            v.setText(rowText);

+        }

+    }

+}

diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerProvider.java b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerProvider.java
new file mode 100644
index 0000000..88f24c3
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerProvider.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.locationtracker.data;
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.util.Log;
+
+/**
+ * Content provider for location tracking.
+ *
+ * It is recommended to use the TrackerDataHelper class to access this data
+ * rather than this class directly
+ */
+public class TrackerProvider extends ContentProvider {
+
+    public static final Uri CONTENT_URI = Uri
+            .parse("content://com.android.locationtracker");
+
+    private static final String DB_NAME = "tracking.db";
+    private static final String TABLE_NAME = "tracking";
+    private static final int DB_VERSION = 1;
+
+    private static final String LOG_TAG = "TrackerProvider";
+
+    /**
+     * This class helps open, create, and upgrade the database file.
+     */
+    private static class DatabaseHelper extends SQLiteOpenHelper {
+
+        DatabaseHelper(Context context) {
+            super(context, DB_NAME, null, DB_VERSION);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            StringBuilder queryBuilder = new StringBuilder();
+            queryBuilder.append(String.format("CREATE TABLE %s (", TABLE_NAME));
+            TrackerEntry.buildCreationString(queryBuilder);
+
+            queryBuilder.append(");");
+            db.execSQL(queryBuilder.toString());
+            db.setVersion(DB_VERSION);
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            // TODO: reimplement this when dB version changes
+            Log.w(LOG_TAG, "Upgrading database from version " + oldVersion
+                            + " to " + newVersion
+                            + ", which will destroy all old data");
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
+            onCreate(db);
+        }
+    }
+
+    private DatabaseHelper mOpenHelper;
+
+    @Override
+    public boolean onCreate() {
+        mOpenHelper = new DatabaseHelper(getContext());
+        return true;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        int result = db.delete(TABLE_NAME, selection, selectionArgs);
+        getContext().getContentResolver().notifyChange(uri, null);
+        return result;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        long rowId = db.insert(TABLE_NAME, null, values);
+        if (rowId > 0) {
+            Uri addedUri = ContentUris.withAppendedId(CONTENT_URI, rowId);
+            getContext().getContentResolver().notifyChange(addedUri, null);
+            return addedUri;
+        }
+        return null;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        // TODO: extract limit from URI ?
+        Cursor cursor = db.query(TABLE_NAME, projection, selection,
+                selectionArgs, null, null, sortOrder);
+        getContext().getContentResolver().notifyChange(uri, null);
+        return cursor;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection,
+            String[] selectionArgs) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp
index 51afc0a..a09cec0 100644
--- a/tools/aapt/StringPool.cpp
+++ b/tools/aapt/StringPool.cpp
@@ -25,8 +25,12 @@
     const size_t NS = pool->size();
     for (size_t s=0; s<NS; s++) {
         size_t len;
-        printf("String #%ld: %s\n", s,
-                String8(pool->stringAt(s, &len)).string());
+        const char *str = (const char*)pool->string8At(s, &len);
+        if (str == NULL) {
+            str = String8(pool->stringAt(s, &len)).string();
+        }
+
+        printf("String #%ld: %s\n", s, str);
     }
 }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
index 73a3986..744bfbe 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -927,6 +927,7 @@
         return null;
     }
 
+    @Override
     public File getExternalCacheDir() {
         // TODO Auto-generated method stub
         return null;
@@ -964,6 +965,7 @@
         return null;
     }
 
+    @Override
     public File getExternalFilesDir(String type) {
         // TODO Auto-generated method stub
         return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java
index efd222e..6a98780 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java
@@ -392,7 +392,8 @@
 
         if (s == null) {
             return defValue;
-        } else if (s.equals(BridgeConstants.MATCH_PARENT)) {
+        } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
+                s.equals(BridgeConstants.FILL_PARENT)) {
             return LayoutParams.MATCH_PARENT;
         } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
             return LayoutParams.WRAP_CONTENT;
@@ -460,7 +461,8 @@
 
         if (s == null) {
             return defValue;
-        } else if (s.equals(BridgeConstants.MATCH_PARENT)) {
+        } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
+                s.equals(BridgeConstants.FILL_PARENT)) {
             return LayoutParams.MATCH_PARENT;
         } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
             return LayoutParams.WRAP_CONTENT;
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index afaed24..810e4d2 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -943,7 +943,7 @@
                         } else {
                             newDetailedState = DetailedState.FAILED;
                         }
-                        handleDisconnectedState(newDetailedState);
+                        handleDisconnectedState(newDetailedState, true);
                         /**
                          * If we were associated with a network (networkId != -1),
                          * assume we reached this state because of a failed attempt
@@ -965,7 +965,7 @@
                     } else if (newState == SupplicantState.DISCONNECTED) {
                         mHaveIpAddress = false;
                         if (isDriverStopped() || mDisconnectExpected) {
-                            handleDisconnectedState(DetailedState.DISCONNECTED);
+                            handleDisconnectedState(DetailedState.DISCONNECTED, true);
                         } else {
                             scheduleDisconnect();
                         }
@@ -1072,16 +1072,10 @@
                      */
                     if (wasDisconnectPending) {
                         DetailedState saveState = getNetworkInfo().getDetailedState();
-                        handleDisconnectedState(DetailedState.DISCONNECTED);
+                        handleDisconnectedState(DetailedState.DISCONNECTED, false);
                         setDetailedStateInternal(saveState);
-                    } else {
-                        /**
-                         *  stop DHCP to ensure there is a new IP address
-                         *  even if the supplicant transitions without disconnect
-                         *  COMPLETED -> ASSOCIATED -> COMPLETED
-                         */
-                        resetConnections(false);
                     }
+
                     configureInterface();
                     mLastBssid = result.BSSID;
                     mLastSsid = mWifiInfo.getSSID();
@@ -1116,7 +1110,7 @@
             
             case EVENT_DEFERRED_DISCONNECT:
                 if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
-                    handleDisconnectedState(DetailedState.DISCONNECTED);
+                    handleDisconnectedState(DetailedState.DISCONNECTED, true);
                 }
                 break;
 
@@ -1284,13 +1278,15 @@
      * Reset our IP state and send out broadcasts following a disconnect.
      * @param newState the {@code DetailedState} to set. Should be either
      * {@code DISCONNECTED} or {@code FAILED}.
+     * @param disableInterface indicates whether the interface should
+     * be disabled
      */
-    private void handleDisconnectedState(DetailedState newState) {
+    private void handleDisconnectedState(DetailedState newState, boolean disableInterface) {
         if (mDisconnectPending) {
             cancelDisconnect();
         }
         mDisconnectExpected = false;
-        resetConnections(true);
+        resetConnections(disableInterface);
         setDetailedState(newState);
         sendNetworkStateChangeBroadcast(mLastBssid);
         mWifiInfo.setBSSID(null);