Introduce special UI modes for night and car usage.

The device mode is now called ui mode. Furthermore is the order of
precedence for the resources now in such a way that the ui mode needs
to be specified after the orientation and before the density.

The ui mode can be set, like it is done for the locale, as follows:

IActivityManager am = ActivityManagerNative.getDefault();
Configuration config = am.getConfiguration();
config.uiMode = Configuration.UI_MODE_TYPE_CAR | Configuration.UI_MODE_NIGHT_ANY;
am.updateConfiguration(config);

To allow users to disable the car mode and set the night mode the IUiModeManager
interface is used.

The automatic night mode switching will be added in a separate change.
diff --git a/Android.mk b/Android.mk
index 7fc121d..4ecf5bd 100644
--- a/Android.mk
+++ b/Android.mk
@@ -90,6 +90,7 @@
 	core/java/android/app/IStatusBar.aidl \
 	core/java/android/app/IThumbnailReceiver.aidl \
 	core/java/android/app/ITransientNotification.aidl \
+	core/java/android/app/IUiModeManager.aidl \
 	core/java/android/app/IWallpaperManager.aidl \
 	core/java/android/app/IWallpaperManagerCallback.aidl \
 	core/java/android/backup/IBackupManager.aidl \
diff --git a/api/current.xml b/api/current.xml
index ab5a114..b2a2bc1 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -38037,6 +38037,17 @@
  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"
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
new file mode 100644
index 0000000..6ac8a2a
--- /dev/null
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+/**
+ * Interface used to control special UI modes.
+ * @hide
+ */
+interface IUiModeManager {
+    /**
+     * Enables the car mode. Only the system can do this.
+     * @hide
+     */
+    void enableCarMode();
+
+    /**
+     * Disables the car mode.
+     */
+    void disableCarMode();
+
+    /**
+     * Sets the night mode.
+     * The mode can be one of:
+     *   1 - notnight mode
+     *   2 - night mode
+     *   3 - automatic mode switching
+     */
+    void setNightMode(int mode);
+
+    /**
+     * Gets the currently configured night mode.  Return 1 for notnight,
+     * 2 for night, and 3 for automatic mode switching.
+     */
+    int getNightMode();
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c32999f..e36eba9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1816,7 +1816,9 @@
     /**
      * Broadcast Action:  A sticky broadcast indicating the phone was docked
      * or undocked.  Includes the extra
-     * field {@link #EXTRA_DOCK_STATE}, containing the current dock state.
+     * field {@link #EXTRA_DOCK_STATE}, containing the current dock state. It also
+     * includes the boolean extra field {@link #EXTRA_CAR_MODE_ENABLED}, indicating
+     * the state of the car mode.
      * 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.
@@ -2152,6 +2154,12 @@
     public static final int EXTRA_DOCK_STATE_CAR = 2;
 
     /**
+     * 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/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index b94bb51..a13f7f9 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -251,6 +251,13 @@
     public static final int CONFIG_SCREEN_LAYOUT = 0x0100;
     /**
      * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle the ui mode. Set from the
+     * {@link android.R.attr#configChanges} attribute.
+     * @hide (UIMODE) Pending API council approval
+     */
+    public static final int CONFIG_UI_MODE = 0x0200;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
      * can itself handle changes to the font scaling factor.  Set from the
      * {@link android.R.attr#configChanges} attribute.  This is
      * not a core resource configutation, but a higher-level value, so its
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 23a408b..7f9a5c6 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -619,7 +619,7 @@
     public native final void setConfiguration(int mcc, int mnc, String locale,
             int orientation, int touchscreen, int density, int keyboard,
             int keyboardHidden, int navigation, int screenWidth, int screenHeight,
-            int screenLayout, int majorVersion);
+            int screenLayout, int uiMode, int majorVersion);
 
     /**
      * Retrieve the resource identifier for the given resource name.
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 1fe34b5..aa5f128 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -161,7 +161,37 @@
      * or {@link #ORIENTATION_SQUARE}.
      */
     public int orientation;
-    
+
+    /** @hide (UIMODE) Pending API council approval */
+    public static final int UI_MODE_TYPE_MASK = 0x0f;
+    /** @hide (UIMODE) Pending API council approval */
+    public static final int UI_MODE_TYPE_NORMAL = 0x00;
+    /** @hide (UIMODE) Pending API council approval */
+    public static final int UI_MODE_TYPE_CAR = 0x01;
+
+    /** @hide (UIMODE) Pending API council approval */
+    public static final int UI_MODE_NIGHT_MASK = 0x30;
+    /** @hide (UIMODE) Pending API council approval */
+    public static final int UI_MODE_NIGHT_UNDEFINED = 0x00;
+    /** @hide (UIMODE) Pending API council approval */
+    public static final int UI_MODE_NIGHT_NO = 0x10;
+    /** @hide (UIMODE) Pending API council approval */
+    public static final int UI_MODE_NIGHT_YES = 0x20;
+
+    /**
+     * 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}.
+     *
+     * <p>The {@link #UI_MODE_NIGHT_MASK} defines whether the screen
+     * is in a special mode. They may be one of
+     * {@link #UI_MODE_NIGHT_NO} or {@link #UI_MODE_NIGHT_YES}.
+     *
+     * @hide (UIMODE) Pending API council approval
+     */
+    public int uiMode;
+
     /**
      * Construct an invalid Configuration.  You must call {@link #setToDefaults}
      * for this object to be valid.  {@more}
@@ -189,6 +219,7 @@
         navigationHidden = o.navigationHidden;
         orientation = o.orientation;
         screenLayout = o.screenLayout;
+        uiMode = o.uiMode;
     }
 
     public String toString() {
@@ -217,6 +248,8 @@
         sb.append(orientation);
         sb.append(" layout=");
         sb.append(screenLayout);
+        sb.append(" uiMode=");
+        sb.append(uiMode);
         sb.append('}');
         return sb.toString();
     }
@@ -237,6 +270,7 @@
         navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
         orientation = ORIENTATION_UNDEFINED;
         screenLayout = SCREENLAYOUT_SIZE_UNDEFINED;
+        uiMode = UI_MODE_TYPE_NORMAL;
     }
 
     /** {@hide} */
@@ -317,6 +351,11 @@
             changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
             screenLayout = delta.screenLayout;
         }
