Merge "Fix deadlock in removing a user" into jb-mr1-dev
diff --git a/Android.mk b/Android.mk
index c2331f2..0665e60 100644
--- a/Android.mk
+++ b/Android.mk
@@ -237,9 +237,6 @@
 LOCAL_MODULE := framework
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
 
-LOCAL_NO_EMMA_INSTRUMENT := true
-LOCAL_NO_EMMA_COMPILE := true
-
 # List of classes and interfaces which should be loaded by the Zygote.
 LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/preloaded-classes
 
@@ -725,9 +722,6 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := ext
 
-LOCAL_NO_EMMA_INSTRUMENT := true
-LOCAL_NO_EMMA_COMPILE := true
-
 LOCAL_DX_FLAGS := --core-library
 
 include $(BUILD_JAVA_LIBRARY)
diff --git a/api/current.txt b/api/current.txt
index e1d07e2..8a7fb79 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -19988,6 +19988,10 @@
 
 package android.security {
 
+  public class AndroidKeyPairGeneratorSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public AndroidKeyPairGeneratorSpec(android.content.Context, java.lang.String, javax.security.auth.x500.X500Principal, java.math.BigInteger, java.util.Date, java.util.Date);
+  }
+
   public final class KeyChain {
     ctor public KeyChain();
     method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String);
@@ -22909,6 +22913,7 @@
     method public static float exp(float);
     method public static float floor(float);
     method public static float hypot(float, float);
+    method public static float pow(float, float);
     method public static float sin(float);
     method public static float sqrt(float);
   }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4740e53..28c5abd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4817,13 +4817,14 @@
         Process.setArgV0("<pre-initialized>");
 
         Looper.prepareMainLooper();
-        if (sMainThreadHandler == null) {
-            sMainThreadHandler = new Handler();
-        }
 
         ActivityThread thread = new ActivityThread();
         thread.attach(false);
 
+        if (sMainThreadHandler == null) {
+            sMainThreadHandler = thread.getHandler();
+        }
+
         AsyncTask.init();
 
         if (false) {
diff --git a/core/java/android/net/arp/ArpPeer.java b/core/java/android/net/arp/ArpPeer.java
index 5f68fdf..2013b11 100644
--- a/core/java/android/net/arp/ArpPeer.java
+++ b/core/java/android/net/arp/ArpPeer.java
@@ -58,9 +58,11 @@
         mInterfaceName = interfaceName;
         mMyAddr = myAddr;
 
-        for (int i = 0; i < MAC_ADDR_LENGTH; i++) {
-            mMyMac[i] = (byte) Integer.parseInt(mac.substring(
-                        i*3, (i*3) + 2), 16);
+        if (mac != null) {
+            for (int i = 0; i < MAC_ADDR_LENGTH; i++) {
+                mMyMac[i] = (byte) Integer.parseInt(mac.substring(
+                            i*3, (i*3) + 2), 16);
+            }
         }
 
         if (myAddr instanceof Inet6Address || peer instanceof Inet6Address) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index efdbcac..a1901a5 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -239,13 +239,6 @@
      */
     public static final int BRIGHTNESS_OFF = 0;
 
-    /**
-     * A nominal default brightness value.
-     * Use {@link #getDefaultScreenBrightnessSetting()} instead.
-     * @hide
-     */
-    private static final int BRIGHTNESS_DEFAULT = 102;
-
     // Note: Be sure to update android.os.BatteryStats and PowerManager.h
     // if adding or modifying user activity event constants.
 
@@ -275,13 +268,6 @@
     public static final int USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS = 1 << 0;
 
     /**
-     * Special wake lock tag used for the wake lock in the Window Manager that handles the
-     * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} flag.
-     * @hide
-     */
-    public static final String KEEP_SCREEN_ON_FLAG_TAG = "KEEP_SCREEN_ON_FLAG";
-
-    /**
      * Go to sleep reason code: Going to sleep due by user request.
      * @hide
      */
@@ -321,7 +307,7 @@
      */
     public int getMinimumScreenBrightnessSetting() {
         return mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_screenBrightnessDim);
+                com.android.internal.R.integer.config_screenBrightnessSettingMinimum);
     }
 
     /**
@@ -331,7 +317,8 @@
      * @hide
      */
     public int getMaximumScreenBrightnessSetting() {
-        return BRIGHTNESS_ON;
+        return mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_screenBrightnessSettingMaximum);
     }
 
     /**
@@ -339,7 +326,8 @@
      * @hide
      */
     public int getDefaultScreenBrightnessSetting() {
-        return BRIGHTNESS_DEFAULT;
+        return mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_screenBrightnessSettingDefault);
     }
 
     /**
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 3513bdc..851b8df 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -582,9 +582,7 @@
             if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
                 argsForZygote.add("--enable-assert");
             }
-            if (mountExternal == Zygote.MOUNT_EXTERNAL_SINGLEUSER) {
-                argsForZygote.add("--mount-external-singleuser");
-            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER) {
+            if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER) {
                 argsForZygote.add("--mount-external-multiuser");
             }
             argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 0338ee7..de8e78c 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -56,6 +56,7 @@
 
     /**
      * Returns the user name of the user making this call.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @return the user name
      */
     public String getUserName() {
@@ -69,6 +70,7 @@
 
     /**
      * Returns the UserInfo object describing a specific user.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @param userHandle the user handle of the user whose information is being requested.
      * @return the UserInfo object for a specific user.
      * @hide
@@ -84,6 +86,7 @@
 
     /**
      * Creates a user with the specified name and options.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      *
      * @param name the user's name
      * @param flags flags that identify the type of user and other properties.
@@ -103,6 +106,7 @@
 
     /**
      * Returns information for all users on this device.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @return the list of users that were created.
      * @hide
      */
@@ -117,6 +121,7 @@
 
     /**
      * Removes a user and all associated data.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @param userHandle the integer handle of the user, where 0 is the primary user.
      * @hide
      */
@@ -131,6 +136,7 @@
 
     /**
      * Updates the user's name.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      *
      * @param userHandle the user's integer handle
      * @param name the new name for the user
@@ -162,6 +168,7 @@
     /**
      * Enable or disable the use of a guest account. If disabled, the existing guest account
      * will be wiped.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @param enable whether to enable a guest account.
      * @hide
      */
@@ -175,6 +182,7 @@
 
     /**
      * Checks if a guest user is enabled for this device.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @return whether a guest user is enabled
      * @hide
      */
@@ -189,6 +197,7 @@
 
     /**
      * Wipes all the data for a user, but doesn't remove the user.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @param userHandle
      * @hide
      */
diff --git a/core/java/android/util/FloatMath.java b/core/java/android/util/FloatMath.java
index e05169a..9556223 100644
--- a/core/java/android/util/FloatMath.java
+++ b/core/java/android/util/FloatMath.java
@@ -82,6 +82,16 @@
     public static native float exp(float value);
 
     /**
+     * Returns the closest float approximation of the result of raising {@code
+     * x} to the power of {@code y}.
+     *
+     * @param x the base of the operation.
+     * @param y the exponent of the operation.
+     * @return {@code x} to the power of {@code y}.
+     */
+    public static native float pow(float x, float y);
+
+    /**
      * Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
      * {@code y}</i><sup>{@code 2}</sup>{@code )}.
      *
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 074f910..e8ff01f 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -17,8 +17,6 @@
 package android.webkit;
 
 import android.content.Context;
-import android.os.Message;
-import android.os.Build;
 
 /**
  * Manages settings state for a WebView. When a WebView is first created, it
@@ -423,7 +421,7 @@
      * Gets the text zoom of the page in percent.
      *
      * @return the text zoom of the page in percent
-     * @see #setTextSizeZoom
+     * @see #setTextZoom
      */
     public synchronized int getTextZoom() {
         throw new MustOverrideException();
@@ -436,7 +434,7 @@
      * @deprecated Use {@link #setTextZoom} instead.
      */
     public synchronized void setTextSize(TextSize t) {
-        throw new MustOverrideException();
+        setTextZoom(t.value);
     }
 
     /**
@@ -449,7 +447,20 @@
      * @deprecated Use {@link #getTextZoom} instead.
      */
     public synchronized TextSize getTextSize() {
-        throw new MustOverrideException();
+        TextSize closestSize = null;
+        int smallestDelta = Integer.MAX_VALUE;
+        int textSize = getTextZoom();
+        for (TextSize size : TextSize.values()) {
+            int delta = Math.abs(textSize - size.value);
+            if (delta == 0) {
+                return size;
+            }
+            if (delta < smallestDelta) {
+                smallestDelta = delta;
+                closestSize = size;
+            }
+        }
+        return closestSize != null ? closestSize : TextSize.NORMAL;
     }
 
     /**
@@ -1235,7 +1246,7 @@
      * @param context a Context object used to access application assets
      */
     public static String getDefaultUserAgent(Context context) {
-        return WebView.getFactory().getDefaultUserAgent(context);
+        return WebViewFactory.getProvider().getStatics().getDefaultUserAgent(context);
     }
 
     /**
diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java
index eac1141..d1f8b4b 100644
--- a/core/java/android/webkit/WebSettingsClassic.java
+++ b/core/java/android/webkit/WebSettingsClassic.java
@@ -665,34 +665,6 @@
     }
 
     /**
-     * @see android.webkit.WebSettings#setTextSize(android.webkit.WebSettingsClassic.TextSize)
-     */
-    @Override
-    public synchronized void setTextSize(TextSize t) {
-        setTextZoom(t.value);
-    }
-
-    /**
-     * @see android.webkit.WebSettings#getTextSize()
-     */
-    @Override
-    public synchronized TextSize getTextSize() {
-        TextSize closestSize = null;
-        int smallestDelta = Integer.MAX_VALUE;
-        for (TextSize size : TextSize.values()) {
-            int delta = Math.abs(mTextSize - size.value);
-            if (delta == 0) {
-                return size;
-            }
-            if (delta < smallestDelta) {
-                smallestDelta = delta;
-                closestSize = size;
-            }
-        }
-        return closestSize != null ? closestSize : TextSize.NORMAL;
-    }
-
-    /**
      * Set the double-tap zoom of the page in percent. Default is 100.
      * @param doubleTapZoom A percent value for increasing or decreasing the double-tap zoom.
      */
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4c5699b..82635d7 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -310,15 +310,15 @@
         /**
          * Notifies the listener about progress made by a find operation.
          *
-         * @param numberOfMatches how many matches have been found
          * @param activeMatchOrdinal the zero-based ordinal of the currently selected match
+         * @param numberOfMatches how many matches have been found
          * @param isDoneCounting whether the find operation has actually completed. The listener
          *                       may be notified multiple times while the
          *                       operation is underway, and the numberOfMatches
          *                       value should not be considered final unless
          *                       isDoneCounting is true.
          */
-        public void onFindResultReceived(int numberOfMatches, int activeMatchOrdinal,
+        public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
             boolean isDoneCounting);
     }
 
@@ -1821,7 +1821,7 @@
         }
     }
 
-    static synchronized WebViewFactoryProvider getFactory() {
+    private static synchronized WebViewFactoryProvider getFactory() {
         // For now the main purpose of this function (and the factory abstration) is to keep
         // us honest and minimize usage of WebViewClassic internals when binding the proxy.
         checkThread();
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index b1d42aa..934ef83 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -42,6 +42,12 @@
          * {@link android.webkit.WebView#disablePlatformNotifications()}
          */
         void setPlatformNotificationsEnabled(boolean enable);
+
+        /**
+         * Implements the API method:
+         * {@link android.webkit.WebSettings#getDefaultUserAgent(Context) }
+         */
+        String getDefaultUserAgent(Context context);
     }
 
     Statics getStatics();
@@ -93,14 +99,4 @@
      * @return the singleton WebViewDatabase instance
      */
     WebViewDatabase getWebViewDatabase(Context context);
-
-    /**
-     * Returns the default User-Agent used by a WebView.
-     * An instance of WebView could use a different User-Agent if a call
-     * is made to {@link WebSettings#setUserAgent(int)} or
-     * {@link WebSettings#setUserAgentString(String)}.
-     *
-     * @param context a Context object used to access application assets
-     */
-    String getDefaultUserAgent(Context context);
 }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 1e268c4..d6f1807 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -527,8 +527,6 @@
                                 "Duplicate arg specified");
                     }
                     niceName = arg.substring(arg.indexOf('=') + 1);
-                } else if (arg.equals("--mount-external-singleuser")) {
-                    mountExternal = Zygote.MOUNT_EXTERNAL_SINGLEUSER;
                 } else if (arg.equals("--mount-external-multiuser")) {
                     mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
                 } else {
diff --git a/core/jni/android_util_FloatMath.cpp b/core/jni/android_util_FloatMath.cpp
index 529fbe9..73b7a6f 100644
--- a/core/jni/android_util_FloatMath.cpp
+++ b/core/jni/android_util_FloatMath.cpp
@@ -30,6 +30,10 @@
         return expf(x);
     }
 
+    static float PowF(JNIEnv* env, jobject clazz, float x, float y) {
+        return powf(x, y);
+    }
+
     static float HypotF(JNIEnv* env, jobject clazz, float x, float y) {
         return hypotf(x, y);
     }
@@ -42,6 +46,7 @@
     {"cos", "(F)F", (void*) MathUtilsGlue::CosF},
     {"sqrt", "(F)F", (void*) MathUtilsGlue::SqrtF},
     {"exp", "(F)F", (void*) MathUtilsGlue::ExpF},
+    {"pow", "(FF)F", (void*) MathUtilsGlue::PowF},
     {"hypot", "(FF)F", (void*) MathUtilsGlue::HypotF},
 };
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 89be5cb..1c9b440 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -790,7 +790,7 @@
          third party applications. -->
     <permission android:name="android.permission.MANAGE_USERS"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="signature"
