Merge "Fix crash in ShapeDrawable" into jb-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index b89f2f4..9d7bb14 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15617,6 +15617,7 @@
     method public static final void flushPendingCommands();
     method public static final int getCallingPid();
     method public static final int getCallingUid();
+    method public static final android.os.UserHandle getCallingUserHandle();
     method public java.lang.String getInterfaceDescriptor();
     method public boolean isBinderAlive();
     method public static final void joinThreadPool();
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 9e83a67..697d8ec 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -420,7 +420,7 @@
         return -1;
     }
     if (chmod(pkgpath, S_IRUSR|S_IWUSR|S_IRGRP) < 0) {
-        ALOGE("failed to chmod '%s': %s\n", pkgpath, strerror(errno));
+        ALOGE("protect(): failed to chmod '%s': %s\n", pkgpath, strerror(errno));
         return -1;
     }
 
@@ -1014,13 +1014,13 @@
 
     if (stat(dataDir, &s) < 0) return -1;
 
-    if (chown(dataDir, 0, 0) < 0) {
+    if (chown(dataDir, AID_INSTALL, AID_INSTALL) < 0) {
         ALOGE("failed to chown '%s': %s\n", dataDir, strerror(errno));
         return -1;
     }
 
     if (chmod(dataDir, 0700) < 0) {
-        ALOGE("failed to chmod '%s': %s\n", dataDir, strerror(errno));
+        ALOGE("linklib() 1: failed to chmod '%s': %s\n", dataDir, strerror(errno));
         rc = -1;
         goto out;
     }
@@ -1058,7 +1058,7 @@
 
 out:
     if (chmod(dataDir, s.st_mode) < 0) {
-        ALOGE("failed to chmod '%s': %s\n", dataDir, strerror(errno));
+        ALOGE("linklib() 2: failed to chmod '%s': %s\n", dataDir, strerror(errno));
         rc = -errno;
     }
 
@@ -1091,13 +1091,13 @@
         return -1;
     }
 
-    if (chown(dataDir, 0, 0) < 0) {
+    if (chown(dataDir, AID_INSTALL, AID_INSTALL) < 0) {
         ALOGE("failed to chown '%s': %s\n", dataDir, strerror(errno));
         return -1;
     }
 
     if (chmod(dataDir, 0700) < 0) {
-        ALOGE("failed to chmod '%s': %s\n", dataDir, strerror(errno));
+        ALOGE("unlinklib() 1: failed to chmod '%s': %s\n", dataDir, strerror(errno));
         rc = -1;
         goto out;
     }
@@ -1140,7 +1140,7 @@
 
 out:
     if (chmod(dataDir, s.st_mode) < 0) {
-        ALOGE("failed to chmod '%s': %s\n", dataDir, strerror(errno));
+        ALOGE("unlinklib() 2: failed to chmod '%s': %s\n", dataDir, strerror(errno));
         rc = -1;
     }
 
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ea14098..16b4835 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -64,7 +64,7 @@
     public static final native int getCallingPid();
     
     /**
-     * Return the ID of the user assigned to the process that sent you the
+     * Return the Linux uid assigned to the process that sent you the
      * current transaction that is being processed.  This uid can be used with
      * higher-level system services to determine its identity and check
      * permissions.  If the current thread is not currently executing an
@@ -73,6 +73,18 @@
     public static final native int getCallingUid();
 
     /**
+     * Return the UserHandle assigned to the process that sent you the
+     * current transaction that is being processed.  This is the user
+     * of the caller.  It is distinct from {@link #getCallingUid()} in that a
+     * particular user will have multiple distinct apps running under it each
+     * with their own uid.  If the current thread is not currently executing an
+     * incoming transaction, then its own UserHandle is returned.
+     */
+    public static final UserHandle getCallingUserHandle() {
+        return new UserHandle(UserHandle.getUserId(getCallingUid()));
+    }
+
+    /**
      * Reset the identity of the incoming IPC on the current thread.  This can
      * be useful if, while handling an incoming call, you will be calling
      * on interfaces of other objects that may be local to your process and
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e5b58dc..841a076 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4782,17 +4782,24 @@
         /**
          * Whether the package manager should send package verification broadcasts for verifiers to
          * review apps prior to installation.
-         *
+         * @deprecated moved to Settings.Global
          * 1 = request apps to be verified prior to installation, if a verifier exists.
          * 0 = do not verify apps before installation
          * {@hide}
          */
+        @Deprecated
         public static final String PACKAGE_VERIFIER_ENABLE = "package_verifier_enable";
 
-        /** Timeout for package verification. {@hide} */
+        /** Timeout for package verification.
+         * @deprecated moved to Settings.Global
+         * {@hide} */
+        @Deprecated
         public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";
 
-        /** Default response code for package verification. {@hide} */
+        /** Default response code for package verification.
+         * @deprecated moved to Settings.Global
+         * {@hide} */
+        @Deprecated
         public static final String PACKAGE_VERIFIER_DEFAULT_RESPONSE = "verifier_default_response";
 
         /** {@hide} */
@@ -5272,6 +5279,23 @@
        public static final String NTP_TIMEOUT = "ntp_timeout";
 
        /**
+        * Whether the package manager should send package verification broadcasts for verifiers to
+        * review apps prior to installation.
+        * 1 = request apps to be verified prior to installation, if a verifier exists.
+        * 0 = do not verify apps before installation
+        * {@hide}
+        */
+       public static final String PACKAGE_VERIFIER_ENABLE = "package_verifier_enable";
+
+       /** Timeout for package verification.
+        * {@hide} */
+       public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";
+
+       /** Default response code for package verification.
+        * {@hide} */
+       public static final String PACKAGE_VERIFIER_DEFAULT_RESPONSE = "verifier_default_response";
+
+       /**
         * The interval in milliseconds at which to check packet counts on the
         * mobile data interface when screen is on, to detect possible data
         * connection problems.
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index e9b0d32..b0b18da 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -437,6 +437,9 @@
      * @throws android.util.TimeFormatException if s cannot be parsed.
      */
     public boolean parse(String s) {
+        if (s == null) {
+            throw new NullPointerException("time string is null");
+        }
         if (nativeParse(s)) {
             timezone = TIMEZONE_UTC;
             return true;
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index db05e10..8f4626f 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -259,7 +259,7 @@
     private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId);
     private static native IBinder nativeCreateDisplay(String name);
     private static native void nativeSetDisplaySurface(
-            IBinder displayToken, SurfaceTexture surfaceTexture);
+            IBinder displayToken, Surface surface);
     private static native void nativeSetDisplayLayerStack(
             IBinder displayToken, int layerStack);
     private static native void nativeSetDisplayProjection(
@@ -597,11 +597,11 @@
     }
 
     /** @hide */
-    public static void setDisplaySurface(IBinder displayToken, SurfaceTexture surfaceTexture) {
+    public static void setDisplaySurface(IBinder displayToken, Surface surface) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
         }
-        nativeSetDisplaySurface(displayToken, surfaceTexture);
+        nativeSetDisplaySurface(displayToken, surface);
     }
 
     /** @hide */
diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java
new file mode 100644
index 0000000..7b8c582
--- /dev/null
+++ b/core/java/com/android/internal/util/DumpUtils.java
@@ -0,0 +1,57 @@
+/*
+ * 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.internal.util;
+
+import android.os.Handler;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Helper functions for dumping the state of system services.
+ */
+public final class DumpUtils {
+    private DumpUtils() {
+    }
+
+    /**
+     * Helper for dumping state owned by a handler thread.
+     *
+     * Because the caller might be holding an important lock that the handler is
+     * trying to acquire, we use a short timeout to avoid deadlocks.  The process
+     * is inelegant but this function is only used for debugging purposes.
+     */
+    public static void dumpAsync(Handler handler, final Dump dump, PrintWriter pw, long timeout) {
+        final StringWriter sw = new StringWriter();
+        if (handler.runWithScissors(new Runnable() {
+            @Override
+            public void run() {
+                PrintWriter lpw = new PrintWriter(sw);
+                dump.dump(lpw);
+                lpw.close();
+            }
+        }, timeout)) {
+            pw.print(sw.toString());
+        } else {
+            pw.println("... timed out");
+        }
+    }
+
+    public interface Dump {
+        void dump(PrintWriter pw);
+    }
+}
diff --git a/core/java/com/android/internal/util/IndentingPrintWriter.java b/core/java/com/android/internal/util/IndentingPrintWriter.java
index 699e9b3..dd5918b 100644
--- a/core/java/com/android/internal/util/IndentingPrintWriter.java
+++ b/core/java/com/android/internal/util/IndentingPrintWriter.java
@@ -28,7 +28,7 @@
     private final String mIndent;
 
     private StringBuilder mBuilder = new StringBuilder();
