Merge "Allow the configuration string to have quotes."
diff --git a/api/current.xml b/api/current.xml
index 3e8b9ac3..fd8a180 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -25564,6 +25564,17 @@
  visibility="public"
 >
 </method>
+<method name="getCurrentModeType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getNightMode"
  return="int"
  abstract="false"
@@ -25588,29 +25599,58 @@
 <parameter name="mode" type="int">
 </parameter>
 </method>
-<field name="MODE_AUTO"
+<field name="ACTION_ENTER_CAR_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_ENTER_DESK_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_EXIT_CAR_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_EXIT_DESK_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODE_NIGHT_AUTO"
  type="int"
  transient="false"
  volatile="false"
- value="3"
+ value="0"
  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"
+<field name="MODE_NIGHT_NO"
  type="int"
  transient="false"
  volatile="false"
@@ -25621,6 +25661,17 @@
  visibility="public"
 >
 </field>
+<field name="MODE_NIGHT_YES"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="WallpaperInfo"
  extends="java.lang.Object"
@@ -39068,17 +39119,6 @@
  visibility="public"
 >
 </field>
-<field name="EXTRA_CAR_MODE_ENABLED"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.extra.CAR_MODE_ENABLED&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="EXTRA_CC"
  type="java.lang.String"
  transient="false"
@@ -39233,17 +39273,6 @@
  visibility="public"
 >
 </field>
-<field name="EXTRA_PHYSICAL_DOCK_STATE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.extra.PHYSICAL_DOCK_STATE&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="EXTRA_REMOTE_INTENT_TOKEN"
  type="java.lang.String"
  transient="false"
@@ -47911,6 +47940,17 @@
  type="int"
  transient="false"
  volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="UI_MODE_TYPE_DESK"
+ type="int"
+ transient="false"
+ volatile="false"
  value="2"
  static="true"
  final="true"
@@ -175426,6 +175466,17 @@
  visibility="public"
 >
 </method>
+<method name="getOverscrollMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getPaddingBottom"
  return="int"
  abstract="false"
@@ -177591,6 +177642,19 @@
 <parameter name="l" type="android.view.View.OnTouchListener">
 </parameter>
 </method>
+<method name="setOverscrollMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="overscrollMode" type="int">
+</parameter>
+</method>
 <method name="setPadding"
  return="void"
  abstract="false"
@@ -178213,6 +178277,39 @@
  visibility="public"
 >
 </field>
+<field name="OVERSCROLL_ALWAYS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OVERSCROLL_IF_CONTENT_SCROLLS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OVERSCROLL_NEVER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET"
  type="int[]"
  transient="false"
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index 6ac8a2a..bd637b7 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -33,6 +33,11 @@
     void disableCarMode();
 
     /**
+     * Return the current running mode.
+     */
+    int getCurrentModeType();
+    
+    /**
      * Sets the night mode.
      * The mode can be one of:
      *   1 - notnight mode
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index 1ae9315..cb947b1 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -32,6 +32,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.StateListDrawable;
 import android.net.Uri;
+import android.os.Bundle;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
@@ -80,7 +81,7 @@
     private int mIconName1Col;
     private int mIconName2Col;
     private int mBackgroundColorCol;
-    
+
     static final int NONE = -1;
 
     private final Runnable mStartSpinnerRunnable;
@@ -121,7 +122,7 @@
                 mSearchDialog.setWorking(false);
             }
         };
-        
+
         // delay 500ms when deleting
         getFilter().setDelayer(new Filter.Delayer() {
 
@@ -129,7 +130,7 @@
 
             public long getPostingDelay(CharSequence constraint) {
                 if (constraint == null) return 0;
-                
+
                 long delay = constraint.length() < mPreviousLength ? DELETE_KEY_POST_DELAY : 0;
                 mPreviousLength = constraint.length();
                 return delay;
@@ -203,15 +204,16 @@
     }
 
     private void updateSpinnerState(Cursor cursor) {
+        Bundle extras = cursor != null ? cursor.getExtras() : null;
         if (DBG) {
             Log.d(LOG_TAG, "updateSpinnerState - extra = "
-                + (cursor != null
-                        ? cursor.getExtras().getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)
+                + (extras != null
+                        ? extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)
                         : null));
         }
         // Check if the Cursor indicates that the query is not complete and show the spinner
-        if (cursor != null
-                && cursor.getExtras().getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)) {
+        if (extras != null
+                && extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)) {
             mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable);
             return;
         }
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index aca8ab4..defe421 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -1,5 +1,7 @@
 package android.app;
 
+import android.content.Context;
+import android.content.res.Configuration;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
@@ -9,6 +11,22 @@
  * 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>These facilities are built on top of the underlying
+ * {@link android.content.Intent#ACTION_DOCK_EVENT} broadcasts that are sent when the user
+ * physical places the device into and out of a dock.  When that happens,
+ * the UiModeManager switches the system {@link android.content.res.Configuration}
+ * to the appropriate UI mode, sends broadcasts about the mode switch, and
+ * starts the corresponding mode activity if appropriate.  See the
+ * broadcasts {@link #ACTION_ENTER_CAR_MODE} and
+ * {@link #ACTION_ENTER_DESK_MODE} for more information.
+ * 
+ * <p>In addition, the user may manually switch the system to car mode without
+ * physically being in a dock.  While in car mode -- whether by manual action
+ * from the user or being physically placed in a dock -- a notification is
+ * displayed allowing the user to exit dock mode.  Thus the dock mode
+ * represented here may be different than the current state of the underlying
+ * dock event broadcast.
  *
  * <p>You do not instantiate this class directly; instead, retrieve it through
  * {@link android.content.Context#getSystemService
@@ -17,19 +35,72 @@
 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;
+    /**
+     * Broadcast sent when the device's UI has switched to car mode, either
+     * by being placed in a car dock or explicit action of the user.  After
+     * sending the broadcast, the system will start the intent
+     * {@link android.content.Intent#ACTION_MAIN} with category
+     * {@link android.content.Intent#CATEGORY_CAR_DOCK}
+     * to display the car UI, which typically what an application would
+     * implement to provide their own interface.  However, applications can
+     * also monitor this Intent in order to be informed of mode changes or
+     * prevent the normal car UI from being displayed by setting the result
+     * of the broadcast to {@link Activity#RESULT_CANCELED}.
+     */
+    public static String ACTION_ENTER_CAR_MODE = "android.app.action.ENTER_CAR_MODE";
+    
+    /**
+     * Broadcast sent when the device's UI has switch away from car mode back
+     * to normal mode.  Typically used by a car mode app, to dismiss itself
+     * when the user exits car mode.
+     */
+    public static String ACTION_EXIT_CAR_MODE = "android.app.action.EXIT_CAR_MODE";
+    
+    /**
+     * Broadcast sent when the device's UI has switched to desk mode,
+     * by being placed in a desk dock.  After
+     * sending the broadcast, the system will start the intent
+     * {@link android.content.Intent#ACTION_MAIN} with category
+     * {@link android.content.Intent#CATEGORY_DESK_DOCK}
+     * to display the desk UI, which typically what an application would
+     * implement to provide their own interface.  However, applications can
+     * also monitor this Intent in order to be informed of mode changes or
+     * prevent the normal desk UI from being displayed by setting the result
+     * of the broadcast to {@link Activity#RESULT_CANCELED}.
+     */
+    public static String ACTION_ENTER_DESK_MODE = "android.app.action.ENTER_DESK_MODE";
+    
+    /**
+     * Broadcast sent when the device's UI has switch away from car mode back
+     * to normal mode.  Typically used by a car mode app, to dismiss itself
+     * when the user exits car mode.
+     */
+    public static String ACTION_EXIT_DESK_MODE = "android.app.action.EXIT_DESK_MODE";
+    
+    /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+     * automatically switch night mode on and off based on the time.
+     */
+    public static final int MODE_NIGHT_AUTO = Configuration.UI_MODE_NIGHT_UNDEFINED >> 4;
+    
+    /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+     * never run in night mode.
+     */
+    public static final int MODE_NIGHT_NO = Configuration.UI_MODE_NIGHT_NO >> 4;
+    
+    /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+     * always run in night mode.
+     */
+    public static final int MODE_NIGHT_YES = Configuration.UI_MODE_NIGHT_YES >> 4;
 
     private IUiModeManager mService;
 
     /*package*/ UiModeManager() {
         mService = IUiModeManager.Stub.asInterface(
-                ServiceManager.getService("uimode"));
+                ServiceManager.getService(Context.UI_MODE_SERVICE));
     }
 
     /**
-     * Disables the car mode.
+     * Turn off special mode if currently in car mode.
      */
     public void disableCarMode() {
         if (mService != null) {
@@ -42,17 +113,35 @@
     }
 
     /**
+     * Return the current running mode type.  May be one of
+     * {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL},
+     * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, or
+     * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR},
+     */
+    public int getCurrentModeType() {
+        if (mService != null) {
+            try {
+                return mService.getCurrentModeType();
+            } catch (RemoteException e) {
+                Log.e(TAG, "getCurrentModeType: RemoteException", e);
+            }
+        }
+        return Configuration.UI_MODE_TYPE_NORMAL;
+    }
+
+    /**
      * Sets the night mode.  Changes to the night mode are only effective when
-     * the car mode is enabled on a device.
+     * the car or desk 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
+     *   <li><em>{@link #MODE_NIGHT_NO}<em> - sets the device into notnight
      *       mode.</li>
-     *   <li><em>{@link #MODE_NIGHT}</em> - sets the device into night mode.
+     *   <li><em>{@link #MODE_NIGHT_YES}</em> - sets the device into night mode.
      *   </li>
-     *   <li><em>{@link #MODE_AUTO}</em> - automatic night/notnight switching
+     *   <li><em>{@link #MODE_NIGHT_AUTO}</em> - automatic night/notnight switching
      *       depending on the location and certain other sensors.</li>
+     * </ul>
      */
     public void setNightMode(int mode) {
         if (mService != null) {
@@ -67,8 +156,8 @@
     /**
      * Returns the currently configured night mode.
      *
-     * @return {@link #MODE_NOTNIGHT}, {@link #MODE_NIGHT} or {@link #MODE_AUTO}
-     *         When an error occurred -1 is returned.
+     * @return {@link #MODE_NIGHT_NO}, {@link #MODE_NIGHT_YES}, or
+     *  {@link #MODE_NIGHT_AUTO}.  When an error occurred -1 is returned.
      */
     public int getNightMode() {
         if (mService != null) {
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 91b1c4e..5fb2aae 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -29,6 +29,7 @@
 import android.database.SQLException;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 
@@ -217,6 +218,13 @@
             return ContentProvider.this.openAssetFile(uri, mode);
         }
 
+        /**
+         * @hide
+         */
+        public Bundle call(String method, String request, Bundle args) {
+            return ContentProvider.this.call(method, request, args);
+        }
+
         private void enforceReadPermission(Uri uri) {
             final int uid = Binder.getCallingUid();
             if (uid == mMyUid) {
@@ -748,4 +756,18 @@
         }
         return results;
     }
-}
\ No newline at end of file
+
+    /**
+     * @hide -- until interface has proven itself
+     *
+     * Call an provider-defined method.  This can be used to implement
+     * interfaces that are cheaper than using a Cursor.
+     *
+     * @param method Method name to call.  Opaque to framework.
+     * @param request Nullable String argument passed to method.
+     * @param args Nullable Bundle argument passed to method.
+     */
+    public Bundle call(String method, String request, Bundle args) {
+        return null;
+    }
+}
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index bacb684..81b8055 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -26,6 +26,7 @@
 import android.database.IContentObserver;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -222,6 +223,21 @@
                     }
                     return true;
                 }
+
+                case CALL_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+
+                    String method = data.readString();
+                    String stringArg = data.readString();
+                    Bundle args = data.readBundle();
+
+                    Bundle responseBundle = call(method, stringArg, args);
+
+                    reply.writeNoException();
+                    reply.writeBundle(responseBundle);
+                    return true;
+                }
             }
         } catch (Exception e) {
             DatabaseUtils.writeExceptionToParcel(reply, e);
@@ -485,6 +501,22 @@
         return fd;
     }
 
+    public Bundle call(String method, String request, Bundle args)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+
+        data.writeInterfaceToken(IContentProvider.descriptor);
+
+        data.writeString(method);
+        data.writeString(request);
+        data.writeBundle(args);
+
+        mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0);
+
+        DatabaseUtils.readExceptionFromParcel(reply);
+        return reply.readBundle();
+    }
+
     private IBinder mRemote;
 }