+        android:protectionLevel="signature|system"
         android:label="@string/permlab_manageUsers"
         android:description="@string/permdesc_manageUsers" />
     
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e3c957b..86d03ad 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -554,9 +554,22 @@
     <integer-array name="config_autoBrightnessLevels">
     </integer-array>
 
+    <!-- Minimum screen brightness setting allowed by the power manager.
+         The user is forbidden from setting the brightness below this level. -->
+    <integer name="config_screenBrightnessSettingMinimum">10</integer>
 
-    <!-- Minimum screen brightness allowed by the power manager. -->
-    <integer name="config_screenBrightnessDim">20</integer>
+    <!-- Maximum screen brightness allowed by the power manager.
+         The user is forbidden from setting the brightness above this level. -->
+    <integer name="config_screenBrightnessSettingMaximum">255</integer>
+
+    <!-- Default screen brightness setting.
+         Must be in the range specified by minimum and maximum. -->
+    <integer name="config_screenBrightnessSettingDefault">102</integer>
+
+    <!-- Screen brightness used to dim the screen when the user activity
+         timeout expires.  May be less than the minimum allowed brightness setting
+         that can be set by the user. -->
+    <integer name="config_screenBrightnessDim">10</integer>
 
     <!-- Array of output values for LCD backlight corresponding to the LUX values
          in the config_autoBrightnessLevels array.  This array should have size one greater
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0d190ee..e17a05d 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1471,6 +1471,9 @@
   <java-symbol type="integer" name="config_notificationsBatteryLowARGB" />
   <java-symbol type="integer" name="config_notificationsBatteryMediumARGB" />
   <java-symbol type="integer" name="config_radioScanningTimeout" />
+  <java-symbol type="integer" name="config_screenBrightnessSettingMinimum" />
+  <java-symbol type="integer" name="config_screenBrightnessSettingMaximum" />
+  <java-symbol type="integer" name="config_screenBrightnessSettingDefault" />
   <java-symbol type="integer" name="config_screenBrightnessDim" />
   <java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
   <java-symbol type="layout" name="am_compat_mode_dialog" />
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 4b51994..2907f10 100755
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -29,6 +29,9 @@
 import android.provider.MediaStore;
 import android.util.Log;
 
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -582,7 +585,28 @@
         if (null == path || path.equals("")) {
             throw new IllegalArgumentException("Given path should be non null");
         }
-        return _getOriginalMimeType(mUniqueId, path);
+
+        String mime = null;
+
+        FileInputStream is = null;
+        try {
+            FileDescriptor fd = null;
+            File file = new File(path);
+            if (file.exists()) {
+                is = new FileInputStream(file);
+                fd = is.getFD();
+            }
+            mime = _getOriginalMimeType(mUniqueId, path, fd);
+        } catch (IOException ioe) {
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch(IOException e) {}
+            }
+        }
+
+        return mime;
     }
 
     /**
@@ -848,7 +872,7 @@
 
     private native int _getDrmObjectType(int uniqueId, String path, String mimeType);
 
-    private native String _getOriginalMimeType(int uniqueId, String path);
+    private native String _getOriginalMimeType(int uniqueId, String path, FileDescriptor fd);
 
     private native int _checkRightsStatus(int uniqueId, String path, int action);
 
diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
index fb685a2..baddf62 100644
--- a/drm/jni/android_drm_DrmManagerClient.cpp
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -587,22 +587,28 @@
 }
 
 static jstring android_drm_DrmManagerClient_getOriginalMimeType(
-            JNIEnv* env, jobject thiz, jint uniqueId, jstring path) {
+            JNIEnv* env, jobject thiz, jint uniqueId, jstring path, jobject fileDescriptor) {
     ALOGV("getOriginalMimeType Enter");
+
+    int fd = (fileDescriptor == NULL)
+                ? -1
+                : jniGetFDFromFileDescriptor(env, fileDescriptor);
+
     String8 mimeType
         = getDrmManagerClientImpl(env, thiz)
-            ->getOriginalMimeType(uniqueId, Utility::getStringValue(env, path));
+            ->getOriginalMimeType(uniqueId,
+                                  Utility::getStringValue(env, path), fd);
     ALOGV("getOriginalMimeType Exit");
     return env->NewStringUTF(mimeType.string());
 }
 
 static jint android_drm_DrmManagerClient_checkRightsStatus(
             JNIEnv* env, jobject thiz, jint uniqueId, jstring path, int action) {
-    ALOGV("getOriginalMimeType Enter");
+    ALOGV("checkRightsStatus Enter");
     int rightsStatus
         = getDrmManagerClientImpl(env, thiz)
             ->checkRightsStatus(uniqueId, Utility::getStringValue(env, path), action);
-    ALOGV("getOriginalMimeType Exit");
+    ALOGV("checkRightsStatus Exit");
     return rightsStatus;
 }
 
@@ -730,7 +736,7 @@
     {"_getDrmObjectType", "(ILjava/lang/String;Ljava/lang/String;)I",
                                     (void*)android_drm_DrmManagerClient_getDrmObjectType},
 
-    {"_getOriginalMimeType", "(ILjava/lang/String;)Ljava/lang/String;",
+    {"_getOriginalMimeType", "(ILjava/lang/String;Ljava/io/FileDescriptor;)Ljava/lang/String;",
                                     (void*)android_drm_DrmManagerClient_getOriginalMimeType},
 
     {"_checkRightsStatus", "(ILjava/lang/String;I)I",
diff --git a/graphics/java/android/renderscript/ScriptIntrinsic.java b/graphics/java/android/renderscript/ScriptIntrinsic.java
index 6ad1527..f275fee 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsic.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsic.java
@@ -28,8 +28,4 @@
     ScriptIntrinsic(int id, RenderScript rs) {
         super(id, rs);
     }
-
-    public void forEach(Allocation ain, Allocation aout) {
-        forEach(0, ain, aout, null);
-    }
 }
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java b/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
index 0ae1449..c7465a7 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
@@ -32,6 +32,7 @@
  **/
 public class ScriptIntrinsicConvolve3x3 extends ScriptIntrinsic {
     private float[] mValues = new float[9];
+    private Allocation mInput;
 
     ScriptIntrinsicConvolve3x3(int id, RenderScript rs) {
         super(id, rs);
@@ -52,8 +53,12 @@
 
     }
 
+    public void setInput(Allocation ain) {
+        mInput = ain;
+        bindAllocation(ain, 1);
+    }
 
-    public void setValues(float v[]) {
+    public void setColorMatrix(float v[]) {
         FieldPacker fp = new FieldPacker(9*4);
         for (int ct=0; ct < mValues.length; ct++) {
             mValues[ct] = v[ct];
@@ -61,5 +66,10 @@
         }
         setVar(0, fp);
     }
+
+    public void forEach(Allocation aout) {
+        forEach(0, null, aout, null);
+    }
+
 }
 
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
new file mode 100644
index 0000000..c42001b
--- /dev/null
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2012 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.security;
+
+import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
+
+import org.apache.harmony.xnet.provider.jsse.OpenSSLEngine;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyPairGeneratorSpi;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+
+/**
+ * Provides a way to create instances of a KeyPair which will be placed in the
+ * Android keystore service usable only by the application that called it. This
+ * can be used in conjunction with
+ * {@link java.security.KeyStore#getInstance(String)} using the
+ * {@code "AndroidKeyStore"} type.
+ * <p>
+ * This class can not be directly instantiated and must instead be used via the
+ * {@link KeyPairGenerator#getInstance(String)
+ * KeyPairGenerator.getInstance("AndroidKeyPairGenerator")} API.
+ *
+ * {@hide}
+ */
+@SuppressWarnings("deprecation")
+public class AndroidKeyPairGenerator extends KeyPairGeneratorSpi {
+    public static final String NAME = "AndroidKeyPairGenerator";
+
+    private android.security.KeyStore mKeyStore;
+
+    private AndroidKeyPairGeneratorSpec mSpec;
+
+    /**
+     * Generate a KeyPair which is backed by the Android keystore service. You
+     * must call {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)}
+     * with an {@link AndroidKeyPairGeneratorSpec} as the {@code params}
+     * argument before calling this otherwise an {@code IllegalStateException}
+     * will be thrown.
+     * <p>
+     * This will create an entry in the Android keystore service with a
+     * self-signed certificate using the {@code params} specified in the
+     * {@code initialize(params)} call.
+     *
+     * @throws IllegalStateException when called before calling
+     *             {@link KeyPairGenerator#initialize(AlgorithmParameterSpec)}
+     * @see java.security.KeyPairGeneratorSpi#generateKeyPair()
+     */
+    @Override
+    public KeyPair generateKeyPair() {
+        if (mKeyStore == null || mSpec == null) {
+            throw new IllegalStateException(
+                    "Must call initialize with an AndroidKeyPairGeneratorSpec first");
+        }
+
+        final String alias = mSpec.getKeystoreAlias();
+
+        Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+
+        final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
+        mKeyStore.generate(privateKeyAlias);
+
+        final PrivateKey privKey;
+        final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
+        try {
+            privKey = engine.getPrivateKeyById(privateKeyAlias);
+        } catch (InvalidKeyException e) {
+            throw new RuntimeException("Can't get key", e);
+        }
+
+        final byte[] pubKeyBytes = mKeyStore.getPubkey(privateKeyAlias);
+
+        final PublicKey pubKey;
+        try {
+            final KeyFactory keyFact = KeyFactory.getInstance("RSA");
+            pubKey = keyFact.generatePublic(new X509EncodedKeySpec(pubKeyBytes));
+        } catch (NoSuchAlgorithmException e) {
+            throw new IllegalStateException("Can't instantiate RSA key generator", e);
+        } catch (InvalidKeySpecException e) {
+            throw new IllegalStateException("keystore returned invalid key encoding", e);
+        }
+
+        final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
+        certGen.setPublicKey(pubKey);
+        certGen.setSerialNumber(mSpec.getSerialNumber());
+        certGen.setSubjectDN(mSpec.getSubjectDN());
+        certGen.setIssuerDN(mSpec.getSubjectDN());
+        certGen.setNotBefore(mSpec.getStartDate());
+        certGen.setNotAfter(mSpec.getEndDate());
+        certGen.setSignatureAlgorithm("sha1WithRSA");
+
+        final X509Certificate cert;
+        try {
+            cert = certGen.generate(privKey);
+        } catch (Exception e) {
+            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+            throw new IllegalStateException("Can't generate certificate", e);
+        }
+
+        byte[] certBytes;
+        try {
+            certBytes = cert.getEncoded();
+        } catch (CertificateEncodingException e) {
+            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+            throw new IllegalStateException("Can't get encoding of certificate", e);
+        }
+
+        if (!mKeyStore.put(Credentials.USER_CERTIFICATE + alias, certBytes)) {
+            Credentials.deleteAllTypesForAlias(mKeyStore, alias);
+            throw new IllegalStateException("Can't store certificate in AndroidKeyStore");
+        }
+
+        return new KeyPair(pubKey, privKey);
+    }
+
+    @Override
+    public void initialize(int keysize, SecureRandom random) {
+        throw new IllegalArgumentException("cannot specify keysize with AndroidKeyPairGenerator");
+    }
+
+    @Override
+    public void initialize(AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidAlgorithmParameterException {
+        if (params == null) {
+            throw new InvalidAlgorithmParameterException(
+                    "must supply params of type AndroidKeyPairGenericSpec");
+        } else if (!(params instanceof AndroidKeyPairGeneratorSpec)) {
+            throw new InvalidAlgorithmParameterException(
+                    "params must be of type AndroidKeyPairGeneratorSpec");
+        }
+
+        AndroidKeyPairGeneratorSpec spec = (AndroidKeyPairGeneratorSpec) params;
+
+        mSpec = spec;
+        mKeyStore = android.security.KeyStore.getInstance();
+    }
+}
diff --git a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
new file mode 100644
index 0000000..311359c
--- /dev/null
+++ b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2012 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.security;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import java.math.BigInteger;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * This provides the required parameters needed for initializing the KeyPair
+ * generator that works with
+ * <a href="{@docRoot}guide/topics/security/keystore.html">Android KeyStore
+ * facility</a>.
+ */
+public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
+    private final String mKeystoreAlias;
+
+    private final Context mContext;
+
+    private final X500Principal mSubjectDN;
+
+    private final BigInteger mSerialNumber;
+
+    private final Date mStartDate;
+
+    private final Date mEndDate;
+
+    /**
+     * Parameter specification for the "{@code AndroidKeyPairGenerator}"
+     * instance of the {@link java.security.KeyPairGenerator} API. The
+     * {@code context} passed in may be used to pop up some UI to ask the user
+     * to unlock or initialize the Android keystore facility.
+     * <p>
+     * After generation, the {@code keyStoreAlias} is used with the
+     * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)}
+     * interface to retrieve the {@link PrivateKey} and its associated
+     * {@link Certificate} chain.
+     * <p>
+     * The KeyPair generator will create a self-signed certificate with the
+     * properties of {@code subjectDN} as its X.509v3 Subject Distinguished Name
+     * and as its X.509v3 Issuer Distinguished Name, using the specified
+     * {@code serialNumber}, and the validity date starting at {@code startDate}
+     * and ending at {@code endDate}.
+     *
+     * @param context Android context for the activity
+     * @param keyStoreAlias name to use for the generated key in the Android
+     *            keystore
+     * @param subjectDN X.509 v3 Subject Distinguished Name
+     * @param serialNumber X509 v3 certificate serial number
+     * @param startDate the start of the self-signed certificate validity period
+     * @param endDate the end date of the self-signed certificate validity
+     *            period
+     * @throws IllegalArgumentException when any argument is {@code null} or
+     *             {@code endDate} is before {@code startDate}.
+     */
+    public AndroidKeyPairGeneratorSpec(Context context, String keyStoreAlias,
+            X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate) {
+        if (context == null) {
+            throw new IllegalArgumentException("context == null");
+        } else if (TextUtils.isEmpty(keyStoreAlias)) {
+            throw new IllegalArgumentException("keyStoreAlias must not be empty");
+        } else if (subjectDN == null) {
+            throw new IllegalArgumentException("subjectDN == null");
+        } else if (serialNumber == null) {
+            throw new IllegalArgumentException("serialNumber == null");
+        } else if (startDate == null) {
+            throw new IllegalArgumentException("startDate == null");
+        } else if (endDate == null) {
+            throw new IllegalArgumentException("endDate == null");
+        } else if (endDate.before(startDate)) {
+            throw new IllegalArgumentException("endDate < startDate");
+        }
+
+        mContext = context;
+        mKeystoreAlias = keyStoreAlias;
+        mSubjectDN = subjectDN;
+        mSerialNumber = serialNumber;
+        mStartDate = startDate;
+        mEndDate = endDate;
+    }
+
+    /**
+     * @hide
+     */
+    String getKeystoreAlias() {
+        return mKeystoreAlias;
+    }
+
+    /**
+     * @hide
+     */
+    Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * @hide
+     */
+    X500Principal getSubjectDN() {
+        return mSubjectDN;
+    }
+
+    /**
+     * @hide
+     */
+    BigInteger getSerialNumber() {
+        return mSerialNumber;
+    }
+
+    /**
+     * @hide
+     */
+    Date getStartDate() {
+        return mStartDate;
+    }
+
+    /**
+     * @hide
+     */
+    Date getEndDate() {
+        return mEndDate;
+    }
+}
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index a629f8d..e19217f 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -46,9 +46,8 @@
 import java.util.Set;
 
 /**
- * A java.security.KeyStore interface for the Android KeyStore. This class is
- * hidden from the Android API, but an instance of it can be created via the
- * {@link java.security.KeyStore#getInstance(String)
+ * A java.security.KeyStore interface for the Android KeyStore. An instance of
+ * it can be created via the {@link java.security.KeyStore#getInstance(String)
  * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a
  * java.security.KeyStore backed by this "AndroidKeyStore" implementation.
  * <p>
@@ -277,7 +276,7 @@
          * Make sure we clear out all the types we know about before trying to
          * write.
          */
-        deleteAllTypesForAlias(alias);
+        Credentials.deleteAllTypesForAlias(mKeyStore, alias);
 
         if (!mKeyStore.importKey(Credentials.USER_PRIVATE_KEY + alias, keyBytes)) {
             throw new KeyStoreException("Couldn't put private key in keystore");
@@ -315,26 +314,11 @@
 
     @Override
     public void engineDeleteEntry(String alias) throws KeyStoreException {
-        if (!deleteAllTypesForAlias(alias)) {
+        if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
             throw new KeyStoreException("No such entry " + alias);
         }
     }
 
-    /**
-     * Delete all types (private key, certificate, CA certificate) for a
-     * particular {@code alias}. All three can exist for any given alias.
-     * Returns {@code true} if there was at least one of those types.
-     */
-    private boolean deleteAllTypesForAlias(String alias) {
-        /*
-         * Make sure every type is deleted. There can be all three types, so
-         * don't use a conditional here.
-         */
-        return mKeyStore.delKey(Credentials.USER_PRIVATE_KEY + alias)
-                | mKeyStore.delete(Credentials.USER_CERTIFICATE + alias)
-                | mKeyStore.delete(Credentials.CA_CERTIFICATE + alias);
-    }
-
     private Set<String> getUniqueAliases() {
         final String[] rawAliases = mKeyStore.saw("");
         if (rawAliases == null) {
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index df22f58..40d7e1a 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -29,6 +29,11 @@
     public AndroidKeyStoreProvider() {
         super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");
 
+        // java.security.KeyStore
         put("KeyStore." + AndroidKeyStore.NAME, AndroidKeyStore.class.getName());
+
+        // java.security.KeyPairGenerator
+        put("KeyPairGenerator." + AndroidKeyPairGenerator.NAME,
+                AndroidKeyPairGenerator.class.getName());
     }
 }
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 68ba2b1..72332eb 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -185,4 +185,19 @@
             Log.w(LOGTAG, e.toString());
         }
     }
+
+    /**
+     * Delete all types (private key, certificate, CA certificate) for a
+     * particular {@code alias}. All three can exist for any given alias.
+     * Returns {@code true} if there was at least one of those types.
+     */
+    static boolean deleteAllTypesForAlias(KeyStore keystore, String alias) {
+        /*
+         * Make sure every type is deleted. There can be all three types, so
+         * don't use a conditional here.
+         */
+        return keystore.delKey(Credentials.USER_PRIVATE_KEY + alias)
+                | keystore.delete(Credentials.USER_CERTIFICATE + alias)
+                | keystore.delete(Credentials.CA_CERTIFICATE + alias);
+    }
 }