-    private String mCurrent = new String();
+    private char[] mCurrent;
     private boolean mEmptyLine = true;
 
     public IndentingPrintWriter(Writer writer, String indent) {
@@ -38,12 +38,12 @@
 
     public void increaseIndent() {
         mBuilder.append(mIndent);
-        mCurrent = mBuilder.toString();
+        mCurrent = null;
     }
 
     public void decreaseIndent() {
         mBuilder.delete(0, mIndent.length());
-        mCurrent = mBuilder.toString();
+        mCurrent = null;
     }
 
     public void printPair(String key, Object value) {
@@ -51,17 +51,35 @@
     }
 
     @Override
-    public void println() {
-        super.println();
-        mEmptyLine = true;
+    public void write(char[] buf, int offset, int count) {
+        final int bufferEnd = offset + count;
+        int lineStart = offset;
+        int lineEnd = offset;
+        while (lineEnd < bufferEnd) {
+            char ch = buf[lineEnd++];
+            if (ch == '\n') {
+                writeIndent();
+                super.write(buf, lineStart, lineEnd - lineStart);
+                lineStart = lineEnd;
+                mEmptyLine = true;
+            }
+        }
+
+        if (lineStart != lineEnd) {
+            writeIndent();
+            super.write(buf, lineStart, lineEnd - lineStart);
+        }
     }
 
-    @Override
-    public void write(char[] buf, int offset, int count) {
+    private void writeIndent() {
         if (mEmptyLine) {
             mEmptyLine = false;
-            super.print(mCurrent);
+            if (mBuilder.length() != 0) {
+                if (mCurrent == null) {
+                    mCurrent = mBuilder.toString().toCharArray();
+                }
+                super.write(mCurrent, 0, mCurrent.length);
+            }
         }
-        super.write(buf, offset, count);
     }
 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 9d45479..5f6042d 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -126,6 +126,7 @@
 	android_media_AudioSystem.cpp \
 	android_media_AudioTrack.cpp \
 	android_media_JetPlayer.cpp \
+	android_media_RemoteDisplay.cpp \
 	android_media_ToneGenerator.cpp \
 	android_hardware_Camera.cpp \
 	android_hardware_SensorManager.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 55563a8..27684d7 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -160,6 +160,7 @@
 extern int register_android_app_backup_FullBackup(JNIEnv *env);
 extern int register_android_app_ActivityThread(JNIEnv *env);
 extern int register_android_app_NativeActivity(JNIEnv *env);
+extern int register_android_media_RemoteDisplay(JNIEnv *env);
 extern int register_android_view_InputChannel(JNIEnv* env);
 extern int register_android_view_InputDevice(JNIEnv* env);
 extern int register_android_view_InputEventReceiver(JNIEnv* env);
@@ -1161,6 +1162,7 @@
     REG_JNI(register_android_media_AudioSystem),
     REG_JNI(register_android_media_AudioTrack),
     REG_JNI(register_android_media_JetPlayer),
+    REG_JNI(register_android_media_RemoteDisplay),
     REG_JNI(register_android_media_ToneGenerator),
 
     REG_JNI(register_android_opengl_classes),
diff --git a/core/jni/android_media_RemoteDisplay.cpp b/core/jni/android_media_RemoteDisplay.cpp
new file mode 100644
index 0000000..5d24f61
--- /dev/null
+++ b/core/jni/android_media_RemoteDisplay.cpp
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "RemoteDisplay"
+
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include "android_os_Parcel.h"
+#include "android_util_Binder.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_view_Surface.h>
+
+#include <binder/IServiceManager.h>
+
+#include <gui/ISurfaceTexture.h>
+
+#include <media/IMediaPlayerService.h>
+#include <media/IRemoteDisplay.h>
+#include <media/IRemoteDisplayClient.h>
+
+#include <utils/Log.h>
+
+#include <ScopedUtfChars.h>
+
+namespace android {
+
+static struct {
+    jmethodID notifyDisplayConnected;
+    jmethodID notifyDisplayDisconnected;
+    jmethodID notifyDisplayError;
+} gRemoteDisplayClassInfo;
+
+// ----------------------------------------------------------------------------
+
+class NativeRemoteDisplayClient : public BnRemoteDisplayClient {
+public:
+    NativeRemoteDisplayClient(JNIEnv* env, jobject remoteDisplayObj) :
+            mRemoteDisplayObjGlobal(env->NewGlobalRef(remoteDisplayObj)) {
+    }
+
+protected:
+    ~NativeRemoteDisplayClient() {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        env->DeleteGlobalRef(mRemoteDisplayObjGlobal);
+    }
+
+public:
+    virtual void onDisplayConnected(const sp<ISurfaceTexture>& surfaceTexture,
+            uint32_t width, uint32_t height, uint32_t flags) {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+        jobject surfaceObj = android_view_Surface_createFromISurfaceTexture(env, surfaceTexture);
+        if (surfaceObj == NULL) {
+            ALOGE("Could not create Surface from surface texture %p provided by media server.",
+                    surfaceTexture.get());
+            return;
+        }
+
+        env->CallVoidMethod(mRemoteDisplayObjGlobal,
+                gRemoteDisplayClassInfo.notifyDisplayConnected,
+                surfaceObj, width, height, flags);
+        env->DeleteLocalRef(surfaceObj);
+        checkAndClearExceptionFromCallback(env, "notifyDisplayConnected");
+    }
+
+    virtual void onDisplayDisconnected() {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+        env->CallVoidMethod(mRemoteDisplayObjGlobal,
+                gRemoteDisplayClassInfo.notifyDisplayDisconnected);
+        checkAndClearExceptionFromCallback(env, "notifyDisplayDisconnected");
+    }
+
+    virtual void onDisplayError(int32_t error) {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+        env->CallVoidMethod(mRemoteDisplayObjGlobal,
+                gRemoteDisplayClassInfo.notifyDisplayError, error);
+        checkAndClearExceptionFromCallback(env, "notifyDisplayError");
+    }
+
+private:
+    jobject mRemoteDisplayObjGlobal;
+
+    static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+        if (env->ExceptionCheck()) {
+            ALOGE("An exception was thrown by callback '%s'.", methodName);
+            LOGE_EX(env);
+            env->ExceptionClear();
+        }
+    }
+};
+
+class NativeRemoteDisplay {
+public:
+    NativeRemoteDisplay(const sp<IRemoteDisplay>& display,
+            const sp<NativeRemoteDisplayClient>& client) :
+            mDisplay(display), mClient(client) {
+    }
+
+    ~NativeRemoteDisplay() {
+        mDisplay->dispose();
+    }
+
+private:
+    sp<IRemoteDisplay> mDisplay;
+    sp<NativeRemoteDisplayClient> mClient;
+};
+
+
+// ----------------------------------------------------------------------------
+
+static jint nativeListen(JNIEnv* env, jobject remoteDisplayObj, jstring ifaceStr) {
+    ScopedUtfChars iface(env, ifaceStr);
+
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(
+            sm->getService(String16("media.player")));
+    if (service == NULL) {
+        ALOGE("Could not obtain IMediaPlayerService from service manager");
+        return 0;
+    }
+
+    sp<NativeRemoteDisplayClient> client(new NativeRemoteDisplayClient(env, remoteDisplayObj));
+    sp<IRemoteDisplay> display = service->listenForRemoteDisplay(
+            client, String8(iface.c_str()));
+    if (display == NULL) {
+        ALOGE("Media player service rejected request to listen for remote display '%s'.",
+                iface.c_str());
+        return 0;
+    }
+
+    NativeRemoteDisplay* wrapper = new NativeRemoteDisplay(display, client);
+    return reinterpret_cast<jint>(wrapper);
+}
+
+static void nativeDispose(JNIEnv* env, jobject remoteDisplayObj, jint ptr) {
+    NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr);
+    delete wrapper;
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gMethods[] = {
+    {"nativeListen", "(Ljava/lang/String;)I",
+            (void*)nativeListen },
+    {"nativeDispose", "(I)V",
+            (void*)nativeDispose },
+};
+
+int register_android_media_RemoteDisplay(JNIEnv* env)
+{
+    int err = AndroidRuntime::registerNativeMethods(env, "android/media/RemoteDisplay",
+            gMethods, NELEM(gMethods));
+
+    jclass clazz = env->FindClass("android/media/RemoteDisplay");
+    gRemoteDisplayClassInfo.notifyDisplayConnected =
+            env->GetMethodID(clazz, "notifyDisplayConnected",
+                    "(Landroid/view/Surface;III)V");
+    gRemoteDisplayClassInfo.notifyDisplayDisconnected =
+            env->GetMethodID(clazz, "notifyDisplayDisconnected", "()V");
+    gRemoteDisplayClassInfo.notifyDisplayError =
+            env->GetMethodID(clazz, "notifyDisplayError", "(I)V");
+    return err;
+}
+
+};
diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp
index 776733c..bb09421 100644
--- a/core/jni/android_text_format_Time.cpp
+++ b/core/jni/android_text_format_Time.cpp
@@ -23,6 +23,7 @@
 #include "jni.h"
 #include "utils/misc.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "ScopedStringChars.h"
 #include "TimeUtils.h"
 #include <nativehelper/JNIHelp.h>
 #include <cutils/tztime.h>
@@ -71,11 +72,10 @@
     t->t.tm_gmtoff = env->GetLongField(o, g_gmtoffField);
     bool allDay = env->GetBooleanField(o, g_allDayField);
     if (allDay &&
-	((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) {
-        char msg[100];
-	sprintf(msg, "allDay is true but sec, min, hour are not 0.");
-	jniThrowException(env, "java/lang/IllegalArgumentException", msg);
-	return false;
+       ((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "allDay is true but sec, min, hour are not 0.");
+        return false;
     }
     return true;
 }
@@ -308,7 +308,7 @@
 static jstring android_text_format_Time_toString(JNIEnv* env, jobject This)
 {
     Time t;
-    if (!java2time(env, &t, This)) return env->NewStringUTF("");;
+    if (!java2time(env, &t, This)) return env->NewStringUTF("");
     ACQUIRE_TIMEZONE(This, t)
 
     String8 r = t.toString();
@@ -360,32 +360,30 @@
 // ============================================================================
 // Just do this here because it's not worth recreating the strings
 
-static int get_char(JNIEnv* env, const jchar *s, int spos, int mul,
-                    bool *thrown)
+static int get_char(JNIEnv* env, const ScopedStringChars& s, int spos, int mul,
+                    bool* thrown)
 {
     jchar c = s[spos];
     if (c >= '0' && c <= '9') {
         return (c - '0') * mul;
     } else {
         if (!*thrown) {
-            char msg[100];
-            sprintf(msg, "Parse error at pos=%d", spos);
-            jniThrowException(env, "android/util/TimeFormatException", msg);
+            jniThrowExceptionFmt(env, "android/util/TimeFormatException",
+                                 "Parse error at pos=%d", spos);
             *thrown = true;
         }
         return 0;
     }
 }
 
-static bool check_char(JNIEnv* env, const jchar *s, int spos, jchar expected)
+static bool check_char(JNIEnv* env, const ScopedStringChars& s, int spos, jchar expected)
 {
     jchar c = s[spos];
     if (c != expected) {
-        char msg[100];
-	sprintf(msg, "Unexpected character 0x%02x at pos=%d.  Expected %c.", c, spos,
-		expected);
-	jniThrowException(env, "android/util/TimeFormatException", msg);
-	return false;
+        jniThrowExceptionFmt(env, "android/util/TimeFormatException",
+                             "Unexpected character 0x%02x at pos=%d.  Expected %c.",
+                             c, spos, expected);
+        return false;
     }
     return true;
 }
@@ -394,20 +392,19 @@
 static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstring strObj)
 {
     jsize len = env->GetStringLength(strObj);
-    const jchar *s = env->GetStringChars(strObj, NULL);
-
-    bool thrown = false;
-    int n;
-    jboolean inUtc = false;
-
     if (len < 8) {
-        char msg[100];
-        sprintf(msg, "String too short -- expected at least 8 characters.");
-	jniThrowException(env, "android/util/TimeFormatException", msg);
-	return false;
+        jniThrowException(env, "android/util/TimeFormatException",
+                          "String too short -- expected at least 8 characters.");
+        return false;
     }
 
+    jboolean inUtc = false;
+
+    ScopedStringChars s(env, strObj);
+
     // year
+    int n;
+    bool thrown = false;
     n = get_char(env, s, 0, 1000, &thrown);
     n += get_char(env, s, 1, 100, &thrown);
     n += get_char(env, s, 2, 10, &thrown);
@@ -454,7 +451,7 @@
         if (len > 15) {
             // Z
             if (!check_char(env, s, 15, 'Z')) return false;
-	    inUtc = true;
+            inUtc = true;
         }
     } else {
         env->SetBooleanField(This, g_allDayField, JNI_TRUE);
@@ -467,8 +464,7 @@
     env->SetIntField(This, g_ydayField, 0);
     env->SetIntField(This, g_isdstField, -1);
     env->SetLongField(This, g_gmtoffField, 0);
-    
-    env->ReleaseStringChars(strObj, s);
+
     return inUtc;
 }
 
@@ -477,19 +473,19 @@
                                            jstring strObj)
 {
     jsize len = env->GetStringLength(strObj);
-    const jchar *s = env->GetStringChars(strObj, NULL);
-
-    bool thrown = false;
-    int n;
-    jboolean inUtc = false;
-
     if (len < 10) {
         jniThrowException(env, "android/util/TimeFormatException",
-                "Time input is too short; must be at least 10 characters");
+                          "String too short --- expected at least 10 characters.");
         return false;
     }
 
+    jboolean inUtc = false;
+
+    ScopedStringChars s(env, strObj);
+
     // year
+    int n;
+    bool thrown = false;
     n = get_char(env, s, 0, 1000, &thrown);    
     n += get_char(env, s, 1, 100, &thrown);
     n += get_char(env, s, 2, 10, &thrown);
@@ -520,28 +516,28 @@
         // T
         if (!check_char(env, s, 10, 'T')) return false;
 
-	env->SetBooleanField(This, g_allDayField, JNI_FALSE);
+        env->SetBooleanField(This, g_allDayField, JNI_FALSE);
         // hour
         n = get_char(env, s, 11, 10, &thrown);
         n += get_char(env, s, 12, 1, &thrown);
         if (thrown) return false;
-	int hour = n;
+        int hour = n;
         // env->SetIntField(This, g_hourField, n);
-	
-	// :
-	if (!check_char(env, s, 13, ':')) return false;
 
-	// minute
+        // :
+        if (!check_char(env, s, 13, ':')) return false;
+
+        // minute
         n = get_char(env, s, 14, 10, &thrown);
         n += get_char(env, s, 15, 1, &thrown);
         if (thrown) return false;
-	int minute = n;
+        int minute = n;
         // env->SetIntField(This, g_minField, n);
 
-	// :
-	if (!check_char(env, s, 16, ':')) return false;
+        // :
+        if (!check_char(env, s, 16, ':')) return false;
 
-	// second
+        // second
         n = get_char(env, s, 17, 10, &thrown);
         n += get_char(env, s, 18, 1, &thrown);
         if (thrown) return false;
@@ -561,64 +557,63 @@
         if (len > tz_index) {
             char c = s[tz_index];
 
-	    // NOTE: the offset is meant to be subtracted to get from local time
-	    // to UTC.  we therefore use 1 for '-' and -1 for '+'.
-	    switch (c) {
-	    case 'Z':
-	        // Zulu time -- UTC
-	        offset = 0;
-		break;
-	    case '-': 
+            // NOTE: the offset is meant to be subtracted to get from local time
+            // to UTC.  we therefore use 1 for '-' and -1 for '+'.
+            switch (c) {
+            case 'Z':
+                // Zulu time -- UTC
+                offset = 0;
+                break;
+            case '-': 
                 offset = 1;
-	        break;
-	    case '+': 
+                break;
+            case '+': 
                 offset = -1;
-	        break;
-	    default:
-	        char msg[100];
-	        sprintf(msg, "Unexpected character 0x%02x at position %d.  Expected + or -",
-			c, tz_index);
-	        jniThrowException(env, "android/util/TimeFormatException", msg);
-	        return false;
-	    }
+                break;
+            default:
+                jniThrowExceptionFmt(env, "android/util/TimeFormatException",
+                                     "Unexpected character 0x%02x at position %d.  Expected + or -",
+                                     c, tz_index);
+                return false;
+            }
             inUtc = true;
 
-	    if (offset != 0) {
-	        if (len < tz_index + 6) {
-	            char msg[100];
-	            sprintf(msg, "Unexpected length; should be %d characters", tz_index + 6);
-	            jniThrowException(env, "android/util/TimeFormatException", msg);
-	            return false;
-	        }
+            if (offset != 0) {
+                if (len < tz_index + 6) {
+                    jniThrowExceptionFmt(env, "android/util/TimeFormatException",
+                                         "Unexpected length; should be %d characters",
+                                         tz_index + 6);
+                    return false;
+                }
 
-	        // hour
-	        n = get_char(env, s, tz_index + 1, 10, &thrown);
-		n += get_char(env, s, tz_index + 2, 1, &thrown);
-		if (thrown) return false;
-		n *= offset;
-		hour += n;
+                // hour
+                n = get_char(env, s, tz_index + 1, 10, &thrown);
+                n += get_char(env, s, tz_index + 2, 1, &thrown);
+                if (thrown) return false;
+                n *= offset;
+                hour += n;
 
-		// :
-		if (!check_char(env, s, tz_index + 3, ':')) return false;
-	    
-		// minute
-		n = get_char(env, s, tz_index + 4, 10, &thrown);
-		n += get_char(env, s, tz_index + 5, 1, &thrown);
-		if (thrown) return false;
-		n *= offset;
-		minute += n;
-	    }
-	}
-	env->SetIntField(This, g_hourField, hour);
+                // :
+                if (!check_char(env, s, tz_index + 3, ':')) return false;
+            
+                // minute
+                n = get_char(env, s, tz_index + 4, 10, &thrown);
+                n += get_char(env, s, tz_index + 5, 1, &thrown);
+                if (thrown) return false;
+                n *= offset;
+                minute += n;
+            }
+        }
+        env->SetIntField(This, g_hourField, hour);
         env->SetIntField(This, g_minField, minute);
 
-	if (offset != 0) {
-	    // we need to normalize after applying the hour and minute offsets
-	    android_text_format_Time_normalize(env, This, false /* use isdst */);
-	    // The timezone is set to UTC in the calling Java code.
-	}
+        if (offset != 0) {
+            // we need to normalize after applying the hour and minute offsets
+            android_text_format_Time_normalize(env, This, false /* use isdst */);
+            // The timezone is set to UTC in the calling Java code.
+        }
     } else {
-	env->SetBooleanField(This, g_allDayField, JNI_TRUE);
+        env->SetBooleanField(This, g_allDayField, JNI_TRUE);
         env->SetIntField(This, g_hourField, 0);
         env->SetIntField(This, g_minField, 0);
         env->SetIntField(This, g_secField, 0);
@@ -628,8 +623,7 @@
     env->SetIntField(This, g_ydayField, 0);
     env->SetIntField(This, g_isdstField, -1);
     env->SetLongField(This, g_gmtoffField, 0);
-    
-    env->ReleaseStringChars(strObj, s);
+
     return inUtc;
 }
 
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 4fbfab6..90e85e6 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -66,6 +66,7 @@
     jfieldID mGenerationId;
     jfieldID mCanvas;
     jfieldID mCanvasSaveCount;
+    jmethodID ctor;
 } gSurfaceClassInfo;
 
 static struct {
@@ -231,6 +232,42 @@
     }
 }
 
+static sp<ISurfaceTexture> getISurfaceTexture(JNIEnv* env, jobject surfaceObj) {
+    if (surfaceObj) {
+        sp<Surface> surface(getSurface(env, surfaceObj));
+        if (surface != NULL) {
+            return surface->getSurfaceTexture();
+        }
+    }
+    return NULL;
+}
+
+jobject android_view_Surface_createFromISurfaceTexture(JNIEnv* env,
+        const sp<ISurfaceTexture>& surfaceTexture) {
+    if (surfaceTexture == NULL) {
+        return NULL;
+    }
+
+    sp<Surface> surface(new Surface(surfaceTexture));
+    if (surface == NULL) {
+        return NULL;
+    }
+
+    jobject surfaceObj = env->NewObject(gSurfaceClassInfo.clazz, gSurfaceClassInfo.ctor);
+    if (surfaceObj == NULL) {
+        if (env->ExceptionCheck()) {
+            ALOGE("Could not create instance of Surface from ISurfaceTexture.");
+            LOGE_EX(env);
+            env->ExceptionClear();
+        }
+        return NULL;
+    }
+
+    setSurface(env, surfaceObj, surface);
+    return surfaceObj;
+}
+
+
 // ----------------------------------------------------------------------------
 
 static void nativeCreate(JNIEnv* env, jobject surfaceObj, jobject sessionObj,
@@ -619,24 +656,12 @@
 }
 
 static void nativeSetDisplaySurface(JNIEnv* env, jclass clazz,
-        jobject tokenObj, jobject surfaceTextureObj) {
+        jobject tokenObj, jobject surfaceObj) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return;
 
-    if (!surfaceTextureObj) {
-        SurfaceComposerClient::setDisplaySurface(token, NULL);
-        return;
-    }
-
-    sp<SurfaceTexture> st(SurfaceTexture_getSurfaceTexture(env, surfaceTextureObj));
-    if (st == NULL) {
-        jniThrowException(env, "java/lang/IllegalArgumentException",
-                "SurfaceTexture has already been released");
-        return;
-    }
-
-    sp<ISurfaceTexture> bq = st->getBufferQueue();
-    SurfaceComposerClient::setDisplaySurface(token, bq);
+    sp<ISurfaceTexture> surfaceTexture(getISurfaceTexture(env, surfaceObj));
+    SurfaceComposerClient::setDisplaySurface(token, surfaceTexture);
 }
 
 static void nativeSetDisplayLayerStack(JNIEnv* env, jclass clazz,
@@ -648,23 +673,23 @@
 }
 
 static void nativeSetDisplayProjection(JNIEnv* env, jclass clazz,
-        jobject tokenObj, jint orientation, jobject rect1Obj, jobject rect2Obj) {
+        jobject tokenObj, jint orientation, jobject layerStackRectObj, jobject displayRectObj) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return;
 
-    Rect rect1;
-    rect1.left = env->GetIntField(rect1Obj, gRectClassInfo.left);
-    rect1.top = env->GetIntField(rect1Obj, gRectClassInfo.top);
-    rect1.right = env->GetIntField(rect1Obj, gRectClassInfo.right);
-    rect1.bottom = env->GetIntField(rect1Obj, gRectClassInfo.bottom);
+    Rect layerStackRect;
+    layerStackRect.left = env->GetIntField(layerStackRectObj, gRectClassInfo.left);
+    layerStackRect.top = env->GetIntField(layerStackRectObj, gRectClassInfo.top);
+    layerStackRect.right = env->GetIntField(layerStackRectObj, gRectClassInfo.right);
+    layerStackRect.bottom = env->GetIntField(layerStackRectObj, gRectClassInfo.bottom);
 
-    Rect rect2;
-    rect2.left = env->GetIntField(rect2Obj, gRectClassInfo.left);
-    rect2.top = env->GetIntField(rect2Obj, gRectClassInfo.top);
-    rect2.right = env->GetIntField(rect2Obj, gRectClassInfo.right);
-    rect2.bottom = env->GetIntField(rect2Obj, gRectClassInfo.bottom);
+    Rect displayRect;
+    displayRect.left = env->GetIntField(displayRectObj, gRectClassInfo.left);
+    displayRect.top = env->GetIntField(displayRectObj, gRectClassInfo.top);
+    displayRect.right = env->GetIntField(displayRectObj, gRectClassInfo.right);
+    displayRect.bottom = env->GetIntField(displayRectObj, gRectClassInfo.bottom);
 
-    SurfaceComposerClient::setDisplayProjection(token, orientation, rect1, rect2);
+    SurfaceComposerClient::setDisplayProjection(token, orientation, layerStackRect, displayRect);
 }
 
 static jboolean nativeGetDisplayInfo(JNIEnv* env, jclass clazz,
@@ -800,7 +825,7 @@
             (void*)nativeGetBuiltInDisplay },
     {"nativeCreateDisplay", "(Ljava/lang/String;)Landroid/os/IBinder;",
             (void*)nativeCreateDisplay },