+        if (delta.uiMode != UI_MODE_TYPE_NORMAL
+                && uiMode != delta.uiMode) {
+            changed |= ActivityInfo.CONFIG_UI_MODE;
+            uiMode = delta.uiMode;
+        }
         
         return changed;
     }
@@ -393,6 +432,10 @@
                 && screenLayout != delta.screenLayout) {
             changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
         }
+        if (delta.uiMode != UI_MODE_TYPE_NORMAL
+                && uiMode != delta.uiMode) {
+            changed |= ActivityInfo.CONFIG_UI_MODE;
+        }
         
         return changed;
     }
@@ -444,6 +487,7 @@
         dest.writeInt(navigationHidden);
         dest.writeInt(orientation);
         dest.writeInt(screenLayout);
+        dest.writeInt(uiMode);
     }
 
     public static final Parcelable.Creator<Configuration> CREATOR
@@ -477,6 +521,7 @@
         navigationHidden = source.readInt();
         orientation = source.readInt();
         screenLayout = source.readInt();
+        uiMode = source.readInt();
     }
 
     public int compareTo(Configuration that) {
@@ -510,6 +555,8 @@
         n = this.orientation - that.orientation;
         if (n != 0) return n;
         n = this.screenLayout - that.screenLayout;
+        if (n != 0) return n;
+        n = this.uiMode - that.uiMode;
         //if (n != 0) return n;
         return n;
     }
@@ -533,6 +580,6 @@
                 + this.locale.hashCode() + this.touchscreen
                 + this.keyboard + this.keyboardHidden + this.hardKeyboardHidden
                 + this.navigation + this.navigationHidden
-                + this.orientation + this.screenLayout;
+                + this.orientation + this.screenLayout + this.uiMode;
     }
 }
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index e4fc259..ae8e297 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1294,7 +1294,7 @@
                     mConfiguration.touchscreen,
                     (int)(mMetrics.density*160), mConfiguration.keyboard,
                     keyboardHidden, mConfiguration.navigation, width, height,