diff --git a/keystore/java/android/security/package.html b/keystore/java/android/security/package.html
new file mode 100644
index 0000000..610cbf0
--- /dev/null
+++ b/keystore/java/android/security/package.html
@@ -0,0 +1,9 @@
+<HTML>
+<BODY>
+  <p>Provides access to a few facilities of the Android security
+    subsystems.</p>
+  <p>For information on how to use this facility, see the <a
+      href="{@docRoot}guide/topics/security/keystore.html">Android
+      KeyStore facility</a> guide.</p>
+</BODY>
+</HTML>
\ No newline at end of file
diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
new file mode 100644
index 0000000..e6a3750
--- /dev/null
+++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2012 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.security;
+
+import android.test.AndroidTestCase;
+
+import java.math.BigInteger;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase {
+    private static final String TEST_ALIAS_1 = "test1";
+
+    private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
+
+    private static final long NOW_MILLIS = System.currentTimeMillis();
+
+    private static final BigInteger SERIAL_1 = BigInteger.ONE;
+
+    /* We have to round this off because X509v3 doesn't store milliseconds. */
+    private static final Date NOW = new Date(NOW_MILLIS - (NOW_MILLIS % 1000L));
+
+    @SuppressWarnings("deprecation")
+    private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
+
+    public void testConstructor_Success() throws Exception {
+        AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec(getContext(),
+                TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW, NOW_PLUS_10_YEARS);
+
+        assertEquals("Context should be the one specified", getContext(), spec.getContext());
+
+        assertEquals("Alias should be the one specified", TEST_ALIAS_1, spec.getKeystoreAlias());
+
+        assertEquals("subjectDN should be the one specified", TEST_DN_1, spec.getSubjectDN());
+
+        assertEquals("startDate should be the one specified", NOW, spec.getStartDate());
+
+        assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate());
+    }
+
+    public void testConstructor_NullContext_Failure() throws Exception {
+        try {
+            new AndroidKeyPairGeneratorSpec(null, TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW,
+                    NOW_PLUS_10_YEARS);
+            fail("Should throw IllegalArgumentException when context is null");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testConstructor_NullKeystoreAlias_Failure() throws Exception {
+        try {
+            new AndroidKeyPairGeneratorSpec(getContext(), null, TEST_DN_1, SERIAL_1, NOW,
+                    NOW_PLUS_10_YEARS);
+            fail("Should throw IllegalArgumentException when keystoreAlias is null");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testConstructor_NullSubjectDN_Failure() throws Exception {
+        try {
+            new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, null, SERIAL_1, NOW,
+                    NOW_PLUS_10_YEARS);
+            fail("Should throw IllegalArgumentException when subjectDN is null");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testConstructor_NullSerial_Failure() throws Exception {
+        try {
+            new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, null, NOW,
+                    NOW_PLUS_10_YEARS);
+            fail("Should throw IllegalArgumentException when startDate is null");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testConstructor_NullStartDate_Failure() throws Exception {
+        try {
+            new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1, null,
+                    NOW_PLUS_10_YEARS);
+            fail("Should throw IllegalArgumentException when startDate is null");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testConstructor_NullEndDate_Failure() throws Exception {
+        try {
+            new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW,
+                    null);
+            fail("Should throw IllegalArgumentException when keystoreAlias is null");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testConstructor_EndBeforeStart_Failure() throws Exception {
+        try {
+            new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1, TEST_DN_1, SERIAL_1,
+                    NOW_PLUS_10_YEARS, NOW);
+            fail("Should throw IllegalArgumentException when end is before start");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+}
diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
new file mode 100644
index 0000000..d108caaa
--- /dev/null
+++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2012 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.security;
+
+import android.test.AndroidTestCase;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
+    private android.security.KeyStore mAndroidKeyStore;
+
+    private java.security.KeyPairGenerator mGenerator;
+
+    private static final String TEST_ALIAS_1 = "test1";
+
+    private static final String TEST_ALIAS_2 = "test2";
+
+    private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
+
+    private static final X500Principal TEST_DN_2 = new X500Principal("CN=test2");
+
+    private static final BigInteger TEST_SERIAL_1 = BigInteger.ONE;
+
+    private static final BigInteger TEST_SERIAL_2 = BigInteger.valueOf(2L);
+
+    private static final long NOW_MILLIS = System.currentTimeMillis();
+
+    /* We have to round this off because X509v3 doesn't store milliseconds. */
+    private static final Date NOW = new Date(NOW_MILLIS - (NOW_MILLIS % 1000L));
+
+    @SuppressWarnings("deprecation")
+    private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
+
+    @Override
+    protected void setUp() throws Exception {
+        mAndroidKeyStore = android.security.KeyStore.getInstance();
+
+        assertTrue(mAndroidKeyStore.reset());
+
+        assertEquals(android.security.KeyStore.State.UNINITIALIZED, mAndroidKeyStore.state());
+
+        assertTrue(mAndroidKeyStore.password("1111"));
+
+        assertEquals(android.security.KeyStore.State.UNLOCKED, mAndroidKeyStore.state());
+
+        assertEquals(0, mAndroidKeyStore.saw("").length);
+
+        mGenerator = java.security.KeyPairGenerator.getInstance(AndroidKeyPairGenerator.NAME);
+    }
+
+    public void testKeyPairGenerator_Initialize_Params_Success() throws Exception {
+        mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1,
+                TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS));
+    }
+
+    public void testKeyPairGenerator_Initialize_KeySize_Failure() throws Exception {
+        try {
+            mGenerator.initialize(1024);
+            fail("KeyPairGenerator should not support setting the key size");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Failure() throws Exception {
+        try {
+            mGenerator.initialize(1024, new SecureRandom());
+            fail("KeyPairGenerator should not support setting the key size");
+        } catch (IllegalArgumentException success) {
+        }
+    }
+
+    public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Failure() throws Exception {
+        mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1,
+                TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS), new SecureRandom());
+    }
+
+    public void testKeyPairGenerator_GenerateKeyPair_Success() throws Exception {
+        mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1,
+                TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS));
+
+        final KeyPair pair = mGenerator.generateKeyPair();
+        assertNotNull("The KeyPair returned should not be null", pair);
+
+        assertKeyPairCorrect(pair, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS);
+    }
+
+    public void testKeyPairGenerator_GenerateKeyPair_Replaced_Success() throws Exception {
+        // Generate the first key
+        {
+            mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_1,
+                    TEST_DN_1, TEST_SERIAL_1, NOW, NOW_PLUS_10_YEARS));
+            final KeyPair pair1 = mGenerator.generateKeyPair();
+            assertNotNull("The KeyPair returned should not be null", pair1);
+            assertKeyPairCorrect(pair1, TEST_ALIAS_1, TEST_DN_1, TEST_SERIAL_1, NOW,
+                    NOW_PLUS_10_YEARS);
+        }
+
+        // Replace the original key
+        {
+            mGenerator.initialize(new AndroidKeyPairGeneratorSpec(getContext(), TEST_ALIAS_2,
+                    TEST_DN_2, TEST_SERIAL_2, NOW, NOW_PLUS_10_YEARS));
+            final KeyPair pair2 = mGenerator.generateKeyPair();
+            assertNotNull("The KeyPair returned should not be null", pair2);
+            assertKeyPairCorrect(pair2, TEST_ALIAS_2, TEST_DN_2, TEST_SERIAL_2, NOW,
+                    NOW_PLUS_10_YEARS);
+        }
+    }
+
+    private void assertKeyPairCorrect(KeyPair pair, String alias, X500Principal dn,
+            BigInteger serial, Date start, Date end) throws Exception {
+        final PublicKey pubKey = pair.getPublic();
+        assertNotNull("The PublicKey for the KeyPair should be not null", pubKey);
+
+        final PrivateKey privKey = pair.getPrivate();
+        assertNotNull("The PrivateKey for the KeyPair should be not null", privKey);
+
+        final byte[] userCertBytes = mAndroidKeyStore.get(Credentials.USER_CERTIFICATE + alias);
+        assertNotNull("The user certificate should exist for the generated entry", userCertBytes);
+
+        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        final Certificate userCert = cf
+                .generateCertificate(new ByteArrayInputStream(userCertBytes));
+
+        assertTrue("Certificate should be in X.509 format", userCert instanceof X509Certificate);
+
+        final X509Certificate x509userCert = (X509Certificate) userCert;
+
+        assertEquals("PublicKey used to sign certificate should match one returned in KeyPair",
+                pubKey, x509userCert.getPublicKey());
+
+        assertEquals("The Subject DN should be the one passed into the params", dn,
+                x509userCert.getSubjectDN());
+
+        assertEquals("The Issuer DN should be the same as the Subject DN", dn,
+                x509userCert.getIssuerDN());
+
+        assertEquals("The Serial should be the one passed into the params", serial,
+                x509userCert.getSerialNumber());
+
+        assertEquals("The notBefore date should be the one passed into the params", start,
+                x509userCert.getNotBefore());
+
+        assertEquals("The notAfter date should be the one passed into the params", end,
+                x509userCert.getNotAfter());
+
+        x509userCert.verify(pubKey);
+
+        final byte[] caCerts = mAndroidKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+        assertNull("A list of CA certificates should not exist for the generated entry", caCerts);
+
+        final byte[] pubKeyBytes = mAndroidKeyStore.getPubkey(Credentials.USER_PRIVATE_KEY + alias);
+        assertNotNull("The keystore should return the public key for the generated key",
+                pubKeyBytes);
+    }
+}
diff --git a/location/java/com/android/internal/location/ProviderProperties.java b/location/java/com/android/internal/location/ProviderProperties.java
index 08aed80..def96f0 100644
--- a/location/java/com/android/internal/location/ProviderProperties.java
+++ b/location/java/com/android/internal/location/ProviderProperties.java
@@ -145,7 +145,7 @@
         parcel.writeInt(mHasMonetaryCost ? 1 : 0);
         parcel.writeInt(mSupportsAltitude ? 1 : 0);
         parcel.writeInt(mSupportsSpeed ? 1 : 0);
-        parcel.writeInt(mSupportsSpeed ? 1 : 0);
+        parcel.writeInt(mSupportsBearing ? 1 : 0);
         parcel.writeInt(mPowerRequirement);
         parcel.writeInt(mAccuracy);
     }
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index cb32d63..8eff1f4 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -27,7 +27,6 @@
     android:background="@drawable/notification_panel_bg"
     android:paddingTop="@dimen/notification_panel_padding_top"
     android:layout_marginLeft="@dimen/notification_panel_margin_left"