-    {"nativeSetDisplaySurface", "(Landroid/os/IBinder;Landroid/graphics/SurfaceTexture;)V",
+    {"nativeSetDisplaySurface", "(Landroid/os/IBinder;Landroid/view/Surface;)V",
             (void*)nativeSetDisplaySurface },
     {"nativeSetDisplayLayerStack", "(Landroid/os/IBinder;I)V",
             (void*)nativeSetDisplayLayerStack },
@@ -835,6 +860,7 @@
             env->GetFieldID(gSurfaceClassInfo.clazz, "mCanvas", "Landroid/graphics/Canvas;");
     gSurfaceClassInfo.mCanvasSaveCount =
             env->GetFieldID(gSurfaceClassInfo.clazz, "mCanvasSaveCount", "I");
+    gSurfaceClassInfo.ctor = env->GetMethodID(gSurfaceClassInfo.clazz, "<init>", "()V");
 
     clazz = env->FindClass("android/graphics/Canvas");
     gCanvasClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas", "I");
diff --git a/core/res/res/layout-land/keyguard_host_view.xml b/core/res/res/layout-land/keyguard_host_view.xml
index b404155..0028a54 100644
--- a/core/res/res/layout-land/keyguard_host_view.xml
+++ b/core/res/res/layout-land/keyguard_host_view.xml
@@ -45,13 +45,8 @@
         android:layout_weight="1"
         android:gravity="center">
 
+        <!-- SelectorView is always used, so add it here. The rest are loaded dynamically -->
         <include layout="@layout/keyguard_selector_view"/>
-        <include layout="@layout/keyguard_account_view"/>
-        <include layout="@layout/keyguard_pattern_view"/>
-        <include layout="@layout/keyguard_password_view"/>
-        <include layout="@layout/keyguard_sim_pin_view"/>
-        <include layout="@layout/keyguard_sim_puk_view"/>
-        <include layout="@layout/keyguard_face_unlock_view"/>
 
     </ViewFlipper>
 
diff --git a/core/res/res/layout-port/keyguard_host_view.xml b/core/res/res/layout-port/keyguard_host_view.xml
index 5dc2225..5e467d1 100644
--- a/core/res/res/layout-port/keyguard_host_view.xml
+++ b/core/res/res/layout-port/keyguard_host_view.xml
@@ -33,13 +33,8 @@
         android:layout_height="match_parent"
         android:gravity="center">
 
+        <!-- SelectorView is always used, so add it here. The rest are loaded dynamically -->
         <include layout="@layout/keyguard_selector_view"/>
-        <include layout="@layout/keyguard_account_view"/>
-        <include layout="@layout/keyguard_pattern_view"/>
-        <include layout="@layout/keyguard_password_view"/>
-        <include layout="@layout/keyguard_sim_pin_view"/>
-        <include layout="@layout/keyguard_sim_puk_view"/>
-        <include layout="@layout/keyguard_face_unlock_view"/>
 
     </ViewFlipper>
 
diff --git a/core/res/res/layout-sw600dp-land/keyguard_host_view.xml b/core/res/res/layout-sw600dp-land/keyguard_host_view.xml
index e77f584..5b6bb2f 100644
--- a/core/res/res/layout-sw600dp-land/keyguard_host_view.xml
+++ b/core/res/res/layout-sw600dp-land/keyguard_host_view.xml
@@ -52,13 +52,8 @@
             android:layout_weight="1"
             android:gravity="center">
 
+            <!-- SelectorView is always used, so add it here. The rest are loaded dynamically -->
             <include layout="@layout/keyguard_selector_view"/>
-            <include layout="@layout/keyguard_account_view"/>
-            <include layout="@layout/keyguard_pattern_view"/>
-            <include layout="@layout/keyguard_password_view"/>
-            <include layout="@layout/keyguard_sim_pin_view"/>
-            <include layout="@layout/keyguard_sim_puk_view"/>
-            <include layout="@layout/keyguard_face_unlock_view"/>
 
         </ViewFlipper>
 
diff --git a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
index 082f481..397b881 100644
--- a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
+++ b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml
@@ -47,13 +47,8 @@
         android:layout_weight="0.6"
         android:layout_gravity="center">
 
+        <!-- SelectorView is always used, so add it here. The rest are loaded dynamically -->
         <include layout="@layout/keyguard_selector_view"/>
-        <include layout="@layout/keyguard_account_view"/>
-        <include layout="@layout/keyguard_pattern_view"/>
-        <include layout="@layout/keyguard_password_view"/>
-        <include layout="@layout/keyguard_sim_pin_view"/>
-        <include layout="@layout/keyguard_sim_puk_view"/>
-        <include layout="@layout/keyguard_face_unlock_view"/>
 
     </ViewFlipper>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4db8cd1..9a8e712 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -486,6 +486,7 @@
   <java-symbol type="string" name="display_manager_hdmi_display_name" />
   <java-symbol type="string" name="display_manager_overlay_display_name" />
   <java-symbol type="string" name="display_manager_overlay_display_title" />
+  <java-symbol type="string" name="display_manager_wifi_display_name" />
   <java-symbol type="string" name="double_tap_toast" />
   <java-symbol type="string" name="elapsed_time_short_format_h_mm_ss" />
   <java-symbol type="string" name="elapsed_time_short_format_mm_ss" />
@@ -1366,6 +1367,13 @@
   <java-symbol type="layout" name="keyguard_screen_tab_unlock_land" />
   <java-symbol type="layout" name="keyguard_screen_unlock_landscape" />
   <java-symbol type="layout" name="keyguard_screen_unlock_portrait" />
+  <java-symbol type="layout" name="keyguard_selector_view" />
+  <java-symbol type="layout" name="keyguard_pattern_view" />
+  <java-symbol type="layout" name="keyguard_password_view" />
+  <java-symbol type="layout" name="keyguard_face_unlock_view" />
+  <java-symbol type="layout" name="keyguard_sim_pin_view" />
+  <java-symbol type="layout" name="keyguard_sim_puk_view" />
+  <java-symbol type="layout" name="keyguard_account_view" />
   <java-symbol type="layout" name="recent_apps_dialog" />
   <java-symbol type="layout" name="screen_action_bar" />
   <java-symbol type="layout" name="screen_action_bar_overlay" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9f254b6..1e7e9fb 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3678,6 +3678,9 @@
     <!-- Title text to show within the overlay.  [CHAR LIMIT=50] -->
     <string name="display_manager_overlay_display_title"><xliff:g id="name">%1$s</xliff:g>: <xliff:g id="width">%2$d</xliff:g>x<xliff:g id="height">%3$d</xliff:g>, <xliff:g id="dpi">%4$d</xliff:g> dpi</string>
 
+    <!-- Name of a wifi display.  [CHAR LIMIT=50] -->
+    <string name="display_manager_wifi_display_name">Wifi display: <xliff:g id="device">%1$s</xliff:g></string>
+
     <!-- Keyguard strings -->
     <!-- Label shown on emergency call button in keyguard -->
     <string name="kg_emergency_call_label">Emergency call</string>
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index a1fd14e..607150a 100755
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -395,6 +395,14 @@
                     assertTrue("The native library path (" + info.nativeLibraryDir
                             + ") should start with " + SECURE_CONTAINERS_PREFIX,
                             info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX));
+                    try {
+                        String compatLib = new File(info.dataDir + "/lib").getCanonicalPath();
+                        assertEquals("The compatibility lib directory should be a symbolic link to "
+                                + info.nativeLibraryDir,
+                                info.nativeLibraryDir, compatLib);
+                    } catch (IOException e) {
+                        fail("compat check: Can't read " + info.dataDir + "/lib");
+                    }
                 } else {
                     assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
                     assertEquals(srcPath, appInstallPath);
diff --git a/graphics/java/android/renderscript/Matrix4f.java b/graphics/java/android/renderscript/Matrix4f.java
index a85d464..4600424 100644
--- a/graphics/java/android/renderscript/Matrix4f.java
+++ b/graphics/java/android/renderscript/Matrix4f.java
@@ -113,6 +113,34 @@
     }
 
     /**
+    * Sets the values of the matrix to those of the parameter
+    *
+    * @param src matrix to load the values from
+    * @hide
+    */
+    public void load(Matrix3f src) {
+        mMat[0] = src.mMat[0];
+        mMat[1] = src.mMat[1];
+        mMat[2] = src.mMat[2];
+        mMat[3] = 0;
+
+        mMat[4] = src.mMat[3];
+        mMat[5] = src.mMat[4];
+        mMat[6] = src.mMat[5];
+        mMat[7] = 0;
+
+        mMat[8] = src.mMat[6];
+        mMat[9] = src.mMat[7];
+        mMat[10] = src.mMat[8];
+        mMat[11] = 0;
+
+        mMat[12] = 0;
+        mMat[13] = 0;
+        mMat[14] = 0;
+        mMat[15] = 1;
+    }
+
+    /**
     * Sets current values to be a rotation matrix of certain angle
     * about a given axis
     *
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java b/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java
index 41e7e00..dce1939 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java
@@ -39,8 +39,7 @@
     }
 
     /**
-     * Supported elements types are float, float4, uchar, uchar4
-     *
+     * Supported elements types are uchar4
      *
      * @param rs
      * @param e
@@ -53,13 +52,98 @@
 
     }
 
-    public void setColorMatrix(Matrix4f m) {
-        mMatrix.load(m);
+    private void setMatrix() {
         FieldPacker fp = new FieldPacker(16*4);
-        fp.addMatrix(m);
+        fp.addMatrix(mMatrix);
         setVar(0, fp);
     }
 
+    /**
+     * Set the color matrix which will be applied to each cell of the image.
+     *
+     * @param m The 4x4 matrix to set.
+     */
+    public void setColorMatrix(Matrix4f m) {
+        mMatrix.load(m);
+        setMatrix();
+    }
+
+    /**
+     * Set the color matrix which will be applied to each cell of the image.
+     * This will set the alpha channel to be a copy.
+     *
+     * @param m The 3x3 matrix to set.
+     */
+    public void setColorMatrix(Matrix3f m) {
+        mMatrix.load(m);
+        setMatrix();
+    }
+
+    /**
+     * Set a color matrix to convert from RGB to luminace. The alpha channel
+     * will be a copy.
+     *
+     */
+    public void setGreyscale() {
+        mMatrix.loadIdentity();
+        mMatrix.set(0, 0, 0.299f);
+        mMatrix.set(1, 0, 0.587f);
+        mMatrix.set(2, 0, 0.114f);
+        mMatrix.set(0, 1, 0.299f);
+        mMatrix.set(1, 1, 0.587f);
+        mMatrix.set(2, 1, 0.114f);
+        mMatrix.set(0, 2, 0.299f);
+        mMatrix.set(1, 2, 0.587f);
+        mMatrix.set(2, 2, 0.114f);
+        setMatrix();
+    }
+
+    /**
+     * Set the matrix to convert from YUV to RGB with a direct copy of the 4th
+     * channel.
+     *
+     */
+    public void setYUVtoRGB() {
+        mMatrix.loadIdentity();
+        mMatrix.set(0, 0, 1.f);
+        mMatrix.set(1, 0, 0.f);
+        mMatrix.set(2, 0, 1.13983f);
+        mMatrix.set(0, 1, 1.f);
+        mMatrix.set(1, 1, -0.39465f);
+        mMatrix.set(2, 1, -0.5806f);
+        mMatrix.set(0, 2, 1.f);
+        mMatrix.set(1, 2, 2.03211f);
+        mMatrix.set(2, 2, 0.f);
+        setMatrix();
+    }
+
+    /**
+     * Set the matrix to convert from RGB to YUV with a direct copy of the 4th
+     * channel.
+     *
+     */
+    public void setRGBtoYUV() {
+        mMatrix.loadIdentity();
+        mMatrix.set(0, 0, 0.299f);
+        mMatrix.set(1, 0, 0.587f);
+        mMatrix.set(2, 0, 0.114f);
+        mMatrix.set(0, 1, -0.14713f);
+        mMatrix.set(1, 1, -0.28886f);
+        mMatrix.set(2, 1, 0.436f);
+        mMatrix.set(0, 2, 0.615f);
+        mMatrix.set(1, 2, -0.51499f);
+        mMatrix.set(2, 2, -0.10001f);
+        setMatrix();
+    }
+
+
+    /**
+     * Invoke the kernel and apply the matrix to each cell of ain and copy to
+     * aout.
+     *
+     * @param ain Input allocation
+     * @param aout Output allocation
+     */
     public void forEach(Allocation ain, Allocation aout) {
         forEach(0, ain, aout, null);
     }
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicYuvToRGB.java b/graphics/java/android/renderscript/ScriptIntrinsicYuvToRGB.java
index ee5f938..b4a228b 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsicYuvToRGB.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsicYuvToRGB.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
diff --git a/include/android_runtime/android_view_Surface.h b/include/android_runtime/android_view_Surface.h
index e50186d..df0fe72 100644
--- a/include/android_runtime/android_view_Surface.h
+++ b/include/android_runtime/android_view_Surface.h
@@ -24,6 +24,7 @@
 namespace android {
 
 class Surface;
+class ISurfaceTexture;
 
 /* Gets the underlying ANativeWindow for a Surface. */
 extern sp<ANativeWindow> android_view_Surface_getNativeWindow(
@@ -35,6 +36,10 @@
 /* Gets the underlying Surface from a Surface Java object. */
 extern sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj);
 
+/* Creates a Surface from an ISurfaceTexture. */
+extern jobject android_view_Surface_createFromISurfaceTexture(JNIEnv* env,
+        const sp<ISurfaceTexture>& surfaceTexture);
+
 } // namespace android
 
 #endif // _ANDROID_VIEW_SURFACE_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 05c7809..9f8b87c 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1978,6 +1978,10 @@
 
         // Find the normal to the line
         vec2 n = (b - a).copyNormalized() * halfStrokeWidth;
+        float x = n.x;
+        n.x = -n.y;
+        n.y = x;
+
         if (isHairLine) {
             if (isAA) {
                 float wideningFactor;
@@ -2002,14 +2006,10 @@
 
             float extendedNLength = extendedN.length();
             // We need to set this value on the shader prior to drawing
-            boundaryWidthProportion = extendedNLength / (halfStrokeWidth + extendedNLength);
+            boundaryWidthProportion = .5 - extendedNLength / (halfStrokeWidth + extendedNLength);
             n += extendedN;
         }
 
-        float x = n.x;
-        n.x = -n.y;
-        n.y = x;
-
         // aa lines expand the endpoint vertices to encompass the AA boundary
         if (isAA) {
             vec2 abVector = (b - a);
diff --git a/media/java/android/media/RemoteDisplay.java b/media/java/android/media/RemoteDisplay.java
new file mode 100644
index 0000000..b463d26
--- /dev/null
+++ b/media/java/android/media/RemoteDisplay.java
@@ -0,0 +1,153 @@
+/*
+ * 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.media;
+
+import dalvik.system.CloseGuard;
+
+import android.os.Handler;
+import android.view.Surface;
+
+/**
+ * Listens for Wifi remote display connections managed by the media server.
+ *
+ * @hide
+ */
+public final class RemoteDisplay {
+    /* these constants must be kept in sync with IRemoteDisplayClient.h */
+
+    public static final int DISPLAY_FLAG_SECURE = 1 << 0;
+
+    public static final int DISPLAY_ERROR_UNKOWN = 1;
+    public static final int DISPLAY_ERROR_CONNECTION_DROPPED = 2;
+
+    private final CloseGuard mGuard = CloseGuard.get();
+    private final Listener mListener;
+    private final Handler mHandler;
+
+    private int mPtr;
+
+    private native int nativeListen(String iface);
+    private native void nativeDispose(int ptr);
+
+    private RemoteDisplay(Listener listener, Handler handler) {
+        mListener = listener;
+        mHandler = handler;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            dispose(true);
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Starts listening for displays to be connected on the specified interface.
+     *
+     * @param iface The interface address and port in the form "x.x.x.x:y".
+     * @param listener The listener to invoke when displays are connected or disconnected.
+     * @param handler The handler on which to invoke the listener.
+     */
+    public static RemoteDisplay listen(String iface, Listener listener, Handler handler) {
+        if (iface == null) {
+            throw new IllegalArgumentException("iface must not be null");
+        }
+        if (listener == null) {
+            throw new IllegalArgumentException("listener must not be null");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("handler must not be null");
+        }
+
+        RemoteDisplay display = new RemoteDisplay(listener, handler);
+        display.startListening(iface);
+        return display;
+    }
+
+    /**
+     * Disconnects the remote display and stops listening for new connections.
+     */
+    public void dispose() {
+        dispose(false);
+    }
+
+    private void dispose(boolean finalized) {
+        if (mPtr != 0) {
+            if (mGuard != null) {
+                if (finalized) {
+                    mGuard.warnIfOpen();
+                } else {
+                    mGuard.close();
+                }
+            }
+
+            nativeDispose(mPtr);
+            mPtr = 0;
+        }
+    }
+
+    private void startListening(String iface) {
+        mPtr = nativeListen(iface);
+        if (mPtr == 0) {
+            throw new IllegalStateException("Could not start listening for "
+                    + "remote display connection on \"" + iface + "\"");
+        }
+        mGuard.open("dispose");
+    }
+
+    // Called from native.
+    private void notifyDisplayConnected(final Surface surface,
+            final int width, final int height, final int flags) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mListener.onDisplayConnected(surface, width, height, flags);
+            }
+        });
+    }
+
+    // Called from native.
+    private void notifyDisplayDisconnected() {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mListener.onDisplayDisconnected();
+            }
+        });
+    }
+
+    // Called from native.
+    private void notifyDisplayError(final int error) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mListener.onDisplayError(error);
+            }
+        });
+    }
+
+    /**
+     * Listener invoked when the remote display connection changes state.
+     */
+    public interface Listener {
+        void onDisplayConnected(Surface surface, int width, int height, int flags);
+        void onDisplayDisconnected();
+        void onDisplayError(int error);
+    }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 77760b4..05673c3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -67,7 +67,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 86;
+    private static final int DATABASE_VERSION = 87;
 
     private Context mContext;
     private int mUserHandle;