-                    mConfiguration.screenLayout, sSdkVersion);
+                    mConfiguration.screenLayout, mConfiguration.uiMode, sSdkVersion);
             int N = mDrawableCache.size();
             if (DEBUG_CONFIG) {
                 Log.d(TAG, "Cleaning up drawables config changes: 0x"
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 2fff727..a82a21e 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -537,7 +537,8 @@
                                                           jint keyboard, jint keyboardHidden,
                                                           jint navigation,
                                                           jint screenWidth, jint screenHeight,
-                                                          jint screenLayout, jint sdkVersion)
+                                                          jint screenLayout, jint uiMode,
+                                                          jint sdkVersion)
 {
     AssetManager* am = assetManagerForJavaObject(env, clazz);
     if (am == NULL) {
@@ -560,6 +561,7 @@
     config.screenWidth = (uint16_t)screenWidth;
     config.screenHeight = (uint16_t)screenHeight;
     config.screenLayout = (uint8_t)screenLayout;
+    config.uiMode = (uint8_t)uiMode;
     config.sdkVersion = (uint16_t)sdkVersion;
     config.minorVersion = 0;
     am->setConfiguration(config, locale8);
@@ -1614,7 +1616,7 @@
         (void*) android_content_AssetManager_setLocale },
     { "getLocales",      "()[Ljava/lang/String;",
         (void*) android_content_AssetManager_getLocales },
-    { "setConfiguration", "(IILjava/lang/String;IIIIIIIIII)V",
+    { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIII)V",
         (void*) android_content_AssetManager_setConfiguration },
     { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
         (void*) android_content_AssetManager_getResourceIdentifier },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1ae736e..f5f5a27 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -564,6 +564,14 @@
         android:label="@string/permlab_changeConfiguration"
         android:description="@string/permdesc_changeConfiguration" />
 
+    <!-- Allows an application to enable the car mode.
+         @hide -->
+    <permission android:name="android.permission.ENABLE_CAR_MODE"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature"
+        android:label="@string/permlab_enableCarMode"
+        android:description="@string/permdesc_enableCarMode" />
+
     <!-- @deprecated The {@link android.app.ActivityManager#restartPackage}
         API is no longer supported. -->
     <permission android:name="android.permission.RESTART_PACKAGES"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0a5c584..7f66111 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -462,6 +462,12 @@
         size.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_enableCarMode">enable car mode</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_enableCarMode">Allows an application to
+        enable the car mode.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_killBackgroundProcesses">kill background processes</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_killBackgroundProcesses">Allows an application to
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index 6090f600..13ea27e 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -939,10 +939,23 @@
         SCREENLONG_YES = 0x20,
     };
     
+    enum {
+        // uiMode bits for the mode type.
+        MASK_UI_MODE_TYPE = 0x0f,
+        UI_MODE_TYPE_NORMAL = 0x00,
+        UI_MODE_TYPE_CAR = 0x01,
+
+        // uiMode bits for the night switch.
+        MASK_UI_MODE_NIGHT = 0x30,
+        UI_MODE_NIGHT_ANY = 0x00,
+        UI_MODE_NIGHT_NO = 0x10,
+        UI_MODE_NIGHT_YES = 0x20,
+    };
+
     union {
         struct {
             uint8_t screenLayout;
-            uint8_t screenConfigPad0;
+            uint8_t uiMode;
             uint8_t screenConfigPad1;
             uint8_t screenConfigPad2;
         };
@@ -996,6 +1009,8 @@
         diff = (int32_t)(version - o.version);
         if (diff != 0) return diff;
         diff = (int32_t)(screenLayout - o.screenLayout);
+        if (diff != 0) return diff;
+        diff = (int32_t)(uiMode - o.uiMode);
         return (int)diff;
     }
     
@@ -1014,7 +1029,8 @@
         CONFIG_DENSITY = 0x0100,
         CONFIG_SCREEN_SIZE = 0x0200,
         CONFIG_VERSION = 0x0400,
-        CONFIG_SCREEN_LAYOUT = 0x0800
+        CONFIG_SCREEN_LAYOUT = 0x0800,
+        CONFIG_UI_MODE = 0x1000
     };
     
     // Compare two configuration, returning CONFIG_* flags set for each value