-    android:animateLayoutChanges="true"
     >
 
     <TextView
@@ -80,18 +79,10 @@
         </ScrollView>
     </LinearLayout>
 
-    <LinearLayout android:id="@+id/handle"
+    <View android:id="@+id/handle"
         android:layout_width="match_parent"
         android:layout_height="@dimen/close_handle_height"
         android:layout_gravity="bottom"
-        android:orientation="vertical"
-        >
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/close_handle_height"
-            android:layout_gravity="bottom"
-            android:scaleType="fitXY"
-            android:src="@drawable/status_bar_close"
-            />
-    </LinearLayout>
+        />
+
 </com.android.systemui.statusbar.phone.NotificationPanelView><!-- end of sliding panel -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 13a34ad..9c978d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -17,11 +17,23 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import com.android.systemui.R;
 
 public class NotificationPanelView extends PanelView {
+
+    Drawable mHandleBar;
+    float mHandleBarHeight;
+
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
+
+        Resources resources = context.getResources();
+        mHandleBar = resources.getDrawable(R.drawable.status_bar_close);
+        mHandleBarHeight = resources.getDimension(R.dimen.close_handle_height);
     }
 
     @Override
@@ -31,4 +43,20 @@
             "notifications,v=" + vel);
         super.fling(vel, always);
     }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (changed) {
+            mHandleBar.setBounds(0, 0, getWidth(), (int) mHandleBarHeight);
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        canvas.translate(0, getHeight() - mHandleBarHeight);
+        mHandleBar.draw(canvas);
+        canvas.translate(0, -getHeight() + mHandleBarHeight);
+    }
 }