@@ -1288,6 +1288,23 @@
             upgradeVersion = 86;
         }
 
+        if (upgradeVersion == 86) {
+            db.beginTransaction();
+            try {
+                String[] settingsToMove = {
+                        Settings.Secure.PACKAGE_VERIFIER_ENABLE,
+                        Settings.Secure.PACKAGE_VERIFIER_TIMEOUT,
+                        Settings.Secure.PACKAGE_VERIFIER_DEFAULT_RESPONSE
+                };
+                moveSettingsToNewTable(db, TABLE_SECURE, TABLE_GLOBAL, settingsToMove, true);
+
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+            upgradeVersion = 87;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 0165977..1096540 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -563,7 +563,7 @@
                 getContentResolver().insert(contentUri, contentValues);
             }
 
-            if (DEBUG) {
+            if (DEBUG || true) {
                 Log.d(TAG, "Restored setting: " + key + "=" + value);
             }
         }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index db2d450..6c62680 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -4152,7 +4152,7 @@
             } catch (ActivityNotFoundException e) {
             }
         }
-        mContext.startActivity(mHomeIntent);
+        mContext.startActivityAsUser(mHomeIntent, UserHandle.CURRENT);
     }
     
     /**
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 00bc9be..e170ec1 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -62,35 +62,13 @@
     private static final String KEYGUARD_WIDGET_PREFS = "keyguard_widget_prefs";
 
     private static final String TAG = "KeyguardViewHost";
-
-    private static final int SECURITY_SELECTOR_ID = R.id.keyguard_selector_view;
-    private static final int SECURITY_PATTERN_ID = R.id.keyguard_pattern_view;
-    private static final int SECURITY_PASSWORD_ID = R.id.keyguard_password_view;
-    private static final int SECURITY_BIOMETRIC_ID = R.id.keyguard_face_unlock_view;
-    private static final int SECURITY_SIM_PIN_ID = R.id.keyguard_sim_pin_view;
-    private static final int SECURITY_SIM_PUK_ID = R.id.keyguard_sim_puk_view;
-    private static final int SECURITY_ACCOUNT_ID = R.id.keyguard_account_view;
-
     private AppWidgetHost mAppWidgetHost;
     private KeyguardWidgetPager mAppWidgetContainer;
-    private ViewFlipper mViewFlipper;
+    private ViewFlipper mSecurityViewContainer;
     private boolean mEnableMenuKey;
     private boolean mIsVerifyUnlockOnly;
     private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
-    private int mCurrentSecurityId = SECURITY_SELECTOR_ID;
-
-    // KeyguardSecurityViews
-    final private int [] mViewIds = {
-        SECURITY_SELECTOR_ID,
-        SECURITY_PATTERN_ID,
-        SECURITY_PASSWORD_ID,
-        SECURITY_BIOMETRIC_ID,
-        SECURITY_SIM_PIN_ID,
-        SECURITY_SIM_PUK_ID,
-        SECURITY_ACCOUNT_ID,
-    };
-
-    private ArrayList<View> mViews = new ArrayList<View>(mViewIds.length);
+    private SecurityMode mCurrentSecuritySelection = SecurityMode.None;
 
     protected Runnable mLaunchRunnable;
 
@@ -125,33 +103,31 @@
     protected void onFinishInflate() {
         mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
         mAppWidgetContainer.setVisibility(VISIBLE);
+        mSecurityViewContainer = (ViewFlipper) findViewById(R.id.view_flipper);
+        updateSecurityViews();
+    }
 
-        // View Flipper
-        mViewFlipper = (ViewFlipper) findViewById(R.id.view_flipper);
+    private void updateSecurityViews() {
+        int children = mSecurityViewContainer.getChildCount();
+        for (int i = 0; i < children; i++) {
+            updateSecurityView(mSecurityViewContainer.getChildAt(i));
+        }
+    }
 
-        // Initialize all security views
-        for (int i = 0; i < mViewIds.length; i++) {
-            View view = findViewById(mViewIds[i]);
-            mViews.add(view);
-            if (view != null) {
-                ((KeyguardSecurityView) view).setKeyguardCallback(mCallback);
-            } else {
-                Log.v("*********", "Can't find view id " + mViewIds[i]);
-            }
+    private void updateSecurityView(View view) {
+        if (view instanceof KeyguardSecurityView) {
+            KeyguardSecurityView ksv = (KeyguardSecurityView) view;
+            ksv.setKeyguardCallback(mCallback);
+            ksv.setLockPatternUtils(mLockPatternUtils);
+        } else {
+            Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
         }
     }
 
     void setLockPatternUtils(LockPatternUtils utils) {
         mSecurityModel.setLockPatternUtils(utils);
         mLockPatternUtils = utils;
-        for (int i = 0; i < mViews.size(); i++) {
-            KeyguardSecurityView ksv = (KeyguardSecurityView) mViews.get(i);
-            if (ksv != null) {
-                ksv.setLockPatternUtils(utils);
-            } else {
-                Log.w(TAG, "**** ksv was null at " + i);
-            }
-        }
+        updateSecurityViews();
     }
 
     @Override
@@ -313,7 +289,7 @@
                     showTimeout = false; // don't show both dialogs
                 } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
                     mLockPatternUtils.setPermanentlyLocked(true);
-                    showSecurityScreen(SECURITY_ACCOUNT_ID);
+                    showSecurityScreen(SecurityMode.Account);
                     // don't show timeout dialog because we show account unlock screen next
                     showTimeout = false;
                 }
@@ -333,56 +309,53 @@
      */
     private void showBackupSecurity() {
         SecurityMode currentMode = mSecurityModel.getAlternateFor(mSecurityModel.getSecurityMode());
-        SecurityMode backup = mSecurityModel.getBackupFor(currentMode);
-        showSecurityScreen(getSecurityViewIdForMode(backup));
+        showSecurityScreen(mSecurityModel.getBackupFor(currentMode));
     }
 
     private void showNextSecurityScreenOrFinish(boolean authenticated) {
         boolean finish = false;
-        if (SECURITY_SELECTOR_ID == mCurrentSecurityId) {
+        if (SecurityMode.None == mCurrentSecuritySelection) {
             SecurityMode securityMode = mSecurityModel.getSecurityMode();
             // Allow an alternate, such as biometric unlock
             securityMode = mSecurityModel.getAlternateFor(securityMode);
-            int realSecurityId = getSecurityViewIdForMode(securityMode);
-            if (SECURITY_SELECTOR_ID == realSecurityId) {
+            if (SecurityMode.None == securityMode) {
                 finish = true; // no security required
             } else {
-                showSecurityScreen(realSecurityId); // switch to the "real" security view
+                showSecurityScreen(securityMode); // switch to the alternate security view
             }
         } else if (authenticated) {
-            switch (mCurrentSecurityId) {
-                case SECURITY_PATTERN_ID:
-                case SECURITY_PASSWORD_ID:
-                case SECURITY_ACCOUNT_ID:
-                case SECURITY_BIOMETRIC_ID:
+            switch (mCurrentSecuritySelection) {
+                case Pattern:
+                case Password:
+                case Account:
+                case Biometric:
                     finish = true;
                     break;
 
-                case SECURITY_SIM_PIN_ID:
-                case SECURITY_SIM_PUK_ID:
+                case SimPin:
+                case SimPuk:
                     // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
                     SecurityMode securityMode = mSecurityModel.getSecurityMode();
                     if (securityMode != SecurityMode.None) {
-                        showSecurityScreen(getSecurityViewIdForMode(securityMode));
+                        showSecurityScreen(securityMode);
                     } else {
                         finish = true;
                     }
                     break;
 
                 default:
-                    showSecurityScreen(SECURITY_SELECTOR_ID);
+                    showSecurityScreen(SecurityMode.None);
                     break;
             }
         } else {
             // Not authenticated but we were asked to dismiss so go back to selector screen.
-            showSecurityScreen(SECURITY_SELECTOR_ID);
+            showSecurityScreen(SecurityMode.None);
         }
         if (finish) {
             // If there's a pending runnable because the user interacted with a widget
             // and we're leaving keyguard, then run it.
             if (mLaunchRunnable != null) {
                 mLaunchRunnable.run();
-                mViewFlipper.setDisplayedChild(0);
                 mLaunchRunnable = null;
             }
             mViewMediatorCallback.keyguardDone(true);
@@ -438,28 +411,38 @@
         mLaunchRunnable = runnable;
     }
 
-    private KeyguardSecurityView getSecurityView(int securitySelectorId) {
-        final int children = mViewFlipper.getChildCount();
+    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
+        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
+        KeyguardSecurityView view = null;
+        final int children = mSecurityViewContainer.getChildCount();
         for (int child = 0; child < children; child++) {
-            if (mViewFlipper.getChildAt(child).getId() == securitySelectorId) {
-                return ((KeyguardSecurityView)mViewFlipper.getChildAt(child));
+            if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) {
+                view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child));
+                break;
             }
         }
-        return null;
+        if (view == null) {
+            final LayoutInflater inflater = LayoutInflater.from(mContext);
+            View v = inflater.inflate(getLayoutIdFor(securityMode), this, false);
+            mSecurityViewContainer.addView(v);
+            updateSecurityView(v);
+            view = (KeyguardSecurityView) v;
+        }
+        return view;
     }
 
     /**
      * Switches to the given security view unless it's already being shown, in which case
      * this is a no-op.
      *
-     * @param securityViewId
+     * @param securityMode
      */
-    private void showSecurityScreen(int securityViewId) {
+    private void showSecurityScreen(SecurityMode securityMode) {
 
-        if (securityViewId == mCurrentSecurityId) return;
+        if (securityMode == mCurrentSecuritySelection) return;
 
-        KeyguardSecurityView oldView = getSecurityView(mCurrentSecurityId);
-        KeyguardSecurityView newView = getSecurityView(securityViewId);
+        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
+        KeyguardSecurityView newView = getSecurityView(securityMode);
 
         // Emulate Activity life cycle
         oldView.onPause();
@@ -468,45 +451,46 @@
         mViewMediatorCallback.setNeedsInput(newView.needsInput());
 
         // Find and show this child.
-        final int childCount = mViewFlipper.getChildCount();
+        final int childCount = mSecurityViewContainer.getChildCount();
 
         // If we're go to/from the selector view, do flip animation, otherwise use fade animation.
-        final boolean doFlip = mCurrentSecurityId == SECURITY_SELECTOR_ID
-                || securityViewId == SECURITY_SELECTOR_ID;
+        final boolean doFlip = mCurrentSecuritySelection == SecurityMode.None
+                || securityMode == SecurityMode.None;
         final int inAnimation = doFlip ? R.anim.keyguard_security_animate_in
                 : R.anim.keyguard_security_fade_in;
         final int outAnimation = doFlip ? R.anim.keyguard_security_animate_out
                 : R.anim.keyguard_security_fade_out;
 
-        mViewFlipper.setInAnimation(AnimationUtils.loadAnimation(mContext, inAnimation));
-        mViewFlipper.setOutAnimation(AnimationUtils.loadAnimation(mContext, outAnimation));
+        mSecurityViewContainer.setInAnimation(AnimationUtils.loadAnimation(mContext, inAnimation));
+        mSecurityViewContainer.setOutAnimation(AnimationUtils.loadAnimation(mContext, outAnimation));
+        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
         for (int i = 0; i < childCount; i++) {
-            if (securityViewId == mViewFlipper.getChildAt(i).getId()) {
-                mViewFlipper.setDisplayedChild(i);
+            if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
+                mSecurityViewContainer.setDisplayedChild(i);
                 break;
             }
         }
 
         // Discard current runnable if we're switching back to the selector view
-        if (securityViewId == SECURITY_SELECTOR_ID) {
+        if (securityMode == SecurityMode.None) {
             setOnDismissRunnable(null);
         }
 
-        mCurrentSecurityId = securityViewId;
+        mCurrentSecuritySelection = securityMode;
     }
 
     @Override
     public void onScreenTurnedOn() {
         if (DEBUG) Log.d(TAG, "screen on");
-        showSecurityScreen(mCurrentSecurityId);
-        getSecurityView(mCurrentSecurityId).onResume();
+        showSecurityScreen(mCurrentSecuritySelection);
+        getSecurityView(mCurrentSecuritySelection).onResume();
     }
 
     @Override
     public void onScreenTurnedOff() {
         if (DEBUG) Log.d(TAG, "screen off");
-        showSecurityScreen(SECURITY_SELECTOR_ID);
-        getSecurityView(mCurrentSecurityId).onPause();
+        showSecurityScreen(SecurityMode.None);
+        getSecurityView(mCurrentSecuritySelection).onPause();
     }
 
     @Override
@@ -537,7 +521,7 @@
         if (DEBUG) Log.d(TAG, "onWakeKey");
         if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) {
             if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
-            showSecurityScreen(SECURITY_SELECTOR_ID);
+            showSecurityScreen(SecurityMode.None);
             mViewMediatorCallback.pokeWakelock();
         } else {
             if (DEBUG) Log.d(TAG, "poking wake lock immediately");
@@ -557,23 +541,37 @@
         } else {
             // otherwise, go to the unlock screen, see if they can verify it
             mIsVerifyUnlockOnly = true;
-            showSecurityScreen(getSecurityViewIdForMode(securityMode));
+            showSecurityScreen(securityMode);
         }
     }
 
     private int getSecurityViewIdForMode(SecurityMode securityMode) {
         switch (securityMode) {
-            case None: return SECURITY_SELECTOR_ID;
-            case Pattern: return SECURITY_PATTERN_ID;
-            case Password: return SECURITY_PASSWORD_ID;
-            case Biometric: return SECURITY_BIOMETRIC_ID;
-            case Account: return SECURITY_ACCOUNT_ID;
-            case SimPin: return SECURITY_SIM_PIN_ID;
-            case SimPuk: return SECURITY_SIM_PUK_ID;
+            case None: return R.id.keyguard_selector_view;
+            case Pattern: return R.id.keyguard_pattern_view;
+            case Password: return R.id.keyguard_password_view;
+            case Biometric: return R.id.keyguard_face_unlock_view;
+            case Account: return R.id.keyguard_account_view;
+            case SimPin: return R.id.keyguard_sim_pin_view;
+            case SimPuk: return R.id.keyguard_sim_puk_view;
         }
         return 0;
     }
 
+    private int getLayoutIdFor(SecurityMode securityMode) {
+        switch (securityMode) {
+            case None: return R.layout.keyguard_selector_view;
+            case Pattern: return R.layout.keyguard_pattern_view;
+            case Password: return R.layout.keyguard_password_view;
+            case Biometric: return R.layout.keyguard_face_unlock_view;
+            case Account: return R.layout.keyguard_account_view;
+            case SimPin: return R.layout.keyguard_sim_pin_view;
+            case SimPuk: return R.layout.keyguard_sim_puk_view;
+            default:
+                throw new RuntimeException("No layout for securityMode " + securityMode);
+        }
+    }
+
     private void addWidget(int appId) {
         AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
         AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
index b2ce73e..7e9aa43 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
@@ -68,6 +68,9 @@
     // any passwords with length less than or equal to this length.
     private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
 
+    // Enable this if we want to hide the on-screen PIN keyboard when a physical one is showing
+    private static final boolean ENABLE_HIDE_KEYBOARD = false;
+
     public KeyguardPasswordView(Context context) {
         super(context);
     }
@@ -123,10 +126,13 @@
             mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
             mKeyboardView.setVisibility(View.GONE);
         } else {
-            // Use lockscreen's numeric keyboard if the physical keyboard isn't showing
             mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
-            mKeyboardView.setVisibility(getResources().getConfiguration().hardKeyboardHidden
-                    == Configuration.HARDKEYBOARDHIDDEN_NO ? View.INVISIBLE : View.VISIBLE);
+
+            // Use lockscreen's numeric keyboard if the physical keyboard isn't showing
+            boolean hardKeyboardVisible = getResources().getConfiguration().hardKeyboardHidden
+                    == Configuration.HARDKEYBOARDHIDDEN_NO;
+            mKeyboardView.setVisibility(
+                    (ENABLE_HIDE_KEYBOARD && hardKeyboardVisible) ? View.INVISIBLE : View.VISIBLE);
 
             // The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
             // not a separate view
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 61517b1..fd6060a 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -1628,8 +1628,8 @@
             } else {
                 // Make sure KEEP_SCREEN_ON is disabled, since that
                 // would allow bypassing of the maximum time to lock.
-                Settings.System.putInt(mContext.getContentResolver(),
-                        Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
+                Settings.Global.putInt(mContext.getContentResolver(),
+                        Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
             }
 
             mLastMaximumTimeToLock = timeMs;
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index f618263..3e83baa 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -463,7 +463,7 @@
 
                 if (homeIntent != null) {
                     try {
-                        mContext.startActivity(homeIntent);
+                        mContext.startActivityAsUser(homeIntent, UserHandle.CURRENT);
                     } catch (ActivityNotFoundException e) {
                     }
                 }
diff --git a/services/java/com/android/server/accessibility/ScreenMagnifier.java b/services/java/com/android/server/accessibility/ScreenMagnifier.java
index bd7f276..78092dd 100644
--- a/services/java/com/android/server/accessibility/ScreenMagnifier.java
+++ b/services/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -286,7 +286,11 @@
     private PointerCoords[] getTempPointerCoordsWithMinSize(int size) {
         final int oldSize = (mTempPointerCoords != null) ? mTempPointerCoords.length : 0;
         if (oldSize < size) {
+            PointerCoords[] oldTempPointerCoords = mTempPointerCoords;
             mTempPointerCoords = new PointerCoords[size];
+            if (oldTempPointerCoords != null) {
+                System.arraycopy(oldTempPointerCoords, 0, mTempPointerCoords, 0, oldSize);
+            }
         }
         for (int i = oldSize; i < size; i++) {
             mTempPointerCoords[i] = new PointerCoords();
@@ -297,7 +301,11 @@
     private PointerProperties[] getTempPointerPropertiesWithMinSize(int size) {
         final int oldSize = (mTempPointerProperties != null) ? mTempPointerProperties.length : 0;
         if (oldSize < size) {
+            PointerProperties[] oldTempPointerProperties = mTempPointerProperties;
             mTempPointerProperties = new PointerProperties[size];
+            if (oldTempPointerProperties != null) {
+                System.arraycopy(oldTempPointerProperties, 0, mTempPointerProperties, 0, oldSize);
+            }
         }
         for (int i = oldSize; i < size; i++) {
             mTempPointerProperties[i] = new PointerProperties();
@@ -324,7 +332,7 @@
                     Slog.i(LOG_TAG, "mCurrentState: STATE_PANNING");
                 } break;
                 case STATE_DECIDE_PAN_OR_SCALE: {
-                    Slog.i(LOG_TAG, "mCurrentState: STATE_DETECTING_PAN_OR_SCALE");
+                    Slog.i(LOG_TAG, "mCurrentState: STATE_DECIDE_PAN_OR_SCALE");
                 } break;
                 default: {
                     throw new IllegalArgumentException("Unknown state: " + state);
@@ -397,6 +405,7 @@
                     }
                     if (scaleDelta > DETECT_SCALING_THRESHOLD) {
                         performScale(detector, true);
+                        clear();
                         transitionToState(STATE_SCALING);
                         return false;
                     }
@@ -409,8 +418,9 @@
                         Slog.i(LOG_TAG, "panDelta: " + panDelta);
                     }
                     if (panDelta > mScaledDetectPanningThreshold) {
-                        transitionToState(STATE_PANNING);
                         performPan(detector, true);
+                        clear();
+                        transitionToState(STATE_PANNING);
                         return false;
                     }
                 } break;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 76a4f15a..ca45946 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -2150,8 +2150,7 @@
             intent.addCategory(Intent.CATEGORY_HOME);
         }
         ActivityInfo aInfo =
-            intent.resolveActivityInfo(mContext.getPackageManager(),
-                    STOCK_PM_FLAGS);
+            resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
         if (aInfo != null) {
             intent.setComponent(new ComponentName(
                     aInfo.applicationInfo.packageName, aInfo.name));
@@ -2174,6 +2173,29 @@
         return true;
     }
 
+    private ActivityInfo resolveActivityInfo(Intent intent, int flags, int userId) {
+        ActivityInfo ai = null;
+        ComponentName comp = intent.getComponent();
+        try {
+            if (comp != null) {
+                ai = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
+            } else {
+                ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(
+                        intent,
+                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                            flags, userId);
+    
+                if (info != null) {
+                    ai = info.activityInfo;
+                }
+            }
+        } catch (RemoteException e) {
+            // ignore
+        }
+
+        return ai;
+    }
+
     /**
      * Starts the "new version setup screen" if appropriate.
      */
@@ -2370,17 +2392,8 @@
             String resultWho, int requestCode, int startFlags,
             String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) {
         enforceNotIsolatedCaller("startActivity");
-        if (userId != UserHandle.getCallingUserId()) {
-            userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
-                    false, true, "startActivity", null);
-        } else {
-            if (intent.getCategories() != null
-                    && intent.getCategories().contains(Intent.CATEGORY_HOME)) {
-                // Requesting home, set the identity to the current user
-                // HACK!
-                userId = mCurrentUserId;
-            }
-        }
+        userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+                false, true, "startActivity", null);
         return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
                 resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
                 null, null, options, userId);
@@ -10838,7 +10851,7 @@
                         builder.append("; this requires ");
                         builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
                         if (!requireFull) {
-                            builder.append("or");
+                            builder.append(" or ");
                             builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
                         }
                         String msg = builder.toString();
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 399ea59..895b52a 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -422,11 +422,10 @@
     }
     
     final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