@@ -1034,6 +1050,7 @@
         if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE;
         if (version != o.version) diffs |= CONFIG_VERSION;
         if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT;
+        if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE;
         return diffs;
     }
     
@@ -1078,19 +1095,28 @@
             }
         }
 
-        if (screenType || o.screenType) {
-            if (orientation != o.orientation) {
-                if (!orientation) return false;
-                if (!o.orientation) return true;
-            }
+        if (orientation != o.orientation) {
+            if (!orientation) return false;
+            if (!o.orientation) return true;
+        }
 
-            // density is never 'more specific'
-            // as the default just equals 160
-
-            if (touchscreen != o.touchscreen) {
-                if (!touchscreen) return false;
-                if (!o.touchscreen) return true;
+        if (screenConfig || o.screenConfig) {
+            if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0) {
+                if (!(uiMode & MASK_UI_MODE_TYPE)) return false;
+                if (!(o.uiMode & MASK_UI_MODE_TYPE)) return true;
             }
+            if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0) {
+                if (!(uiMode & MASK_UI_MODE_NIGHT)) return false;
+                if (!(o.uiMode & MASK_UI_MODE_NIGHT)) return true;
+            }
+        }
+
+        // density is never 'more specific'
+        // as the default just equals 160
+
+        if (touchscreen != o.touchscreen) {
+            if (!touchscreen) return false;
+            if (!o.touchscreen) return true;
         }
 
         if (input || o.input) {
@@ -1186,11 +1212,22 @@
                 }
             }
 