diff --git a/services/common_time/common_time_server.cpp b/services/common_time/common_time_server.cpp
index 17f99b9..21e706f 100644
--- a/services/common_time/common_time_server.cpp
+++ b/services/common_time/common_time_server.cpp
@@ -53,9 +53,9 @@
 
 namespace android {
 
-const char*    CommonTimeServer::kDefaultMasterElectionAddr = "239.195.128.88";
-const uint16_t CommonTimeServer::kDefaultMasterElectionPort = 8887;
-const uint64_t CommonTimeServer::kDefaultSyncGroupID = 0;
+const char*    CommonTimeServer::kDefaultMasterElectionAddr = "255.255.255.255";
+const uint16_t CommonTimeServer::kDefaultMasterElectionPort = 8886;
+const uint64_t CommonTimeServer::kDefaultSyncGroupID = 1;
 const uint8_t  CommonTimeServer::kDefaultMasterPriority = 1;
 const uint32_t CommonTimeServer::kDefaultMasterAnnounceIntervalMs = 10000;
 const uint32_t CommonTimeServer::kDefaultSyncRequestIntervalMs = 1000;
@@ -752,6 +752,9 @@
 bool CommonTimeServer::handleWhoIsMasterRequest(
         const WhoIsMasterRequestPacket* request,
         const sockaddr_storage& srcAddr) {
+    // Skip our own messages which come back via broadcast loopback.
+    if (request->senderDeviceID == mDeviceID)
+        return true;
 
     char srcEPStr[64];
     sockaddrToString(srcAddr, true, srcEPStr, sizeof(srcEPStr));
@@ -829,6 +832,10 @@
 bool CommonTimeServer::handleWhoIsMasterResponse(
         const WhoIsMasterResponsePacket* response,
         const sockaddr_storage& srcAddr) {
+    // Skip our own messages which come back via broadcast loopback.
+    if (response->deviceID == mDeviceID)
+        return true;
+
     char srcEPStr[64];
     sockaddrToString(srcAddr, true, srcEPStr, sizeof(srcEPStr));
     mElectionLog.log("RXed WhoIs master response while in state %s.  "
@@ -996,6 +1003,10 @@
     uint8_t  newDevicePrio = packet->devicePriority;
     uint64_t newTimelineID = packet->timelineID;
 
+    // Skip our own messages which come back via broadcast loopback.
+    if (newDeviceID == mDeviceID)
+        return true;
+
     char srcEPStr[64];
     sockaddrToString(srcAddr, true, srcEPStr, sizeof(srcEPStr));
     mElectionLog.log("RXed master announcement while in state %s.  "
diff --git a/services/java/Android.mk b/services/java/Android.mk
index e70a6c9..95b28d9 100644
--- a/services/java/Android.mk
+++ b/services/java/Android.mk
@@ -13,9 +13,6 @@
 
 LOCAL_JAVA_LIBRARIES := android.policy telephony-common
 
-LOCAL_NO_EMMA_INSTRUMENT := true
-LOCAL_NO_EMMA_COMPILE := true
-
 include $(BUILD_JAVA_LIBRARY)
 
 include $(BUILD_DROIDDOC)
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 8a564f7..23b2706 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -1566,7 +1566,8 @@
             MockProvider provider = new MockProvider(name, this, properties);
             // remove the real provider if we are replacing GPS or network provider
             if (LocationManager.GPS_PROVIDER.equals(name)
-                    || LocationManager.NETWORK_PROVIDER.equals(name)) {
+                    || LocationManager.NETWORK_PROVIDER.equals(name)
+                    || LocationManager.FUSED_PROVIDER.equals(name)) {
                 LocationProviderInterface p = mProvidersByName.get(name);
                 if (p != null) {
                     removeProviderLocked(p);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a117b06..a6af144 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -139,6 +139,7 @@
         DockObserver dock = null;
         UsbService usb = null;
         SerialService serial = null;
+        TwilightService twilight = null;
         UiModeManagerService uiMode = null;
         RecognitionManagerService recognition = null;
         ThrottleService throttle = null;
@@ -586,9 +587,16 @@
             }
 
             try {
+                Slog.i(TAG, "Twilight Service");
+                twilight = new TwilightService(context);
+            } catch (Throwable e) {
+                reportWtf("starting TwilightService", e);
+            }
+
+            try {
                 Slog.i(TAG, "UI Mode Manager Service");
                 // Listen for UI mode changes
-                uiMode = new UiModeManagerService(context);
+                uiMode = new UiModeManagerService(context, twilight);
             } catch (Throwable e) {
                 reportWtf("starting UiModeManagerService", e);
             }
@@ -733,7 +741,12 @@
         w.getDefaultDisplay().getMetrics(metrics);
         context.getResources().updateConfiguration(config, metrics);
 
-        power.systemReady();
+        try {
+            power.systemReady(twilight);
+        } catch (Throwable e) {
+            reportWtf("making Power Manager Service ready", e);
+        }
+
         try {
             pm.systemReady();
         } catch (Throwable e) {
@@ -750,6 +763,7 @@
         final DockObserver dockF = dock;
         final UsbService usbF = usb;
         final ThrottleService throttleF = throttle;
+        final TwilightService twilightF = twilight;
         final UiModeManagerService uiModeF = uiMode;
         final AppWidgetService appWidgetF = appWidget;
         final WallpaperManagerService wallpaperF = wallpaper;
@@ -810,6 +824,11 @@
                     reportWtf("making USB Service ready", e);
                 }
                 try {
+                    if (twilightF != null) twilightF.systemReady();
+                } catch (Throwable e) {
+                    reportWtf("makin Twilight Service ready", e);
+                }
+                try {
                     if (uiModeF != null) uiModeF.systemReady();
                 } catch (Throwable e) {
                     reportWtf("making UI Mode Service ready", e);
diff --git a/services/java/com/android/server/TwilightService.java b/services/java/com/android/server/TwilightService.java
new file mode 100644
index 0000000..a7bce54
--- /dev/null
+++ b/services/java/com/android/server/TwilightService.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2012 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.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.text.format.DateUtils;
+import android.text.format.Time;
+import android.util.Slog;
+
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+
+import libcore.util.Objects;
+
+/**
+ * Figures out whether it's twilight time based on the user's location.
+ *
+ * Used by the UI mode manager and other components to adjust night mode
+ * effects based on sunrise and sunset.
+ */
+public final class TwilightService {
+    private static final String TAG = "TwilightService";
+
+    private static final boolean DEBUG = false;
+
+    private static final String ACTION_UPDATE_TWILIGHT_STATE =
+            "com.android.server.action.UPDATE_TWILIGHT_STATE";
+
+    private final Context mContext;
+    private final AlarmManager mAlarmManager;
+    private final LocationManager mLocationManager;
+    private final LocationHandler mLocationHandler;
+
+    private final Object mLock = new Object();
+
+    private final ArrayList<TwilightListenerRecord> mListeners =
+            new ArrayList<TwilightListenerRecord>();
+
+    private boolean mSystemReady;
+
+    private TwilightState mTwilightState;
+
+    public TwilightService(Context context) {
+        mContext = context;
+
+        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+        mLocationManager = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
+        mLocationHandler = new LocationHandler();
+    }
+
+    void systemReady() {
+        synchronized (mLock) {
+            mSystemReady = true;
+
+            IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+            filter.addAction(Intent.ACTION_TIME_CHANGED);
+            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+            filter.addAction(ACTION_UPDATE_TWILIGHT_STATE);
+            mContext.registerReceiver(mUpdateLocationReceiver, filter);
+
+            if (!mListeners.isEmpty()) {
+                mLocationHandler.enableLocationUpdates();
+            }
+        }
+    }
+
+    /**
+     * Gets the current twilight state.
+     *
+     * @return The current twilight state, or null if no information is available.
+     */
+    public TwilightState getCurrentState() {
+        synchronized (mLock) {
+            return mTwilightState;
+        }
+    }
+
+    /**
+     * Listens for twilight time.
+     *
+     * @param listener The listener.
+     * @param handler The handler on which to post calls into the listener.
+     */
+    public void registerListener(TwilightListener listener, Handler handler) {
+        synchronized (mLock) {
+            mListeners.add(new TwilightListenerRecord(listener, handler));
+
+            if (mSystemReady && mListeners.size() == 1) {
+                mLocationHandler.enableLocationUpdates();
+            }
+        }
+    }
+
+    private void setTwilightState(TwilightState state) {
+        synchronized (mLock) {
+            if (!Objects.equal(mTwilightState, state)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Twilight state changed: " + state);
+                }
+
+                mTwilightState = state;
+                int count = mListeners.size();
+                for (int i = 0; i < count; i++) {
+                    mListeners.get(i).post();
+                }
+            }
+        }
+    }
+
+    // The user has moved if the accuracy circles of the two locations don't overlap.
+    private static boolean hasMoved(Location from, Location to) {
+        if (to == null) {
+            return false;
+        }
+
+        if (from == null) {
+            return true;
+        }
+
+        // if new location is older than the current one, the device hasn't moved.
+        if (to.getElapsedRealtimeNano() < from.getElapsedRealtimeNano()) {
+            return false;
+        }
+
+        // Get the distance between the two points.
+        float distance = from.distanceTo(to);
+
+        // Get the total accuracy radius for both locations.
+        float totalAccuracy = from.getAccuracy() + to.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;
+    }
+
+    /**
+     * Describes whether it is day or night.
+     * This object is immutable.
+     */
+    public static final class TwilightState {
+        private final boolean mIsNight;
+        private final long mYesterdaySunset;
+        private final long mTodaySunrise;
+        private final long mTodaySunset;
+        private final long mTomorrowSunrise;
+
+        TwilightState(boolean isNight,
+                long yesterdaySunset,
+                long todaySunrise, long todaySunset,
+                long tomorrowSunrise) {
+            mIsNight = isNight;
+            mYesterdaySunset = yesterdaySunset;
+            mTodaySunrise = todaySunrise;
+            mTodaySunset = todaySunset;
+            mTomorrowSunrise = tomorrowSunrise;
+        }
+
+        /**
+         * Returns true if it is currently night time.
+         */
+        public boolean isNight() {
+            return mIsNight;
+        }
+
+        /**
+         * Returns the time of yesterday's sunset in the System.currentTimeMillis() timebase,
+         * or -1 if the sun never sets.
+         */
+        public long getYesterdaySunset() {
+            return mYesterdaySunset;
+        }
+
+        /**
+         * Returns the time of today's sunrise in the System.currentTimeMillis() timebase,
+         * or -1 if the sun never rises.
+         */
+        public long getTodaySunrise() {
+            return mTodaySunrise;
+        }
+
+        /**
+         * Returns the time of today's sunset in the System.currentTimeMillis() timebase,
+         * or -1 if the sun never sets.
+         */
+        public long getTodaySunset() {
+            return mTodaySunset;
+        }
+
+        /**
+         * Returns the time of tomorrow's sunrise in the System.currentTimeMillis() timebase,
+         * or -1 if the sun never rises.
+         */
+        public long getTomorrowSunrise() {
+            return mTomorrowSunrise;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return o instanceof TwilightState && equals((TwilightState)o);
+        }
+
+        public boolean equals(TwilightState other) {
+            return other != null
+                    && mIsNight == other.mIsNight
+                    && mYesterdaySunset == other.mYesterdaySunset
+                    && mTodaySunrise == other.mTodaySunrise
+                    && mTodaySunset == other.mTodaySunset
+                    && mTomorrowSunrise == other.mTomorrowSunrise;
+        }
+
+        @Override
+        public int hashCode() {
+            return 0; // don't care
+        }
+
+        @Override
+        public String toString() {
+            DateFormat f = DateFormat.getDateTimeInstance();
+            return "{TwilightState: isNight=" + mIsNight
+                    + ", mYesterdaySunset=" + f.format(new Date(mYesterdaySunset))
+                    + ", mTodaySunrise=" + f.format(new Date(mTodaySunrise))
+                    + ", mTodaySunset=" + f.format(new Date(mTodaySunset))
+                    + ", mTomorrowSunrise=" + f.format(new Date(mTomorrowSunrise))
+                    + "}";
+        }
+    }
+
+    /**
+     * Listener for changes in twilight state.
+     */
+    public interface TwilightListener {
+        public void onTwilightStateChanged();
+    }
+
+    private static final class TwilightListenerRecord implements Runnable {
+        private final TwilightListener mListener;
+        private final Handler mHandler;
+
+        public TwilightListenerRecord(TwilightListener listener, Handler handler) {
+            mListener = listener;
+            mHandler = handler;
+        }
+
+        public void post() {
+            mHandler.post(this);
+        }
+
+        @Override
+        public void run() {
+            mListener.onTwilightStateChanged();
+        }
+    }
+
+    private final class LocationHandler extends Handler {
+        private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
+        private static final int MSG_GET_NEW_LOCATION_UPDATE = 2;
+        private static final int MSG_PROCESS_NEW_LOCATION = 3;
+        private static final int MSG_DO_TWILIGHT_UPDATE = 4;
+
+        private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS;
+        private static final long MIN_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 =
+                15 * DateUtils.MINUTE_IN_MILLIS;
+        private static final double FACTOR_GMT_OFFSET_LONGITUDE =
+                1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
+
+        private boolean mPassiveListenerEnabled;
+        private boolean mNetworkListenerEnabled;
+        private boolean mDidFirstInit;
+        private long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS;
+        private long mLastUpdateInterval;
+        private Location mLocation;
+        private final TwilightCalculator mTwilightCalculator = new TwilightCalculator();
+
+        public void processNewLocation(Location location) {
+            Message msg = obtainMessage(MSG_PROCESS_NEW_LOCATION, location);
+            sendMessage(msg);
+        }
+
+        public void enableLocationUpdates() {
+            sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
+        }
+
+        public void requestLocationUpdate() {
+            sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
+        }
+
+        public void requestTwilightUpdate() {
+            sendEmptyMessage(MSG_DO_TWILIGHT_UPDATE);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_PROCESS_NEW_LOCATION: {
+                    final Location location = (Location)msg.obj;
+                    final boolean hasMoved = hasMoved(mLocation, location);
+                    final boolean hasBetterAccuracy = mLocation == null
+                            || location.getAccuracy() < mLocation.getAccuracy();
+                    if (DEBUG) {
+                        Slog.d(TAG, "Processing new location: " + location
+                               + ", hasMoved=" + hasMoved
+                               + ", hasBetterAccuracy=" + hasBetterAccuracy);
+                    }
+                    if (hasMoved || hasBetterAccuracy) {
+                        setLocation(location);
+                    }
+                    break;
+                }
+
+                case MSG_GET_NEW_LOCATION_UPDATE:
+                    if (!mNetworkListenerEnabled) {
+                        // Don't do anything -- we are still trying to get a
+                        // location.
+                        return;
+                    }
+                    if ((mLastNetworkRegisterTime + MIN_LOCATION_UPDATE_MS) >=
+                            SystemClock.elapsedRealtime()) {
+                        // Don't do anything -- it hasn't been long enough
+                        // since we last requested an update.
+                        return;
+                    }
+
+                    // Unregister the current location monitor, so we can
+                    // register a new one for it to get an immediate update.
+                    mNetworkListenerEnabled = false;
+                    mLocationManager.removeUpdates(mEmptyLocationListener);
+
+                    // Fall through to re-register listener.
+                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;
+                        mLastNetworkRegisterTime = SystemClock.elapsedRealtime();
+                        mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
+                                LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
+
+                        if (!mDidFirstInit) {
+                            mDidFirstInit = true;
+                            if (mLocation == null) {
+                                retrieveLocation();
+                            }
+                        }
+                    }
+
+                    // 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)) {
+                        mLastUpdateInterval *= 1.5;
+                        if (mLastUpdateInterval == 0) {
+                            mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN;
+                        } else if (mLastUpdateInterval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) {
+                            mLastUpdateInterval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX;
+                        }
+                        sendEmptyMessageDelayed(MSG_ENABLE_LOCATION_UPDATES, mLastUpdateInterval);
+                    }
+                    break;
+
+                case MSG_DO_TWILIGHT_UPDATE:
+                    updateTwilightState();
+                    break;
+            }
+        }
+
+        private void retrieveLocation() {
+            Location location = null;
+            final Iterator<String> providers =
+                    mLocationManager.getProviders(new Criteria(), true).iterator();
+            while (providers.hasNext()) {
+                final Location lastKnownLocation =
+                        mLocationManager.getLastKnownLocation(providers.next());
+                // pick the most recent location
+                if (location == null || (lastKnownLocation != null &&
+                        location.getElapsedRealtimeNano() <
+                        lastKnownLocation.getElapsedRealtimeNano())) {
+                    location = lastKnownLocation;
+                }
+            }
+
+            // 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());
+                location.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano());
+
+                if (DEBUG) {
+                    Slog.d(TAG, "Estimated location from timezone: " + location);
+                }
+            }
+
+            setLocation(location);
+        }
+
+        private void setLocation(Location location) {
+            mLocation = location;
+            updateTwilightState();
+        }
+
+        private void updateTwilightState() {
+            if (mLocation == null) {
+                setTwilightState(null);
+                return;
+            }
+
+            final long now = System.currentTimeMillis();
+
+            // calculate yesterday's twilight
+            mTwilightCalculator.calculateTwilight(now - DateUtils.DAY_IN_MILLIS,
+                    mLocation.getLatitude(), mLocation.getLongitude());
+            final long yesterdaySunset = mTwilightCalculator.mSunset;
+
+            // calculate today's twilight
+            mTwilightCalculator.calculateTwilight(now,
+                    mLocation.getLatitude(), mLocation.getLongitude());
+            final boolean isNight = (mTwilightCalculator.mState == TwilightCalculator.NIGHT);
+            final long todaySunrise = mTwilightCalculator.mSunrise;
+            final long todaySunset = mTwilightCalculator.mSunset;
+
+            // calculate tomorrow's twilight
+            mTwilightCalculator.calculateTwilight(now + DateUtils.DAY_IN_MILLIS,
+                    mLocation.getLatitude(), mLocation.getLongitude());
+            final long tomorrowSunrise = mTwilightCalculator.mSunrise;
+
+            // set twilight state
+            TwilightState state = new TwilightState(isNight, yesterdaySunset,
+                    todaySunrise, todaySunset, tomorrowSunrise);
+            if (DEBUG) {
+                Slog.d(TAG, "Updating twilight state: " + state);
+            }
+            setTwilightState(state);
+
+            // schedule next update
+            long nextUpdate = 0;
+            if (todaySunrise == -1 || todaySunset == -1) {
+                // In the case the day or night never ends the update is scheduled 12 hours later.
+                nextUpdate = now + 12 * DateUtils.HOUR_IN_MILLIS;
+            } else {
+                // add some extra time to be on the safe side.
+                nextUpdate += DateUtils.MINUTE_IN_MILLIS;
+
+                if (now > todaySunset) {
+                    nextUpdate += tomorrowSunrise;
+                } else if (now > todaySunrise) {
+                    nextUpdate += todaySunset;
+                } else {
+                    nextUpdate += todaySunrise;
+                }
+            }
+
+            if (DEBUG) {
+                Slog.d(TAG, "Next update in " + (nextUpdate - now) + " ms");
+            }
+
+            Intent updateIntent = new Intent(ACTION_UPDATE_TWILIGHT_STATE);
+            PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
+            mAlarmManager.cancel(pendingIntent);
+            mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
+        }
+    };
+
+    private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())
+                    && !intent.getBooleanExtra("state", false)) {
+                // Airplane mode is now off!
+                mLocationHandler.requestLocationUpdate();
+                return;
+            }
+
+            // Time zone has changed or alarm expired.
+            mLocationHandler.requestTwilightUpdate();
+        }
+    };
+
+    // 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) {
+            mLocationHandler.processNewLocation(location);
+        }
+
+        public void onProviderDisabled(String provider) {
+        }
+
+        public void onProviderEnabled(String provider) {
+        }
+
+        public void onStatusChanged(String provider, int status, Bundle extras) {
+        }
+    };
+}
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 5299b71..617b29d 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -18,7 +18,6 @@
 
 import android.app.Activity;
 import android.app.ActivityManagerNative;
-import android.app.AlarmManager;
 import android.app.IUiModeManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -32,55 +31,33 @@
 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.os.SystemClock;
 import android.provider.Settings;
-import android.text.format.DateUtils;
-import android.text.format.Time;
 import android.util.Slog;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.Iterator;
 
 import com.android.internal.R;
 import com.android.internal.app.DisableCarModeActivity;
+import com.android.server.TwilightService.TwilightState;
 
 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";
-
     // Enable launching of applications when entering the dock.
     private static final boolean ENABLE_LAUNCH_CAR_DOCK_APP = true;
     private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true;
 
-    private static final int MSG_UPDATE_TWILIGHT = 0;
-    private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
-    private static final int MSG_GET_NEW_LOCATION_UPDATE = 2;
-
-    private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS;
-    private static final long MIN_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 = 15 * 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;
+    private final TwilightService mTwilightService;
+    private final Handler mHandler = new Handler();
 
     final Object mLock = new Object();
 
@@ -106,10 +83,6 @@
 
     private NotificationManager mNotificationManager;
 
-    private AlarmManager mAlarmManager;
-
-    private LocationManager mLocationManager;
-    private Location mLocation;
     private StatusBarManager mStatusBarManager;
     private final PowerManager.WakeLock mWakeLock;
 
@@ -206,15 +179,6 @@
         }
     };
 
-    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) {
@@ -236,113 +200,24 @@
         }
     };
 
-    private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() {
+    private final TwilightService.TwilightListener mTwilightListener =
+            new TwilightService.TwilightListener() {
         @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
-                if (!intent.getBooleanExtra("state", false)) {
-                    // Airplane mode is now off!
-                    mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
-                }
-            } else {
-                // Time zone has changed!
-                mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
-            }
+        public void onTwilightStateChanged() {
+            updateTwilight();
         }
     };
 
-    // 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.getElapsedRealtimeNano() < mLocation.getElapsedRealtimeNano()) {
-                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) {
+    public UiModeManagerService(Context context, TwilightService twilight) {
         mContext = context;
+        mTwilightService = twilight;
 
         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));
-        IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-        mContext.registerReceiver(mUpdateLocationReceiver, filter);
 
         PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
@@ -360,6 +235,8 @@
 
         mNightMode = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.UI_NIGHT_MODE, UiModeManager.MODE_NIGHT_AUTO);
+
+        mTwilightService.registerListener(mTwilightListener, mHandler);
     }
 
     public void disableCarMode(int flags) {
@@ -419,8 +296,8 @@
         synchronized (mLock) {
             mSystemReady = true;
             mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
+            updateComputedNightModeLocked();
             updateLocked(0, 0);
-            mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES);
         }
     }
 
@@ -467,7 +344,7 @@
         }
         if (mCarModeEnabled) {
             if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                updateTwilightLocked();
+                updateComputedNightModeLocked();
                 uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
                         : Configuration.UI_MODE_NIGHT_NO;
             } else {
@@ -651,191 +528,20 @@
         }
     }
 