-        // TODO: Don't look for any tasks from other users
         int i = mHistory.size()-1;
         while (i >= 0) {
             ActivityRecord r = mHistory.get(i);
-            if (!r.finishing && r != notTop) {
+            if (!r.finishing && r != notTop && r.userId == mCurrentUser) {
                 return r;
             }
             i--;
@@ -435,11 +434,10 @@
     }
 
     final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
-        // TODO: Don't look for any tasks from other users
         int i = mHistory.size()-1;
         while (i >= 0) {
             ActivityRecord r = mHistory.get(i);
-            if (!r.finishing && !r.delayedResume && r != notTop) {
+            if (!r.finishing && !r.delayedResume && r != notTop && r.userId == mCurrentUser) {
                 return r;
             }
             i--;
@@ -457,12 +455,12 @@
      * @return Returns the HistoryRecord of the next activity on the stack.
      */
     final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
-        // TODO: Don't look for any tasks from other users
         int i = mHistory.size()-1;
         while (i >= 0) {
             ActivityRecord r = mHistory.get(i);
             // Note: the taskId check depends on real taskId fields being non-zero
-            if (!r.finishing && (token != r.appToken) && (taskId != r.task.taskId)) {
+            if (!r.finishing && (token != r.appToken) && (taskId != r.task.taskId)
+                    && r.userId == mCurrentUser) {
                 return r;
             }
             i--;
@@ -1400,7 +1398,7 @@
             // Launcher...
             if (mMainStack) {
                 ActivityOptions.abort(options);
-                return mService.startHomeActivityLocked(0, null);
+                return mService.startHomeActivityLocked(mCurrentUser, null);
             }
         }
 
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index c80d63a..de306b5 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -85,7 +85,7 @@
 
     public boolean canRunHere(ProcessRecord app) {
         return (info.multiprocess || info.processName.equals(app.processName))
-                && (uid == Process.SYSTEM_UID || uid == app.info.uid);
+                && uid == app.info.uid;
     }
 
     public void addExternalProcessHandleLocked(IBinder token) {
diff --git a/services/java/com/android/server/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java
index bdc87f9..995c553 100644
--- a/services/java/com/android/server/display/DisplayDevice.java
+++ b/services/java/com/android/server/display/DisplayDevice.java
@@ -17,7 +17,6 @@
 package com.android.server.display;
 
 import android.graphics.Rect;
-import android.graphics.SurfaceTexture;
 import android.os.IBinder;
 import android.view.Surface;
 
@@ -41,9 +40,9 @@
     private Rect mCurrentLayerStackRect;
     private Rect mCurrentDisplayRect;
 
-    // The display device does own its surface texture, but it should only set it
+    // The display device owns its surface, but it should only set it
     // within a transaction from performTraversalInTransactionLocked.
-    private SurfaceTexture mCurrentSurfaceTexture;
+    private Surface mCurrentSurface;
 
     public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken) {
         mDisplayAdapter = displayAdapter;
@@ -109,11 +108,10 @@
      * Sets the display layer stack while in a transaction.
      */
     public final void setLayerStackInTransactionLocked(int layerStack) {
-        if (mCurrentLayerStack == layerStack) {
-            return;
+        if (mCurrentLayerStack != layerStack) {
+            mCurrentLayerStack = layerStack;
+            Surface.setDisplayLayerStack(mDisplayToken, layerStack);
         }
-        mCurrentLayerStack = layerStack;
-        Surface.setDisplayLayerStack(mDisplayToken, layerStack);
     }
 
     /**
@@ -126,28 +124,35 @@
      *            mapped to. displayRect is specified post-orientation, that is
      *            it uses the orientation seen by the end-user
      */
-    public final void setProjectionInTransactionLocked(int orientation, Rect layerStackRect, Rect displayRect) {
-        mCurrentOrientation = orientation;
-        if (mCurrentLayerStackRect == null) {
-            mCurrentLayerStackRect = new Rect();
+    public final void setProjectionInTransactionLocked(int orientation,
+            Rect layerStackRect, Rect displayRect) {
+        if (mCurrentOrientation != orientation
+                || mCurrentLayerStackRect == null
+                || !mCurrentLayerStackRect.equals(layerStackRect)
+                || mCurrentDisplayRect == null
+                || !mCurrentDisplayRect.equals(displayRect)) {
+            mCurrentOrientation = orientation;
+            if (mCurrentLayerStackRect == null) {
+                mCurrentLayerStackRect = new Rect();
+            }
+            mCurrentLayerStackRect.set(layerStackRect);
+            if (mCurrentDisplayRect == null) {
+                mCurrentDisplayRect = new Rect();
+            }
+            mCurrentDisplayRect.set(displayRect);
+            Surface.setDisplayProjection(mDisplayToken,
+                    orientation, layerStackRect, displayRect);
         }
-        mCurrentLayerStackRect.set(layerStackRect);
-        if (mCurrentDisplayRect == null) {
-            mCurrentDisplayRect = new Rect();
-        }
-        mCurrentDisplayRect.set(displayRect);
-        Surface.setDisplayProjection(mDisplayToken, orientation, layerStackRect, displayRect);
     }
 
     /**
-     * Sets the surface texture while in a transaction.
+     * Sets the display surface while in a transaction.
      */
-    public final void setSurfaceTextureInTransactionLocked(SurfaceTexture surfaceTexture) {
-        if (mCurrentSurfaceTexture == surfaceTexture) {
-            return;
+    public final void setSurfaceInTransactionLocked(Surface surface) {
+        if (mCurrentSurface != surface) {
+            mCurrentSurface = surface;
+            Surface.setDisplaySurface(mDisplayToken, surface);
         }
-        mCurrentSurfaceTexture = surfaceTexture;
-        Surface.setDisplaySurface(mDisplayToken, surfaceTexture);
     }
 
     /**
@@ -156,10 +161,11 @@
      */
     public void dumpLocked(PrintWriter pw) {
         pw.println("mAdapter=" + mDisplayAdapter.getName());
+        pw.println("mDisplayToken=" + mDisplayToken);
         pw.println("mCurrentLayerStack=" + mCurrentLayerStack);
         pw.println("mCurrentOrientation=" + mCurrentOrientation);
-        pw.println("mCurrentViewport=" + mCurrentLayerStackRect);
-        pw.println("mCurrentFrame=" + mCurrentDisplayRect);
-        pw.println("mCurrentSurfaceTexture=" + mCurrentSurfaceTexture);
+        pw.println("mCurrentLayerStackRect=" + mCurrentLayerStackRect);
+        pw.println("mCurrentDisplayRect=" + mCurrentDisplayRect);
+        pw.println("mCurrentSurface=" + mCurrentSurface);
     }
 }
diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java
index 6f82119..c90a1c6 100644
--- a/services/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display;
 
+import android.util.DisplayMetrics;
+
 import libcore.util.Objects;
 
 /**
@@ -58,13 +60,44 @@
      */
     public int height;
 
+    /**
+     * The refresh rate of the display.
+     */
     public float refreshRate;
+
+    /**
+     * The nominal apparent density of the display in DPI used for layout calculations.
+     * This density is sensitive to the viewing distance.  A big TV and a tablet may have
+     * the same apparent density even though the pixels on the TV are much bigger than
+     * those on the tablet.
+     */
     public int densityDpi;
+
+    /**
+     * The physical density of the display in DPI in the X direction.
+     * This density should specify the physical size of each pixel.
+     */
     public float xDpi;
+
+    /**
+     * The physical density of the display in DPI in the X direction.
+     * This density should specify the physical size of each pixel.
+     */
     public float yDpi;
 
+    /**
+     * Display flags.
+     */
     public int flags;
 
+    public void setAssumedDensityForExternalDisplay(int width, int height) {
+        densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
+        // Technically, these values should be smaller than the apparent density
+        // but we don't know the physical size of the display.
+        xDpi = densityDpi;
+        yDpi = densityDpi;
+    }
+
     @Override
     public boolean equals(Object o) {
         return o instanceof DisplayDeviceInfo && equals((DisplayDeviceInfo)o);
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index 706007a..dc85d3f 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -58,7 +58,7 @@
  * </p><p>
  * Display adapters are only weakly coupled to the display manager service.
  * Display adapters communicate changes in display device state to the display manager
- * service asynchronously via a {@link DisplayAdapter.DisplayAdapterListener} registered
+ * service asynchronously via a {@link DisplayAdapter.Listener} registered
  * by the display manager service.  This separation of concerns is important for
  * two main reasons.  First, it neatly encapsulates the responsibilities of these
  * two classes: display adapters handle individual display devices whereas
@@ -254,8 +254,8 @@
      * Returns information about the specified logical display.
      *
      * @param displayId The logical display id.
-     * @param The logical display info, or null if the display does not exist.
-     * This object must be treated as immutable.
+     * @return The logical display info, or null if the display does not exist.  The
+     * returned object must be treated as immutable.
      */
     @Override // Binder call
     public DisplayInfo getDisplayInfo(int displayId) {
@@ -333,6 +333,8 @@
             if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
                 registerDisplayAdapterLocked(new OverlayDisplayAdapter(
                         mSyncRoot, mContext, mHandler, mDisplayAdapterListener, mUiHandler));
+                registerDisplayAdapterLocked(new WifiDisplayAdapter(
+                        mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
             }
         }
     }
@@ -479,14 +481,34 @@
         }
     }
 
-    private void configureDisplayInTransactionLocked(DisplayDevice device) {
-        // TODO: add a proper per-display mirroring control
-        boolean isMirroring = SystemProperties.getBoolean("debug.display.mirror", true);
+    /**
+     * Tells the display manager whether there is interesting unique content on the
+     * specified logical display.  This is used to control automatic mirroring.
+     * <p>
+     * If the display has unique content, then the display manager arranges for it
+     * to be presented on a physical display if appropriate.  Otherwise, the display manager
+     * may choose to make the physical display mirror some other logical display.
+     * </p>
+     *
+     * @param displayId The logical display id to update.
+     * @param hasContent True if the logical display has content.
+     */
+    public void setDisplayHasContent(int displayId, boolean hasContent) {
+        synchronized (mSyncRoot) {
+            LogicalDisplay display = mLogicalDisplays.get(displayId);
+            if (display != null && display.hasContentLocked() != hasContent) {
+                display.setHasContentLocked(hasContent);
+                scheduleTraversalLocked();
+            }
+        }
 
+    }
+
+    private void configureDisplayInTransactionLocked(DisplayDevice device) {
         // Find the logical display that the display device is showing.
-        LogicalDisplay display = null;
-        if (!isMirroring) {
-            display = findLogicalDisplayForDeviceLocked(device);
+        LogicalDisplay display = findLogicalDisplayForDeviceLocked(device);
+        if (display != null && !display.hasContentLocked()) {
+            display = null;
         }
         if (display == null) {
             display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
@@ -609,8 +631,9 @@
      */
     public interface WindowManagerFuncs {
         /**
-         * Request that the window manager call {@link #performTraversalInTransaction}
-         * within a surface transaction at a later time.
+         * Request that the window manager call
+         * {@link #performTraversalInTransactionFromWindowManager} within a surface
+         * transaction at a later time.
          */
         void requestTraversal();
     }
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
index 4a8829a..4962753 100644
--- a/services/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -120,19 +120,20 @@
                 mInfo.width = mPhys.width;
                 mInfo.height = mPhys.height;
                 mInfo.refreshRate = mPhys.refreshRate;
-                mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
-                mInfo.xDpi = mPhys.xDpi;
-                mInfo.yDpi = mPhys.yDpi;
                 if (mBuiltInDisplayId == Surface.BUILT_IN_DISPLAY_ID_MAIN) {
                     mInfo.name = getContext().getResources().getString(
                             com.android.internal.R.string.display_manager_built_in_display_name);
                     mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
                             | DisplayDeviceInfo.FLAG_SECURE
                             | DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION;
+                    mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
+                    mInfo.xDpi = mPhys.xDpi;
+                    mInfo.yDpi = mPhys.yDpi;
                 } else {
                     mInfo.name = getContext().getResources().getString(
                             com.android.internal.R.string.display_manager_hdmi_display_name);
                     mInfo.flags = DisplayDeviceInfo.FLAG_SECURE;
+                    mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height);
                 }
             }
             return mInfo;
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
index c864189..e0f63dd 100644
--- a/services/java/com/android/server/display/LogicalDisplay.java
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -63,6 +63,9 @@
     private DisplayDevice mPrimaryDisplayDevice;
     private DisplayDeviceInfo mPrimaryDisplayDeviceInfo;
 
+    // True if the logical display has unique content.
+    private boolean mHasContent;
+
     // Temporary rectangle used when needed.
     private final Rect mTempLayerStackRect = new Rect();
     private final Rect mTempDisplayRect = new Rect();
@@ -126,7 +129,7 @@
 
     /**
      * Returns true if the logical display is in a valid state.
-     * This method should be checked after calling {@link #update} to handle the
+     * This method should be checked after calling {@link #updateLocked} to handle the
      * case where a logical display should be removed because all of its associated
      * display devices are gone or if it is otherwise no longer needed.
      *
@@ -256,6 +259,29 @@
         device.setProjectionInTransactionLocked(orientation, mTempLayerStackRect, mTempDisplayRect);
     }
 
+    /**
+     * Returns true if the logical display has unique content.
+     * <p>
+     * If the display has unique content then we will try to ensure that it is
+     * visible on at least its primary display device.  Otherwise we will ignore the
+     * logical display and perhaps show mirrored content on the primary display device.
+     * </p>
+     *
+     * @return True if the display has unique content.
+     */
+    public boolean hasContentLocked() {
+        return mHasContent;
+    }
+
+    /**
+     * Sets whether the logical display has unique content.
+     *
+     * @param hasContent True if the display has unique content.
+     */
+    public void setHasContentLocked(boolean hasContent) {
+        mHasContent = hasContent;
+    }
+
     public void dumpLocked(PrintWriter pw) {
         pw.println("mLayerStack=" + mLayerStack);
         pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
diff --git a/services/java/com/android/server/display/OverlayDisplayAdapter.java b/services/java/com/android/server/display/OverlayDisplayAdapter.java
index ea7e88de..e2d3059 100644
--- a/services/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -16,9 +16,11 @@
 
 package com.android.server.display;
 
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+
 import android.content.Context;
 import android.database.ContentObserver;
-import android.graphics.SurfaceTexture;
 import android.os.Handler;
 import android.os.IBinder;
 import android.provider.Settings;
@@ -28,7 +30,6 @@
 import android.view.Surface;
 
 import java.io.PrintWriter;
-import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -71,6 +72,7 @@
     @Override
     public void dumpLocked(PrintWriter pw) {
         super.dumpLocked(pw);
+
         pw.println("mCurrentOverlaySetting=" + mCurrentOverlaySetting);
         pw.println("mOverlays: size=" + mOverlays.size());
         for (OverlayDisplayHandle overlay : mOverlays) {
@@ -81,17 +83,19 @@
     @Override
     public void registerLocked() {
         super.registerLocked();
-        getContext().getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(Settings.Secure.OVERLAY_DISPLAY_DEVICES), true,
-                new ContentObserver(getHandler()) {
-                    @Override
-                    public void onChange(boolean selfChange) {
-                        synchronized (getSyncRoot()) {
-                            updateOverlayDisplayDevicesLocked();
-                        }
-                    }
-                });
-        updateOverlayDisplayDevicesLocked();
+
+        getHandler().post(new Runnable() {
+            @Override
+            public void run() {
+                getContext().getContentResolver().registerContentObserver(
+                        Settings.System.getUriFor(Settings.Secure.OVERLAY_DISPLAY_DEVICES),
+                        true, new SettingsObserver(getHandler()));
+
+                synchronized (getSyncRoot()) {
+                    updateOverlayDisplayDevicesLocked();
+                }
+            }
+        });
     }
 
     private void updateOverlayDisplayDevicesLocked() {
@@ -167,6 +171,19 @@
         }
     }
 
+    private final class SettingsObserver extends ContentObserver {
+        public SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            synchronized (getSyncRoot()) {
+                updateOverlayDisplayDevicesLocked();
+            }
+        }
+    }
+
     private final class OverlayDisplayDevice extends DisplayDevice {
         private final String mName;
         private final int mWidth;
@@ -174,35 +191,29 @@
         private final float mRefreshRate;
         private final int mDensityDpi;
 
-        private SurfaceTexture mSurfaceTexture;
-        private boolean mSurfaceTextureChanged;
-
+        private Surface mSurface;
         private DisplayDeviceInfo mInfo;
 
         public OverlayDisplayDevice(IBinder displayToken, String name,
-                int width, int height, float refreshRate, int densityDpi) {
+                int width, int height, float refreshRate, int densityDpi,
+                Surface surface) {
             super(OverlayDisplayAdapter.this, displayToken);
             mName = name;
             mWidth = width;
             mHeight = height;
             mRefreshRate = refreshRate;
             mDensityDpi = densityDpi;
+            mSurface = surface;
         }
 
-        public void setSurfaceTextureLocked(SurfaceTexture surfaceTexture) {
-            if (surfaceTexture != mSurfaceTexture) {
-                mSurfaceTexture = surfaceTexture;
-                mSurfaceTextureChanged = true;
-                sendTraversalRequestLocked();
-            }
+        public void clearSurfaceLocked() {
+            mSurface = null;
+            sendTraversalRequestLocked();
         }
 
         @Override
         public void performTraversalInTransactionLocked() {
-            if (mSurfaceTextureChanged) {
-                setSurfaceTextureInTransactionLocked(mSurfaceTexture);
-                mSurfaceTextureChanged = false;
-            }
+            setSurfaceInTransactionLocked(mSurface);
         }
 
         @Override
@@ -256,12 +267,11 @@
 
         // Called on the UI thread.
         @Override
-        public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate) {
+        public void onWindowCreated(Surface surface, float refreshRate) {
             synchronized (getSyncRoot()) {
                 IBinder displayToken = Surface.createDisplay(mName);
                 mDevice = new OverlayDisplayDevice(displayToken, mName,
-                        mWidth, mHeight, refreshRate, mDensityDpi);
-                mDevice.setSurfaceTextureLocked(surfaceTexture);
+                        mWidth, mHeight, refreshRate, mDensityDpi, surface);
 
                 sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
             }
@@ -272,40 +282,24 @@
         public void onWindowDestroyed() {
             synchronized (getSyncRoot()) {
                 if (mDevice != null) {
-                    mDevice.setSurfaceTextureLocked(null);
-
+                    mDevice.clearSurfaceLocked();
                     sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
                 }
             }
         }
 
         public void dumpLocked(PrintWriter pw) {
-            pw.println("  " + mName + ": ");
+            pw.println("  " + mName + ":");
             pw.println("    mWidth=" + mWidth);
             pw.println("    mHeight=" + mHeight);
             pw.println("    mDensityDpi=" + mDensityDpi);
             pw.println("    mGravity=" + mGravity);
 
             // Try to dump the window state.
-            // This call may hang if the UI thread is waiting to acquire our lock so
-            // we use a short timeout to recover just in case.
             if (mWindow != null) {
-                final StringWriter sw = new StringWriter();
-                final OverlayDisplayWindow window = mWindow;
-                if (mUiHandler.runWithScissors(new Runnable() {
-                    @Override
-                    public void run() {
-                        PrintWriter lpw = new PrintWriter(sw);
-                        window.dump(lpw);
-                        lpw.close();
-                    }
-                }, 200)) {
-                    for (String line : sw.toString().split("\n")) {
-                        pw.println(line);
-                    }
-                } else {
-                    pw.println("    ... timed out while attempting to dump window state");
-                }
+                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
+                ipw.increaseIndent();
+                DumpUtils.dumpAsync(mUiHandler, mWindow, ipw, 200);
             }
         }
 
diff --git a/services/java/com/android/server/display/OverlayDisplayWindow.java b/services/java/com/android/server/display/OverlayDisplayWindow.java
index 6adfa0f..d08f65f 100644
--- a/services/java/com/android/server/display/OverlayDisplayWindow.java
+++ b/services/java/com/android/server/display/OverlayDisplayWindow.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display;
 
+import com.android.internal.util.DumpUtils;
+
 import android.content.Context;
 import android.graphics.SurfaceTexture;
 import android.hardware.display.DisplayManager;
@@ -27,6 +29,7 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.ScaleGestureDetector;
+import android.view.Surface;
 import android.view.TextureView;
 import android.view.View;
 import android.view.WindowManager;
@@ -42,7 +45,7 @@
  * No locks are held by this object and locks must not be held while making called into it.
  * </p>
  */
-final class OverlayDisplayWindow {
+final class OverlayDisplayWindow implements DumpUtils.Dump {
     private static final String TAG = "OverlayDisplayWindow";
     private static final boolean DEBUG = false;
 
@@ -144,18 +147,18 @@
     }
 
     public void dump(PrintWriter pw) {
-        pw.println("    mWindowVisible=" + mWindowVisible);
-        pw.println("    mWindowX=" + mWindowX);
-        pw.println("    mWindowY=" + mWindowY);
-        pw.println("    mWindowScale=" + mWindowScale);
-        pw.println("    mWindowParams=" + mWindowParams);
+        pw.println("mWindowVisible=" + mWindowVisible);
+        pw.println("mWindowX=" + mWindowX);
+        pw.println("mWindowY=" + mWindowY);
+        pw.println("mWindowScale=" + mWindowScale);
+        pw.println("mWindowParams=" + mWindowParams);
         if (mTextureView != null) {
-            pw.println("    mTextureView.getScaleX()=" + mTextureView.getScaleX());
-            pw.println("    mTextureView.getScaleY()=" + mTextureView.getScaleY());
+            pw.println("mTextureView.getScaleX()=" + mTextureView.getScaleX());
+            pw.println("mTextureView.getScaleY()=" + mTextureView.getScaleY());
         }
-        pw.println("    mLiveTranslationX=" + mLiveTranslationX);
-        pw.println("    mLiveTranslationY=" + mLiveTranslationY);
-        pw.println("    mLiveScale=" + mLiveScale);
+        pw.println("mLiveTranslationX=" + mLiveTranslationX);
+        pw.println("mLiveTranslationY=" + mLiveTranslationY);
+        pw.println("mLiveScale=" + mLiveScale);
     }
 
     private boolean updateDefaultDisplayInfo() {
@@ -286,22 +289,25 @@
     private final SurfaceTextureListener mSurfaceTextureListener =
             new SurfaceTextureListener() {
         @Override
-        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
-            mListener.onWindowCreated(surface, mDefaultDisplayInfo.refreshRate);
+        public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
+                int width, int height) {
+            mListener.onWindowCreated(new Surface(surfaceTexture),
+                    mDefaultDisplayInfo.refreshRate);
         }
 
         @Override
-        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+        public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
             mListener.onWindowDestroyed();
             return true;
         }
 
         @Override
-        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+        public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
+                int width, int height) {
         }
 
         @Override
-        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+        public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
         }
     };
 
@@ -355,7 +361,7 @@
      * Watches for significant changes in the overlay display window lifecycle.
      */
     public interface Listener {
-        public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate);
+        public void onWindowCreated(Surface surface, float refreshRate);
         public void onWindowDestroyed();
     }
 }
\ No newline at end of file
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
new file mode 100644
index 0000000..38007af
--- /dev/null
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -0,0 +1,259 @@
+/*
+ * 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.display;
+
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+
+import android.content.Context;
+import android.media.RemoteDisplay;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Slog;
+import android.view.Surface;
+
+import java.io.PrintWriter;
+
+/**
+ * Connects to Wifi displays that implement the Miracast protocol.
+ * <p>
+ * The Wifi display protocol relies on Wifi direct for discovering and pairing
+ * with the display.  Once connected, the Media Server opens an RTSP socket and accepts
+ * a connection from the display.  After session negotiation, the Media Server
+ * streams encoded buffers to the display.
+ * </p><p>
+ * This class is responsible for connecting to Wifi displays and mediating
+ * the interactions between Media Server, Surface Flinger and the Display Manager Service.
+ * </p><p>
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+final class WifiDisplayAdapter extends DisplayAdapter {
+    private static final String TAG = "WifiDisplayAdapter";
+
+    private WifiDisplayHandle mDisplayHandle;
+    private WifiDisplayController mDisplayController;
+
+    public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+            Context context, Handler handler, Listener listener) {
+        super(syncRoot, context, handler, listener, TAG);
+    }
+
+    @Override
+    public void dumpLocked(PrintWriter pw) {
+        super.dumpLocked(pw);
+
+        if (mDisplayHandle == null) {
+            pw.println("mDisplayHandle=null");
+        } else {
+            pw.println("mDisplayHandle:");
+            mDisplayHandle.dumpLocked(pw);
+        }
+
+        // Try to dump the controller state.
+        if (mDisplayController == null) {
+            pw.println("mDisplayController=null");
+        } else {
+            pw.println("mDisplayController:");
+            final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+            ipw.increaseIndent();
+            DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, 200);
+        }
+    }
+
+    @Override
+    public void registerLocked() {
+        super.registerLocked();
+
+        getHandler().post(new Runnable() {
+            @Override
+            public void run() {
+                mDisplayController = new WifiDisplayController(
+                        getContext(), getHandler(), mWifiDisplayListener);
+            }
+        });
+    }
+
+    private void connectLocked(String deviceName, String iface) {
+        disconnectLocked();
+
+        String name = getContext().getResources().getString(
+                com.android.internal.R.string.display_manager_wifi_display_name,
+                deviceName);
+        mDisplayHandle = new WifiDisplayHandle(name, iface);
+    }
+
+    private void disconnectLocked() {
+        if (mDisplayHandle != null) {
+            mDisplayHandle.disposeLocked();
+            mDisplayHandle = null;
+        }
+    }
+
+    private final WifiDisplayController.Listener mWifiDisplayListener =
+            new WifiDisplayController.Listener() {
+        @Override
+        public void onDisplayConnected(String deviceName, String iface) {
+            synchronized (getSyncRoot()) {
+                connectLocked(deviceName, iface);
+            }
+        }
+
+        @Override
+        public void onDisplayDisconnected() {
+            // Stop listening.
+            synchronized (getSyncRoot()) {
+                disconnectLocked();
+            }
+        }
+    };
+
+    private final class WifiDisplayDevice extends DisplayDevice {
+        private final String mName;
+        private final int mWidth;
+        private final int mHeight;
+        private final float mRefreshRate;
+        private final int mFlags;
+
+        private Surface mSurface;
+        private DisplayDeviceInfo mInfo;
+
+        public WifiDisplayDevice(IBinder displayToken, String name,
+                int width, int height, float refreshRate, int flags,
+                Surface surface) {
+            super(WifiDisplayAdapter.this, displayToken);
+            mName = name;
+            mWidth = width;
+            mHeight = height;
+            mRefreshRate = refreshRate;
+            mFlags = flags;
+            mSurface = surface;
+        }
+
+        public void clearSurfaceLocked() {
+            mSurface = null;
+            sendTraversalRequestLocked();
+        }
+
+        @Override
+        public void performTraversalInTransactionLocked() {
+            setSurfaceInTransactionLocked(mSurface);
+        }
+
+        @Override
+        public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+            if (mInfo == null) {
+                mInfo = new DisplayDeviceInfo();
+                mInfo.name = mName;
+                mInfo.width = mWidth;
+                mInfo.height = mHeight;
+                mInfo.refreshRate = mRefreshRate;
+                mInfo.flags = mFlags;
+                mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
+            }
+            return mInfo;
+        }
+    }
+
+    private final class WifiDisplayHandle implements RemoteDisplay.Listener {
+        private final String mName;
+        private final String mIface;
+        private final RemoteDisplay mRemoteDisplay;
+
+        private WifiDisplayDevice mDevice;
+        private int mLastError;
+
+        public WifiDisplayHandle(String name, String iface) {
+            mName = name;
+            mIface = iface;
+            mRemoteDisplay = RemoteDisplay.listen(iface, this, getHandler());
+
+            Slog.i(TAG, "Listening for Wifi display connections on " + iface
+                    + " from " + mName);
+        }
+
+        public void disposeLocked() {
+            Slog.i(TAG, "Stopped listening for Wifi display connections on " + mIface
+                    + " from " + mName);
+
+            removeDisplayLocked();
+            mRemoteDisplay.dispose();
+        }
+
+        public void dumpLocked(PrintWriter pw) {
+            pw.println("  " + mName + ": " + (mDevice != null ? "connected" : "disconnected"));
+            pw.println("    mIface=" + mIface);
+            pw.println("    mLastError=" + mLastError);
+        }
+
+        // Called on the handler thread.
+        @Override
+        public void onDisplayConnected(Surface surface, int width, int height, int flags) {
+            synchronized (getSyncRoot()) {
+                mLastError = 0;
+                removeDisplayLocked();
+                addDisplayLocked(surface, width, height, flags);
+
+                Slog.i(TAG, "Wifi display connected: " + mName);
+            }
+        }
+
+        // Called on the handler thread.
+        @Override
+        public void onDisplayDisconnected() {
+            synchronized (getSyncRoot()) {
+                mLastError = 0;
+                removeDisplayLocked();
+
+                Slog.i(TAG, "Wifi display disconnected: " + mName);
+            }
+        }
+
+        // Called on the handler thread.
+        @Override
+        public void onDisplayError(int error) {
+            synchronized (getSyncRoot()) {
+                mLastError = error;
+                removeDisplayLocked();
+
+                Slog.i(TAG, "Wifi display disconnected due to error " + error + ": " + mName);
+            }
+        }
+
+        private void addDisplayLocked(Surface surface, int width, int height, int flags) {
+            int deviceFlags = 0;
+            if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) {
+                deviceFlags |= DisplayDeviceInfo.FLAG_SECURE;
+            }
+
+            float refreshRate = 60.0f; // TODO: get this for real
+
+            IBinder displayToken = Surface.createDisplay(mName);
+            mDevice = new WifiDisplayDevice(displayToken, mName, width, height,
+                    refreshRate, deviceFlags, surface);
+            sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
+        }
+
+        private void removeDisplayLocked() {
+            if (mDevice != null) {
+                mDevice.clearSurfaceLocked();
+                sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
+                mDevice = null;
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java
new file mode 100644
index 0000000..b446e66
--- /dev/null
+++ b/services/java/com/android/server/display/WifiDisplayController.java
@@ -0,0 +1,555 @@
+/*
+ * 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.display;
+
+import com.android.internal.util.DumpUtils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.NetworkInfo;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pDeviceList;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.p2p.WifiP2pWfdInfo;
+import android.net.wifi.p2p.WifiP2pManager.ActionListener;
+import android.net.wifi.p2p.WifiP2pManager.Channel;
+import android.net.wifi.p2p.WifiP2pManager.GroupInfoListener;
+import android.net.wifi.p2p.WifiP2pManager.PeerListListener;
+import android.os.Handler;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+
+/**
+ * Manages all of the various asynchronous interactions with the {@link WifiP2pManager}
+ * on behalf of {@link WifiDisplayAdapter}.
+ * <p>
+ * This code is isolated from {@link WifiDisplayAdapter} so that we can avoid
+ * accidentally introducing any deadlocks due to the display manager calling
+ * outside of itself while holding its lock.  It's also way easier to write this
+ * asynchronous code if we can assume that it is single-threaded.
+ * </p><p>
+ * The controller must be instantiated on the handler thread.
+ * </p>
+ */
+final class WifiDisplayController implements DumpUtils.Dump {
+    private static final String TAG = "WifiDisplayController";
+    private static final boolean DEBUG = true;
+
+    private static final int DEFAULT_CONTROL_PORT = 7236;
+    private static final int MAX_THROUGHPUT = 50;
+    private static final int CONNECTION_TIMEOUT_SECONDS = 30;
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final Listener mListener;
+    private final WifiP2pManager mWifiP2pManager;
+    private final Channel mWifiP2pChannel;
+
+    private boolean mWifiP2pEnabled;
+    private boolean mWfdEnabled;
+    private boolean mWfdEnabling;
+    private NetworkInfo mNetworkInfo;
+
+    private final ArrayList<WifiP2pDevice> mKnownWifiDisplayPeers =
+            new ArrayList<WifiP2pDevice>();
+
+    // The device to which we want to connect, or null if we want to be disconnected.
+    private WifiP2pDevice mDesiredDevice;
+
+    // The device to which we are currently connecting, or null if we have already connected
+    // or are not trying to connect.
+    private WifiP2pDevice mConnectingDevice;
+
+    // The device to which we are currently connected, which means we have an active P2P group.
+    private WifiP2pDevice mConnectedDevice;
+
+    // The group info obtained after connecting.
+    private WifiP2pGroup mConnectedDeviceGroupInfo;
+
+    // The device that we announced to the rest of the system.
+    private WifiP2pDevice mPublishedDevice;
+
+    public WifiDisplayController(Context context, Handler handler, Listener listener) {
+        mContext = context;
+        mHandler = handler;
+        mListener = listener;
+
+        mWifiP2pManager = (WifiP2pManager)context.getSystemService(Context.WIFI_P2P_SERVICE);
+        mWifiP2pChannel = mWifiP2pManager.initialize(context, handler.getLooper(), null);
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
+        intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
+        intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+        context.registerReceiver(mWifiP2pReceiver, intentFilter);
+    }
+
+    public void dump(PrintWriter pw) {
+        pw.println("mWifiP2pEnabled=" + mWifiP2pEnabled);
+        pw.println("mWfdEnabled=" + mWfdEnabled);
+        pw.println("mWfdEnabling=" + mWfdEnabling);
+        pw.println("mNetworkInfo=" + mNetworkInfo);
+        pw.println("mDesiredDevice=" + describeWifiP2pDevice(mDesiredDevice));
+        pw.println("mConnectingDisplay=" + describeWifiP2pDevice(mConnectingDevice));
+        pw.println("mConnectedDevice=" + describeWifiP2pDevice(mConnectedDevice));
+        pw.println("mPublishedDevice=" + describeWifiP2pDevice(mPublishedDevice));
+
+        pw.println("mKnownWifiDisplayPeers: size=" + mKnownWifiDisplayPeers.size());
+        for (WifiP2pDevice device : mKnownWifiDisplayPeers) {
+            pw.println("  " + describeWifiP2pDevice(device));
+        }
+    }
+
+    private void enableWfd() {
+        if (!mWfdEnabled && !mWfdEnabling) {
+            mWfdEnabling = true;
+
+            WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo();
+            wfdInfo.setWfdEnabled(true);
+            wfdInfo.setDeviceType(WifiP2pWfdInfo.WFD_SOURCE);
+            wfdInfo.setSessionAvailable(true);
+            wfdInfo.setControlPort(DEFAULT_CONTROL_PORT);
+            wfdInfo.setMaxThroughput(MAX_THROUGHPUT);
+            mWifiP2pManager.setWFDInfo(mWifiP2pChannel, wfdInfo, new ActionListener() {
+                @Override
+                public void onSuccess() {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Successfully set WFD info.");
+                    }
+                    if (mWfdEnabling) {
+                        mWfdEnabled = true;
+                        mWfdEnabling = false;
+                        discoverPeers();
+                    }
+                }
+
+                @Override
+                public void onFailure(int reason) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Failed to set WFD info with reason " + reason + ".");
+                    }
+                    mWfdEnabling = false;
+                }
+            });
+        }
+    }
+
+    private void discoverPeers() {
+        mWifiP2pManager.discoverPeers(mWifiP2pChannel, new ActionListener() {
+            @Override
+            public void onSuccess() {
+                if (DEBUG) {
+                    Slog.d(TAG, "Discover peers succeeded.  Requesting peers now.");
+                }
+
+                requestPeers();
+            }
+
+            @Override
+            public void onFailure(int reason) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Discover peers failed with reason " + reason + ".");
+                }
+            }
+        });
+    }
+
+    private void requestPeers() {
+        mWifiP2pManager.requestPeers(mWifiP2pChannel, new PeerListListener() {
+            @Override
+            public void onPeersAvailable(WifiP2pDeviceList peers) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Received list of peers.");
+                }
+
+                mKnownWifiDisplayPeers.clear();
+                for (WifiP2pDevice device : peers.getDeviceList()) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "  " + describeWifiP2pDevice(device));
+                    }
+
+                    if (isWifiDisplay(device)) {
+                        mKnownWifiDisplayPeers.add(device);
+                    }
+                }
+
+                // TODO: shouldn't auto-connect like this, let UI do it explicitly
+                if (!mKnownWifiDisplayPeers.isEmpty()) {
+                    final WifiP2pDevice device = mKnownWifiDisplayPeers.get(0);
+
+                    if (device.status == WifiP2pDevice.AVAILABLE) {
+                        connect(device);
+                    }
+                }
+
+                // TODO: publish this information to applications
+            }
+        });
+    }
+
+    private void connect(final WifiP2pDevice device) {
+        if (mDesiredDevice != null
+                && !mDesiredDevice.deviceAddress.equals(device.deviceAddress)) {
+            if (DEBUG) {
+                Slog.d(TAG, "connect: nothing to do, already connecting to "
+                        + describeWifiP2pDevice(device));
+            }
+            return;
+        }
+
+        if (mConnectedDevice != null
+                && !mConnectedDevice.deviceAddress.equals(device.deviceAddress)
+                && mDesiredDevice == null) {
+            if (DEBUG) {
+                Slog.d(TAG, "connect: nothing to do, already connected to "
+                        + describeWifiP2pDevice(device) + " and not part way through "
+                        + "connecting to a different device.");
+            }
+        }
+
+        mDesiredDevice = device;
+        updateConnection();
+    }
+
+    private void disconnect() {
+        mDesiredDevice = null;
+        updateConnection();
+    }
+
+    /**
+     * This function is called repeatedly after each asynchronous operation
+     * until all preconditions for the connection have been satisfied and the
+     * connection is established (or not).
+     */
+    private void updateConnection() {
+        // Step 1. Before we try to connect to a new device, tell the system we
+        // have disconnected from the old one.
+        if (mPublishedDevice != null && mPublishedDevice != mDesiredDevice) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mListener.onDisplayDisconnected();
+                }
+            });
+            mPublishedDevice = null;
+
+            // continue to next step
+        }
+
+        // Step 2. Before we try to connect to a new device, disconnect from the old one.
+        if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) {
+            Slog.i(TAG, "Disconnecting from Wifi display: " + mConnectedDevice.deviceName);
+
+            final WifiP2pDevice oldDevice = mConnectedDevice;
+            mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() {
+                @Override
+                public void onSuccess() {
+                    Slog.i(TAG, "Disconnected from Wifi display: " + oldDevice.deviceName);
+                    next();
+                }
+
+                @Override
+                public void onFailure(int reason) {
+                    Slog.i(TAG, "Failed to disconnect from Wifi display: "
+                            + oldDevice.deviceName + ", reason=" + reason);
+                    next();
+                }
+
+                private void next() {
+                    if (mConnectedDevice == oldDevice) {
+                        mConnectedDevice = null;
+                        updateConnection();
+                    }
+                }
+            });
+            return; // wait for asynchronous callback
+        }
+
+        // Step 3. Before we try to connect to a new device, stop trying to connect
+        // to the old one.
+        if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) {
+            Slog.i(TAG, "Canceling connection to Wifi display: " + mConnectingDevice.deviceName);
+
+            mHandler.removeCallbacks(mConnectionTimeout);
+
+            final WifiP2pDevice oldDevice = mConnectingDevice;
+            mWifiP2pManager.cancelConnect(mWifiP2pChannel, new ActionListener() {
+                @Override
+                public void onSuccess() {
+                    Slog.i(TAG, "Canceled connection to Wifi display: " + oldDevice.deviceName);
+                    next();
+                }
+
+                @Override
+                public void onFailure(int reason) {
+                    Slog.i(TAG, "Failed to cancel connection to Wifi display: "
+                            + oldDevice.deviceName + ", reason=" + reason);
+                    next();
+                }
+
+                private void next() {
+                    if (mConnectingDevice == oldDevice) {
+                        mConnectingDevice = null;
+                        updateConnection();
+                    }
+                }
+            });
+            return; // wait for asynchronous callback
+        }
+
+        // Step 4. If we wanted to disconnect, then mission accomplished.
+        if (mDesiredDevice == null) {
+            return; // done
+        }
+
+        // Step 5. Try to connect.
+        if (mConnectedDevice == null && mConnectingDevice == null) {
+            Slog.i(TAG, "Connecting to Wifi display: " + mDesiredDevice.deviceName);
+
+            mConnectingDevice = mDesiredDevice;
+            WifiP2pConfig config = new WifiP2pConfig();
+            config.deviceAddress = mConnectingDevice.deviceAddress;
+
+            final WifiP2pDevice newDevice = mDesiredDevice;
+            mWifiP2pManager.connect(mWifiP2pChannel, config, new ActionListener() {
+                @Override
+                public void onSuccess() {
+                    // The connection may not yet be established.  We still need to wait
+                    // for WIFI_P2P_CONNECTION_CHANGED_ACTION.  However, we might never
+                    // get that broadcast, so we register a timeout.
+                    Slog.i(TAG, "Initiated connection to Wifi display: " + newDevice.deviceName);
+
+                    mHandler.postDelayed(mConnectionTimeout, CONNECTION_TIMEOUT_SECONDS * 1000);
+                }
+
+                @Override
+                public void onFailure(int reason) {
+                    Slog.i(TAG, "Failed to initiate connection to Wifi display: "
+                            + newDevice.deviceName + ", reason=" + reason);
+                    if (mConnectingDevice == newDevice) {
+                        mConnectingDevice = null;
+                        handleConnectionFailure();
+                    }
+                }
+            });
+            return; // wait for asynchronous callback
+        }
+
+        // Step 6. Publish the new connection.
+        if (mConnectedDevice != null && mPublishedDevice == null) {
+            Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo);
+            if (addr == null) {
+                Slog.i(TAG, "Failed to get local interface address for communicating "
+                        + "with Wifi display: " + mConnectedDevice.deviceName);
+                handleConnectionFailure();
+                return; // done
+            }
+
+            WifiP2pWfdInfo wfdInfo = mConnectedDevice.wfdInfo;
+            int port = (wfdInfo != null ? wfdInfo.getControlPort() : DEFAULT_CONTROL_PORT);
+            final String name = mConnectedDevice.deviceName;
+            final String iface = addr.getHostAddress() + ":" + port;
+
+            mPublishedDevice = mConnectedDevice;
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mListener.onDisplayConnected(name, iface);
+                }
+            });
+        }
+    }
+
+    private void handleStateChanged(boolean enabled) {
+        if (mWifiP2pEnabled != enabled) {
+            mWifiP2pEnabled = enabled;
+            if (enabled) {
+                if (mWfdEnabled) {
+                    discoverPeers();
+                } else {
+                    enableWfd();
+                }
+            } else {
+                mWfdEnabled = false;
+                disconnect();
+            }
+        }
+    }
+
+    private void handlePeersChanged() {
+        if (mWifiP2pEnabled) {
+            if (mWfdEnabled) {
+                requestPeers();
+            } else {
+                enableWfd();
+            }
+        }
+    }
+
+    private void handleConnectionChanged(NetworkInfo networkInfo) {
+        mNetworkInfo = networkInfo;
+        if (mWifiP2pEnabled && mWfdEnabled && networkInfo.isConnected()) {
+            if (mDesiredDevice != null) {
+                mWifiP2pManager.requestGroupInfo(mWifiP2pChannel, new GroupInfoListener() {
+                    @Override
+                    public void onGroupInfoAvailable(WifiP2pGroup info) {
+                        if (DEBUG) {
+                            Slog.d(TAG, "Received group info: " + describeWifiP2pGroup(info));
+                        }
+
+                        if (mConnectingDevice != null && !info.contains(mConnectingDevice)) {
+                            Slog.i(TAG, "Aborting connection to Wifi display because "
+                                    + "the current P2P group does not contain the device "
+                                    + "we expected to find: " + mConnectingDevice.deviceName);
+                            handleConnectionFailure();
+                            return;
+                        }
+
+                        if (mDesiredDevice != null && !info.contains(mDesiredDevice)) {
+                            disconnect();
+                            return;
+                        }
+
+                        if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) {
+                            Slog.i(TAG, "Connected to Wifi display: " + mConnectingDevice.deviceName);
+
+                            mHandler.removeCallbacks(mConnectionTimeout);
+                            mConnectedDeviceGroupInfo = info;
+                            mConnectedDevice = mConnectingDevice;
+                            mConnectingDevice = null;
+                            updateConnection();
+                        }
+                    }
+                });
+            }
+        } else {
+            disconnect();
+        }
+    }
+
+    private final Runnable mConnectionTimeout = new Runnable() {
+        @Override
+        public void run() {
+            if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) {
+                Slog.i(TAG, "Timed out waiting for Wifi display connection after "
+                        + CONNECTION_TIMEOUT_SECONDS + " seconds: "
+                        + mConnectingDevice.deviceName);
+                handleConnectionFailure();
+            }
+        }
+    };
+
+    private void handleConnectionFailure() {
+        if (mDesiredDevice != null) {
+            Slog.i(TAG, "Wifi display connection failed!");
+            disconnect();
+        }
+    }
+
+    private static Inet4Address getInterfaceAddress(WifiP2pGroup info) {
+        NetworkInterface iface;
+        try {
+            iface = NetworkInterface.getByName(info.getInterface());
+        } catch (SocketException ex) {
+            Slog.w(TAG, "Could not obtain address of network interface "
+                    + info.getInterface(), ex);
+            return null;
+        }
+
+        Enumeration<InetAddress> addrs = iface.getInetAddresses();
+        while (addrs.hasMoreElements()) {
+            InetAddress addr = addrs.nextElement();
+            if (addr instanceof Inet4Address) {
+                return (Inet4Address)addr;
+            }
+        }
+
+        Slog.w(TAG, "Could not obtain address of network interface "
+                + info.getInterface() + " because it had no IPv4 addresses.");
+        return null;
+    }
+
+    private static boolean isWifiDisplay(WifiP2pDevice device) {
+        // FIXME: the wfdInfo API doesn't work yet
+        return false;
+        //return device.deviceName.equals("DWD-300-22ACC2");
+        //return device.deviceName.startsWith("DWD-")
+        //        || device.deviceName.startsWith("DIRECT-")
+        //        || device.deviceName.startsWith("CAVM-");
+        //return device.wfdInfo != null && device.wfdInfo.isWfdEnabled();
+    }
+
+    private static String describeWifiP2pDevice(WifiP2pDevice device) {
+        return device != null ? device.toString().replace('\n', ',') : "null";
+    }
+
+    private static String describeWifiP2pGroup(WifiP2pGroup group) {
+        return group != null ? group.toString().replace('\n', ',') : "null";
+    }
+
+    private final BroadcastReceiver mWifiP2pReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) {
+                boolean enabled = (intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,
+                        WifiP2pManager.WIFI_P2P_STATE_DISABLED)) ==
+                        WifiP2pManager.WIFI_P2P_STATE_ENABLED;
+                if (DEBUG) {
+                    Slog.d(TAG, "Received WIFI_P2P_STATE_CHANGED_ACTION: enabled="
+                            + enabled);
+                }
+
+                handleStateChanged(enabled);
+            } else if (action.equals(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Received WIFI_P2P_PEERS_CHANGED_ACTION.");
+                }
+
+                handlePeersChanged();
+            } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {
+                NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
+                        WifiP2pManager.EXTRA_NETWORK_INFO);
+                if (DEBUG) {
+                    Slog.d(TAG, "Received WIFI_P2P_CONNECTION_CHANGED_ACTION: networkInfo="
+                            + networkInfo);
+                }
+
+                handleConnectionChanged(networkInfo);
+            }
+        }
+    };
+
+    /**
+     * Called on the handler thread when displays are connected or disconnected.
+     */
+    public interface Listener {
+        void onDisplayConnected(String deviceName, String iface);
+        void onDisplayDisconnected();
+    }
+}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 5ddf3c3..133d926 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -4110,8 +4110,13 @@
                     NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir);
                 } else {
                     Slog.i(TAG, "Linking native library dir for " + path);
-                    mInstaller.linkNativeLibraryDirectory(dataPathString,
+                    int ret = mInstaller.linkNativeLibraryDirectory(dataPathString,
                             pkg.applicationInfo.nativeLibraryDir);
+                    if (ret < 0) {
+                        Slog.w(TAG, "Failed linking native library dir for " + path);
+                        mLastScanError = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
+                        return null;
+                    }
                 }
             } catch (IOException ioe) {
                 Log.e(TAG, "Unable to get canonical file " + ioe.toString());
@@ -5815,8 +5820,8 @@
      * @return verification timeout in milliseconds
      */
     private long getVerificationTimeout() {
-        return android.provider.Settings.Secure.getLong(mContext.getContentResolver(),
-                android.provider.Settings.Secure.PACKAGE_VERIFIER_TIMEOUT,
+        return android.provider.Settings.Global.getLong(mContext.getContentResolver(),
+                android.provider.Settings.Global.PACKAGE_VERIFIER_TIMEOUT,
                 DEFAULT_VERIFICATION_TIMEOUT);
     }
 
@@ -5826,8 +5831,8 @@
      * @return default verification response code
      */
     private int getDefaultVerificationResponse() {
-        return android.provider.Settings.Secure.getInt(mContext.getContentResolver(),
-                android.provider.Settings.Secure.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
+        return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
+                android.provider.Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
                 DEFAULT_VERIFICATION_RESPONSE);
     }
 
@@ -5837,8 +5842,8 @@
      * @return true if verification should be performed
      */
     private boolean isVerificationEnabled() {
-        return android.provider.Settings.Secure.getInt(mContext.getContentResolver(),
-                android.provider.Settings.Secure.PACKAGE_VERIFIER_ENABLE,
+        return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
+                android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE,
                 DEFAULT_VERIFY_ENABLE ? 1 : 0) == 1 ? true : false;
     }
 
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index d058a82..b85353c 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -1259,7 +1259,7 @@
                     final ArrayList<PackageCleanItem> pkgs = mPackagesToBeCleaned.valueAt(i);
                     for (int j=0; j<pkgs.size(); j++) {
                         serializer.startTag(null, "cleaning-package");
-                        PackageCleanItem item = pkgs.get(i);
+                        PackageCleanItem item = pkgs.get(j);
                         serializer.attribute(null, ATTR_NAME, item.packageName);
                         serializer.attribute(null, ATTR_CODE, item.andCode ? "true" : "false");
                         serializer.attribute(null, ATTR_USER, userStr);
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 0ea051f..4df692b 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -94,10 +94,13 @@
     }
 
     DisplayInfo getDisplayInfo() {
-        // TODO: Add a listener for changes to Display and update mDisplayInfo when appropriate.
         return mDisplayInfo;
     }
 
+    public void updateDisplayInfo() {
+        mDisplay.getDisplayInfo(mDisplayInfo);
+    }
+
     public void dump(PrintWriter pw) {
         pw.print("  Display: mDisplayId="); pw.println(mDisplayId);
         pw.print("  init="); pw.print(mInitialDisplayWidth); pw.print("x");
@@ -121,7 +124,7 @@
         pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
         pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
         pw.print("  layoutNeeded="); pw.println(layoutNeeded);
-        pw.print("magnificationSpec="); pw.println(mMagnificationSpec.toString());
+        pw.print("magnificationSpec="); pw.println(mMagnificationSpec);
         pw.println();
     }
 }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 9a0d280..18e793d 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -165,7 +165,7 @@
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
         implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs,
-                DisplayManagerService.WindowManagerFuncs {
+                DisplayManagerService.WindowManagerFuncs, DisplayManager.DisplayListener {
     static final String TAG = "WindowManager";
     static final boolean DEBUG = false;
     static final boolean DEBUG_ADD_REMOVE = false;
@@ -782,9 +782,15 @@
         mLimitedAlphaCompositing = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_sf_limitedAlpha);
         mDisplayManagerService = displayManager;
-        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
         mHeadless = displayManager.isHeadless();
 
+        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+        mDisplayManager.registerDisplayListener(this, null);
+        Display[] displays = mDisplayManager.getDisplays();
+        for (Display display : displays) {
+            createDisplayContent(display);
+        }
+
         mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
 
         mPowerManager = pm;
@@ -887,6 +893,12 @@
         return -1;
     }
 
+    /**
+     * Return the list of Windows from the passed token on the given Display.
+     * @param token The token with all the windows.
+     * @param displayContent The display we are interested in.
+     * @return List of windows from token that are on displayContent.
+     */
     WindowList getTokenWindowsOnDisplay(WindowToken token, DisplayContent displayContent) {
         final WindowList windowList = new WindowList();
         final int count = token.windows.size();
@@ -928,7 +940,7 @@
                         WindowState lastWindow = tokenWindowList.get(index);
                         if (atoken != null && lastWindow == atoken.startingWindow) {
                             placeWindowBefore(lastWindow, win);
-                            tokenWindowsPos = token.windows.indexOf(lastWindow) - 1;
+                            tokenWindowsPos = token.windows.indexOf(lastWindow);
                         } else {
                             int newIdx = findIdxBasedOnAppTokens(win);
                             //there is a window above this one associated with the same
@@ -937,10 +949,15 @@
                             //windows associated with this token.
                             if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) {
                                 Slog.v(TAG, "Adding window " + win + " at "
-                                        + (newIdx+1) + " of " + N);
+                                        + (newIdx + 1) + " of " + N);
                             }
-                            windows.add(newIdx+1, win);
-                            tokenWindowsPos = token.windows.indexOf(windows.get(newIdx)) + 1;
+                            windows.add(newIdx + 1, win);
+                            if (newIdx < 0) {
+                                // No window from token found on win's display.
+                                tokenWindowsPos = 0;
+                            } else {
+                                tokenWindowsPos = token.windows.indexOf(windows.get(newIdx)) + 1;
+                            }
                             mWindowsChanged = true;
                         }
                     }