-
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 1b0ef34..bcef75e 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -736,7 +736,7 @@
      * @hide
      */
     public final IContentProvider acquireProvider(String name) {
-        if(name == null) {
+        if (name == null) {
             return null;
         }
         return acquireProvider(mContext, name);
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 1b0ca58..67e7581 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -22,10 +22,11 @@
 import android.database.IBulkCursor;
 import android.database.IContentObserver;
 import android.net.Uri;
-import android.os.RemoteException;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
 
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
@@ -58,6 +59,17 @@
             throws RemoteException, FileNotFoundException;
     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws RemoteException, OperationApplicationException;
+    /**
+     * @hide -- until interface has proven itself
+     *
+     * Call an provider-defined method.  This can be used to implement
+     * interfaces that are cheaper than using a Cursor.
+     *
+     * @param method Method name to call.  Opaque to framework.
+     * @param request Nullable String argument passed to method.
+     * @param args Nullable Bundle argument passed to method.
+     */
+    public Bundle call(String method, String request, Bundle args) throws RemoteException;
 
     /* IPC constants */
     static final String descriptor = "android.content.IContentProvider";
@@ -71,4 +83,5 @@
     static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13;
     static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14;
     static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19;
+    static final int CALL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 20;
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e63851f..fd5591d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1838,21 +1838,17 @@
             "android.intent.action.REBOOT";
 
     /**
-     * Broadcast Action:  A sticky broadcast indicating the phone was docked
-     * or undocked.
+     * Broadcast Action:  A sticky broadcast for changes in the physical
+     * docking state of the device.
      *
      * <p>The intent will have the following extra values:
      * <ul>
      *   <li><em>{@link #EXTRA_DOCK_STATE}</em> - the current dock
-     *       state, which depends on the state of the car mode.</li>
-     *   <li><em>{@link #EXTRA_PHYSICAL_DOCK_STATE}</em> - the physical dock
-     *       state.</li>
-     *   <li><em>{@link #EXTRA_CAR_MODE_ENABLED}</em> - a boolean indicating the
-     *       state of the car mode.</li>
+     *       state, indicating which dock the device is physically in.</li>
      * </ul>
-     * <p>This is intended for monitoring the current dock state.
-     * To launch an activity from a dock state change, use {@link #CATEGORY_CAR_DOCK}
-     * or {@link #CATEGORY_DESK_DOCK} instead.
+     * <p>This is intended for monitoring the current physical dock state.
+     * See {@link android.app.UiModeManager} for the normal API dealing with
+     * dock mode changes.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_DOCK_EVENT =
@@ -2014,15 +2010,15 @@
             "android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST";
     /**
      * An activity to run when device is inserted into a car dock.
-     * Used with {@link #ACTION_MAIN} to launch an activity.
-     * To monitor dock state, use {@link #ACTION_DOCK_EVENT} instead.
+     * Used with {@link #ACTION_MAIN} to launch an activity.  For more
+     * information, see {@link android.app.UiModeManager}.
      */
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK";
     /**
      * An activity to run when device is inserted into a car dock.
-     * Used with {@link #ACTION_MAIN} to launch an activity.
-     * To monitor dock state, use {@link #ACTION_DOCK_EVENT} instead.
+     * Used with {@link #ACTION_MAIN} to launch an activity.  For more
+     * information, see {@link android.app.UiModeManager}.
      */
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_DESK_DOCK = "android.intent.category.DESK_DOCK";
@@ -2191,22 +2187,6 @@
     public static final int EXTRA_DOCK_STATE_CAR = 2;
 
     /**
-     * Used as an int extra field in {@link android.content.Intent#ACTION_DOCK_EVENT}
-     * intents to request the physical dock state. Possible values are
-     * {@link android.content.Intent#EXTRA_DOCK_STATE_UNDOCKED},
-     * {@link android.content.Intent#EXTRA_DOCK_STATE_DESK}, or
-     * {@link android.content.Intent#EXTRA_DOCK_STATE_CAR}.
-     */
-    public static final String EXTRA_PHYSICAL_DOCK_STATE =
-            "android.intent.extra.PHYSICAL_DOCK_STATE";
-
-    /**
-     * Used as an boolean extra field in {@link android.content.Intent#ACTION_DOCK_EVENT}
-     * intents to indicate that the car mode is enabled or not.
-     */
-    public static final String EXTRA_CAR_MODE_ENABLED = "android.intent.extra.CAR_MODE_ENABLED";
-
-    /**
      * Boolean that can be supplied as meta-data with a dock activity, to
      * indicate that the dock should take over the home key when it is active.
      */
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index a737283..61e3004 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -165,7 +165,8 @@
     public static final int UI_MODE_TYPE_MASK = 0x0f;
     public static final int UI_MODE_TYPE_UNDEFINED = 0x00;
     public static final int UI_MODE_TYPE_NORMAL = 0x01;
-    public static final int UI_MODE_TYPE_CAR = 0x02;
+    public static final int UI_MODE_TYPE_DESK = 0x02;
+    public static final int UI_MODE_TYPE_CAR = 0x03;
 
     public static final int UI_MODE_NIGHT_MASK = 0x30;
     public static final int UI_MODE_NIGHT_UNDEFINED = 0x00;
@@ -175,11 +176,12 @@
     /**
      * Bit mask of the ui mode.  Currently there are two fields:
      * <p>The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the
-     * device. They may be one of
-     * {@link #UI_MODE_TYPE_NORMAL} or {@link #UI_MODE_TYPE_CAR}.
+     * device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED},
+     * {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK},
+     * or {@link #UI_MODE_TYPE_CAR}.
      *
      * <p>The {@link #UI_MODE_NIGHT_MASK} defines whether the screen
-     * is in a special mode. They may be one of
+     * is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED},
      * {@link #UI_MODE_NIGHT_NO} or {@link #UI_MODE_NIGHT_YES}.
      */
     public int uiMode;
@@ -272,7 +274,7 @@
         navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
         orientation = ORIENTATION_UNDEFINED;
         screenLayout = SCREENLAYOUT_SIZE_UNDEFINED;
-        uiMode = UI_MODE_TYPE_NORMAL;
+        uiMode = UI_MODE_TYPE_UNDEFINED;
         seq = 0;
     }
 
@@ -354,10 +356,17 @@
             changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
             screenLayout = delta.screenLayout;
         }
-        if (delta.uiMode != UI_MODE_TYPE_NORMAL
+        if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
                 && uiMode != delta.uiMode) {
             changed |= ActivityInfo.CONFIG_UI_MODE;
-            uiMode = delta.uiMode;
+            if ((delta.uiMode&UI_MODE_TYPE_MASK) != UI_MODE_TYPE_UNDEFINED) {
+                uiMode = (uiMode&~UI_MODE_TYPE_MASK)
+                        | (delta.uiMode&UI_MODE_TYPE_MASK);
+            }
+            if ((delta.uiMode&UI_MODE_NIGHT_MASK) != UI_MODE_NIGHT_UNDEFINED) {
+                uiMode = (uiMode&~UI_MODE_NIGHT_MASK)
+                        | (delta.uiMode&UI_MODE_NIGHT_MASK);
+            }
         }
         
         if (delta.seq != 0) {
@@ -439,7 +448,7 @@
                 && screenLayout != delta.screenLayout) {
             changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
         }
-        if (delta.uiMode != UI_MODE_TYPE_NORMAL
+        if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
                 && uiMode != delta.uiMode) {
             changed |= ActivityInfo.CONFIG_UI_MODE;
         }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 5d5bd9c..8c8d3e5 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2025,7 +2025,10 @@
             InputConnection ic = getCurrentInputConnection();
             mExtractedText = ic == null? null
                     : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
-            
+            if (mExtractedText == null || ic == null) {
+                Log.e(TAG, "Unexpected null in startExtractingText : mExtractedText = "
+                        + mExtractedText + ", input connection = " + ic);
+            }
             final EditorInfo ei = getCurrentInputEditorInfo();
             
             try {
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index d6fe107..0ec1c74 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -132,6 +132,45 @@
     }
 
     /**
+     * Make a Bundle for a single key/value pair.
+     *
+     * @hide
+     */
+    public static Bundle forPair(String key, String value) {
+        // TODO: optimize this case.
+        Bundle b = new Bundle(1);
+        b.putString(key, value);
+        return b;
+    }
+
+    /**
+     * TODO: optimize this later (getting just the value part of a Bundle
+     * with a single pair) once Bundle.forPair() above is implemented
+     * with a special single-value Map implementation/serialization.
+     *
+     * Note: value in single-pair Bundle may be null.
+     *
+     * @hide
+     */
+    public String getPairValue() {
+        unparcel();
+        int size = mMap.size();
+        if (size > 1) {
+            Log.w(LOG_TAG, "getPairValue() used on Bundle with multiple pairs.");
+        }
+        if (size == 0) {
+            return null;
+        }
+        Object o = mMap.values().iterator().next();
+        try {
+            return (String) o;
+        } catch (ClassCastException e) {
+            typeWarning("getPairValue()", o, "String", e);
+            return null;
+        }
+    }
+
+    /**
      * Changes the ClassLoader this Bundle uses when instantiating objects.
      *
      * @param loader An explicit ClassLoader to use when instantiating objects
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7df509f..726f98a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -27,6 +27,7 @@
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.IContentProvider;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -492,6 +493,16 @@
     // End of Intent actions for Settings
 
     /**
+     * @hide - Private call() method on SettingsProvider to read from 'system' table.
+     */
+    public static final String CALL_METHOD_GET_SYSTEM = "GET_system";
+
+    /**
+     * @hide - Private call() method on SettingsProvider to read from 'secure' table.
+     */
+    public static final String CALL_METHOD_GET_SECURE = "GET_secure";
+
+    /**
      * 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
@@ -544,23 +555,36 @@
         }
     }
 
+    // Thread-safe.
     private static class NameValueCache {
         private final String mVersionSystemProperty;
         private final Uri mUri;
 
-        // Must synchronize(mValues) to access mValues and mValuesVersion.
+        private static final String[] SELECT_VALUE =
+            new String[] { Settings.NameValueTable.VALUE };
+        private static final String NAME_EQ_PLACEHOLDER = "name=?";
+
+        // Must synchronize on 'this' to access mValues and mValuesVersion.
         private final HashMap<String, String> mValues = new HashMap<String, String>();
         private long mValuesVersion = 0;
 
-        public NameValueCache(String versionSystemProperty, Uri uri) {
+        // Initially null; set lazily and held forever.  Synchronized on 'this'.
+        private IContentProvider mContentProvider = null;
+
+        // The method we'll call (or null, to not use) on the provider
+        // for the fast path of retrieving settings.
+        private final String mCallCommand;
+
+        public NameValueCache(String versionSystemProperty, Uri uri, String callCommand) {
             mVersionSystemProperty = versionSystemProperty;
             mUri = uri;
+            mCallCommand = callCommand;
         }
 
         public String getString(ContentResolver cr, String name) {
             long newValuesVersion = SystemProperties.getLong(mVersionSystemProperty, 0);
 
-            synchronized (mValues) {
+            synchronized (this) {
                 if (mValuesVersion != newValuesVersion) {
                     if (LOCAL_LOGV) {
                         Log.v(TAG, "invalidate [" + mUri.getLastPathSegment() + "]: current " +
@@ -576,17 +600,47 @@
                 }
             }
 
+            IContentProvider cp = null;
+            synchronized (this) {
+                cp = mContentProvider;
+                if (cp == null) {
+                    cp = mContentProvider = cr.acquireProvider(mUri.getAuthority());
+                }
+            }
+
+            // Try the fast path first, not using query().  If this
+            // fails (alternate Settings provider that doesn't support
+            // this interface?) then we fall back to the query/table
+            // interface.
+            if (mCallCommand != null) {
+                try {
+                    Bundle b = cp.call(mCallCommand, name, null);
+                    if (b != null) {
+                        String value = b.getPairValue();
+                        synchronized (this) {
+                            mValues.put(name, value);
+                        }
+                        return value;
+                    }
+                    // If the response Bundle is null, we fall through
+                    // to the query interface below.
+                } catch (RemoteException e) {
+                    // Not supported by the remote side?  Fall through
+                    // to query().
+                }
+            }
+
             Cursor c = null;
             try {
-                c = cr.query(mUri, new String[] { Settings.NameValueTable.VALUE },
-                        Settings.NameValueTable.NAME + "=?", new String[]{name}, null);
+                c = cp.query(mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER,
+                             new String[]{name}, null);
                 if (c == null) {
                     Log.w(TAG, "Can't get key " + name + " from " + mUri);
                     return null;
                 }
 
                 String value = c.moveToNext() ? c.getString(0) : null;
-                synchronized (mValues) {
+                synchronized (this) {
                     mValues.put(name, value);
                 }
                 if (LOCAL_LOGV) {
@@ -594,7 +648,7 @@
                             name + " = " + (value == null ? "(null)" : value));
                 }
                 return value;
-            } catch (SQLException e) {
+            } catch (RemoteException e) {
                 Log.w(TAG, "Can't get key " + name + " from " + mUri, e);
                 return null;  // Return null, but don't cache it.
             } finally {
@@ -611,7 +665,8 @@
     public static final class System extends NameValueTable {
         public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
 
-        private static volatile NameValueCache mNameValueCache = null;
+        // Populated lazily, guarded by class object:
+        private static NameValueCache sNameValueCache = null;
 
         private static final HashSet<String> MOVED_TO_SECURE;
         static {
@@ -660,10 +715,11 @@
                         + " to android.provider.Settings.Secure, returning read-only value.");
                 return Secure.getString(resolver, name);
             }
-            if (mNameValueCache == null) {
-                mNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI);
+            if (sNameValueCache == null) {
+                sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI,
+                                                     CALL_METHOD_GET_SYSTEM);
             }
-            return mNameValueCache.getString(resolver, name);
+            return sNameValueCache.getString(resolver, name);
         }
 
         /**
@@ -1905,7 +1961,8 @@
     public static final class Secure extends NameValueTable {
         public static final String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version";
 
-        private static volatile NameValueCache mNameValueCache = null;
+        // Populated lazily, guarded by class object:
+        private static NameValueCache sNameValueCache = null;
 
         /**
          * Look up a name in the database.
@@ -1914,10 +1971,11 @@
          * @return the corresponding value, or null if not present
          */
         public synchronized static String getString(ContentResolver resolver, String name) {
-            if (mNameValueCache == null) {
-                mNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI);
+            if (sNameValueCache == null) {
+                sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI,
+                                                     CALL_METHOD_GET_SECURE);
             }