-    private final Handler mHandler = new Handler() {
-
-        boolean mPassiveListenerEnabled;
-        boolean mNetworkListenerEnabled;
-        boolean mDidFirstInit;
-        long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS;
-
-        @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(0, 0);
-                        }
-                    }
-                    break;
-                case MSG_GET_NEW_LOCATION_UPDATE:
-                    if (!mNetworkListenerEnabled) {
-                        // Don't do anything -- we are still trying to get a
-                        // location.
-                        return;
-                    }
-                    if ((mLastNetworkRegisterTime+MIN_LOCATION_UPDATE_MS)
-                            >= SystemClock.elapsedRealtime()) {
-                        // Don't do anything -- it hasn't been long enough
-                        // since we last requested an update.
-                        return;
-                    }
-
-                    // Unregister the current location monitor, so we can
-                    // register a new one for it to get an immediate update.
-                    mNetworkListenerEnabled = false;
-                    mLocationManager.removeUpdates(mEmptyLocationListener);
-
-                    // Fall through to re-register listener.
-                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;
-                        mLastNetworkRegisterTime = SystemClock.elapsedRealtime();
-                        mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
-                                LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
-
-                        if (!mDidFirstInit) {
-                            mDidFirstInit = true;
-                            if (mLocation == null) {
-                                retrieveLocation();
-                            }
-                            synchronized (mLock) {
-                                if (isDoingNightMode() && mLocation != null
-                                        && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                                    updateTwilightLocked();
-                                    updateLocked(0, 0);
-                                }
-                            }
-                        }
-                    }
-                   // 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 updateTwilight() {
+        synchronized (mLock) {
+            if (isDoingNightMode() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+                updateComputedNightModeLocked();
+                updateLocked(0, 0);
             }
         }
+    }
 
-        private void retrieveLocation() {
-            Location location = null;
-            final Iterator<String> providers =
-                    mLocationManager.getProviders(new Criteria(), true).iterator();
-            while (providers.hasNext()) {
-                final Location lastKnownLocation =
-                        mLocationManager.getLastKnownLocation(providers.next());
-                // pick the most recent location
-                if (location == null || (lastKnownLocation != null &&
-                        location.getElapsedRealtimeNano() <
-                        lastKnownLocation.getElapsedRealtimeNano())) {
-                    location = lastKnownLocation;
-                }
-            }
-            // 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());
-                location.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano());
-            }
-            synchronized (mLock) {
-                mLocation = location;
-            }
+    private void updateComputedNightModeLocked() {
+        TwilightState state = mTwilightService.getCurrentState();
+        if (state != null) {
+            mComputedNightMode = state.isNight();
         }
-    };
-
-    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
@@ -860,9 +566,8 @@
                     pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode));
             pw.print("  mHoldingConfiguration="); pw.print(mHoldingConfiguration);
                     pw.print(" mSystemReady="); pw.println(mSystemReady);
-            if (mLocation != null) {
-                pw.print("  mLocation="); pw.println(mLocation);
-            }
+            pw.print("  mTwilightService.getCurrentState()=");
+                    pw.println(mTwilightService.getCurrentState());
         }
     }
 }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 7f2b69f..8fc12d3 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -105,6 +105,7 @@
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.SELinux;
 import android.os.ServiceManager;
 import android.os.StrictMode;
 import android.os.SystemClock;
@@ -1982,18 +1983,13 @@
                 try {
                     final PackageManager pm = mContext.getPackageManager();
                     gids = pm.getPackageGids(app.info.packageName);
-                    if (pm.checkPermission(
-                            android.Manifest.permission.READ_EXTERNAL_STORAGE, app.info.packageName)
-                            == PERMISSION_GRANTED) {
-                        if (Environment.isExternalStorageEmulated()) {
-                            mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
-                        } else {
-                            mountExternal = Zygote.MOUNT_EXTERNAL_SINGLEUSER;
-                        }
-                    }
                 } catch (PackageManager.NameNotFoundException e) {
                     Slog.w(TAG, "Unable to retrieve gids", e);
                 }
+
+                if (Environment.isExternalStorageEmulated()) {
+                    mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
+                }
             }
             if (mFactoryTest != SystemServer.FACTORY_TEST_OFF) {
                 if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
@@ -3040,7 +3036,12 @@
         File tracesFile = new File(tracesPath);
         try {
             File tracesDir = tracesFile.getParentFile();
-            if (!tracesDir.exists()) tracesFile.mkdirs();
+            if (!tracesDir.exists()) {
+                tracesFile.mkdirs();
+                if (!SELinux.restorecon(tracesDir)) {
+                    return null;
+                }
+            }
             FileUtils.setPermissions(tracesDir.getPath(), 0775, -1, -1);  // drwxrwxr-x
 
             if (clearTraces && tracesFile.exists()) tracesFile.delete();
@@ -3144,7 +3145,12 @@
             final File tracesDir = tracesFile.getParentFile();
             final File tracesTmp = new File(tracesDir, "__tmp__");
             try {
-                if (!tracesDir.exists()) tracesFile.mkdirs();
+                if (!tracesDir.exists()) {
+                    tracesFile.mkdirs();
+                    if (!SELinux.restorecon(tracesDir.getPath())) {
+                        return;
+                    }
+                }
                 FileUtils.setPermissions(tracesDir.getPath(), 0775, -1, -1);  // drwxrwxr-x
 
                 if (tracesFile.exists()) {
diff --git a/services/java/com/android/server/am/DeviceMonitor.java b/services/java/com/android/server/am/DeviceMonitor.java
index 5f3b0ce..21e7252 100644
--- a/services/java/com/android/server/am/DeviceMonitor.java
+++ b/services/java/com/android/server/am/DeviceMonitor.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.os.SELinux;
 import android.util.Slog;
 
 import java.io.*;
@@ -80,6 +81,9 @@
         if (!BASE.isDirectory() && !BASE.mkdirs()) {
             throw new AssertionError("Couldn't create " + BASE + ".");
         }
+        if (!SELinux.restorecon(BASE)) {
+            throw new AssertionError("Couldn't restorecon " + BASE + ".");
+        }
     }
 
     private static final File[] PATHS = {
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 8d7e92a..2d4fa2b 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -496,6 +496,7 @@
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(ALARM_WAKEUP);
         intentFilter.addAction(ALARM_TIMEOUT);
+        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         mContext.registerReceiver(mBroadcastReciever, intentFilter);
     }
 
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index b13bb68..6a76c5d 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -137,11 +137,17 @@
     public UserInfo getUserInfo(int userId) {
         checkManageUsersPermission("query user");
         synchronized (mUsers) {
-            UserInfo info = mUsers.get(userId);
-            return info;
+            return getUserInfoLocked(userId);
         }
     }
 
+    /*
+     * Should be locked on mUsers before calling this.
+     */
+    private UserInfo getUserInfoLocked(int userId) {
+        return mUsers.get(userId);
+    }
+
     public boolean exists(int userId) {
         synchronized (mUsers) {
             return ArrayUtils.contains(mUserIds, userId);
@@ -212,8 +218,9 @@
     }
 
     /**
-     * Enforces that only the system UID or root's UID can call a method exposed
-     * via Binder.
+     * Enforces that only the system UID or root's UID or apps that have the
+     * {@link android.Manifest.permission.MANAGE_USERS MANAGE_USERS}
+     * permission can make certain calls to the UserManager.
      *
      * @param message used as message if SecurityException is thrown
      * @throws SecurityException if the caller is not system or root
@@ -538,7 +545,7 @@
     public int getUserSerialNumber(int userHandle) {
         synchronized (mUsers) {
             if (!exists(userHandle)) return -1;
-            return getUserInfo(userHandle).serialNumber;
+            return getUserInfoLocked(userHandle).serialNumber;
         }
     }
 
@@ -546,7 +553,7 @@
     public int getUserHandle(int userSerialNumber) {
         synchronized (mUsers) {
             for (int userId : mUserIds) {
-                if (getUserInfo(userId).serialNumber == userSerialNumber) return userId;
+                if (getUserInfoLocked(userId).serialNumber == userSerialNumber) return userId;
             }
             // Not found
             return -1;
@@ -619,14 +626,16 @@
      * @return
      */
     private int getNextAvailableId() {
-        int i = 0;
-        while (i < Integer.MAX_VALUE) {
-            if (mUsers.indexOfKey(i) < 0) {
-                break;
+        synchronized (mUsers) {
+            int i = 0;
+            while (i < Integer.MAX_VALUE) {
+                if (mUsers.indexOfKey(i) < 0) {
+                    break;
+                }
+                i++;
             }
-            i++;
+            return i;
         }
-        return i;
     }
 
     private boolean createPackageFolders(int id, File userPath) {
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index cf12b20..5f917af 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -17,6 +17,8 @@
 package com.android.server.power;
 
 import com.android.server.LightsService;
+import com.android.server.TwilightService;
+import com.android.server.TwilightService.TwilightState;
 
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
@@ -32,7 +34,10 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerManager;
 import android.os.SystemClock;
+import android.text.format.DateUtils;
+import android.util.FloatMath;
 import android.util.Slog;
 import android.util.Spline;
 import android.util.TimeUtils;
@@ -78,8 +83,31 @@
     // screen state returns.  Playing the animation can also be somewhat slow.
     private static final boolean USE_ELECTRON_BEAM_ON_ANIMATION = false;
 
-    private static final int ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS = 300;
-    private static final int ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS = 600;
+    // If true, enables the use of the screen auto-brightness adjustment setting.
+    private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = false;
+
+    // The maximum range of gamma adjustment possible using the screen
+    // auto-brightness adjustment setting.
+    private static final float SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA = 3.0f;
+
+    // If true, enables the use of the current time as an auto-brightness adjustment.
+    // The basic idea here is to expand the dynamic range of auto-brightness
+    // when it is especially dark outside.  The light sensor tends to perform
+    // poorly at low light levels so we compensate for it by making an
+    // assumption about the environment.
+    private static final boolean USE_TWILIGHT_ADJUSTMENT = true;
+
+    // Specifies the maximum magnitude of the time of day adjustment.
+    private static final float TWILIGHT_ADJUSTMENT_MAX_GAMMA = 1.5f;
+
+    // The amount of time after or before sunrise over which to start adjusting
+    // the gamma.  We want the change to happen gradually so that it is below the
+    // threshold of perceptibility and so that the adjustment has maximum effect
+    // well after dusk.
+    private static final long TWILIGHT_ADJUSTMENT_TIME = DateUtils.HOUR_IN_MILLIS * 2;
+
+    private static final int ELECTRON_BEAM_ON_ANIMATION_DURATION_MILLIS = 250;
+    private static final int ELECTRON_BEAM_OFF_ANIMATION_DURATION_MILLIS = 450;
 
     private static final int MSG_UPDATE_POWER_STATE = 1;
     private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
@@ -138,6 +166,9 @@
     // The lights service.
     private final LightsService mLights;
 
+    // The twilight service.
+    private final TwilightService mTwilight;
+
     // The sensor manager.
     private final SensorManager mSensorManager;
 
@@ -150,8 +181,11 @@
     // The dim screen brightness.
     private final int mScreenBrightnessDimConfig;
 
-    // Auto-brightness.
+    // True if auto-brightness should be used.
     private boolean mUseSoftwareAutoBrightnessConfig;
+
+    // The auto-brightness spline adjustment.
+    // The brightness values have been scaled to a range of 0..1.
     private Spline mScreenAutoBrightnessSpline;
 
     // Amount of time to delay auto-brightness after screen on while waiting for
@@ -266,6 +300,9 @@
     // Use -1 if there is no current auto-brightness value available.
     private int mScreenAutoBrightness = -1;
 
+    // The last screen auto-brightness gamma.  (For printing in dump() only.)
+    private float mLastScreenAutoBrightnessGamma = 1.0f;
+
     // True if the screen auto-brightness value is actually being used to
     // set the display brightness.
     private boolean mUsingScreenAutoBrightness;
@@ -275,11 +312,14 @@
     private ObjectAnimator mElectronBeamOffAnimator;
     private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
 
+    // Twilight changed.  We might recalculate auto-brightness values.
+    private boolean mTwilightChanged;
+
     /**
      * Creates the display power controller.
      */
     public DisplayPowerController(Looper looper, Context context, Notifier notifier,
-            LightsService lights, SuspendBlocker suspendBlocker,
+            LightsService lights, TwilightService twilight, SuspendBlocker suspendBlocker,
             Callbacks callbacks, Handler callbackHandler) {
         mHandler = new DisplayControllerHandler(looper);
         mNotifier = notifier;
@@ -288,6 +328,7 @@
         mCallbackHandler = callbackHandler;
 
         mLights = lights;
+        mTwilight = twilight;
         mSensorManager = new SystemSensorManager(mHandler.getLooper());
 
         final Resources resources = context.getResources();
@@ -328,6 +369,10 @@
                 && !DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
             mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
         }
+
+        if (mUseSoftwareAutoBrightnessConfig && USE_TWILIGHT_ADJUSTMENT) {
+            mTwilight.registerListener(mTwilightListener, mHandler);
+        }
     }
 
     private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
@@ -335,10 +380,10 @@
             final int n = brightness.length;
             float[] x = new float[n];
             float[] y = new float[n];
-            y[0] = brightness[0];
+            y[0] = (float)brightness[0] / PowerManager.BRIGHTNESS_ON;
             for (int i = 1; i < n; i++) {
                 x[i] = lux[i - 1];
-                y[i] = brightness[i];
+                y[i] = (float)brightness[i] / PowerManager.BRIGHTNESS_ON;
             }
 
             Spline spline = Spline.createMonotoneCubicSpline(x, y);
@@ -470,6 +515,9 @@
         // Update the power state request.
         final boolean mustNotify;
         boolean mustInitialize = false;
+        boolean updateAutoBrightness = mTwilightChanged;
+        mTwilightChanged = false;
+
         synchronized (mLock) {
             mPendingUpdatePowerStateLocked = false;
             if (mPendingRequestLocked == null) {
@@ -483,6 +531,10 @@
                 mPendingRequestChangedLocked = false;
                 mustInitialize = true;
             } else if (mPendingRequestChangedLocked) {
+                if (mPowerRequest.screenAutoBrightnessAdjustment
+                        != mPendingRequestLocked.screenAutoBrightnessAdjustment) {
+                    updateAutoBrightness = true;
+                }
                 mPowerRequest.copyFrom(mPendingRequestLocked);
                 mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
                 mPendingWaitForNegativeProximityLocked = false;
@@ -530,19 +582,21 @@
         // Turn on the light sensor if needed.
         if (mLightSensor != null) {
             setLightSensorEnabled(mPowerRequest.useAutoBrightness
-                    && wantScreenOn(mPowerRequest.screenState));
+                    && wantScreenOn(mPowerRequest.screenState), updateAutoBrightness);
         }
 
         // Set the screen brightness.
         if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) {
             // Screen is dimmed.  Overrides everything else.
-            animateScreenBrightness(mScreenBrightnessDimConfig, BRIGHTNESS_RAMP_RATE_FAST);
+            animateScreenBrightness(
+                    clampScreenBrightness(mScreenBrightnessDimConfig),
+                    BRIGHTNESS_RAMP_RATE_FAST);
             mUsingScreenAutoBrightness = false;
         } else if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT) {
             if (mScreenAutoBrightness >= 0 && mLightSensorEnabled) {
                 // Use current auto-brightness value.
                 animateScreenBrightness(
-                        Math.max(mScreenAutoBrightness, mScreenBrightnessDimConfig),
+                        clampScreenBrightness(mScreenAutoBrightness),
                         mUsingScreenAutoBrightness ? BRIGHTNESS_RAMP_RATE_SLOW :
                                 BRIGHTNESS_RAMP_RATE_FAST);
                 mUsingScreenAutoBrightness = true;
@@ -552,7 +606,7 @@
                 // provide a nominal default value for the case where auto-brightness
                 // is not ready yet.
                 animateScreenBrightness(
-                        Math.max(mPowerRequest.screenBrightness, mScreenBrightnessDimConfig),
+                        clampScreenBrightness(mPowerRequest.screenBrightness),
                         BRIGHTNESS_RAMP_RATE_FAST);
                 mUsingScreenAutoBrightness = false;
             }
@@ -630,6 +684,10 @@
         }
     }
 
+    private int clampScreenBrightness(int value) {
+        return Math.min(Math.max(Math.max(value, mScreenBrightnessDimConfig), 0), 255);
+    }
+
     private void animateScreenBrightness(int target, int rate) {
         if (mScreenBrightnessRampAnimator.animateTo(target, rate)) {
             mNotifier.onScreenBrightness(target);
@@ -691,9 +749,10 @@
         }
     }
 
-    private void setLightSensorEnabled(boolean enable) {
+    private void setLightSensorEnabled(boolean enable, boolean updateAutoBrightness) {
         if (enable) {
             if (!mLightSensorEnabled) {
+                updateAutoBrightness = true;
                 mLightSensorEnabled = true;
                 mLightSensorEnableTime = SystemClock.uptimeMillis();
                 mSensorManager.registerListener(mLightSensorListener, mLightSensor,
@@ -703,11 +762,13 @@
             if (mLightSensorEnabled) {
                 mLightSensorEnabled = false;
                 mLightMeasurementValid = false;
-                updateAutoBrightness(false);
                 mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
                 mSensorManager.unregisterListener(mLightSensorListener);
             }
         }
+        if (updateAutoBrightness) {
+            updateAutoBrightness(false);
+        }
     }
 
     private void handleLightSensorEvent(long time, float lux) {
@@ -818,24 +879,83 @@
             return;
         }
 
-        final int newScreenAutoBrightness = interpolateBrightness(
-                mScreenAutoBrightnessSpline, mLightMeasurement);
+        float value = mScreenAutoBrightnessSpline.interpolate(mLightMeasurement);
+        float gamma = 1.0f;
+
+        if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
+                && mPowerRequest.screenAutoBrightnessAdjustment != 0.0f) {
+            final float adjGamma = FloatMath.pow(SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA,
+                    Math.min(1.0f, Math.max(-1.0f,
+                            -mPowerRequest.screenAutoBrightnessAdjustment)));
+            gamma *= adjGamma;
+            if (DEBUG) {
+                Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
+            }
+        }
+
+        if (USE_TWILIGHT_ADJUSTMENT) {
+            TwilightState state = mTwilight.getCurrentState();
+            if (state != null && state.isNight()) {
+                final long now = System.currentTimeMillis();
+                final float earlyGamma =
+                        getTwilightGamma(now, state.getYesterdaySunset(), state.getTodaySunrise());
+                final float lateGamma =
+                        getTwilightGamma(now, state.getTodaySunset(), state.getTomorrowSunrise());
+                gamma *= earlyGamma * lateGamma;
+                if (DEBUG) {
+                    Slog.d(TAG, "updateAutoBrightness: earlyGamma=" + earlyGamma
+                            + ", lateGamma=" + lateGamma);
+                }
+            }
+        }
+
+        if (gamma != 1.0f) {
+            final float in = value;
+            value = FloatMath.pow(value, gamma);
+            if (DEBUG) {
+                Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
+                        + ", in=" + in + ", out=" + value);
+            }
+        }
+
+        int newScreenAutoBrightness = clampScreenBrightness(
+                (int)Math.round(value * PowerManager.BRIGHTNESS_ON));
         if (mScreenAutoBrightness != newScreenAutoBrightness) {
             if (DEBUG) {
                 Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
-                        + mScreenAutoBrightness + "newScreenAutoBrightness="
+                        + mScreenAutoBrightness + ", newScreenAutoBrightness="
                         + newScreenAutoBrightness);
             }
 
             mScreenAutoBrightness = newScreenAutoBrightness;
+            mLastScreenAutoBrightnessGamma = gamma;
             if (sendUpdate) {
                 sendUpdatePowerState();
             }
         }
     }
 
-    private static int interpolateBrightness(Spline spline, float lux) {
-        return Math.min(255, Math.max(0, (int)Math.round(spline.interpolate(lux))));
+    private static float getTwilightGamma(long now, long lastSunset, long nextSunrise) {
+        if (lastSunset < 0 || nextSunrise < 0
+                || now < lastSunset || now > nextSunrise) {
+            return 1.0f;
+        }
+
+        if (now < lastSunset + TWILIGHT_ADJUSTMENT_TIME) {
+            return lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
+                    (float)(now - lastSunset) / TWILIGHT_ADJUSTMENT_TIME);
+        }
+
+        if (now > nextSunrise - TWILIGHT_ADJUSTMENT_TIME) {
+            return lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
+                    (float)(nextSunrise - now) / TWILIGHT_ADJUSTMENT_TIME);
+        }
+
+        return TWILIGHT_ADJUSTMENT_MAX_GAMMA;
+    }
+
+    private static float lerp(float x, float y, float alpha) {
+        return x + (y - x) * alpha;
     }
 
     private void sendOnStateChanged() {
@@ -943,6 +1063,8 @@
                 + TimeUtils.formatUptime(mPendingLightSensorDebounceTime));
         pw.println("  mScreenAutoBrightness=" + mScreenAutoBrightness);
         pw.println("  mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness);
+        pw.println("  mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
+        pw.println("  mTwilight.getCurrentState()=" + mTwilight.getCurrentState());
 
         if (mElectronBeamOnAnimator != null) {
             pw.println("  mElectronBeamOnAnimator.isStarted()=" +
@@ -1043,4 +1165,13 @@
             // Not used.
         }
     };
+
+    private final TwilightService.TwilightListener mTwilightListener =
+            new TwilightService.TwilightListener() {
+        @Override
+        public void onTwilightStateChanged() {
+            mTwilightChanged = true;
+            updatePowerState();
+        }
+    };
 }
diff --git a/services/java/com/android/server/power/DisplayPowerRequest.java b/services/java/com/android/server/power/DisplayPowerRequest.java
index 7e4607e..2d74292 100644
--- a/services/java/com/android/server/power/DisplayPowerRequest.java
+++ b/services/java/com/android/server/power/DisplayPowerRequest.java
@@ -46,6 +46,9 @@
     // value to use while waiting for the light sensor to report enough data.
     public int screenBrightness;
 
+    // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter).
+    public float screenAutoBrightnessAdjustment;
+
     // If true, enables automatic brightness control.
     public boolean useAutoBrightness;
 
@@ -53,6 +56,7 @@
         screenState = SCREEN_STATE_BRIGHT;
         useProximitySensor = false;
         screenBrightness = PowerManager.BRIGHTNESS_ON;
+        screenAutoBrightnessAdjustment = 0.0f;
         useAutoBrightness = false;
     }
 
@@ -64,6 +68,7 @@
         screenState = other.screenState;
         useProximitySensor = other.useProximitySensor;
         screenBrightness = other.screenBrightness;
+        screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment;
         useAutoBrightness = other.useAutoBrightness;
     }
 
@@ -78,6 +83,7 @@
                 && screenState == other.screenState
                 && useProximitySensor == other.useProximitySensor
                 && screenBrightness == other.screenBrightness
+                && screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment
                 && useAutoBrightness == other.useAutoBrightness;
     }
 