@@ -1109,6 +1126,10 @@
         if (win.mAppToken != null && addToToken) {
             win.mAppToken.allAppWindows.add(win);
         }
+
+        if (windows.size() == 1) {
+            mDisplayManagerService.setDisplayHasContent(win.getDisplayId(), true);
+        }
     }
 
     /** TODO(cmautner): Is this the same as {@link WindowState#canReceiveKeys()} */
@@ -2397,6 +2418,9 @@
 
         final WindowList windows = win.getWindowList();
         windows.remove(win);
+        if (windows.isEmpty()) {
+            mDisplayManagerService.setDisplayHasContent(win.getDisplayId(), false);
+        }
         mPendingRemove.remove(win);
         mWindowsChanged = true;
         if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win);
@@ -7183,6 +7207,10 @@
         public static final int NOTIFY_WINDOW_TRANSITION = 29;
         public static final int NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 30;
 
+        public static final int DO_DISPLAY_ADDED = 31;
+        public static final int DO_DISPLAY_REMOVED = 32;
+        public static final int DO_DISPLAY_CHANGED = 33;
+
         public static final int ANIMATOR_WHAT_OFFSET = 100000;
         public static final int SET_TRANSPARENT_REGION = ANIMATOR_WHAT_OFFSET + 1;
         public static final int CLEAR_PENDING_ACTIONS = ANIMATOR_WHAT_OFFSET + 2;