-            return mNameValueCache.getString(resolver, name);
+            return sNameValueCache.getString(resolver, name);
         }
 
         /**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index abbab0e..7a0c445 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1516,19 +1516,28 @@
     /**
      * Always allow a user to overscroll this view, provided it is a
      * view that can scroll.
+     * 
+     * @see #getOverscrollMode()
+     * @see #setOverscrollMode(int)
      */
-    private static final int OVERSCROLL_ALWAYS = 0;
+    public static final int OVERSCROLL_ALWAYS = 0;
     
     /**
      * Allow a user to overscroll this view only if the content is large
      * enough to meaningfully scroll, provided it is a view that can scroll.
+     * 
+     * @see #getOverscrollMode()
+     * @see #setOverscrollMode(int)
      */
-    private static final int OVERSCROLL_IF_CONTENT_SCROLLS = 1;
+    public static final int OVERSCROLL_IF_CONTENT_SCROLLS = 1;
     
     /**
      * Never allow a user to overscroll this view.
+     * 
+     * @see #getOverscrollMode()
+     * @see #setOverscrollMode(int)
      */
-    private static final int OVERSCROLL_NEVER = 2;
+    public static final int OVERSCROLL_NEVER = 2;
     
     /**
      * Controls the overscroll mode for this view.
@@ -8770,6 +8779,38 @@
             boolean clampedX, boolean clampedY) {
         // Intentionally empty.
     }
+    
+    /**
+     * Returns the overscroll mode for this view. The result will be
+     * one of {@link #OVERSCROLL_ALWAYS} (default), {@link #OVERSCROLL_IF_CONTENT_SCROLLS}
+     * (allow overscrolling only if the view content is larger than the container),
+     * or {@link #OVERSCROLL_NEVER}.
+     * 
+     * @return This view's overscroll mode.
+     */
+    public int getOverscrollMode() {
+        return mOverscrollMode;
+    }
+    
+    /**
+     * Set the overscroll mode for this view. Valid overscroll modes are
+     * {@link #OVERSCROLL_ALWAYS} (default), {@link #OVERSCROLL_IF_CONTENT_SCROLLS}
+     * (allow overscrolling only if the view content is larger than the container),
+     * or {@link #OVERSCROLL_NEVER}.
+     * 
+     * Setting the overscroll mode of a view will have an effect only if the
+     * view is capable of scrolling.
+     * 
+     * @param overscrollMode The new overscroll mode for this view.
+     */
+    public void setOverscrollMode(int overscrollMode) {
+        if (overscrollMode != OVERSCROLL_ALWAYS &&
+                overscrollMode != OVERSCROLL_IF_CONTENT_SCROLLS &&
+                overscrollMode != OVERSCROLL_NEVER) {
+            throw new IllegalArgumentException("Invalid overscroll mode " + overscrollMode);
+        }
+        mOverscrollMode = overscrollMode;
+    }
 
     /**
      * A MeasureSpec encapsulates the layout requirements passed from parent to child.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6a95831..b8d71b9 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -24,12 +24,17 @@
 import android.content.pm.PackageManager;
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Interpolator;
+import android.graphics.Paint;
 import android.graphics.Picture;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
 import android.net.http.SslCertificate;
 import android.net.Uri;
@@ -70,7 +75,7 @@
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.ListView;
-import android.widget.Scroller;
+import android.widget.OverScroller;
 import android.widget.Toast;
 import android.widget.ZoomButtonsController;
 import android.widget.ZoomControls;
@@ -455,7 +460,9 @@
     // time for the longest scroll animation
     private static final int MAX_DURATION = 750;   // milliseconds
     private static final int SLIDE_TITLE_DURATION = 500;   // milliseconds
-    private Scroller mScroller;
+    private OverScroller mScroller;
+    private boolean mInOverScrollMode = false;
+    private static Paint mOverScrollBackground;
 
     private boolean mWrapContent;
     private static final int MOTIONLESS_FALSE           = 0;
@@ -810,7 +817,7 @@
         mViewManager = new ViewManager(this);
         mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces);
         mDatabase = WebViewDatabase.getInstance(context);
-        mScroller = new Scroller(context);
+        mScroller = new OverScroller(context);
 
         mZoomButtonsController = new ZoomButtonsController(this);
         mZoomButtonsController.setOnZoomListener(mZoomListener);
@@ -1024,7 +1031,8 @@
      * Return the amount of the titlebarview (if any) that is visible
      */
     private int getVisibleTitleHeight() {
-        return Math.max(getTitleHeight() - mScrollY, 0);
+        // need to restrict mScrollY due to over scroll
+        return Math.max(getTitleHeight() - Math.max(0, mScrollY), 0);
     }
 
     /*
@@ -1867,11 +1875,13 @@
 
     // Expects x in view coordinates
     private int pinLocX(int x) {
+        if (mInOverScrollMode) return x;
         return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
     }
 
     // Expects y in view coordinates
     private int pinLocY(int y) {
+        if (mInOverScrollMode) return y;
         int titleH = getTitleHeight();
         // if the titlebar is still visible, just pin against 0
         if (y <= titleH) {
@@ -2269,6 +2279,24 @@
         scrollBar.draw(canvas);
     }
 
+    @Override
+    protected void onOverscrolled(int scrollX, int scrollY, boolean clampedX,
+            boolean clampedY) {
+        mInOverScrollMode = false;
+        int maxX = computeMaxScrollX();
+        if (Math.abs(mMinZoomScale - mMaxZoomScale) < 0.01f && maxX == 0) {
+            // do not over scroll x if the page can't be zoomed and it just fits
+            // the screen
+            scrollX = pinLocX(scrollX);
+        } else if (scrollX < 0 || scrollX > maxX) {
+            mInOverScrollMode = true;
+        }
+        if (scrollY < 0 || scrollY > computeMaxScrollY()) {
+            mInOverScrollMode = true;
+        }
+        super.scrollTo(scrollX, scrollY);
+    }
+
     /**
      * Get the url for the current page. This is not always the same as the url
      * passed to WebViewClient.onPageStarted because although the load for
@@ -2611,13 +2639,14 @@
         if (mScroller.computeScrollOffset()) {
             int oldX = mScrollX;
             int oldY = mScrollY;
-            mScrollX = mScroller.getCurrX();
-            mScrollY = mScroller.getCurrY();
+            int x = mScroller.getCurrX();
+            int y = mScroller.getCurrY();
             postInvalidate();  // So we draw again
-            if (oldX != mScrollX || oldY != mScrollY) {
-                // as onScrollChanged() is not called, sendOurVisibleRect()
-                // needs to be call explicitly
-                sendOurVisibleRect();
+            if (oldX != x || oldY != y) {
+                overscrollBy(x - oldX, y - oldY, oldX, oldY,
+                        computeMaxScrollX(), computeMaxScrollY(),
+                        getViewWidth() / 3, getViewHeight() / 3);
+                onScrollChanged(mScrollX, mScrollY, oldX, oldY);
             }
         } else {
             super.computeScroll();
@@ -3028,8 +3057,13 @@
     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
         if (child == mTitleBar) {
             // When drawing the title bar, move it horizontally to always show
-            // at the top of the WebView.
+            // at the top of the WebView. While overscroll, stick the title bar
+            // on the top otherwise we may have two during loading, one is drawn
+            // here, another is drawn by the Browser.
             mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft());
+            if (mScrollY <= 0) {
+                mTitleBar.offsetTopAndBottom(mScrollY - mTitleBar.getTop());
+            }
         }
         return super.drawChild(canvas, child, drawingTime);
     }
@@ -3057,6 +3091,29 @@
         }
 
         int saveCount = canvas.save();
+        if (mInOverScrollMode) {
+            if (mOverScrollBackground == null) {
+                mOverScrollBackground = new Paint();
+                Bitmap bm = BitmapFactory.decodeResource(
+                        mContext.getResources(),
+                        com.android.internal.R.drawable.pattern_underwear);
+                mOverScrollBackground.setShader(new BitmapShader(bm,
+                        Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
+            }
+            int top = getTitleHeight();
+            // first draw the background and anchor to the top of the view
+            canvas.save();
+            canvas.translate(mScrollX, mScrollY);
+            canvas.clipRect(-mScrollX, top - mScrollY,
+                    computeHorizontalScrollRange() - mScrollX, top
+                            + computeVerticalScrollRange() - mScrollY,
+                    Region.Op.DIFFERENCE);
+            canvas.drawPaint(mOverScrollBackground);
+            canvas.restore();
+            // next clip the region for the content
+            canvas.clipRect(0, top, computeHorizontalScrollRange(), top
+                    + computeVerticalScrollRange());
+        }
         if (mTitleBar != null) {
             canvas.translate(0, (int) mTitleBar.getHeight());
         }
@@ -3074,12 +3131,12 @@
         canvas.restoreToCount(saveCount);
 
         // Now draw the shadow.
-        if (mTitleBar != null) {
-            int y = mScrollY + getVisibleTitleHeight();
+        int titleH = getVisibleTitleHeight();
+        if (mTitleBar != null && titleH == 0) {
             int height = (int) (5f * getContext().getResources()
                     .getDisplayMetrics().density);
-            mTitleShadow.setBounds(mScrollX, y, mScrollX + getWidth(),
-                    y + height);
+            mTitleShadow.setBounds(mScrollX, mScrollY, mScrollX + getWidth(),
+                    mScrollY + height);
             mTitleShadow.draw(canvas);
         }
         if (AUTO_REDRAW_HACK && mAutoRedraw) {
@@ -4680,18 +4737,6 @@
                 }
 
                 // do pan
-                int newScrollX = pinLocX(mScrollX + deltaX);
-                int newDeltaX = newScrollX - mScrollX;
-                if (deltaX != newDeltaX) {
-                    deltaX = newDeltaX;
-                    fDeltaX = (float) newDeltaX;
-                }
-                int newScrollY = pinLocY(mScrollY + deltaY);
-                int newDeltaY = newScrollY - mScrollY;
-                if (deltaY != newDeltaY) {
-                    deltaY = newDeltaY;
-                    fDeltaY = (float) newDeltaY;
-                }
                 boolean done = false;
                 boolean keepScrollBarsVisible = false;
                 if (Math.abs(fDeltaX) < 1.0f && Math.abs(fDeltaY) < 1.0f) {
@@ -4736,7 +4781,9 @@
                         }
                     }
                     if ((deltaX | deltaY) != 0) {
-                        scrollBy(deltaX, deltaY);
+                        overscrollBy(deltaX, deltaY, mScrollX, mScrollY,
+                                computeMaxScrollX(), computeMaxScrollY(),
+                                getViewWidth() / 3, getViewHeight() / 3);
                         if (deltaX != 0) {
                             mLastTouchX = x;
                         }
@@ -4819,8 +4866,8 @@
                             Log.w(LOGTAG, "Miss a drag as we are waiting for" +
                                     " WebCore's response for touch down.");
                             if (mFullScreenHolder == null
-                                    && (computeHorizontalScrollExtent() < computeHorizontalScrollRange()
-                                    || computeVerticalScrollExtent() < computeVerticalScrollRange())) {
+                                    && (computeMaxScrollX() > 0
+                                            || computeMaxScrollY() > 0)) {
                                 // remove the pending TOUCH_EVENT and send a
                                 // cancel
                                 mWebViewCore
@@ -4866,6 +4913,12 @@
                             mVelocityTracker.addMovement(ev);
                             doFling();
                             break;
+                        } else {
+                            if (mScroller.springback(mScrollX, mScrollY, 0,
+                                    computeMaxScrollX(), 0,
+                                    computeMaxScrollY())) {
+                                invalidate();
+                            }
                         }
                         mLastVelocity = 0;
                         WebViewCore.resumePriority();
@@ -4886,6 +4939,12 @@
             }
             case MotionEvent.ACTION_CANCEL: {
                 cancelTouch();
+                if (mTouchMode == TOUCH_DRAG_MODE) {
+                    if (mScroller.springback(mScrollX, mScrollY, 0,
+                            computeMaxScrollX(), 0, computeMaxScrollY())) {
+                        invalidate();
+                    }
+                }
                 break;
             }
         }
@@ -5204,16 +5263,18 @@
         }
     }
 
+    private int computeMaxScrollX() {
+        return Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
+    }
+
     private int computeMaxScrollY() {
-        int maxContentH = computeVerticalScrollRange() + getTitleHeight();
-        return Math.max(maxContentH - getViewHeightWithTitle(), getTitleHeight());
+        return Math.max(computeVerticalScrollRange() + getTitleHeight()
+                - getViewHeightWithTitle(), getTitleHeight());
     }
 
     public void flingScroll(int vx, int vy) {
-        int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
-        int maxY = computeMaxScrollY();
-
-        mScroller.fling(mScrollX, mScrollY, vx, vy, 0, maxX, 0, maxY);
+        mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0,
+                computeMaxScrollY(), getViewWidth() / 3, getViewHeight() / 3);
         invalidate();
     }
 
@@ -5221,7 +5282,7 @@
         if (mVelocityTracker == null) {
             return;
         }
-        int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
+        int maxX = computeMaxScrollX();
         int maxY = computeMaxScrollY();
 
         mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
@@ -5243,6 +5304,10 @@
         }
         if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
             WebViewCore.resumePriority();
+            if (mScroller.springback(mScrollX, mScrollY, 0, computeMaxScrollX(),
+                    0, computeMaxScrollY())) {
+                invalidate();
+            }
             return;
         }
         float currentVelocity = mScroller.getCurrVelocity();
@@ -5270,7 +5335,8 @@
         mLastVelY = vy;
         mLastVelocity = (float) Math.hypot(vx, vy);
 
-        mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
+        mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY,
+                getViewWidth() / 3, getViewHeight() / 3);
         // TODO: duration is calculated based on velocity, if the range is
         // small, the animation will stop before duration is up. We may
         // want to calculate how long the animation is going to run to precisely
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a41d25b..f1e614b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -52,7 +52,13 @@
     <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_OK" />
     <protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
     <protected-broadcast android:name="android.intent.action.REBOOT" />
+    <protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
 
+    <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
+    <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
+    <protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" />
+    <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" />
+    
     <protected-broadcast android:name="android.backup.intent.RUN" />
     <protected-broadcast android:name="android.backup.intent.CLEAR" />
     <protected-broadcast android:name="android.backup.intent.INIT" />
diff --git a/core/res/res/drawable/pattern_underwear.png b/core/res/res/drawable/pattern_underwear.png
new file mode 100644
index 0000000..651212f
--- /dev/null
+++ b/core/res/res/drawable/pattern_underwear.png
Binary files differ
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index cbcef4e..0e796dc 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -946,7 +946,8 @@
         MASK_UI_MODE_TYPE = 0x0f,
         UI_MODE_TYPE_ANY = 0x00,
         UI_MODE_TYPE_NORMAL = 0x01,
-        UI_MODE_TYPE_CAR = 0x02,
+        UI_MODE_TYPE_DESK = 0x02,
+        UI_MODE_TYPE_CAR = 0x03,
 
         // uiMode bits for the night switch.
         MASK_UI_MODE_NIGHT = 0x30,
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 2269352..8089389 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -3247,7 +3247,10 @@
                             if (mBytesRead < 0) {
                                 LOGE("Error reading audio input");
                                 if (mActiveTrack->mState == TrackBase::ACTIVE) {
-                                    sleep(1);
+                                    // Force input into standby so that it tries to
+                                    // recover at next read attempt
+                                    mInput->standby();
+                                    usleep(5000);
                                 }
                                 mRsmpInIndex = mFrameCount;
                                 framesOut = 0;
@@ -3429,7 +3432,10 @@
         if (mBytesRead < 0) {
             LOGE("RecordThread::getNextBuffer() Error reading audio input");
             if (mActiveTrack->mState == TrackBase::ACTIVE) {
-                sleep(1);
+                // Force input into standby so that it tries to
+                // recover at next read attempt
+                mInput->standby();
+                usleep(5000);
             }
             buffer->raw = 0;
             buffer->frameCount = 0;
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index bce3371..ad037d6 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -552,13 +552,17 @@
 
         audioBuffer.frameCount = userSize/frameSize();
 
-        // Calling obtainBuffer() with a negative wait count causes
-        // an (almost) infinite wait time.
-        status_t err = obtainBuffer(&audioBuffer, -1);
+        // By using a wait count corresponding to twice the timeout period in
+        // obtainBuffer() we give a chance to recover once for a read timeout
+        // (if media_server crashed for instance) before returning a length of
+        // 0 bytes read to the client
+        status_t err = obtainBuffer(&audioBuffer, ((2 * MAX_RUN_TIMEOUT_MS) / WAIT_PERIOD_MS));
         if (err < 0) {
             // out of buffers, return #bytes written
             if (err == status_t(NO_MORE_BUFFERS))
                 break;
+            if (err == status_t(TIMED_OUT))
+                err = 0;
             return ssize_t(err);
         }
 
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index ab65b44..5090c39 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -297,13 +297,11 @@
         CHECK(meta->findCString(kKeyMIMEType, &mime));
 
         if (!haveVideo && !strncasecmp(mime, "video/", 6)) {
-            if (setVideoSource(extractor->getTrack(i)) == OK) {
-                haveVideo = true;
-            }
+            setVideoSource(extractor->getTrack(i));
+            haveVideo = true;
         } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {
-            if (setAudioSource(extractor->getTrack(i)) == OK) {
-                haveAudio = true;
-            }
+            setAudioSource(extractor->getTrack(i));
+            haveAudio = true;
         }
 
         if (haveAudio && haveVideo) {
@@ -331,6 +329,9 @@
     }
     mPrefetcher.clear();
 
+    mAudioTrack.clear();
+    mVideoTrack.clear();
+
     // Shutdown audio first, so that the respone to the reset request
     // appears to happen instantaneously as far as the user is concerned
     // If we did this later, audio would continue playing while we
@@ -699,32 +700,34 @@
     return OK;
 }
 
-status_t AwesomePlayer::setAudioSource(sp<MediaSource> source) {
-    if (source == NULL) {
-        return UNKNOWN_ERROR;
-    }
+void AwesomePlayer::setAudioSource(sp<MediaSource> source) {
+    CHECK(source != NULL);
 
     if (mPrefetcher != NULL) {
         source = mPrefetcher->addSource(source);
     }
 
-    sp<MetaData> meta = source->getFormat();
+    mAudioTrack = source;
+}
+
+status_t AwesomePlayer::initAudioDecoder() {
+    sp<MetaData> meta = mAudioTrack->getFormat();
 
     const char *mime;
     CHECK(meta->findCString(kKeyMIMEType, &mime));
 
     if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
-        mAudioSource = source;
+        mAudioSource = mAudioTrack;
     } else {
         mAudioSource = OMXCodec::Create(
-                mClient.interface(), source->getFormat(),
+                mClient.interface(), mAudioTrack->getFormat(),
                 false, // createEncoder
-                source);
+                mAudioTrack);
     }
 
     if (mAudioSource != NULL) {
         int64_t durationUs;
-        if (source->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+        if (mAudioTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
             if (mDurationUs < 0 || durationUs > mDurationUs) {
                 mDurationUs = durationUs;
             }
@@ -734,30 +737,32 @@
     return mAudioSource != NULL ? OK : UNKNOWN_ERROR;
 }
 
-status_t AwesomePlayer::setVideoSource(sp<MediaSource> source) {
-    if (source == NULL) {
-        return UNKNOWN_ERROR;
-    }
+void AwesomePlayer::setVideoSource(sp<MediaSource> source) {
+    CHECK(source != NULL);
 
     if (mPrefetcher != NULL) {
         source = mPrefetcher->addSource(source);
     }
 
+    mVideoTrack = source;
+}
+
+status_t AwesomePlayer::initVideoDecoder() {
     mVideoSource = OMXCodec::Create(
-            mClient.interface(), source->getFormat(),
+            mClient.interface(), mVideoTrack->getFormat(),
             false, // createEncoder
-            source);
+            mVideoTrack);
 
     if (mVideoSource != NULL) {
         int64_t durationUs;
-        if (source->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+        if (mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
             if (mDurationUs < 0 || durationUs > mDurationUs) {
                 mDurationUs = durationUs;
             }
         }
 
-        CHECK(source->getFormat()->findInt32(kKeyWidth, &mVideoWidth));
-        CHECK(source->getFormat()->findInt32(kKeyHeight, &mVideoHeight));
+        CHECK(mVideoTrack->getFormat()->findInt32(kKeyWidth, &mVideoWidth));
+        CHECK(mVideoTrack->getFormat()->findInt32(kKeyHeight, &mVideoHeight));
 
         mVideoSource->start();
     }
@@ -1045,6 +1050,19 @@
     return setDataSource_l(extractor);
 }
 
+void AwesomePlayer::abortPrepare(status_t err) {
+    CHECK(err != OK);
+
+    if (mIsAsyncPrepare) {
+        notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+    }
+
+    mPrepareResult = err;
+    mFlags &= ~PREPARING;
+    mAsyncPrepareEvent = NULL;
+    mPreparedCondition.broadcast();
+}
+
 void AwesomePlayer::onPrepareAsyncEvent() {
     {
         Mutex::Autolock autoLock(mLock);
@@ -1053,15 +1071,7 @@
             status_t err = finishSetDataSource_l();
 
             if (err != OK) {
-                if (mIsAsyncPrepare) {
-                    notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
-                }
-
-                mPrepareResult = err;
-                mFlags &= ~PREPARING;
-                mAsyncPrepareEvent = NULL;
-                mPreparedCondition.broadcast();
-
+                abortPrepare(err);
                 return;
             }
         }
@@ -1081,6 +1091,24 @@
 
     Mutex::Autolock autoLock(mLock);
 
+    if (mVideoTrack != NULL && mVideoSource == NULL) {
+        status_t err = initVideoDecoder();
+
+        if (err != OK) {
+            abortPrepare(err);
+            return;
+        }
+    }
+
+    if (mAudioTrack != NULL && mAudioSource == NULL) {
+        status_t err = initAudioDecoder();
+
+        if (err != OK) {
+            abortPrepare(err);
+            return;
+        }
+    }
+
     if (mIsAsyncPrepare) {
         if (mVideoWidth < 0 || mVideoHeight < 0) {
             notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 3590987..7106524 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -112,10 +112,12 @@
 
     sp<DataSource> mFileSource;
 
+    sp<MediaSource> mVideoTrack;
     sp<MediaSource> mVideoSource;
     sp<AwesomeRenderer> mVideoRenderer;
     bool mVideoRendererIsPreview;
 
+    sp<MediaSource> mAudioTrack;
     sp<MediaSource> mAudioSource;
     AudioPlayer *mAudioPlayer;
     int64_t mDurationUs;
@@ -199,8 +201,11 @@
 
     void cancelPlayerEvents(bool keepBufferingGoing = false);
 
-    status_t setAudioSource(sp<MediaSource> source);
-    status_t setVideoSource(sp<MediaSource> source);
+    void setAudioSource(sp<MediaSource> source);
+    status_t initAudioDecoder();
+
+    void setVideoSource(sp<MediaSource> source);
+    status_t initVideoDecoder();
 
     void onStreamDone();
 
@@ -210,6 +215,8 @@
     void onBufferingUpdate();
     void onCheckAudioStatus();
     void onPrepareAsyncEvent();
+    void abortPrepare(status_t err);
+
     status_t finishSetDataSource_l();
 
     AwesomePlayer(const AwesomePlayer &);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index db802d3..4f1146b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -30,9 +30,11 @@
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.media.RingtoneManager;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemProperties;
 import android.provider.DrmStore;
@@ -48,6 +50,8 @@
     private static final String TABLE_FAVORITES = "favorites";
     private static final String TABLE_OLD_FAVORITES = "old_favorites";
 
+    private static final String[] COLUMN_VALUE = new String[] { "value" };
+
     protected DatabaseHelper mOpenHelper;
     private BackupManager mBackupManager;
 
@@ -220,6 +224,44 @@
         }
     }
 
+    /**
+     * Fast path that avoids the use of chatty remoted Cursors.
+     */
+    @Override
+    public Bundle call(String method, String request, Bundle args) {
+        if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) {
+            return lookupValue("system", request);
+        }
+
+        if (Settings.CALL_METHOD_GET_SECURE.equals(method)) {
+            return lookupValue("secure", request);
+        }
+        return null;
+    }
+
+    // Looks up value 'key' in 'table' and returns either a single-pair Bundle,
+    // possibly with a null value, or null on failure.
+    private Bundle lookupValue(String table, String key) {
+        // TODO: avoid database lookup and serve from in-process cache.
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        Cursor cursor = null;
+        try {
+            cursor = db.query(table, COLUMN_VALUE, "name=?", new String[]{key},
+                              null, null, null, null);
+            if (cursor != null && cursor.getCount() == 1) {
+                cursor.moveToFirst();
+                String value = cursor.getString(0);
+                return Bundle.forPair("value", value);
+            }
+        } catch (SQLiteException e) {
+            Log.w(TAG, "settings lookup error", e);
+            return null;
+        } finally {
+            if (cursor != null) cursor.close();
+        }
+        return Bundle.forPair("value", null);
+    }
+
     @Override
     public Cursor query(Uri url, String[] select, String where, String[] whereArgs, String sort) {
         SqlArguments args = new SqlArguments(url, where, whereArgs);
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index 28236f0..bee8872 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -16,53 +16,24 @@
 
 package com.android.server;
 
-import android.app.Activity;
-import android.app.ActivityManagerNative;
-import android.app.AlarmManager;
-import android.app.IActivityManager;
-import android.app.IUiModeManager;
-import android.app.KeyguardManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.StatusBarManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.location.Criteria;
-import android.location.Location;
-import android.location.LocationListener;
-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;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UEventObserver;
 import android.provider.Settings;
 import android.server.BluetoothService;
-import android.text.format.DateUtils;
-import android.text.format.Time;
 import android.util.Log;
 import android.util.Slog;
 
-import com.android.internal.R;
-import com.android.internal.app.DisableCarModeActivity;
-import com.android.internal.widget.LockPatternUtils;
-
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 
@@ -76,187 +47,22 @@
     private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
     private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
 
-    private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL";
-
     private static final int MSG_DOCK_STATE = 0;
-    private static final int MSG_UPDATE_TWILIGHT = 1;
-    private static final int MSG_ENABLE_LOCATION_UPDATES = 2;
-
-    public static final int MODE_NIGHT_AUTO = Configuration.UI_MODE_NIGHT_MASK >> 4;
-    public static final int MODE_NIGHT_NO = Configuration.UI_MODE_NIGHT_NO >> 4;
-    public static final int MODE_NIGHT_YES = Configuration.UI_MODE_NIGHT_YES >> 4;
-
-    private static final long LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
-    private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
-    private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
-    private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 5 * DateUtils.MINUTE_IN_MILLIS;
-    private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
-
-    private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE";
 
     private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
     private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
 
-    private int mNightMode = MODE_NIGHT_NO;
-    private boolean mCarModeEnabled = false;
-
     private boolean mSystemReady;
 
     private final Context mContext;
 
     private PowerManagerService mPowerManager;
-    private NotificationManager mNotificationManager;
-
-    private KeyguardManager.KeyguardLock mKeyguardLock;
-    private boolean mKeyguardDisabled;
-    private LockPatternUtils mLockPatternUtils;
-
-    private AlarmManager mAlarmManager;
-
-    private LocationManager mLocationManager;
-    private Location mLocation;
-    private StatusBarManager mStatusBarManager;
-
-    // The broadcast receiver which receives the result of the ordered broadcast sent when
-    // the dock state changes. The original ordered broadcast is sent with an initial result
-    // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g.,
-    // to RESULT_CANCELED, then the intent to start a dock app will not be sent.
-    private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (getResultCode() != Activity.RESULT_OK) {
-                return;
-            }
-
-            // Launch a dock activity
-            String category;
-            if (mCarModeEnabled) {
-                // Only launch car home when car mode is enabled.
-                category = Intent.CATEGORY_CAR_DOCK;
-            } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
-                category = Intent.CATEGORY_DESK_DOCK;
-            } else {
-                category = null;
-            }
-            if (category != null) {
-                intent = new Intent(Intent.ACTION_MAIN);
-                intent.addCategory(category);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                try {
-                    mContext.startActivity(intent);
-                } catch (ActivityNotFoundException e) {
-                    Slog.w(TAG, e.getCause());
-                }
-            }
-        }
-    };
-
-    private final BroadcastReceiver mTwilightUpdateReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (mCarModeEnabled && mNightMode == MODE_NIGHT_AUTO) {
-                mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
-            }
-        }
-    };
-
-    // A LocationListener to initialize the network location provider. The location updates
-    // are handled through the passive location provider.
-    private final LocationListener mEmptyLocationListener =  new LocationListener() {
-        public void onLocationChanged(Location location) {
-        }
-
-        public void onProviderDisabled(String provider) {
-        }
-
-        public void onProviderEnabled(String provider) {
-        }
-
-        public void onStatusChanged(String provider, int status, Bundle extras) {
-        }
-    };
-
-    private final LocationListener mLocationListener = new LocationListener() {
-
-        public void onLocationChanged(Location location) {
-            final boolean hasMoved = hasMoved(location);
-            if (hasMoved || hasBetterAccuracy(location)) {
-                synchronized (this) {
-                    mLocation = location;
-                }
-                if (hasMoved && mCarModeEnabled && mNightMode == MODE_NIGHT_AUTO) {
-                    mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
-                }
-            }
-        }
-
-        public void onProviderDisabled(String provider) {
-        }
-
-        public void onProviderEnabled(String provider) {
-        }
-
-        public void onStatusChanged(String provider, int status, Bundle extras) {
-        }
-
-        private boolean hasBetterAccuracy(Location location) {
-            if (location == null) {
-                return false;
-            }
-            if (mLocation == null) {
-                return true;
-            }
-            return location.getAccuracy() < mLocation.getAccuracy();
-        }
-
-        /*
-         * The user has moved if the accuracy circles of the two locations
-         * don't overlap.
-         */
-        private boolean hasMoved(Location location) {
-            if (location == null) {
-                return false;
-            }
-            if (mLocation == null) {
-                return true;
-            }
-
-            /* if new location is older than the current one, the devices hasn't
-             * moved.
-             */
-            if (location.getTime() < mLocation.getTime()) {
-                return false;
-            }
-
-            /* Get the distance between the two points */
-            float distance = mLocation.distanceTo(location);
-
-            /* Get the total accuracy radius for both locations */
-            float totalAccuracy = mLocation.getAccuracy() + location.getAccuracy();
-
-            /* If the distance is greater than the combined accuracy of the two
-             * points then they can't overlap and hence the user has moved.
-             */
-            return distance >= totalAccuracy;
-        }
-    };
 
     public DockObserver(Context context, PowerManagerService pm) {
         mContext = context;
         mPowerManager = pm;
-        mLockPatternUtils = new LockPatternUtils(context);
         init();  // set initial status
 
-        ServiceManager.addService("uimode", mBinder);
-
-        mAlarmManager =
-            (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
-        mLocationManager =
-            (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
-        mContext.registerReceiver(mTwilightUpdateReceiver,
-                new IntentFilter(ACTION_UPDATE_NIGHT_MODE));
-
         startObserving(DOCK_UEVENT_MATCH);
     }
 
@@ -272,14 +78,6 @@
                 if (newState != mDockState) {
                     mPreviousDockState = mDockState;
                     mDockState = newState;
-                    boolean carModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
-                    if (mCarModeEnabled != carModeEnabled) {
-                        try {
-                            setCarMode(carModeEnabled);
-                        } catch (RemoteException e1) {
-                            Slog.w(TAG, "Unable to change car mode.", e1);
-                        }
-                    }
                     if (mSystemReady) {
                         // Don't force screen on when undocking from the desk dock.
                         // The change in power state will do this anyway.
@@ -315,24 +113,11 @@
 
     void systemReady() {
         synchronized (this) {
-            KeyguardManager keyguardManager =
-                    (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
-            mKeyguardLock = keyguardManager.newKeyguardLock(TAG);
-
-            final boolean enableCarMode = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
-            if (enableCarMode) {
-                try {
-                    setCarMode(enableCarMode);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Unable to change car mode.", e);
-                }
-            }
             // don't bother broadcasting undocked here
             if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
                 update();
             }
             mSystemReady = true;
-            mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
         }
     }
 
@@ -341,10 +126,6 @@
     }
 
     private final Handler mHandler = new Handler() {
-
-        boolean mPassiveListenerEnabled;
-        boolean mNetworkListenerEnabled;
-
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -362,17 +143,7 @@
                         // Pack up the values and broadcast them to everyone
                         Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
                         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-                        if (mCarModeEnabled && mDockState != Intent.EXTRA_DOCK_STATE_CAR) {
-                            // Pretend to be in DOCK_STATE_CAR.
-                            intent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_CAR);
-                        } else if (!mCarModeEnabled && mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
-                            // Pretend to be in DOCK_STATE_UNDOCKED.
-                            intent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
-                        } else {
-                            intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
-                        }
-                        intent.putExtra(Intent.EXTRA_PHYSICAL_DOCK_STATE, mDockState);
-                        intent.putExtra(Intent.EXTRA_CAR_MODE_ENABLED, mCarModeEnabled);
+                        intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
 
                         // Check if this is Bluetooth Dock
                         String address = BluetoothService.readDockBluetoothAddress();
@@ -415,296 +186,10 @@
                             }
                         }
 
-                        // Send the ordered broadcast; the result receiver will receive after all
-                        // broadcasts have been sent. If any broadcast receiver changes the result
-                        // code from the initial value of RESULT_OK, then the result receiver will
-                        // not launch the corresponding dock application. This gives apps a chance
-                        // to override the behavior and stay in their app even when the device is
-                        // placed into a dock.
-                        mContext.sendStickyOrderedBroadcast(
-                                intent, mResultReceiver, null, Activity.RESULT_OK, null, null);
-
-                    }
-                    break;
-                case MSG_UPDATE_TWILIGHT:
-                    synchronized (this) {
-                        if (mCarModeEnabled && mLocation != null && mNightMode == MODE_NIGHT_AUTO) {
-                            try {
-                                DockObserver.this.updateTwilight();
-                            } catch (RemoteException e) {
-                                Slog.w(TAG, "Unable to change night mode.", e);
-                            }
-                        }
-                    }
-                    break;
-                case MSG_ENABLE_LOCATION_UPDATES:
-                   // enable passive provider to receive updates from location fixes (gps
-                   // and network).
-                   boolean passiveLocationEnabled;
-                    try {
-                        passiveLocationEnabled =
-                            mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
-                    } catch (Exception e) {
-                        // we may get IllegalArgumentException if passive location provider
-                        // does not exist or is not yet installed.
-                        passiveLocationEnabled = false;
-                    }
-                    if (!mPassiveListenerEnabled && passiveLocationEnabled) {
-                        mPassiveListenerEnabled = true;
-                        mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
-                                0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
-                    }
-                    // enable network provider to receive at least location updates for a given
-                    // distance.
-                    boolean networkLocationEnabled;
-                    try {
-                        networkLocationEnabled =
-                            mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
-                    } catch (Exception e) {
-                        // we may get IllegalArgumentException if network location provider
-                        // does not exist or is not yet installed.
-                        networkLocationEnabled = false;
-                    }
-                    if (!mNetworkListenerEnabled && networkLocationEnabled) {
-                        mNetworkListenerEnabled = true;
-                        mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
-                                LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
-
-                        retrieveLocation();
-                        if (mCarModeEnabled && mLocation != null && mNightMode == MODE_NIGHT_AUTO) {
-                            try {
-                                DockObserver.this.updateTwilight();
-                            } catch (RemoteException e) {
-                                Slog.w(TAG, "Unable to change night mode.", e);
-                            }
-                        }
-                    }
-                    if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
-                        long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL);
-                        interval *= 1.5;
-                        if (interval == 0) {
-                            interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
-                        } else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
-                            interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
-                        }
-                        Bundle bundle = new Bundle();
-                        bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval);
-                        Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES);
-                        newMsg.setData(bundle);
-                        mHandler.sendMessageDelayed(newMsg, interval);
+                        mContext.sendStickyBroadcast(intent);
                     }
                     break;
             }
         }
-
-        private void retrieveLocation() {
-            final Location gpsLocation =
-                mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
-            Location location;
-            Criteria criteria = new Criteria();
-            criteria.setSpeedRequired(false);
-            criteria.setAltitudeRequired(false);
-            criteria.setBearingRequired(false);
-            criteria.setAccuracy(Criteria.ACCURACY_FINE);
-            final String bestProvider = mLocationManager.getBestProvider(criteria, true);
-            location = mLocationManager.getLastKnownLocation(bestProvider);
-            // In the case there is no location available (e.g. GPS fix or network location
-            // is not available yet), the longitude of the location is estimated using the timezone,
-            // latitude and accuracy are set to get a good average.
-            if (location == null) {
-                Time currentTime = new Time();
-                currentTime.set(System.currentTimeMillis());
-                double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
-                        (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
-                location = new Location("fake");
-                location.setLongitude(lngOffset);
-                location.setLatitude(0);
-                location.setAccuracy(417000.0f);
-                location.setTime(System.currentTimeMillis());
-            }
-            synchronized (this) {
-                mLocation = location;
-            }
-        }
-    };
-
-    private void adjustStatusBarCarMode() {
-        if (mStatusBarManager == null) {
-            mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);
-        }
-
-        // Fear not: StatusBarService manages a list of requests to disable
-        // features of the status bar; these are ORed together to form the
-        // active disabled list. So if (for example) the device is locked and
-        // the status bar should be totally disabled, the calls below will
-        // have no effect until the device is unlocked.
-        if (mStatusBarManager != null) {
-            long ident = Binder.clearCallingIdentity();
-            mStatusBarManager.disable(mCarModeEnabled
-                ? StatusBarManager.DISABLE_NOTIFICATION_TICKER
-                : StatusBarManager.DISABLE_NONE);
-            Binder.restoreCallingIdentity(ident);
-        }
-
-        if (mNotificationManager == null) {
-            mNotificationManager = (NotificationManager)
-                    mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        }
-
-        if (mNotificationManager != null) {
-            long ident = Binder.clearCallingIdentity();
-            if (mCarModeEnabled) {
-                Intent carModeOffIntent = new Intent(mContext, DisableCarModeActivity.class);
-
-                Notification n = new Notification();
-                n.icon = R.drawable.stat_notify_car_mode;
-                n.defaults = Notification.DEFAULT_LIGHTS;
-                n.flags = Notification.FLAG_ONGOING_EVENT;
-                n.when = 0;
-                n.setLatestEventInfo(
-                        mContext,
-                        mContext.getString(R.string.car_mode_disable_notification_title),
-                        mContext.getString(R.string.car_mode_disable_notification_message),
-                        PendingIntent.getActivity(mContext, 0, carModeOffIntent, 0));
-                mNotificationManager.notify(0, n);
-            } else {
-                mNotificationManager.cancel(0);
-            }
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    private void setCarMode(boolean enabled) throws RemoteException {
-        mCarModeEnabled = enabled;
-        if (enabled) {
-            if (mNightMode == MODE_NIGHT_AUTO) {
-                updateTwilight();
-            } else {
-                setMode(Configuration.UI_MODE_TYPE_CAR, mNightMode << 4);
-            }
-        } else {
-            // Disabling the car mode clears the night mode.
-            setMode(Configuration.UI_MODE_TYPE_NORMAL,
-                    Configuration.UI_MODE_NIGHT_UNDEFINED);
-        }
-        adjustStatusBarCarMode();
-    }
-
-    private void setMode(int modeType, int modeNight) throws RemoteException {
-        long ident = Binder.clearCallingIdentity();
-        final IActivityManager am = ActivityManagerNative.getDefault();
-        Configuration config = am.getConfiguration();
-        if (config.uiMode != (modeType | modeNight)) {
-            config.uiMode = modeType | modeNight;
-            am.updateConfiguration(config);
-        }
-        Binder.restoreCallingIdentity(ident);
-    }
-
-    private void setNightMode(int mode) throws RemoteException {
-        if (mNightMode != mode) {
-            mNightMode = mode;
-            switch (mode) {
-                case MODE_NIGHT_NO:
-                case MODE_NIGHT_YES:
-                    setMode(Configuration.UI_MODE_TYPE_CAR, mode << 4);
-                    break;
-                case MODE_NIGHT_AUTO:
-                    long ident = Binder.clearCallingIdentity();
-                    updateTwilight();
-                    Binder.restoreCallingIdentity(ident);
-                    break;
-                default:
-                    setMode(Configuration.UI_MODE_TYPE_CAR, MODE_NIGHT_NO << 4);
-                    break;
-            }
-        }
-    }
-
-    private void updateTwilight() throws RemoteException {
-        synchronized (this) {
-            if (mLocation == null) {
-                return;
-            }
-            final long currentTime = System.currentTimeMillis();
-            int nightMode;
-            // calculate current twilight
-            TwilightCalculator tw = new TwilightCalculator();
-            tw.calculateTwilight(currentTime,
-                    mLocation.getLatitude(), mLocation.getLongitude());
-            if (tw.mState == TwilightCalculator.DAY) {
-                nightMode = MODE_NIGHT_NO;
-            } else {
-                nightMode =  MODE_NIGHT_YES;
-            }
-
-            // schedule next update
-            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 {
-                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);
-            PendingIntent pendingIntent =
-                    PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
-            mAlarmManager.cancel(pendingIntent);
-            mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
-
-            // 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);
-            }
-        }
-    }
-
-    /**
-     * Wrapper class implementing the IUiModeManager interface.
-     */
-    private final IUiModeManager.Stub mBinder = new IUiModeManager.Stub() {
-
-        public void disableCarMode() throws RemoteException {
-            if (mCarModeEnabled) {
-                setCarMode(false);
-                update();
-            }
-        }
-
-        public void enableCarMode() throws RemoteException {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.ENABLE_CAR_MODE,
-                    "Need ENABLE_CAR_MODE permission");
-            if (!mCarModeEnabled) {
-                setCarMode(true);
-                update();
-            }
-        }
-
-        public void setNightMode(int mode) throws RemoteException {
-            if (mCarModeEnabled) {
-                DockObserver.this.setNightMode(mode);
-            }
-        }
-
-        public int getNightMode() throws RemoteException {
-            return mNightMode;
-        }
     };
 }
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 9e11546..6ce2582 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -2884,17 +2884,19 @@
                 int i;
                 for (i=0; i<N; i++) {
                     PackageParser.Provider p = pkg.providers.get(i);
-                    String names[] = p.info.authority.split(";");
-                    for (int j = 0; j < names.length; j++) {
-                        if (mProviders.containsKey(names[j])) {
-                            PackageParser.Provider other = mProviders.get(names[j]);
-                            Log.w(TAG, "Can't install because provider name " + names[j] +
-                                    " (in package " + pkg.applicationInfo.packageName +
-                                    ") is already used by "
-                                    + ((other != null && other.getComponentName() != null)
-                                            ? other.getComponentName().getPackageName() : "?"));
-                            mLastScanError = PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
-                            return null;
+                    if (p.info.authority != null) {
+                        String names[] = p.info.authority.split(";");
+                        for (int j = 0; j < names.length; j++) {
+                            if (mProviders.containsKey(names[j])) {
+                                PackageParser.Provider other = mProviders.get(names[j]);
+                                Log.w(TAG, "Can't install because provider name " + names[j] +
+                                        " (in package " + pkg.applicationInfo.packageName +
+                                        ") is already used by "
+                                        + ((other != null && other.getComponentName() != null)
+                                                ? other.getComponentName().getPackageName() : "?"));
+                                mLastScanError = PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
+                                return null;
+                            }
                         }
                     }
                 }
@@ -3103,38 +3105,40 @@
                 mProvidersByComponent.put(new ComponentName(p.info.packageName,
                         p.info.name), p);
                 p.syncable = p.info.isSyncable;
-                String names[] = p.info.authority.split(";");
-                p.info.authority = null;
-                for (int j = 0; j < names.length; j++) {
-                    if (j == 1 && p.syncable) {
-                        // We only want the first authority for a provider to possibly be
-                        // syncable, so if we already added this provider using a different
-                        // authority clear the syncable flag. We copy the provider before
-                        // changing it because the mProviders object contains a reference
-                        // to a provider that we don't want to change.
-                        // Only do this for the second authority since the resulting provider
-                        // object can be the same for all future authorities for this provider.
-                        p = new PackageParser.Provider(p);
-                        p.syncable = false;
-                    }
-                    if (!mProviders.containsKey(names[j])) {
-                        mProviders.put(names[j], p);
-                        if (p.info.authority == null) {
-                            p.info.authority = names[j];
-                        } else {
-                            p.info.authority = p.info.authority + ";" + names[j];
+                if (p.info.authority != null) {
+                    String names[] = p.info.authority.split(";");
+                    p.info.authority = null;
+                    for (int j = 0; j < names.length; j++) {
+                        if (j == 1 && p.syncable) {
+                            // We only want the first authority for a provider to possibly be
+                            // syncable, so if we already added this provider using a different
+                            // authority clear the syncable flag. We copy the provider before
+                            // changing it because the mProviders object contains a reference
+                            // to a provider that we don't want to change.
+                            // Only do this for the second authority since the resulting provider
+                            // object can be the same for all future authorities for this provider.
+                            p = new PackageParser.Provider(p);
+                            p.syncable = false;
                         }
-                        if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD)
-                            Log.d(TAG, "Registered content provider: " + names[j] +
-                            ", className = " + p.info.name +
-                            ", isSyncable = " + p.info.isSyncable);
-                    } else {
-                        PackageParser.Provider other = mProviders.get(names[j]);
-                        Log.w(TAG, "Skipping provider name " + names[j] +
-                              " (in package " + pkg.applicationInfo.packageName +
-                              "): name already used by "
-                              + ((other != null && other.getComponentName() != null)
-                                      ? other.getComponentName().getPackageName() : "?"));
+                        if (!mProviders.containsKey(names[j])) {
+                            mProviders.put(names[j], p);
+                            if (p.info.authority == null) {
+                                p.info.authority = names[j];
+                            } else {
+                                p.info.authority = p.info.authority + ";" + names[j];
+                            }
+                            if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGD)
+                                Log.d(TAG, "Registered content provider: " + names[j] +
+                                        ", className = " + p.info.name +
+                                        ", isSyncable = " + p.info.isSyncable);
+                        } else {
+                            PackageParser.Provider other = mProviders.get(names[j]);
+                            Log.w(TAG, "Skipping provider name " + names[j] +
+                                    " (in package " + pkg.applicationInfo.packageName +
+                                    "): name already used by "
+                                    + ((other != null && other.getComponentName() != null)
+                                            ? other.getComponentName().getPackageName() : "?"));
+                        }
                     }
                 }
                 if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index deee7f3..b023958 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;
+        UiModeManagerService uiMode = null;
         RecognitionManagerService recognition = null;
 
         // Critical services...
@@ -363,6 +364,14 @@
             }
 
             try {
+                Slog.i(TAG, "UI Mode Manager Service");
+                // Listen for dock station changes
+                uiMode = new UiModeManagerService(context);
+            } catch (Throwable e) {
+                Slog.e(TAG, "Failure starting UiModeManagerService", e);
+            }
+
+            try {
                 Slog.i(TAG, "Backup Service");
                 ServiceManager.addService(Context.BACKUP_SERVICE,
                         new BackupManagerService(context));
@@ -441,6 +450,7 @@
         final BatteryService batteryF = battery;
         final ConnectivityService connectivityF = connectivity;
         final DockObserver dockF = dock;
+        final UiModeManagerService uiModeF = uiMode;
         final AppWidgetService appWidgetF = appWidget;
         final WallpaperManagerService wallpaperF = wallpaper;
         final InputMethodManagerService immF = imm;
@@ -460,6 +470,7 @@
                 if (batteryF != null) batteryF.systemReady();
                 if (connectivityF != null) connectivityF.systemReady();
                 if (dockF != null) dockF.systemReady();
+                if (uiModeF != null) uiModeF.systemReady();
                 if (recognitionF != null) recognitionF.systemReady();
                 Watchdog.getInstance().start();
 
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
new file mode 100644
index 0000000..71826ff
--- /dev/null
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -0,0 +1,692 @@
+/*
+ * 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.server;
+
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.app.AlarmManager;
+import android.app.IActivityManager;
+import android.app.KeyguardManager;
+import android.app.IUiModeManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.StatusBarManager;
+import android.app.UiModeManager;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.BatteryManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.format.DateUtils;
+import android.text.format.Time;
+import android.util.Slog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import com.android.internal.R;
+import com.android.internal.app.DisableCarModeActivity;
+
+class UiModeManagerService extends IUiModeManager.Stub {
+    private static final String TAG = UiModeManager.class.getSimpleName();
+    private static final boolean LOG = false;
+
+    private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL";
+
+    private static final int MSG_UPDATE_TWILIGHT = 0;
+    private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
+
+    private static final long LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
+    private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
+    private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
+    private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 5 * DateUtils.MINUTE_IN_MILLIS;
+    private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
+
+    private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE";
+
+    private final Context mContext;
+
+    final Object mLock = new Object();
+    
+    private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+    private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+    
+    private int mNightMode = UiModeManager.MODE_NIGHT_NO;
+    private boolean mCarModeEnabled = false;
+    private boolean mCharging = false;
+    private final boolean mCarModeKeepsScreenOn;
+    private final boolean mDeskModeKeepsScreenOn;
+
+    private boolean mComputedNightMode;
+    private int mCurUiMode = 0;
+    
+    private Configuration mConfiguration = new Configuration();
+    
+    private boolean mSystemReady;
+
+    private NotificationManager mNotificationManager;
+
+    private AlarmManager mAlarmManager;
+
+    private LocationManager mLocationManager;
+    private Location mLocation;
+    private StatusBarManager mStatusBarManager;
+    private KeyguardManager.KeyguardLock mKeyguardLock;
+    private final PowerManager.WakeLock mWakeLock;
+
+    // The broadcast receiver which receives the result of the ordered broadcast sent when
+    // the dock state changes. The original ordered broadcast is sent with an initial result
+    // code of RESULT_OK. If any of the registered broadcast receivers changes this value, e.g.,
+    // to RESULT_CANCELED, then the intent to start a dock app will not be sent.
+    private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (getResultCode() != Activity.RESULT_OK) {
+                return;
+            }
+
+            // Launch a dock activity
+            String category;
+            if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
+                // Only launch car home when car mode is enabled.
+                category = Intent.CATEGORY_CAR_DOCK;
+            } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(intent.getAction())) {
+                category = Intent.CATEGORY_DESK_DOCK;
+            } else {
+                category = null;
+            }
+            if (category != null) {
+                intent = new Intent(Intent.ACTION_MAIN);
+                intent.addCategory(category);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                try {
+                    mContext.startActivity(intent);
+                } catch (ActivityNotFoundException e) {
+                    Slog.w(TAG, e.getCause());
+                }
+            }
+        }
+    };
+
+    private final BroadcastReceiver mTwilightUpdateReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+                mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
+            }
+        }
+    };
+
+    private final BroadcastReceiver mDockModeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+                    Intent.EXTRA_DOCK_STATE_UNDOCKED);
+            updateDockState(state);
+        }
+    };
+
+    private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mCharging = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
+            synchronized (mLock) {
+                if (mSystemReady) {
+                    updateLocked();
+                }
+            }
+        }
+    };
+
+    // A LocationListener to initialize the network location provider. The location updates
+    // are handled through the passive location provider.
+    private final LocationListener mEmptyLocationListener =  new LocationListener() {
+        public void onLocationChanged(Location location) {
+        }
+
+        public void onProviderDisabled(String provider) {
+        }
+
+        public void onProviderEnabled(String provider) {
+        }
+
+        public void onStatusChanged(String provider, int status, Bundle extras) {
+        }
+    };
+
+    private final LocationListener mLocationListener = new LocationListener() {
+
+        public void onLocationChanged(Location location) {
+            final boolean hasMoved = hasMoved(location);
+            final boolean hasBetterAccuracy = mLocation == null
+                    || location.getAccuracy() < mLocation.getAccuracy();
+            if (hasMoved || hasBetterAccuracy) {
+                synchronized (mLock) {
+                    mLocation = location;
+                    if (hasMoved && isDoingNightMode()
+                            && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+                        mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT);
+                    }
+                }
+            }
+        }
+
+        public void onProviderDisabled(String provider) {
+        }
+
+        public void onProviderEnabled(String provider) {
+        }
+
+        public void onStatusChanged(String provider, int status, Bundle extras) {
+        }
+
+        /*
+         * The user has moved if the accuracy circles of the two locations
+         * don't overlap.
+         */
+        private boolean hasMoved(Location location) {
+            if (location == null) {
+                return false;
+            }
+            if (mLocation == null) {
+                return true;
+            }
+
+            /* if new location is older than the current one, the devices hasn't
+             * moved.
+             */
+            if (location.getTime() < mLocation.getTime()) {
+                return false;
+            }
+
+            /* Get the distance between the two points */
+            float distance = mLocation.distanceTo(location);
+
+            /* Get the total accuracy radius for both locations */
+            float totalAccuracy = mLocation.getAccuracy() + location.getAccuracy();
+
+            /* If the distance is greater than the combined accuracy of the two
+             * points then they can't overlap and hence the user has moved.
+             */
+            return distance >= totalAccuracy;
+        }
+    };
+
+    public UiModeManagerService(Context context) {
+        mContext = context;
+
+        ServiceManager.addService(Context.UI_MODE_SERVICE, this);
+        
+        mAlarmManager =
+            (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+        mLocationManager =
+            (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
+        mContext.registerReceiver(mTwilightUpdateReceiver,
+                new IntentFilter(ACTION_UPDATE_NIGHT_MODE));
+        mContext.registerReceiver(mDockModeReceiver,
+                new IntentFilter(Intent.ACTION_DOCK_EVENT));
+        mContext.registerReceiver(mBatteryReceiver,
+                new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+
+        PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
+
+        mConfiguration.setToDefaults();
+
+        mCarModeKeepsScreenOn = (context.getResources().getInteger(
+                com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
+        mDeskModeKeepsScreenOn = (context.getResources().getInteger(
+                com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1);
+    }
+
+    public void disableCarMode() {
+        synchronized (mLock) {
+            setCarModeLocked(false);
+            if (mSystemReady) {
+                updateLocked();
+            }
+        }
+    }
+
+    public void enableCarMode() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.ENABLE_CAR_MODE,
+                "Need ENABLE_CAR_MODE permission");
+        synchronized (mLock) {
+            setCarModeLocked(true);
+            if (mSystemReady) {
+                updateLocked();
+            }
+        }
+    }
+
+    public int getCurrentModeType() {
+        synchronized (mLock) {
+            return mCurUiMode & Configuration.UI_MODE_TYPE_MASK;
+        }
+    }
+    
+    public void setNightMode(int mode) throws RemoteException {
+        synchronized (mLock) {
+            switch (mode) {
+                case UiModeManager.MODE_NIGHT_NO:
+                case UiModeManager.MODE_NIGHT_YES:
+                case UiModeManager.MODE_NIGHT_AUTO:
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown mode: " + mode);
+            }
+            if (!isDoingNightMode()) {
+                return;
+            }
+            
+            if (mNightMode != mode) {
+                mNightMode = mode;
+                updateLocked();
+            }
+        }
+    }
+    
+    public int getNightMode() throws RemoteException {
+        return mNightMode;
+    }
+    
+    void systemReady() {
+        synchronized (mLock) {
+            mSystemReady = true;
+            mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
+            updateLocked();
+            mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
+        }
+    }
+
+    boolean isDoingNightMode() {
+        return mCarModeEnabled || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+    }
+    
+    void setCarModeLocked(boolean enabled) {
+        if (mCarModeEnabled != enabled) {
+            mCarModeEnabled = enabled;
+
+            // Disable keyguard when in car mode
+            if (mKeyguardLock == null) {
+                KeyguardManager km =
+                        (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
+                if (km != null) {
+                    mKeyguardLock = km.newKeyguardLock(TAG);
+                }
+            }
+            if (mKeyguardLock != null) {
+                if (enabled) {
+                    mKeyguardLock.disableKeyguard();
+                } else {
+                    mKeyguardLock.reenableKeyguard();
+                }
+            }
+        }
+    }
+
+    void updateDockState(int newState) {
+        synchronized (mLock) {
+            if (newState != mDockState) {
+                mDockState = newState;
+                setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR);
+                if (mSystemReady) {
+                    updateLocked();
+                }
+            }
+        }
+    }
+
+    final void updateLocked() {
+        long ident = Binder.clearCallingIdentity();
+        
+        try {
+            int uiMode = 0;
+            if (mCarModeEnabled) {
+                uiMode = Configuration.UI_MODE_TYPE_CAR;
+            } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+                uiMode = Configuration.UI_MODE_TYPE_DESK;
+            }
+            if (uiMode != 0) {
+                if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+                    updateTwilightLocked();
+                    uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
+                            : Configuration.UI_MODE_NIGHT_NO;
+                } else {
+                    uiMode |= mNightMode << 4;
+                }
+            } else {
+                // Disabling the car mode clears the night mode.
+                uiMode = Configuration.UI_MODE_TYPE_NORMAL |
+                        Configuration.UI_MODE_NIGHT_NO;
+            }
+            
+            if (uiMode != mCurUiMode) {
+                mCurUiMode = uiMode;
+                
+                try {
+                    final IActivityManager am = ActivityManagerNative.getDefault();
+                    mConfiguration.uiMode = uiMode;
+                    am.updateConfiguration(mConfiguration);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failure communicating with activity manager", e);
+                }
+            }
+            
+            String action = null;
+            String oldAction = null;
+            if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
+                oldAction = UiModeManager.ACTION_EXIT_CAR_MODE;
+            } else if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_DESK) {
+                oldAction = UiModeManager.ACTION_EXIT_DESK_MODE;
+            }
+            
+            if (mCarModeEnabled) {
+                if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) {
+                    adjustStatusBarCarModeLocked();
+                    
+                    if (oldAction != null) {
+                        mContext.sendBroadcast(new Intent(oldAction));
+                    }
+                    mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR;
+                    action = UiModeManager.ACTION_ENTER_CAR_MODE;
+                }
+            } else if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+                if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_DESK) {
+                    if (oldAction != null) {
+                        mContext.sendBroadcast(new Intent(oldAction));
+                    }
+                    mLastBroadcastState = Intent.EXTRA_DOCK_STATE_DESK;
+                    action = UiModeManager.ACTION_ENTER_DESK_MODE;
+                }
+            } else {
+                if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
+                    adjustStatusBarCarModeLocked();
+                }
+                
+                mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+                action = oldAction;
+            }
+            
+            if (action != null) {
+                // Send the ordered broadcast; the result receiver will receive after all
+                // broadcasts have been sent. If any broadcast receiver changes the result
+                // code from the initial value of RESULT_OK, then the result receiver will
+                // not launch the corresponding dock application. This gives apps a chance
+                // to override the behavior and stay in their app even when the device is
+                // placed into a dock.
+                mContext.sendOrderedBroadcast(new Intent(action), null,
+                        mResultReceiver, null, Activity.RESULT_OK, null, null);
+            }
+
+            // keep screen on when charging and in car mode
+            boolean keepScreenOn = mCharging &&
+                    ((mCarModeEnabled && mCarModeKeepsScreenOn) ||
+                     (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
+            if (keepScreenOn != mWakeLock.isHeld()) {
+                if (keepScreenOn) {
+                    mWakeLock.acquire();
+                } else {
+                    mWakeLock.release();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void adjustStatusBarCarModeLocked() {
+        if (mStatusBarManager == null) {
+            mStatusBarManager = (StatusBarManager) mContext.getSystemService(Context.STATUS_BAR_SERVICE);
+        }
+
+        // Fear not: StatusBarService manages a list of requests to disable
+        // features of the status bar; these are ORed together to form the
+        // active disabled list. So if (for example) the device is locked and
+        // the status bar should be totally disabled, the calls below will
+        // have no effect until the device is unlocked.
+        if (mStatusBarManager != null) {
+            mStatusBarManager.disable(mCarModeEnabled
+                ? StatusBarManager.DISABLE_NOTIFICATION_TICKER
+                : StatusBarManager.DISABLE_NONE);
+        }
+
+        if (mNotificationManager == null) {
+            mNotificationManager = (NotificationManager)
+                    mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        }
+
+        if (mNotificationManager != null) {
+            if (mCarModeEnabled) {
+                Intent carModeOffIntent = new Intent(mContext, DisableCarModeActivity.class);
+
+                Notification n = new Notification();
+                n.icon = R.drawable.stat_notify_car_mode;
+                n.defaults = Notification.DEFAULT_LIGHTS;
+                n.flags = Notification.FLAG_ONGOING_EVENT;
+                n.when = 0;
+                n.setLatestEventInfo(
+                        mContext,
+                        mContext.getString(R.string.car_mode_disable_notification_title),
+                        mContext.getString(R.string.car_mode_disable_notification_message),
+                        PendingIntent.getActivity(mContext, 0, carModeOffIntent, 0));
+                mNotificationManager.notify(0, n);
+            } else {
+                mNotificationManager.cancel(0);
+            }
+        }
+    }
+
+    private final Handler mHandler = new Handler() {
+
+        boolean mPassiveListenerEnabled;
+        boolean mNetworkListenerEnabled;
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_UPDATE_TWILIGHT:
+                    synchronized (mLock) {
+                        if (isDoingNightMode() && mLocation != null
+                                && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+                            updateTwilightLocked();
+                            updateLocked();
+                        }
+                    }
+                    break;
+                case MSG_ENABLE_LOCATION_UPDATES:
+                    // enable network provider to receive at least location updates for a given
+                    // distance.
+                    boolean networkLocationEnabled;
+                    try {
+                        networkLocationEnabled =
+                            mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
+                    } catch (Exception e) {
+                        // we may get IllegalArgumentException if network location provider
+                        // does not exist or is not yet installed.
+                        networkLocationEnabled = false;
+                    }
+                    if (!mNetworkListenerEnabled && networkLocationEnabled) {
+                        mNetworkListenerEnabled = true;
+                        mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
+                                LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
+
+                        if (mLocation == null) {
+                            retrieveLocation();
+                        }
+                        synchronized (mLock) {
+                            if (isDoingNightMode() && mLocation != null
+                                    && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+                                updateTwilightLocked();
+                                updateLocked();
+                            }
+                        }
+                    }
+                   // enable passive provider to receive updates from location fixes (gps
+                   // and network).
+                   boolean passiveLocationEnabled;
+                    try {
+                        passiveLocationEnabled =
+                            mLocationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
+                    } catch (Exception e) {
+                        // we may get IllegalArgumentException if passive location provider
+                        // does not exist or is not yet installed.
+                        passiveLocationEnabled = false;
+                    }
+                    if (!mPassiveListenerEnabled && passiveLocationEnabled) {
+                        mPassiveListenerEnabled = true;
+                        mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
+                                0, LOCATION_UPDATE_DISTANCE_METER , mLocationListener);
+                    }
+                    if (!(mNetworkListenerEnabled && mPassiveListenerEnabled)) {
+                        long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL);
+                        interval *= 1.5;
+                        if (interval == 0) {
+                            interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
+                        } else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
+                            interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
+                        }
+                        Bundle bundle = new Bundle();
+                        bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval);
+                        Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES);
+                        newMsg.setData(bundle);
+                        mHandler.sendMessageDelayed(newMsg, interval);
+                    }
+                    break;
+            }
+        }
+
+        private void retrieveLocation() {
+            Location location;
+            Criteria criteria = new Criteria();
+            criteria.setSpeedRequired(false);
+            criteria.setAltitudeRequired(false);
+            criteria.setBearingRequired(false);
+            criteria.setAccuracy(Criteria.ACCURACY_FINE);
+            final String bestProvider = mLocationManager.getBestProvider(criteria, true);
+            location = mLocationManager.getLastKnownLocation(bestProvider);
+            // In the case there is no location available (e.g. GPS fix or network location
+            // is not available yet), the longitude of the location is estimated using the timezone,
+            // latitude and accuracy are set to get a good average.
+            if (location == null) {
+                Time currentTime = new Time();
+                currentTime.set(System.currentTimeMillis());
+                double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
+                        (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
+                location = new Location("fake");
+                location.setLongitude(lngOffset);
+                location.setLatitude(0);
+                location.setAccuracy(417000.0f);
+                location.setTime(System.currentTimeMillis());
+            }
+            synchronized (mLock) {
+                mLocation = location;
+            }
+        }
+    };
+
+    void updateTwilightLocked() {
+        if (mLocation == null) {
+            return;
+        }
+        final long currentTime = System.currentTimeMillis();
+        boolean nightMode;
+        // calculate current twilight
+        TwilightCalculator tw = new TwilightCalculator();
+        tw.calculateTwilight(currentTime,
+                mLocation.getLatitude(), mLocation.getLongitude());
+        if (tw.mState == TwilightCalculator.DAY) {
+            nightMode = false;
+        } else {
+            nightMode = true;
+        }
+
+        // schedule next update
+        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 {
+            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);
+        PendingIntent pendingIntent =
+                PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
+        mAlarmManager.cancel(pendingIntent);
+        mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
+
+        mComputedNightMode = nightMode;
+    }
+    
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            
+            pw.println("Permission Denial: can't dump uimode service from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+        
+        synchronized (mLock) {
+            pw.println("Current UI Mode Service state:");
+            pw.print("  mDockState="); pw.print(mDockState);
+                    pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
+            pw.print("  mNightMode="); pw.print(mNightMode);
+                    pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled);
+                    pw.print(" mComputedNightMode="); pw.println(mComputedNightMode);
+            pw.print("  mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode));
+                    pw.print(" mSystemReady="); pw.println(mSystemReady);
+            if (mLocation != null) {
+                pw.print("  mLocation="); pw.println(mLocation);
+            }
+        }
+    }
+}
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index 4078622..3fd71c8 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -32,6 +32,7 @@
 import android.database.IBulkCursor;
 import android.database.IContentObserver;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -113,6 +114,15 @@
             return MockContentProvider.this.update(url, values, selection, selectionArgs);
         }
 