@@ -91,6 +97,7 @@
         return "screenState=" + screenState
                 + ", useProximitySensor=" + useProximitySensor
                 + ", screenBrightness=" + screenBrightness
+                + ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment
                 + ", useAutoBrightness=" + useAutoBrightness;
     }
 }
diff --git a/services/java/com/android/server/power/ElectronBeam.java b/services/java/com/android/server/power/ElectronBeam.java
index 2d24249..aad5a9a 100644
--- a/services/java/com/android/server/power/ElectronBeam.java
+++ b/services/java/com/android/server/power/ElectronBeam.java
@@ -60,7 +60,7 @@
     // The relative proportion of the animation to spend performing
     // the horizontal stretch effect.  The remainder is spent performing
     // the vertical stretch effect.
-    private static final float HSTRETCH_DURATION = 0.3f;
+    private static final float HSTRETCH_DURATION = 0.4f;
     private static final float VSTRETCH_DURATION = 1.0f - HSTRETCH_DURATION;
 
     // Set to true when the animation context has been fully prepared.
diff --git a/services/java/com/android/server/power/Notifier.java b/services/java/com/android/server/power/Notifier.java
index 37384d2..9d928e5 100644
--- a/services/java/com/android/server/power/Notifier.java
+++ b/services/java/com/android/server/power/Notifier.java
@@ -28,7 +28,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.WorkSource;
@@ -117,19 +116,15 @@
                     + ", workSource=" + workSource);
         }
 
-        if (!isWakeLockAlreadyReportedToBatteryStats(tag, ownerUid)) {
-            try {
-                final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
-                if (workSource != null) {
-                    mBatteryStats.noteStartWakelockFromSource(
-                            workSource, ownerPid, tag, monitorType);
-                } else {
-                    mBatteryStats.noteStartWakelock(
-                            ownerUid, ownerPid, tag, monitorType);
-                }
-            } catch (RemoteException ex) {
-                // Ignore
+        try {
+            final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+            if (workSource != null) {
+                mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag, monitorType);
+            } else {
+                mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, monitorType);
             }
+        } catch (RemoteException ex) {
+            // Ignore
         }
     }
 
@@ -144,30 +139,18 @@
                     + ", workSource=" + workSource);
         }
 
-        if (!isWakeLockAlreadyReportedToBatteryStats(tag, ownerUid)) {
-            try {
-                final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
-                if (workSource != null) {
-                    mBatteryStats.noteStopWakelockFromSource(
-                            workSource, ownerPid, tag, monitorType);
-                } else {
-                    mBatteryStats.noteStopWakelock(
-                            ownerUid, ownerPid, tag, monitorType);
-                }
-            } catch (RemoteException ex) {
-                // Ignore
+        try {
+            final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+            if (workSource != null) {
+                mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, monitorType);
+            } else {
+                mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, monitorType);
             }
+        } catch (RemoteException ex) {
+            // Ignore
         }
     }
 
-    private static boolean isWakeLockAlreadyReportedToBatteryStats(String tag, int uid) {
-        // The window manager already takes care of reporting battery stats associated
-        // with the use of the KEEP_SCREEN_ON_FLAG.
-        // TODO: Use a WorkSource to handle this situation instead of hardcoding it here.
-        return uid == Process.SYSTEM_UID
-                && tag.equals(PowerManager.KEEP_SCREEN_ON_FLAG_TAG);
-    }
-
     private static int getBatteryStatsWakeLockMonitorType(int flags) {
         switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
             case PowerManager.PARTIAL_WAKE_LOCK:
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 2d91e6c..d3a3ffb 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -20,6 +20,7 @@
 import com.android.server.BatteryService;
 import com.android.server.EventLogTags;
 import com.android.server.LightsService;
+import com.android.server.TwilightService;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.display.DisplayManagerService;
@@ -249,6 +250,10 @@
     // Use -1 if no value has been set.
     private int mScreenBrightnessSetting;
 
+    // The screen auto-brightness adjustment setting, from -1 to 1.
+    // Use 0 if there is no adjustment.
+    private float mScreenAutoBrightnessAdjustmentSetting;
+
     // The screen brightness mode.
     // One of the Settings.System.SCREEN_BRIGHTNESS_MODE_* constants.
     private int mScreenBrightnessModeSetting;
@@ -263,6 +268,12 @@
     // Use -1 to disable.
     private int mTemporaryScreenBrightnessSettingOverride = -1;
 
+    // The screen brightness adjustment setting override from the settings
+    // application to temporarily adjust the auto-brightness adjustment factor
+    // until next updated, in the range -1..1.
+    // Use NaN to disable.
+    private float mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = Float.NaN;
+
     private native void nativeInit();
     private static native void nativeShutdown();
     private static native void nativeReboot(String reason) throws IOException;
@@ -308,7 +319,7 @@
         }
     }
 