@@ -7620,18 +7648,21 @@
                     }
                     break;
                 }
+
                 case NOTIFY_ROTATION_CHANGED: {
                     final int displayId = msg.arg1;
                     final int rotation = msg.arg2;
                     handleNotifyRotationChanged(displayId, rotation);
                     break;
                 }
+
                 case NOTIFY_WINDOW_TRANSITION: {
                     final int transition = msg.arg1;
                     WindowInfo info = (WindowInfo) msg.obj;
                     handleNotifyWindowTranstion(transition, info);
                     break;
                 }
+
                 case NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
                     final int displayId = msg.arg1;
                     final boolean immediate = (msg.arg2 == 1);
@@ -7639,6 +7670,24 @@
                     handleNotifyRectangleOnScreenRequested(displayId, rectangle, immediate);
                     break;
                 }
+
+                case DO_DISPLAY_ADDED:
+                    synchronized (mWindowMap) {
+                        handleDisplayAddedLocked(msg.arg1);
+                    }
+                    break;
+
+                case DO_DISPLAY_REMOVED:
+                    synchronized (mWindowMap) {
+                        handleDisplayRemovedLocked(msg.arg1);
+                    }
+                    break;
+
+                case DO_DISPLAY_CHANGED:
+                    synchronized (mWindowMap) {
+                        handleDisplayChangedLocked(msg.arg1);
+                    }
+                    break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG, "handleMessage: exit");
@@ -8855,8 +8904,6 @@
         final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo();
         final int defaultDw = defaultInfo.logicalWidth;
         final int defaultDh = defaultInfo.logicalHeight;