-            if (screenType || o.screenType) {
-                if ((orientation != o.orientation) && requested->orientation) {
-                    return (orientation);
-                }
+            if ((orientation != o.orientation) && requested->orientation) {
+                return (orientation);
+            }
 
+            if (screenConfig || o.screenConfig) {
+                if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0
+                        && (requested->uiMode & MASK_UI_MODE_TYPE)) {
+                    return (uiMode & MASK_UI_MODE_TYPE);
+                }
+                if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0
+                        && (requested->uiMode & MASK_UI_MODE_NIGHT)) {
+                    return (uiMode & MASK_UI_MODE_NIGHT);
+                }
+            }
+
+            if (screenType || o.screenType) {
                 if (density != o.density) {
                     // density is tough.  Any density is potentially useful
                     // because the system will scale it.  Scaling down
@@ -1340,6 +1377,20 @@
                     && screenLong != setScreenLong) {
                 return false;
             }
+
+            const int uiModeType = uiMode&MASK_UI_MODE_TYPE;
+            const int setUiModeType = settings.uiMode&MASK_UI_MODE_TYPE;
+            if (setUiModeType != 0 && uiModeType != 0
+                    && uiModeType != setUiModeType) {
+                return false;
+            }
+
+            const int uiModeNight = uiMode&MASK_UI_MODE_NIGHT;
+            const int setUiModeNight = settings.uiMode&MASK_UI_MODE_NIGHT;
+            if (setUiModeNight != 0 && uiModeNight != 0
+                    && uiModeNight != setUiModeNight) {
+                return false;
+            }
         }
         if (screenType != 0) {
             if (settings.orientation != 0 && orientation != 0
@@ -1420,13 +1471,15 @@
     String8 toString() const {
         char buf[200];
         sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=%d touch=%d dens=%d "
-                "kbd=%d nav=%d input=%d scrnW=%d scrnH=%d sz=%d long=%d vers=%d.%d",
+                "kbd=%d nav=%d input=%d scrnW=%d scrnH=%d sz=%d long=%d "
+                "ui=%d night=%d vers=%d.%d",
                 mcc, mnc,
                 language[0] ? language[0] : '-', language[1] ? language[1] : '-',
                 country[0] ? country[0] : '-', country[1] ? country[1] : '-',
                 orientation, touchscreen, density, keyboard, navigation, inputFlags,
                 screenWidth, screenHeight,
                 screenLayout&MASK_SCREENSIZE, screenLayout&MASK_SCREENLONG,
+                uiMode&MASK_UI_MODE_TYPE, uiMode&MASK_UI_MODE_NIGHT,
                 sdkVersion, minorVersion);
         return String8(buf);
     }
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index 363e93e..742a7d8 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -17,6 +17,9 @@
 package com.android.server;
 
 import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.IUiModeManager;
 import android.app.KeyguardManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
@@ -24,8 +27,12 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Binder;
 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;
@@ -47,7 +54,13 @@
     private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
     private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
 
+    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 int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+    private int mNightMode = MODE_NIGHT_NO;
+    private boolean mCarModeEnabled = false;
     private boolean mSystemReady;
 
     private final Context mContext;
@@ -71,16 +84,12 @@
 
             // Launch a dock activity
             String category;
-            switch (mDockState) {
-                case Intent.EXTRA_DOCK_STATE_CAR:
-                    category = Intent.CATEGORY_CAR_DOCK;
-                    break;
-                case Intent.EXTRA_DOCK_STATE_DESK:
-                    category = Intent.CATEGORY_DESK_DOCK;
-                    break;
-                default:
-                    category = null;
-                    break;
+            if (mCarModeEnabled || mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
+                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);
@@ -101,6 +110,9 @@
         mPowerManager = pm;
         mLockPatternUtils = new LockPatternUtils(context);
         init();  // set initial status
+
+        ServiceManager.addService("uimode", mBinder);
+
         startObserving(DOCK_UEVENT_MATCH);
     }
 
@@ -116,6 +128,14 @@
                 if (newState != mDockState) {
                     int oldState = mDockState;
                     mDockState = newState;
+                    boolean carModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
+                    if (mCarModeEnabled != carModeEnabled) {
+                        try {
+                            setCarMode(carModeEnabled);
+                        } catch (RemoteException e1) {
+                            Log.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.
@@ -180,7 +200,13 @@
                 // Pack up the values and broadcast them to everyone
                 Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
                 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-                intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
+                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 {
+                    intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
+                }
+                intent.putExtra(Intent.EXTRA_CAR_MODE_ENABLED, mCarModeEnabled);
 
                 // Check if this is Bluetooth Dock
                 String address = BluetoothService.readDockBluetoothAddress();
@@ -199,4 +225,76 @@
             }
         }
     };
+
+    private void setCarMode(boolean enabled) throws RemoteException {
+        mCarModeEnabled = enabled;
+        if (enabled) {
+            setMode(Configuration.UI_MODE_TYPE_CAR, mNightMode);
+        } else {
+            // Disabling the car mode clears the night mode.
+            setMode(Configuration.UI_MODE_TYPE_NORMAL, MODE_NIGHT_NO);
+        }
+    }
+
+    private void setMode(int modeType, int modeNight) throws RemoteException {
+        final IActivityManager am = ActivityManagerNative.getDefault();
+        Configuration config = am.getConfiguration();
+
+        if (config.uiMode != (modeType | modeNight)) {
+            config.uiMode = modeType | modeNight;
+            long ident = Binder.clearCallingIdentity();
+            am.updateConfiguration(config);
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void setNightMode(int mode) throws RemoteException {
+        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:
+                // FIXME: not yet supported, this functionality will be
+                // added in a separate change.
+                break;
+            default:
+                setMode(Configuration.UI_MODE_TYPE_CAR, MODE_NIGHT_NO << 4);
+                break;
+        }
+    }
+
+    /**
+     * 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/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index c346b90..663a33a 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -163,6 +163,20 @@
         return 0;
     }
 
+    // ui mode type
+    if (getUiModeTypeName(part.string(), &config)) {
+        *axis = AXIS_UIMODETYPE;
+        *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+        return 0;
+    }
+
+    // ui mode night
+    if (getUiModeNightName(part.string(), &config)) {
+        *axis = AXIS_UIMODENIGHT;
+        *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+        return 0;
+    }
+
     // density
     if (getDensityName(part.string(), &config)) {
         *axis = AXIS_DENSITY;
@@ -229,6 +243,7 @@
 
     String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
     String8 touch, key, keysHidden, nav, navHidden, size, vers;
+    String8 uiModeType, uiModeNight;
 
     const char *p = dir;
     const char *q;
@@ -352,6 +367,32 @@
         //printf("not orientation: %s\n", part.string());
     }
 
+    // ui mode type
+    if (getUiModeTypeName(part.string())) {
+        uiModeType = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not ui mode type: %s\n", part.string());
+    }
+
+    // ui mode night
+    if (getUiModeNightName(part.string())) {
+        uiModeNight = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not ui mode night: %s\n", part.string());
+    }
+
     // density
     if (getDensityName(part.string())) {
         den = part;
@@ -463,6 +504,8 @@
     this->screenLayoutSize = layoutsize;
     this->screenLayoutLong = layoutlong;
     this->orientation = orient;
+    this->uiModeType = uiModeType;
+    this->uiModeNight = uiModeNight;
     this->density = den;
     this->touchscreen = touch;
     this->keysHidden = keysHidden;
@@ -493,6 +536,10 @@
     s += ",";
     s += this->orientation;
     s += ",";
+    s += uiModeType;
+    s += ",";
+    s += uiModeNight;
+    s += ",";
     s += density;
     s += ",";
     s += touchscreen;
@@ -539,6 +586,14 @@
         s += "-";
         s += orientation;
     }
+    if (this->uiModeType != "") {
+        s += "-";
+        s += uiModeType;
+    }
+    if (this->uiModeNight != "") {
+        s += "-";
+        s += uiModeNight;
+    }
     if (this->density != "") {
         s += "-";
         s += density;
@@ -759,6 +814,47 @@
     return false;
 }
 
+bool AaptGroupEntry::getUiModeTypeName(const char* name,
+                                       ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->uiMode =
+                (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+                | ResTable_config::UI_MODE_TYPE_NORMAL;
+        return true;
+    } else if (strcmp(name, "car") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_CAR;
+        return true;
+    }
+
+    return false;
+}
+
+bool AaptGroupEntry::getUiModeNightName(const char* name,
+                                          ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->uiMode =
+                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+                | ResTable_config::UI_MODE_NIGHT_ANY;
+        return true;
+    } else if (strcmp(name, "night") == 0) {
+        if (out) out->uiMode =
+                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+                | ResTable_config::UI_MODE_NIGHT_YES;
+        return true;
+    } else if (strcmp(name, "notnight") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+              | ResTable_config::UI_MODE_NIGHT_NO;
+        return true;
+    }
+
+    return false;
+}
+
 bool AaptGroupEntry::getDensityName(const char* name,
                                     ResTable_config* out)
 {
@@ -1004,6 +1100,8 @@
     if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
     if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
     if (v == 0) v = orientation.compare(o.orientation);
+    if (v == 0) v = uiModeType.compare(o.uiModeType);
+    if (v == 0) v = uiModeNight.compare(o.uiModeNight);
     if (v == 0) v = density.compare(o.density);
     if (v == 0) v = touchscreen.compare(o.touchscreen);
     if (v == 0) v = keysHidden.compare(o.keysHidden);
@@ -1025,6 +1123,8 @@
     getScreenLayoutSizeName(screenLayoutSize.string(), &params);
     getScreenLayoutLongName(screenLayoutLong.string(), &params);
     getOrientationName(orientation.string(), &params);
+    getUiModeTypeName(uiModeType.string(), &params);
+    getUiModeNightName(uiModeNight.string(), &params);
     getDensityName(density.string(), &params);
     getTouchscreenName(touchscreen.string(), &params);
     getKeysHiddenName(keysHidden.string(), &params);
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 26500a3..9a848e4 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -33,6 +33,8 @@
     AXIS_SCREENLAYOUTSIZE,
     AXIS_SCREENLAYOUTLONG,
     AXIS_ORIENTATION,
+    AXIS_UIMODETYPE,
+    AXIS_UIMODENIGHT,
     AXIS_DENSITY,
     AXIS_TOUCHSCREEN,
     AXIS_KEYSHIDDEN,
@@ -61,6 +63,8 @@
     String8 screenLayoutSize;
     String8 screenLayoutLong;
     String8 orientation;
+    String8 uiModeType;
+    String8 uiModeNight;
     String8 density;
     String8 touchscreen;
     String8 keysHidden;
@@ -80,6 +84,8 @@
     static bool getScreenLayoutSizeName(const char* name, ResTable_config* out = NULL);
     static bool getScreenLayoutLongName(const char* name, ResTable_config* out = NULL);
     static bool getOrientationName(const char* name, ResTable_config* out = NULL);
+    static bool getUiModeTypeName(const char* name, ResTable_config* out = NULL);
+    static bool getUiModeNightName(const char* name, ResTable_config* out = NULL);
     static bool getDensityName(const char* name, ResTable_config* out = NULL);
     static bool getTouchscreenName(const char* name, ResTable_config* out = NULL);
     static bool getKeysHiddenName(const char* name, ResTable_config* out = NULL);
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 0d2ea60..88c5441 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -101,13 +101,13 @@
             String8 leaf(group->getLeaf());
             mLeafName = String8(leaf);
             mParams = file->getGroupEntry().toParams();
-            NOISY(printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d density=%d touch=%d key=%d inp=%d nav=%d\n",
+            NOISY(printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d ui=%d density=%d touch=%d key=%d inp=%d nav=%d\n",
                    group->getPath().string(), mParams.mcc, mParams.mnc,
                    mParams.language[0] ? mParams.language[0] : '-',
                    mParams.language[1] ? mParams.language[1] : '-',
                    mParams.country[0] ? mParams.country[0] : '-',
                    mParams.country[1] ? mParams.country[1] : '-',
-                   mParams.orientation,
+                   mParams.orientation, mParams.uiMode,
                    mParams.density, mParams.touchscreen, mParams.keyboard,
                    mParams.inputFlags, mParams.navigation));
             mPath = "res";
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index b682702..a389bfb 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2472,6 +2472,12 @@
     if (!match(AXIS_ORIENTATION, config.orientation)) {
         return false;
     }
+    if (!match(AXIS_UIMODETYPE, (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE))) {
+        return false;
+    }
+    if (!match(AXIS_UIMODENIGHT, (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT))) {
+        return false;
+    }
     if (!match(AXIS_DENSITY, config.density)) {
         return false;
     }
@@ -2674,7 +2680,7 @@
                 ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
 
                 NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
-                     "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+                     "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
                       ti+1,
                       config.mcc, config.mnc,
                       config.language[0] ? config.language[0] : '-',
@@ -2682,6 +2688,7 @@
                       config.country[0] ? config.country[0] : '-',
                       config.country[1] ? config.country[1] : '-',
                       config.orientation,
+                      config.uiMode,
                       config.touchscreen,
                       config.density,
                       config.keyboard,
@@ -2711,7 +2718,7 @@
                 tHeader->entriesStart = htodl(typeSize);
                 tHeader->config = config;
                 NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
-                     "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+                     "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
                       ti+1,
                       tHeader->config.mcc, tHeader->config.mnc,
                       tHeader->config.language[0] ? tHeader->config.language[0] : '-',
@@ -2719,6 +2726,7 @@
                       tHeader->config.country[0] ? tHeader->config.country[0] : '-',
                       tHeader->config.country[1] ? tHeader->config.country[1] : '-',
                       tHeader->config.orientation,
+                      tHeader->config.uiMode,
                       tHeader->config.touchscreen,
                       tHeader->config.density,
                       tHeader->config.keyboard,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
index 6c1b5b3..43ff424 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
@@ -59,7 +59,7 @@
     public void setConfiguration(int mcc, int mnc, String locale,
             int orientation, int touchscreen, int density, int keyboard,
             int keyboardHidden, int navigation, int screenWidth, int screenHeight,
-            int screenLayout, int version)  {
+            int screenLayout, int uiMode, int version)  {
         
         Configuration c = new Configuration();
         c.mcc = mcc;
@@ -71,5 +71,6 @@
         c.navigation = navigation;
         c.orientation = orientation;
         c.screenLayout = screenLayout;
+        c.uiMode = uiMode;
     }
 }