Refactor car mode.

Extract all UI behavior from dock observer and ACTION_DOCK_EVENT.

Also introduce a desk type to go along with the car type all through
the resource system, since we now need to have corresponding high-level
broadcasts for desk dock mode.  As part of that I also reworked some
of the logic for switching modes to all funnel through a single
update() call that looks all of the current state to decide what to
do next, and fixed various locking issues.

In addition I found there were bugs in the configuration change
handling causing us to only switch into the car mode config and
then never get out of it.  Unfortunately now that we are actually
changing the configuration for each mode change, the transitions
between them are really crummy as we restart all kinds of
activities. :(
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/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/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;
         }