-        final int defaultInnerDw = defaultInfo.appWidth;
-        final int defaultInnerDh = defaultInfo.appHeight;
 
         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                 ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
@@ -9430,6 +9477,7 @@
         }
     }
 
+    @Override
     public void requestTraversal() {
         synchronized (mWindowMap) {
             requestTraversalLocked();
@@ -10454,7 +10502,7 @@
 
     boolean dumpWindows(PrintWriter pw, String name, String[] args,
             int opti, boolean dumpAll) {
-        ArrayList<WindowState> windows = new ArrayList<WindowState>();
+        WindowList windows = new WindowList();
         if ("visible".equals(name)) {
             synchronized(mWindowMap) {
                 final AllWindowsIterator iterator = new AllWindowsIterator(REVERSE_ITERATOR);
@@ -10662,6 +10710,14 @@
         }
     }
 
+    public void createDisplayContent(final Display display) {
+        if (display == null) {
+            throw new IllegalArgumentException("getDisplayContent: display must not be null");
+        }
+        final DisplayContent displayContent = new DisplayContent(display);
+        mDisplayContents.put(display.getDisplayId(), displayContent);
+    }
+
     public DisplayContent getDisplayContent(final int displayId) {
         DisplayContent displayContent = mDisplayContents.get(displayId);
         if (displayContent == null) {
@@ -10769,4 +10825,40 @@
     public WindowList getWindowList(final Display display) {
         return getDisplayContent(display.getDisplayId()).getWindowList();
     }
+
+    @Override
+    public void onDisplayAdded(int displayId) {
+        mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_ADDED, displayId, 0));
+    }
+
+    private void handleDisplayAddedLocked(int displayId) {
+        createDisplayContent(mDisplayManager.getDisplay(displayId));
+    }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_REMOVED, displayId, 0));
+    }
+
+    private void handleDisplayRemovedLocked(int displayId) {
+        final DisplayContent displayContent = getDisplayContent(displayId);
+        mDisplayContents.delete(displayId);
+        WindowList windows = displayContent.getWindowList();
+        for (int i = windows.size() - 1; i >= 0; --i) {
+            final WindowState win = windows.get(i);
+            removeWindowLocked(win.mSession, win);
+        }
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) {
+        mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_CHANGED, displayId, 0));
+    }
+
+    private void handleDisplayChangedLocked(int displayId) {
+        final DisplayContent displayContent = getDisplayContent(displayId);
+        if (displayContent != null) {
+            displayContent.updateDisplayInfo();
+        }
+    }
 }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
index f0abb50..eed0ec8 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
@@ -153,6 +153,14 @@
             canvas.drawLine(10.0f, 45.0f, 20.0f, 55.0f, mSmallPaint);
             canvas.drawLine(10.0f, 60.0f, 50.0f, 60.0f, mHairLinePaint);
             canvas.restore();
+
+            canvas.save();
+            canvas.scale(10.0f, 50.0f);
+            mSmallPaint.setStrokeWidth(0.0f);
+            canvas.drawLine(20.0f, 9.0f, 30.0f, 11.0f, mSmallPaint);
+            mSmallPaint.setStrokeWidth(1.0f);
+            canvas.drawLine(30.0f, 9.0f, 40.0f, 11.0f, mSmallPaint);
+            canvas.restore();
         }
     }
 }
diff --git a/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/city.png b/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/city.png
deleted file mode 100644
index 856eeff..0000000
--- a/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/city.png
+++ /dev/null
Binary files differ
diff --git a/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/img1600x1067.jpg b/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/img1600x1067.jpg
new file mode 100644
index 0000000..05d3ee2
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/img1600x1067.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/img640x427.jpg b/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/img640x427.jpg
new file mode 100644
index 0000000..5bce392
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/img640x427.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ColorMatrix.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ColorMatrix.java
index 87a2de1..2ac40a1 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ColorMatrix.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ColorMatrix.java
@@ -33,9 +33,11 @@
     private ScriptC_colormatrix mScript;
     private ScriptIntrinsicColorMatrix mIntrinsic;
     private boolean mUseIntrinsic;
+    private boolean mUseGrey;
 
-    public ColorMatrix(boolean useIntrinsic) {
+    public ColorMatrix(boolean useIntrinsic, boolean useGrey) {
         mUseIntrinsic = useIntrinsic;
+        mUseGrey = useGrey;
     }
 
     public void createTest(android.content.res.Resources res) {
@@ -46,7 +48,11 @@
 
         if (mUseIntrinsic) {
             mIntrinsic = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS));
-            mIntrinsic.setColorMatrix(m);
+            if (mUseGrey) {
+                mIntrinsic.setGreyscale();
+            } else {
+                mIntrinsic.setColorMatrix(m);
+            }
         } else {
             mScript = new ScriptC_colormatrix(mRS, res, R.raw.colormatrix);
             mScript.invoke_setMatrix(m);
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 001dea8..37577eb 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -180,12 +180,15 @@
             mTest = new Convolve3x3(true);
             break;
         case 19:
-            mTest = new ColorMatrix(false);
+            mTest = new ColorMatrix(false, false);
             break;
         case 20:
-            mTest = new ColorMatrix(true);
+            mTest = new ColorMatrix(true, false);
             break;
         case 21:
+            mTest = new ColorMatrix(true, true);
+            break;
+        case 22:
             mTest = new Copy();
             break;
         }
@@ -200,7 +203,7 @@
     }
 
     void setupTests() {
-        mTestNames = new String[22];
+        mTestNames = new String[23];
         mTestNames[0] = "Levels Vec3 Relaxed";
         mTestNames[1] = "Levels Vec4 Relaxed";
         mTestNames[2] = "Levels Vec3 Full";
@@ -222,7 +225,8 @@
         mTestNames[18] = "Intrinsics Convolve 3x3";
         mTestNames[19] = "ColorMatrix";
         mTestNames[20] = "Intrinsics ColorMatrix";
-        mTestNames[21] = "Copy";
+        mTestNames[21] = "Intrinsics ColorMatrix Grey";
+        mTestNames[22] = "Copy";
         mTestSpinner.setAdapter(new ArrayAdapter<String>(
             this, R.layout.spinner_layout, mTestNames));
     }
@@ -243,8 +247,8 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
 
-        mBitmapIn = loadBitmap(R.drawable.city);
-        mBitmapOut = loadBitmap(R.drawable.city);
+        mBitmapIn = loadBitmap(R.drawable.img1600x1067);
+        mBitmapOut = loadBitmap(R.drawable.img1600x1067);
 
         mSurfaceView = (SurfaceView) findViewById(R.id.surface);