+        /**
+         * @hide
+         */
+        @SuppressWarnings("unused")
+        public Bundle call(String method, String request, Bundle args)
+                throws RemoteException {
+            return MockContentProvider.this.call(method, request, args);
+        }
+
         public IBinder asBinder() {
             throw new UnsupportedOperationException();
         }
@@ -205,6 +215,14 @@
     }
 
     /**
+     * @hide
+     */
+    @Override
+    public Bundle call(String method, String request, Bundle args) {
+        throw new UnsupportedOperationException("unimplemented mock method call");
+    }
+
+    /**
      * Returns IContentProvider which calls back same methods in this class.
      * By overriding this class, we avoid the mechanism hidden behind ContentProvider
      * (IPC, etc.)
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index 7c0a1e2..0be5bea 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -27,6 +27,7 @@
 import android.database.IBulkCursor;
 import android.database.IContentObserver;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -38,7 +39,7 @@
  * {@link java.lang.UnsupportedOperationException}.  Tests can extend this class to
  * implement behavior needed for tests.
  *
- * @hide - @hide because this exposes bulkQuery(), which must also be hidden.
+ * @hide - @hide because this exposes bulkQuery() and call(), which must also be hidden.
  */
 public class MockIContentProvider implements IContentProvider {
     public int bulkInsert(Uri url, ContentValues[] initialValues) {
@@ -93,6 +94,11 @@
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
+    public Bundle call(String method, String request, Bundle args)
+            throws RemoteException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
     public IBinder asBinder() {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 6e7a66d..fc655a7 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -820,7 +820,12 @@
     if (strcmp(name, kWildcardName) == 0) {
         if (out) out->uiMode =
                 (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
-                | ResTable_config::UI_MODE_TYPE_NORMAL;
+                | ResTable_config::UI_MODE_TYPE_ANY;
+        return true;
+    } else if (strcmp(name, "desk") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_DESK;
         return true;
     } else if (strcmp(name, "car") == 0) {
       if (out) out->uiMode =
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index ea021d8..b7580b3 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -447,7 +447,7 @@
 
 static bool applyFileOverlay(Bundle *bundle,
                              const sp<AaptAssets>& assets,
-                             const sp<ResourceTypeSet>& baseSet,
+                             sp<ResourceTypeSet> *baseSet,
                              const char *resType)
 {
     if (bundle->getVerbose()) {
@@ -475,13 +475,16 @@
                 if (bundle->getVerbose()) {
                     printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
                 }
-                size_t baseIndex = baseSet->indexOfKey(overlaySet->keyAt(overlayIndex));
+                size_t baseIndex = UNKNOWN_ERROR;
+                if (baseSet->get() != NULL) {
+                    baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex));
+                }
                 if (baseIndex < UNKNOWN_ERROR) {
                     // look for same flavor.  For a given file (strings.xml, for example)
                     // there may be a locale specific or other flavors - we want to match
                     // the same flavor.
                     sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
-                    sp<AaptGroup> baseGroup = baseSet->valueAt(baseIndex);
+                    sp<AaptGroup> baseGroup = (*baseSet)->valueAt(baseIndex);
 
                     DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
                             overlayGroup->getFiles();
@@ -520,8 +523,12 @@
                         assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
                     }
                 } else {
+                    if (baseSet->get() == NULL) {
+                        *baseSet = new ResourceTypeSet();
+                        assets->getResources()->add(String8(resType), *baseSet);
+                    }
                     // this group doesn't exist (a file that's only in the overlay)
-                    baseSet->add(overlaySet->keyAt(overlayIndex),
+                    (*baseSet)->add(overlaySet->keyAt(overlayIndex),
                             overlaySet->valueAt(overlayIndex));
                     // make sure all flavors are defined in the resources.
                     sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
@@ -751,13 +758,13 @@
         current = current->getOverlay();
     }
     // apply the overlay files to the base set
-    if (!applyFileOverlay(bundle, assets, drawables, "drawable") ||
-            !applyFileOverlay(bundle, assets, layouts, "layout") ||
-            !applyFileOverlay(bundle, assets, anims, "anim") ||
-            !applyFileOverlay(bundle, assets, xmls, "xml") ||
-            !applyFileOverlay(bundle, assets, raws, "raw") ||
-            !applyFileOverlay(bundle, assets, colors, "color") ||
-            !applyFileOverlay(bundle, assets, menus, "menu")) {
+    if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
+            !applyFileOverlay(bundle, assets, &layouts, "layout") ||
+            !applyFileOverlay(bundle, assets, &anims, "anim") ||
+            !applyFileOverlay(bundle, assets, &xmls, "xml") ||
+            !applyFileOverlay(bundle, assets, &raws, "raw") ||
+            !applyFileOverlay(bundle, assets, &colors, "color") ||
+            !applyFileOverlay(bundle, assets, &menus, "menu")) {
         return UNKNOWN_ERROR;
     }
 
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 0b531c2..1f9d152 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2369,7 +2369,7 @@
                         if (configSet.find(region) == configSet.end()) {
                             if (configSet.count(defaultLocale) == 0) {
                                 fprintf(stdout, "aapt: warning: "
-                                        "*** string '%s' has no default or required localization "
+                                        "**** string '%s' has no default or required localization "
                                         "for '%s' in %s\n",
                                         String8(nameIter->first).string(),
                                         config.string(),