-    public void systemReady() {
+    public void systemReady(TwilightService twilight) {
         synchronized (mLock) {
             mSystemReady = true;
 
@@ -321,7 +332,7 @@
                     createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
                     mPolicy, mScreenOnListener);
             mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(),
-                    mContext, mNotifier, mLightsService,
+                    mContext, mNotifier, mLightsService, twilight,
                     createSuspendBlockerLocked("PowerManagerService.Display"),
                     mDisplayPowerControllerCallbacks, mHandler);
 
@@ -389,6 +400,14 @@
             mTemporaryScreenBrightnessSettingOverride = -1;
         }
 
+        final float oldScreenAutoBrightnessAdjustmentSetting =
+                mScreenAutoBrightnessAdjustmentSetting;
+        mScreenAutoBrightnessAdjustmentSetting = Settings.System.getFloat(resolver,
+                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f);
+        if (oldScreenAutoBrightnessAdjustmentSetting != mScreenAutoBrightnessAdjustmentSetting) {
+            mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = Float.NaN;
+        }
+
         mScreenBrightnessModeSetting = Settings.System.getInt(resolver,
                 Settings.System.SCREEN_BRIGHTNESS_MODE,
                 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
@@ -1275,6 +1294,7 @@
             }
 
             int screenBrightness = mScreenBrightnessSettingDefault;
+            float screenAutoBrightnessAdjustment = 0.0f;
             boolean autoBrightness = (mScreenBrightnessModeSetting ==
                     Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
             if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
@@ -1283,14 +1303,26 @@
             } else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) {
                 screenBrightness = mTemporaryScreenBrightnessSettingOverride;
             } else if (isValidBrightness(mScreenBrightnessSetting)) {
-                screenBrightness =  mScreenBrightnessSetting;
+                screenBrightness = mScreenBrightnessSetting;
             }
             if (autoBrightness) {
                 screenBrightness = mScreenBrightnessSettingDefault;
+                if (isValidAutoBrightnessAdjustment(
+                        mTemporaryScreenAutoBrightnessAdjustmentSettingOverride)) {
+                    screenAutoBrightnessAdjustment =
+                            mTemporaryScreenAutoBrightnessAdjustmentSettingOverride;
+                } else if (isValidAutoBrightnessAdjustment(
+                        mScreenAutoBrightnessAdjustmentSetting)) {
+                    screenAutoBrightnessAdjustment = mScreenAutoBrightnessAdjustmentSetting;
+                }
             }
             screenBrightness = Math.max(Math.min(screenBrightness,
                     mScreenBrightnessSettingMaximum), mScreenBrightnessSettingMinimum);
+            screenAutoBrightnessAdjustment = Math.max(Math.min(
+                    screenAutoBrightnessAdjustment, 1.0f), -1.0f);
             mDisplayPowerRequest.screenBrightness = screenBrightness;
+            mDisplayPowerRequest.screenAutoBrightnessAdjustment =
+                    screenAutoBrightnessAdjustment;
             mDisplayPowerRequest.useAutoBrightness = autoBrightness;
 
             mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
@@ -1314,6 +1346,11 @@
         return value >= 0 && value <= 255;
     }
 
+    private static boolean isValidAutoBrightnessAdjustment(float value) {
+        // Handles NaN by always returning false.
+        return value >= -1.0f && value <= 1.0f;
+    }
+
     private int getDesiredScreenPowerState() {
         if (mWakefulness == WAKEFULNESS_ASLEEP) {
             return DisplayPowerRequest.SCREEN_STATE_OFF;
@@ -1690,15 +1727,32 @@
      *
      * The override will be canceled when the setting value is next updated.
      *
-     * @param adj The overridden brightness, or -1 to disable the override.
+     * @param adj The overridden brightness, or Float.NaN to disable the override.
      *
      * @see Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ
      */
     @Override // Binder call
     public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj) {
-        // Not implemented.
-        // The SCREEN_AUTO_BRIGHTNESS_ADJ setting is not currently supported.
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(adj);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(float adj) {
+        synchronized (mLock) {
+            // Note: This condition handles NaN because NaN is not equal to any other
+            // value, including itself.
+            if (mTemporaryScreenAutoBrightnessAdjustmentSettingOverride != adj) {
+                mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = adj;
+                mDirty |= DIRTY_SETTINGS;
+                updatePowerStateLocked();
+            }
+        }
     }
 
     /**
@@ -1774,11 +1828,15 @@
                     + isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")");
             pw.println("  mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting);
             pw.println("  mScreenBrightnessSetting=" + mScreenBrightnessSetting);
+            pw.println("  mScreenAutoBrightnessAdjustmentSetting="
+                    + mScreenAutoBrightnessAdjustmentSetting);
             pw.println("  mScreenBrightnessModeSetting=" + mScreenBrightnessModeSetting);
             pw.println("  mScreenBrightnessOverrideFromWindowManager="
                     + mScreenBrightnessOverrideFromWindowManager);
             pw.println("  mTemporaryScreenBrightnessSettingOverride="
                     + mTemporaryScreenBrightnessSettingOverride);
+            pw.println("  mTemporaryScreenAutoBrightnessAdjustmentSettingOverride="
+                    + mTemporaryScreenAutoBrightnessAdjustmentSettingOverride);
             pw.println("  mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum);
             pw.println("  mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum);
             pw.println("  mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index fd90082..b0c5835 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -94,6 +94,7 @@
 import android.os.SystemProperties;
 import android.os.TokenWatcher;
 import android.os.Trace;
+import android.os.WorkSource;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -923,7 +924,7 @@
         mContext.registerReceiver(mBroadcastReceiver, filter);
 
         mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
-                | PowerManager.ON_AFTER_RELEASE, PowerManager.KEEP_SCREEN_ON_FLAG_TAG);
+                | PowerManager.ON_AFTER_RELEASE, TAG);
         mHoldingScreenWakeLock.setReferenceCounted(false);
 
         mInputManager = new InputManagerService(context, mInputMonitor);
@@ -7020,7 +7021,7 @@
         public static final int REPORT_APPLICATION_TOKEN_WINDOWS = 8;
         public static final int REPORT_APPLICATION_TOKEN_DRAWN = 9;
         public static final int WINDOW_FREEZE_TIMEOUT = 11;
-        public static final int HOLD_SCREEN_CHANGED = 12;
+
         public static final int APP_TRANSITION_TIMEOUT = 13;
         public static final int PERSIST_ANIMATION_SCALE = 14;
         public static final int FORCE_GC = 15;
@@ -7041,8 +7042,6 @@
         public static final int SET_TRANSPARENT_REGION = ANIMATOR_WHAT_OFFSET + 1;
         public static final int CLEAR_PENDING_ACTIONS = ANIMATOR_WHAT_OFFSET + 2;
 
-        private Session mLastReportedHold;
-
         public H() {
         }
 
@@ -7294,33 +7293,6 @@
                     break;
                 }
 
-                case HOLD_SCREEN_CHANGED: {
-                    Session oldHold;
-                    Session newHold;
-                    synchronized (mWindowMap) {
-                        oldHold = mLastReportedHold;
-                        newHold = (Session)msg.obj;
-                        mLastReportedHold = newHold;
-                    }
-
-                    if (oldHold != newHold) {
-                        try {
-                            if (oldHold != null) {
-                                mBatteryStats.noteStopWakelock(oldHold.mUid, -1,
-                                        "window",
-                                        BatteryStats.WAKE_TYPE_WINDOW);
-                            }
-                            if (newHold != null) {
-                                mBatteryStats.noteStartWakelock(newHold.mUid, -1,
-                                        "window",
-                                        BatteryStats.WAKE_TYPE_WINDOW);
-                            }
-                        } catch (RemoteException e) {
-                        }
-                    }
-                    break;
-                }
-
                 case APP_TRANSITION_TIMEOUT: {
                     synchronized (mWindowMap) {
                         if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
@@ -9134,7 +9106,7 @@
         // Finally update all input windows now that the window changes have stabilized.
         mInputMonitor.updateInputWindowsLw(true /*force*/);
 
-        setHoldScreenLocked(mInnerFields.mHoldScreen != null);
+        setHoldScreenLocked(mInnerFields.mHoldScreen);
         if (!mDisplayFrozen) {
             if (mInnerFields.mScreenBrightness < 0 || mInnerFields.mScreenBrightness > 1.0f) {
                 mPowerManager.setScreenBrightnessOverrideFromWindowManager(-1);
@@ -9149,11 +9121,6 @@
                         toBrightnessOverride(mInnerFields.mButtonBrightness));
             }
         }
-        if (mInnerFields.mHoldScreen != mHoldingScreenOn) {
-            mHoldingScreenOn = mInnerFields.mHoldScreen;
-            Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, mInnerFields.mHoldScreen);
-            mH.sendMessage(m);
-        }
 
         if (mTurnOnScreen) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "Turning screen on after layout!");
@@ -9237,13 +9204,17 @@
         }
     }
 
-    /**
-     * Must be called with the main window manager lock held.
-     */
-    void setHoldScreenLocked(boolean holding) {
-        boolean state = mHoldingScreenWakeLock.isHeld();
-        if (holding != state) {
-            if (holding) {
+    void setHoldScreenLocked(final Session newHoldScreen) {
+        final boolean hold = newHoldScreen != null;
+
+        if (hold && mHoldingScreenOn != newHoldScreen) {
+            mHoldingScreenWakeLock.setWorkSource(new WorkSource(newHoldScreen.mUid));
+        }
+        mHoldingScreenOn = newHoldScreen;
+
+        final boolean state = mHoldingScreenWakeLock.isHeld();
+        if (hold != state) {
+            if (hold) {
                 mPolicy.screenOnStartedLw();
                 mHoldingScreenWakeLock.acquire();
             } else {
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 89b2474..8aeb2af 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -34,7 +34,8 @@
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
-
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+    
     <application>
         <uses-library android:name="android.test.runner" />
 
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Intrinsics.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Intrinsics.java
index ea8a018..dab8111 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Intrinsics.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Intrinsics.java
@@ -46,7 +46,7 @@
         v[0] = 0.f;     v[1] = -s;      v[2] = 0.f;
         v[3] = -s;      v[4] = s*4+1;   v[5] = -s;
         v[6] = 0.f;     v[7] = -s;      v[8] = 0.f;
-        mScript.setValues(v);
+        mScript.setColorMatrix(v);
     }
 
 
@@ -55,7 +55,8 @@
     }
 
     public void runTest() {
-        mScript.forEach(mInPixelsAllocation, mOutPixelsAllocation);
+        mScript.setInput(mInPixelsAllocation);
+        mScript.forEach(mOutPixelsAllocation);
     }
 
 }
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/grain.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/grain.rs
index 97ae4fb..7d9d3ac 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/grain.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/grain.rs
@@ -43,10 +43,10 @@
 
 rs_allocation gBlendSource;
 void blend9(uchar *out, uint32_t x, uint32_t y) {
-    uint32_t x1 = min(x+1, (uint32_t)gWidth);
-    uint32_t x2 = max(x-1, (uint32_t)0);
-    uint32_t y1 = min(y+1, (uint32_t)gHeight);
-    uint32_t y2 = max(y-1, (uint32_t)0);
+    uint32_t x1 = min((int32_t)x+1, (int32_t)gWidth);
+    uint32_t x2 = max((int32_t)x-1, (int32_t)0);
+    uint32_t y1 = min((int32_t)y+1, (int32_t)gHeight);
+    uint32_t y2 = max((int32_t)y-1, (int32_t)0);
 
     uint p00 = 56 *  ((uchar *)rsGetElementAt(gBlendSource, x1, y1))[0];
     uint p01 = 114 * ((uchar *)rsGetElementAt(gBlendSource, x, y1))[0];
diff --git a/tests/RenderScriptTests/ImageProcessing2/Android.mk b/tests/RenderScriptTests/ImageProcessing2/Android.mk
index c81fd93..e05a518 100644
--- a/tests/RenderScriptTests/ImageProcessing2/Android.mk
+++ b/tests/RenderScriptTests/ImageProcessing2/Android.mk
@@ -25,6 +25,10 @@
 LOCAL_STATIC_JAVA_LIBRARIES := android.support.v8.renderscript
 
 LOCAL_PACKAGE_NAME := ImageProcessing2
+LOCAL_SDK_VERSION := 8
+LOCAL_RENDERSCRIPT_TARGET_API := 17
+LOCAL_RENDERSCRIPT_INCLUDES_OVERRIDE := $(TOPDIR)external/clang/lib/Headers \
+                                        $(TOPDIR)frameworks/rs/scriptc
 
 LOCAL_RENDERSCRIPT_FLAGS := -rs-package-name=android.support.v8.renderscript
 LOCAL_REQUIRED_MODULES := librsjni
diff --git a/tests/RenderScriptTests/ImageProcessing2/AndroidManifest.xml b/tests/RenderScriptTests/ImageProcessing2/AndroidManifest.xml
index 1ef04c2..20ee053 100644
--- a/tests/RenderScriptTests/ImageProcessing2/AndroidManifest.xml
+++ b/tests/RenderScriptTests/ImageProcessing2/AndroidManifest.xml
@@ -2,9 +2,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.rs.image2">
-    <uses-sdk android:minSdkVersion="11" />
-    <application android:label="IP GB"
-                 android:hardwareAccelerated="true">
+    <uses-sdk android:minSdkVersion="8" />
+    <application android:label="IP GB">
         <activity android:name="ImageProcessingActivity2">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />